async def char_command(ctx: tanjun.abc.Context, characters: str, file: bool = False) -> None: """Get information about the UTF-8 characters in the executing message. Running `char file...` will ensure that the output is always sent as a markdown file. """ if len(characters) > 20: file = True content: hikari.UndefinedOr[str] content = "\n".join(_format_char_line(char, file) for char in characters) response_file: hikari.UndefinedOr[hikari.Bytes] = hikari.UNDEFINED # highly doubt this'll ever be over 1990 when file is False but better safe than sorry. if file or len(content) >= 1990: response_file = hikari.Bytes(content.encode(), "character-info.md", mimetype="text/markdown; charset=UTF-8") content = hikari.UNDEFINED else: content = content await ctx.respond(content=content or "hi there", component=utility.delete_row(ctx)) if response_file is not hikari.UNDEFINED: await ctx.edit_last_response(content=None, attachment=response_file)
async def rating(ctx): """Plot rating progression""" peak = ctx.options.peak usernames = ctx.options.usernames query = Query() if usernames == []: usernames = [query.get_handle(ctx.author.id, ctx.get_guild().id)] try: users = await asyncio.gather(*[query.get_user(username) for username in usernames]) except ObjectNotFound: return await ctx.respond("User not found") usernames = [user.username for user in users] for i in range(len(users)): if users[i] is None: return await ctx.respond(f"{usernames[i]} does not exist on DMOJ") if len(users) > 10: return await ctx.respond("Too many users given, max 10") cond = [Contest_DB.rankings.contains(user.username) for user in users] q = session.query(Contest_DB).filter(or_(*cond)).filter(Contest_DB.is_rated == 1) contests = q.all() def get_rating_change(rankings, users): ret = {} for ranking in rankings: for user in users: if user.username == ranking["user"] and ranking["new_rating"]: ret[user.username] = ranking["new_rating"] return ret data = {} data["users"] = [user.username for user in users] userPrevRating = {} for contest in contests: changes = get_rating_change(contest.rankings, users) data[contest.end_time] = [] for user in users: if user.username in changes and ( not peak or changes[user.username] >= userPrevRating.get(user.username, -9999) ): change = changes[user.username] userPrevRating[user.username] = change data[contest.end_time].append(change) else: data[contest.end_time].append(None) plot_rating(data) embed = hikari.Embed( title="Rating Progression", color=0xFCDB05, ) with open("./graphs/plot.png", "rb") as file: embed.set_image(hikari.Bytes(file.read(), "plot.png")) return await ctx.respond(embed=embed)
def _bytes_from_io( stream: io.StringIO, name: str, mimetype: typing.Optional[str] = "text/x-python;charset=utf-8" ) -> hikari.Bytes: index = stream.tell() stream.seek(0) data = stream.read() stream.seek(index) return hikari.Bytes(data, name, mimetype=mimetype)
async def factsmeme(self, ctx, *, text=None): """Generates a facts meme with given text""" if text: async with aiohttp.ClientSession() as session: data = await fetch(session, f"https://api.alexflipnote.dev/facts?text={text}") bio = BytesIO(data) bio.seek(0) await ctx.reply(attachment=hikari.Bytes(bio, "facts.png")) else: await ctx.reply("Please enter text that I can use to create a meme.")
async def floorislava(self, ctx, user=None): """Generates a facts meme with given text""" if user: if user.startswith("<@"): user = await self.bot.rest.fetch_user(await mention_converter(user)) else: await ctx.reply("You need to mention the user") return async with aiohttp.ClientSession() as session: data = await fetch(session, f"https://api.alexflipnote.dev/floor?image={user.avatar_url}&text=lava") bio = BytesIO(data) bio.seek(0) await ctx.reply(attachment=hikari.Bytes(bio, "floorislava.png")) else: async with aiohttp.ClientSession() as session: data = await fetch(session, f"https://api.alexflipnote.dev/floor?image={ctx.message.author.avatar_url}&text=lava") bio = BytesIO(data) bio.seek(0) await ctx.reply(attachment=hikari.Bytes(bio, "floorislava.png"))
def make_response(self) -> tuple[str, hikari.UndefinedOr[hikari.Bytes]]: if self.failed and self.passed: page = "Failed bans:\n" + "\n".join(f"* {user_id}: {exc}" for user_id, exc in self.failed.items()) return ( f"Successfully banned {len(self.passed)} member(s) but failed to ban {len(self.failed)} member(s)", hikari.Bytes(page.encode(), "failed_bans.md", mimetype="text/markdown;charset=UTF-8"), ) elif self.failed: page = "Failed bans:\n" + "\n".join(f"* {user_id}: {exc}" for user_id, exc in self.failed.items()) return ( f"Failed to ban {len(self.failed)} member(s)", hikari.Bytes(page.encode(), "failed_bans.md", mimetype="text/markdown;charset=UTF-8"), ) elif self.passed: return f"Successfully banned {len(self.passed)} member(s)", hikari.UNDEFINED else: return "No members were banned", hikari.UNDEFINED
async def ship(self, ctx, user1: lightbulb.member_converter=None, user2: lightbulb.member_converter=None): """Ship two users""" if user1 and user2: async with aiohttp.ClientSession() as session: data = await fetch( session, f"https://api.alexflipnote.dev/ship?user={user1.avatar_url}&user2={user2.avatar_url}" ) bio = BytesIO(data) bio.seek(0) await ctx.reply(attachment=hikari.Bytes(bio, "ship.png")) else: await ctx.reply("Please mention two users to ship")
async def solved(ctx): """Plot problems solved over time""" usernames = ctx.options.usernames query = Query() if usernames == []: usernames = [query.get_handle(ctx.author.id, ctx.get_guild().id)] try: users = await asyncio.gather(*[query.get_user(username) for username in usernames]) except ObjectNotFound: return await ctx.respond("User not found") usernames = [user.username for user in users] for i in range(len(users)): if users[i] is None: return await ctx.respond(f"{usernames[i]} does not exist on DMOJ") if len(users) > 10: return await ctx.respond("Too many users given, max 10") total_data = {} for username in usernames: q = session.query(Submission_DB).filter(Submission_DB._user == username) if q.count() == 0: await ctx.respond(f"`{username}` does not have any cached submissions, caching now") await query.get_submissions(username) q = ( session.query(func.min(Submission_DB.date)) .join(Problem_DB, Problem_DB.code == Submission_DB._code) .filter(Submission_DB._user == username) .filter(Submission_DB.points == Problem_DB.points) .group_by(Submission_DB._code) ) dates = list(map(itemgetter(0), q.all())) dates.sort() data_to_plot = {} cnt = 0 for date in dates: cnt += 1 data_to_plot[date] = cnt total_data[username] = data_to_plot plot_solved(total_data) embed = hikari.Embed( title="Problems Solved", color=0xFCDB05, ) with open("./graphs/plot.png", "rb") as file: embed.set_image(hikari.Bytes(file.read(), "plot.png")) return await ctx.respond(embed=embed)
async def supreme(self, ctx, *, text=None): """Generate Supreme logo from text""" if text: async with aiohttp.ClientSession() as session: data = await fetch( session, f"https://api.alexflipnote.dev/supreme?text={text[:20]}") bio = BytesIO(data) bio.seek(0) await ctx.reply(attachment=hikari.Bytes(bio, "supreme.png")) else: await ctx.reply( "Please enter text that I can use to create the logo.")
async def captcha(self, ctx, *, text=None): """Generate Google Captcha image from text""" if text: async with aiohttp.ClientSession() as session: data = await fetch( session, f"https://api.alexflipnote.dev/captcha?text={text[:500]}") bio = BytesIO(data) bio.seek(0) await ctx.reply(attachment=hikari.Bytes(bio, "captcha.png")) else: await ctx.reply( "Please enter text that I can use to create the image.")
async def pornhub(self, ctx, text1=None, text2="Hub"): """Generates a pornhub logo with given text""" if text1: async with aiohttp.ClientSession() as session: data = await fetch( session, f"https://api.alexflipnote.dev/pornhub?text={text1}&text2={text2}" ) bio = BytesIO(data) bio.seek(0) await ctx.reply(attachment=hikari.Bytes(bio, "phub.png")) else: await ctx.reply( "Please enter text that I can use to create the logo.")
async def challenge(self, ctx, *, text=None): """Generate Minecraft challenge from text""" if text: async with aiohttp.ClientSession() as session: data = await fetch( session, f"https://api.alexflipnote.dev/challenge?text={text[:50]}&icon={random.randint(1,45)}" ) bio = BytesIO(data) bio.seek(0) await ctx.reply(attachment=hikari.Bytes(bio, "challenge.png")) else: await ctx.reply( "Please enter text that I can use to create the image.")
async def salty(self, ctx, *, image_url=None): """Generates a salty image from given image""" if image_url: async with aiohttp.ClientSession() as session: data = await fetch( session, f"https://api.alexflipnote.dev/salty?image={image_url}") bio = BytesIO(data) bio.seek(0) await ctx.reply(attachment=hikari.Bytes(bio, "salty.png")) else: await ctx.reply( "Please enter raw image url that I can use to create the image." )
async def colorify(self, ctx, image_url=None): """Generates colourified image from raw image""" if image_url == None: await ctx.reply("Please enter image url") return if not image_url.startswith("http"): await ctx.reply("Invalid image url") hexcode = "#fa391b" async with aiohttp.ClientSession() as session: data = await fetch( session, f"https://api.alexflipnote.dev/colourify?image={image_url}&b={hexcode if hexcode.startswith('#') else '#' + hexcode}" ) bio = BytesIO(data) bio.seek(0) await ctx.reply(attachment=hikari.Bytes(bio, "colorified.png"))
async def on_error(event: lightbulb.CommandErrorEvent) -> None: try: logger.warning("Error handling: raised %s", event) if isinstance(event.exception, lightbulb.NotEnoughArguments): return await event.context.respond( f"Not enough arguments ({event.exception})") if isinstance(event.exception, lightbulb.CommandInvocationError): if isinstance(event.exception.original, hikari.ForbiddenError): return await event.context.respond( f"I do not have permissions to do this ({event.exception.original.message})" ) if isinstance(event.exception, lightbulb.CommandNotFound): return await event.context.respond( f"Where command? ({event.exception})") if isinstance(event.exception, lightbulb.errors.MissingRequiredRole): return await event.context.respond( f"Missing required roles ({event.exception})") if isinstance(event.exception, lightbulb.errors.NotOwner): return await event.context.respond( f"You are not the owner of this bot!") trace = "".join( traceback.format_exception(None, event.exception, event.exception.__traceback__)) await event.context.respond( """Unhandled exception ({0}) Backtrace:""".format(event.exception), attachment=hikari.Bytes(trace, "traceback.txt"), ) except Exception as e: logger.critical( "Error handling raised error: was handling %s, raised %s", event, e) await event.context.respond("Crtitical error encountered, check logs")
for n in range(1, 3) if (s := str(n))}, } style = style_map.get(args.style, "2") async with self.bot.rest.trigger_typing(ctx.channel_id): with BytesIO() as fp: await self.spotify_client( fp, data, args.hidden, args.color, style, ) kwargs: typing.Dict[str, typing.Any] = { "attachment": hikari.Bytes(fp, f"{data}-card.png") } # if random.randint(0, 101) < 25: # kwargs["content"] = ( # kwargs.get("content") or "" # ) + "\n\nHave you tried the slash version of this command?" await ctx.respond(**kwargs) # pylint: disable=no-self-use @utils.checks.require_env(*_spotify_vars) @core.commands.group() @core.cooldown(1, 2, lightbulb.cooldowns.UserBucket) async def spotify(self, ctx: Context) -> None: """Contains subcommands that utilizes Spotify API."""
async def eval_command( ctx: tanjun.abc.MessageContext, component: alluka.Injected[tanjun.abc.Component], component_client: alluka.Injected[yuyo.ComponentClient], file_output: bool = False, # ephemeral_response: bool = False, suppress_response: bool = False, ) -> None: """Dynamically evaluate a script in the bot's environment. This can only be used by the bot's owner. Arguments: * code: Greedy multi-line string argument of the code to execute. This should be in a code block. * suppress_response (-s, --suppress): Whether to suppress this command's confirmation response. This defaults to false and will be set to true if no value is provided. """ assert ctx.message.content is not None # This shouldn't ever be the case in a command client. code = re.findall(r"```(?:[\w]*\n?)([\s\S(^\\`{3})]*?)\n*```", ctx.message.content) if not code: raise tanjun.CommandError("Expected a python code block.") if suppress_response: await eval_python_code_no_capture(ctx, component, "<string>", code[0]) return stdout, stderr, exec_time, failed = await eval_python_code( ctx, component, code[0]) if file_output: await ctx.respond( attachments=[ hikari.Bytes(stdout, "stdout.py", mimetype="text/x-python;charset=utf-8"), hikari.Bytes(stderr, "stderr.py", mimetype="text/x-python;charset=utf-8"), ], component=utility.delete_row(ctx), ) return colour = utility.FAILED_COLOUR if failed else utility.PASS_COLOUR string_paginator = yuyo.sync_paginate_string(_yields_results( stdout, stderr), wrapper="```python\n{}\n```", char_limit=2034) embed_generator = (( hikari.UNDEFINED, hikari.Embed(colour=colour, description=text, title=f"Eval page {page}").set_footer( text=f"Time taken: {exec_time} ms"), ) for text, page in string_paginator) paginator = yuyo.ComponentPaginator( embed_generator, authors=[ctx.author.id], triggers=( yuyo.pagination.LEFT_DOUBLE_TRIANGLE, yuyo.pagination.LEFT_TRIANGLE, yuyo.pagination.STOP_SQUARE, yuyo.pagination.RIGHT_TRIANGLE, yuyo.pagination.RIGHT_DOUBLE_TRIANGLE, ), timeout=datetime.timedelta( days=99999), # TODO: switch to passing None here ) first_response = await paginator.get_next_entry() executor = utility.paginator_with_to_file( ctx, paginator, make_files=lambda: [ _bytes_from_io(stdout, "stdout.py"), _bytes_from_io(stderr, "stderr.py") ]) assert first_response is not None content, embed = first_response message = await ctx.respond(content=content, embed=embed, components=executor.builders, ensure_result=True) component_client.set_executor(message, executor)
yuyo.pagination.STOP_SQUARE, yuyo.pagination.RIGHT_TRIANGLE, yuyo.pagination.RIGHT_DOUBLE_TRIANGLE, ), timeout=datetime.timedelta( days=500), # TODO: switch to passing None here once its supported ) first_response = await paginator.get_next_entry() assert first_response content, embed = first_response executor = utility.paginator_with_to_file( ctx, paginator, make_files=lambda: [hikari.Bytes(data["lyrics"], f"{title} lyrics.txt")]) message = await ctx.respond(content=content, embed=embed, components=executor.builders, ensure_result=True) component_client.set_executor(message, executor) _T = typing.TypeVar("_T") def _assert_in_choices( choices: typing.Collection[_T]) -> typing.Callable[[_T], _T]: def verify(value: _T) -> _T: if value in choices: return value
async def type(ctx): """Graph problems solved by popular problem types""" # TODO: This is aids, pls fix usernames = ctx.options.usernames graph_type = ctx.options.graph_type as_percent = ctx.options.as_percent query = Query() if usernames == []: usernames = [query.get_handle(ctx.author.id, ctx.get_guild().id)] try: users = await asyncio.gather(*[query.get_user(username) for username in usernames]) except ObjectNotFound: return await ctx.respond("User not found") for i in range(len(users)): if users[i] is None: return await ctx.respond(f"{usernames[i]} does not exist on DMOJ") if len(users) > 6: return await ctx.respond("Too many users given, max 6") usernames = [data.username for data in users] important_types = [ ["Data Structures"], ["Dynamic Programming"], ["Graph Theory"], ["String Algorithms"], ["Advanced Math", "Geometry", "Intermediate Math", "Simple Math"], ["Ad Hoc"], ["Greedy Algorithms"], ] labels = [ "Data Structures", "Dynamic Programming", "Graph Theory", "String Algorithms", "Math", "Ad Hoc", "Greedy Algorithms", ] data = {} data["group"] = [] for label in labels: data[label] = [] for username in usernames: data["group"].append(username) def calculate_partial_points(points: int): p = 0 for i in range(min(100, len(points))): p += (0.95**i) * points[i] return p max_percentage = 0 for username in usernames: q = session.query(Submission_DB).filter(Submission_DB._user == username) if q.count() == 0: await ctx.respond(f"`{username}` does not have any cached submissions, caching now") await query.get_submissions(username) for i, types in enumerate(important_types): total_problems = await query.get_problems(_type=types, cached=True) total_points = list(map(attrgetter("points"), total_problems)) total_points.sort(reverse=True) total_points = calculate_partial_points(total_points) for username in usernames: points = query.get_attempted_problems(username, types) points.sort(reverse=True) points = calculate_partial_points(points) if as_percent: percentage = 100 * points / total_points else: percentage = points max_percentage = max(max_percentage, percentage) data[labels[i]].append(percentage) logger.debug("plot type data: %s", data) if graph_type == "radar": plot_type_radar(data, as_percent, max_percentage) elif graph_type == "bar": plot_type_bar(data, as_percent) embed = hikari.Embed( title="Problem types solved", color=0xFCDB05, ) with open("./graphs/plot.png", "rb") as file: embed.set_image(hikari.Bytes(file.read(), "plot.png")) return await ctx.respond(embed=embed)
async def points(ctx): """Plot point progression""" usernames = ctx.options.usernames query = Query() if usernames == []: usernames = [query.get_handle(ctx.author.id, ctx.get_guild().id)] try: users = await asyncio.gather(*[query.get_user(username) for username in usernames]) except ObjectNotFound: return await ctx.respond("User not found") usernames = [user.username for user in users] for i in range(len(users)): if users[i] is None: return await ctx.respond(f"{usernames[i]} does not exist on DMOJ") if len(users) > 10: return await ctx.respond("Too many users given, max 10") total_data = {} for username in usernames: q = ( session.query(Submission_DB) .options(orm.joinedload("problem")) .join(User_DB, User_DB.username == Submission_DB._user, aliased=True) .filter(User_DB.username == username) .order_by(Submission_DB.date) ) submissions = q.all() if len(submissions) == 0: await ctx.respond(f"`{username}` does not have any cached submissions, caching now") await query.get_submissions(username) submissions = q.all() problems_ACed = dict() code_to_points = dict() points_arr = [] data_to_plot = {} # O(N^2logN) :blobcreep: for submission in submissions: code = submission.problem[0].code points = submission.points result = submission.result if points is not None: if result == "AC": problems_ACed[code] = 1 if code not in code_to_points: # log N search, N insert code_to_points[code] = points bisect.insort(points_arr, points) elif points > code_to_points[code]: # N remove, log N search, N insert points_arr.remove(code_to_points[code]) code_to_points[code] = points bisect.insort(points_arr, points) cur_points = calculate_points(points_arr[::-1], len(problems_ACed)) data_to_plot[submission.date] = cur_points total_data[username] = data_to_plot plot_points(total_data) embed = hikari.Embed( title="Problems Progression", color=0xFCDB05, ) with open("./graphs/plot.png", "rb") as file: embed.set_image(hikari.Bytes(file.read(), "plot.png")) return await ctx.respond(embed=embed)
async def _docs_command( ctx: tanjun.abc.Context, component_client: yuyo.ComponentClient, index: DocIndex, base_url: str, docs_url: str, name: str, path: str | None, public: bool, desc_splitter: str = "\n", **kwargs: typing.Any, ) -> None: if not path: await ctx.respond(base_url, component=utility.delete_row(ctx)) return if kwargs["list"]: iterator = utility.embed_iterator( utility.chunk((f"[{m.fullname}]({index.make_link(docs_url, m)})" for m in index.search(path)), 10), lambda entries: "\n".join(entries), title=f"{name} Documentation", url=docs_url, ) paginator = yuyo.ComponentPaginator( iterator, authors=(ctx.author,) if not public else (), triggers=( yuyo.pagination.LEFT_DOUBLE_TRIANGLE, yuyo.pagination.LEFT_TRIANGLE, yuyo.pagination.STOP_SQUARE, yuyo.pagination.RIGHT_TRIANGLE, yuyo.pagination.RIGHT_DOUBLE_TRIANGLE, ), timeout=datetime.timedelta(days=99999), # TODO: switch to passing None here ) executor = utility.paginator_with_to_file( ctx, paginator, make_files=lambda: [hikari.Bytes("\n".join(m.fullname for m in index.search(str(path))), "results.txt")], ) components = executor.builders else: iterator = ( ( hikari.UNDEFINED, hikari.Embed( description=_form_description(metadata, desc_splitter=desc_splitter), color=utility.embed_colour(), title=metadata.fullname, url=index.make_link(docs_url, metadata), ), ) for metadata in index.search(path) ) executor = paginator = yuyo.ComponentPaginator( iterator, authors=(ctx.author,) if not public else (), triggers=( yuyo.pagination.LEFT_DOUBLE_TRIANGLE, yuyo.pagination.LEFT_TRIANGLE, yuyo.pagination.STOP_SQUARE, yuyo.pagination.RIGHT_TRIANGLE, yuyo.pagination.RIGHT_DOUBLE_TRIANGLE, ), ) components = executor.builder() if first_response := await paginator.get_next_entry(): content, embed = first_response message = await ctx.respond(content=content, components=components, embed=embed, ensure_result=True) component_client.set_executor(message, executor) return
authors=(ctx.author, ) if not public else (), triggers=( yuyo.pagination.LEFT_DOUBLE_TRIANGLE, yuyo.pagination.LEFT_TRIANGLE, yuyo.pagination.STOP_SQUARE, yuyo.pagination.RIGHT_TRIANGLE, yuyo.pagination.RIGHT_DOUBLE_TRIANGLE, ), timeout=datetime.timedelta( days=99999), # TODO: switch to passing None here ) executor = utility.paginator_with_to_file( ctx, paginator, make_files=lambda: [hikari.Bytes("\n".join(uses), "results.txt")]) first_response = await paginator.get_next_entry() assert first_response content, embed = first_response message = await ctx.respond(content=content, components=executor.builders, embed=embed, ensure_result=True) component_client.set_executor(message, executor) @dataclasses.dataclass(frozen=True, slots=True) class _IndexAutocomplete: index: ReferenceIndex