This commit is contained in:
2024-10-31 22:38:59 +01:00
parent 2f4e606fbf
commit e71ba34371
12 changed files with 304 additions and 35 deletions

View File

@ -1,5 +1,6 @@
"""A collection of all Gwendolyn functions."""
__all__ = ["Other"]
__all__ = ["Other","BetterNetflix", "Sonarr", "Radarr", "TMDb"]
from .other import Other
from .other import Other
from .better_netflix import Radarr, Sonarr, BetterNetflix, TMDb

View File

@ -0,0 +1,5 @@
"""Better Netflix functions for Gwendolyn."""
__all__ = ["BetterNetflix", "Sonarr", "Radarr", "TMDb"]
from .better_netflix import BetterNetflix, Sonarr, Radarr, TMDb

View File

@ -0,0 +1,159 @@
from requests import get
from random import choice
import io
from PIL import Image
from interactions import SlashContext, Embed, EmbedFooter, EmbedAttachment, File
from gwendolyn.exceptions import CannotConnectToService
class Service():
def __init__(self, ip: str, port: str, api_key: str) -> None:
self.ip = ip
self.port = port
self.header = {
"accept": "application/json",
"X-Api-Key": api_key
}
def test(self):
return get(f"http://{self.ip}:{self.port}/api", headers=self.header).ok
class Radarr(Service):
def movies(self):
response = get(f"http://{self.ip}:{self.port}/api/v3/movie", headers=self.header)
if not response.ok:
return []
return [m for m in response.json() if m["hasFile"]]
class Sonarr(Service):
def shows(self):
response = get(f"http://{self.ip}:{self.port}/api/v3/series", headers=self.header)
if not response.ok:
return []
return response.json()
class TMDb():
def __init__(self, api_token: str) -> None:
self.header = {
"accept": "application/json",
"Authorization": "Bearer "+api_token
}
def test(self):
return get(f"https://api.themoviedb.org/3/account", headers=self.header).ok
class BetterNetflix():
def __init__(self, radarr: Radarr, sonarr: Sonarr, tmdb: TMDb, bot) -> None:
self.radarr = radarr
self.sonarr = sonarr
self.tmdb = tmdb
self.bot = bot
services = ["radarr","sonarr","tmdb"]
for service in services:
if getattr(self,service).test():
self.bot.log(f"Connected to {service}")
else:
raise CannotConnectToService(service)
def _poster_color(self, poster_url: str):
response = get(poster_url, stream=True)
img = Image.open(io.BytesIO(response.content))
img.thumbnail((150, 100))
# Reduce colors (uses k-means internally)
paletted = img.convert('P', palette=Image.ADAPTIVE, colors=6)
buf = io.BytesIO()
img2 = paletted.convert("RGB")
img2.save("gwendolyn/resources/temp.jpg")
# Find the color that occurs most often
palette = paletted.getpalette()
color_counts = sorted(paletted.getcolors(), reverse=True)
palette_index = color_counts[0][1]
dominant_color = palette[palette_index*3:palette_index*3+3]
return dominant_color
async def movie(self, ctx: SlashContext):
msg = await ctx.send("Finding a random movie...")
movies = self.radarr.movies()
if len(movies) == 0:
await msg.edit(content="Unable to find any movies")
return
picked_movie = choice(movies)
description = ""
if "imdb" in picked_movie['ratings']:
description += f"<:imdb:1301506320603676782> {picked_movie['ratings']['imdb']['value']}"
description += "\u1CBC\u1CBC"
if "rottenTomatoes" in picked_movie['ratings']:
rt_value = picked_movie['ratings']['rottenTomatoes']['value']
rt_icon = "<:fresh:1301509701338660894>" if rt_value >= 60 else "<:rotten:1301509685907820607>"
description += f"{rt_icon} {rt_value}%"
if description != "":
description += "\n\n"
year = EmbedFooter(str(picked_movie["year"])) if "year" in picked_movie else None
has_poster = len(picked_movie["images"]) > 0 and "remoteUrl" in picked_movie["images"][0]
poster = EmbedAttachment(picked_movie["images"][0]["remoteUrl"]) if has_poster else None
color = self._poster_color(picked_movie["images"][0]["remoteUrl"]) if has_poster else "#ff0000"
description += picked_movie["overview"]
embed = Embed(
picked_movie["title"],
description,
color,
thumbnail=poster,
footer=year
)
await msg.edit(content="",embed=embed)
async def show(self, ctx: SlashContext):
msg = await ctx.send("Finding a random show...")
shows = self.sonarr.shows()
if len(shows) == 0:
await msg.edit(content="Unable to find any shows")
return
picked_show = choice(shows)
description = ""
if "imdb" in picked_show['ratings']:
description += f"<:imdb:1301506320603676782> {picked_show['ratings']['imdb']['value']}"
description += "\u1CBC\u1CBC"
if "rottenTomatoes" in picked_show['ratings']:
rt_value = picked_show['ratings']['rottenTomatoes']['value']
rt_icon = "<:fresh:1301509701338660894>" if rt_value >= 60 else "<:rotten:1301509685907820607>"
description += f"{rt_icon} {rt_value}%"
if description != "":
description += "\n\n"
year = EmbedFooter(str(picked_show["year"])) if "year" in picked_show else None
images = {i["coverType"]:i for i in picked_show["images"]}
has_poster = "poster" in images and "remoteUrl" in images["poster"]
poster = EmbedAttachment(images["poster"]["remoteUrl"]) if has_poster else None
color = self._poster_color(images["poster"]["remoteUrl"]) if has_poster else "#ff0000"
description += picked_show["overview"]
embed = Embed(
picked_show["title"],
description,
color,
thumbnail=poster,
footer=year
)
await msg.edit(content="",embed=embed)

View File

@ -357,8 +357,14 @@ class ComparePoint(Exp):
return None
if self.comp_op == "=":
return 1 if val == r else 0
if self.comp_op == "<":
return 1 if val < r else 0
if self.comp_op == "<=":
return 1 if val <= r else 0
if self.comp_op == ">":
return 1 if val <= r else 0
if self.comp_op == ">=":
return 1 if val <= r else 0
else:
raise Exception(f"Unknown binop {self.op}")
@ -451,11 +457,12 @@ class Roll(Exp):
self.die_type = r2
self.result = [randint(1,r2) for _ in range(r1)]
self.show_list = [str(i) for i in self.result]
return self.result
def _show(self, vtable):
result = self.eval(vtable)
return [str(i) for i in result]
self.eval(vtable)
return self.show_list
@property
def die(self) -> int:
@ -505,10 +512,6 @@ class RollKeepHighest(Roll):
self.show_list = [str(n) if i in max_indices else f"~~{n}~~" for i, n in enumerate(r1)]
return self.result
def _show(self, vtable):
self.eval(vtable)
return self.show_list
def __repr__(self) -> str:
return f"kep_highest({self.roll},{self.exp})"
@ -548,10 +551,6 @@ class RollKeepLowest(Roll):
self.show_list = [str(n) if i in min_indices else f"~~{n}~~" for i, n in enumerate(r1)]
return self.result
def _show(self, vtable):
self.eval(vtable)
return self.show_list
def __repr__(self) -> str:
return f"kep_lowest({self.roll},{self.exp})"
@ -591,10 +590,6 @@ class RollMin(Roll):
self.result = r1
return self.result
def _show(self, vtable):
self.eval(vtable)
return self.show_list
def __repr__(self) -> str:
return f"min({self.roll},{self.exp})"
@ -634,10 +629,6 @@ class RollMax(Roll):
self.result = r1
return self.result
def _show(self, vtable):
self.eval(vtable)
return self.show_list
def __repr__(self) -> str:
return f"max({self.roll},{self.exp})"
@ -687,9 +678,53 @@ class RollExplode(Roll):
return self.result
def _show(self, vtable):
self.eval(vtable)
return self.show_list
def __repr__(self) -> str:
return f"max({self.roll},{self.exp})"
def __eq__(self, other: Exp) -> bool:
return (
isinstance(other, RollMax) and
self.roll == other.roll and
self.exp == other.exp
)
class RollReroll(Roll):
def __init__(self, roll: Roll, comp: ComparePoint = None, once: bool = False):
self.roll = roll
self.comp = comp
self.once = once
self.result = None
self.show_list = None
def _eval(self, vtable):
if self.result is not None:
return self.result
r1 = self.roll.eval(vtable)
if not (isinstance(r1,list) and all(isinstance(i,int) for i in r1)):
return []
d = self.die
if self.comp is None:
self.comp = ComparePoint("=", ExpInt(1))
self.result = []
self.show_list = []
def compare(n, rerolled):
if self.comp.eval(vtable, n) and not (rerolled and self.once):
self.show_list.append(f"~~{n}~~")
compare(randint(1,d), True)
else:
self.result.append(n)
self.show_list.append(str(n))
for n in r1:
compare(n, False)
return self.result
def __repr__(self) -> str:
return f"max({self.roll},{self.exp})"

View File

@ -2,18 +2,22 @@
from string import ascii_letters, digits
from rply import LexerGenerator
VALID_CHARACTERS = ascii_letters[:3]+ascii_letters[4:]+"_"+digits
VALID_CHARACTERS = ascii_letters+"_"+digits
TOKENS = [
("ROLL_DIE", r"d"),
("ROLL_KEEP_HIGHEST", r"kh"),
("ROLL_KEEP_LOWEST", r"kl"),
("ROLL_KEEP_HIGHEST", r"kh?"),
("ROLL_REROLL_ONCE", r"ro"),
("ROLL_REROLL", r"r"),
("ROLL_MIN", r"min"),
("ROLL_MAX", r"max"),
("ROLL_EXPLODE", r"!"),
("SYMBOL_LE", r"\<\="),
# ("SYMBOL_GE", r"\>\="),
("SYMBOL_GE", r"\>\="),
("SYMBOL_LT", r"\<"),
("SYMBOL_GT", r"\>"),
("SYMBOL_EQUALS", r"\="),
("SYMBOL_ARROW", r"\-\>"),
("SYMBOL_BACKSLASH", r"\\"),

View File

@ -1,7 +1,7 @@
from rply import ParserGenerator
from lexer import TOKENS
from ast_nodes import Exp, ExpInt, ExpBinop, ExpLet, ExpVar, ExpMin, ExpMax, ExpRoll, Roll, RollKeepHighest, RollKeepLowest, RollMin, RollMax, RollExplode, ComparePoint, ExpIf, ExpTest, ExpApply, ExpLambda, ExpNeg
from ast_nodes import Exp, ExpInt, ExpBinop, ExpLet, ExpVar, ExpMin, ExpMax, ExpRoll, Roll, RollKeepHighest, RollKeepLowest, RollMin, RollMax, RollExplode, ComparePoint, ExpIf, ExpTest, ExpApply, ExpLambda, ExpNeg, RollReroll
class Parser():
@ -11,11 +11,11 @@ class Parser():
precedence=[
('left', ["SYMBOL_BACKSLASH","SYMBOL_ARROW"]),
('left', ["KEYWORD_LET", "KEYWORD_IN", "KEYWORD_IF", "KEYWORD_THEN", "KEYWORD_ELSE"]),
('left', ["SYMBOL_EQUALS", "SYMBOL_LE"]),
('left', ["SYMBOL_EQUALS", "SYMBOL_LT", "SYMBOL_LE", "SYMBOL_GT", "SYMBOL_GE"]),
('left', ["SYMBOL_PLUS", "SYMBOL_MINUS"]),
('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE"]),
('left', ["ROLL_DIE"]),
('right', ["ROLL_KEEP_HIGHEST","ROLL_KEEP_LOWEST","ROLL_MIN","ROLL_MAX","ROLL_EXPLODE"])
('right', ["ROLL_KEEP_HIGHEST","ROLL_KEEP_LOWEST","ROLL_MIN","ROLL_MAX","ROLL_EXPLODE","ROLL_REROLL","ROLL_REROLL_ONCE"])
]
)
self._get_parser()
@ -97,6 +97,22 @@ class Parser():
def roll_explode_comp(tokens):
return RollExplode(tokens[0], tokens[2])
@self.pg.production('roll : roll ROLL_REROLL ')
def roll_reroll(tokens):
return RollReroll(tokens[0])
@self.pg.production('roll : roll ROLL_REROLL comp')
def roll_reroll_comp(tokens):
return RollReroll(tokens[0], tokens[2])
@self.pg.production('roll : roll ROLL_REROLL_ONCE')
def roll_reroll_once(tokens):
return RollReroll(tokens[0], None, True)
@self.pg.production('roll : roll ROLL_REROLL_ONCE comp')
def roll_reroll_once_comp(tokens):
return RollReroll(tokens[0], tokens[2], True)
@self.pg.production('roll : atom ROLL_DIE atom')
def roll(tokens):
return Roll(tokens[0], tokens[2])
@ -108,7 +124,10 @@ class Parser():
# Compare Points
@self.pg.production("comp : SYMBOL_EQUALS atom")
@self.pg.production("comp : SYMBOL_LT atom")
@self.pg.production("comp : SYMBOL_LE atom")
@self.pg.production("comp : SYMBOL_GT atom")
@self.pg.production("comp : SYMBOL_GE atom")
def comp_point(tokens):
return ComparePoint(tokens[0].value,tokens[1])