async def unban(ctx: MrvnCommandContext, member: Member): try: await member.unban() except Forbidden: await ctx.respond_embed(Style.ERROR, ctx.translate("moderation_bot_insufficient_perms")) else: await ctx.respond_embed(Style.OK, ctx.format("moderation_command_unban_successful", member.mention))
async def add(ctx: MrvnCommandContext, status: Option(str, choices=[ OptionChoice("Online", "online"), OptionChoice("Invisible", "invisible"), OptionChoice("Idle", "idle"), OptionChoice("Do Not Disturb", "dnd") ]), activity: Option(str, choices=[ OptionChoice("Playing", "playing"), OptionChoice("Streaming", "streaming"), OptionChoice("Listening", "listening"), OptionChoice("Watching", "watching"), OptionChoice("Competing", "competing") ]), text: ParseUntilEndsOption(str)): if len(await BotStatusEntry.filter()) == ENTRIES_LIMIT: await ctx.respond_embed(Style.ERROR, ctx.translate("bot_status_command_add_limit_reached")) return elif len(text) > 50: await ctx.respond_embed(Style.ERROR, ctx.translate("bot_status_command_add_text_too_long")) return await BotStatusEntry.create(status=status, activity=activity.lower(), text=text) status_update.start_task() await ctx.respond_embed(Style.OK, ctx.translate("bot_status_command_add_status_added"))
async def shell(ctx: MrvnCommandContext, command: ParseUntilEndsOption(str)): if "shutdown" in command.lower() or "restart" in command.lower(): await ctx.respond_embed( Style.ERROR, ctx.translate("execute_command_shell_prohibited_word")) return await ctx.defer() encoding = "cp866" if platform.system() == "Windows" else "utf-8" try: result = subprocess.check_output(command, shell=True, timeout=5, stderr=subprocess.STDOUT) except subprocess.TimeoutExpired: await ctx.respond_embed( Style.WARN, ctx.translate("execute_command_shell_executed_timeout")) except subprocess.CalledProcessError as ex: await ctx.respond_embed(Style.ERROR, ex.output.decode(encoding), ctx.translate("execute_command_shell_error")) else: await ctx.respond_embed(Style.OK, result.decode(encoding), ctx.translate("execute_command_shell_ok"))
async def cmds(ctx: MrvnCommandContext): cat_commands = {} for cat in categories.categories: cat_commands[cat] = runtime.bot.get_category_commands(cat, ctx.guild_id) cat_commands = {k: v for k, v in sorted(cat_commands.items(), key=lambda item: len(item[1]), reverse=True)} items = [Button(label=f"{ctx.translate(cat.name)} ({len(items)})", disabled=not len(items), style=ButtonStyle.blurple if len(items) else ButtonStyle.gray) for cat, items in cat_commands.items()] view = CategoryView(ctx, items, author=ctx.author, timeout=10) message = await ctx.respond(ctx.translate("std_command_help_choose_category"), view=view) await view.wait() if view.category_len is None: return category, items = list(cat_commands.items())[view.category_len] count = len(items) num_pages = math.ceil(count / PAGE_SIZE) if count > PAGE_SIZE else 1 paginator = CmdsPaginator(ctx, items, ctx.translate(category.name), num_pages=num_pages, timeout=30, original_author=ctx.author, guild=ctx.guild) await paginator.attach(message)
async def wiki(ctx: MrvnCommandContext, query: ParseUntilEndsOption(str)): await ctx.defer() session = ClientSession(timeout=ClientTimeout(20)) params = {"action": "query", "format": "json", "list": "search", "srsearch": query} try: response = await session.get("https://ru.wikipedia.org/w/api.php", params=params) except (asyncio.TimeoutError, ClientConnectionError): await ctx.respond_embed(Style.ERROR, ctx.translate("search_connection_error")) return data = await response.json() await session.close() response.close() items = [x["title"] for x in data["query"]["search"]] if not len(items): await ctx.respond_embed(Style.ERROR, ctx.format("search_command_not_found", query)) return paginator = WikiPaginator("ru", tr=ctx, author=ctx.author, original_author=ctx.author, timeout=30, pages=items, guild=ctx.guild) await paginator.respond_ctx(ctx)
async def unmute(ctx: MrvnCommandContext, member: Member): try: await member.edit(communication_disabled_until=None) except Forbidden: await ctx.respond_embed( Style.ERROR, ctx.translate("moderation_bot_insufficient_perms")) else: await ctx.respond_embed( Style.OK, ctx.format("moderation_command_unmute_successful", member.mention))
async def send_command_exception_message(ctx: MrvnCommandContext, exc): logger.error(traceback.format_exc()) await ctx.respond_embed( Style.ERROR, ctx.format( "mrvn_api_command_execution_error_desc", "".join( traceback.format_exception(value=exc, etype=type(exc), tb=exc.__traceback__))), ctx.translate("mrvn_api_command_execution_error_title"))
async def set_prefix(ctx: MrvnCommandContext, command: command_option, prefix: str): if prefix not in PREFIX_LIST: await ctx.respond_embed(Style.ERROR, ctx.format("std_command_override_prefix_not_in_list", " ".join(PREFIX_LIST))) return override = (await CommandOverride.get_or_create(guild_id=ctx.guild_id, command_name=command.name))[0] override.prefix = prefix await override.save() await ctx.respond_embed(Style.OK, ctx.format("std_command_override_prefix_changed", command.name))
async def list_(ctx: MrvnCommandContext, global_setting: bool): items = [] for cat in settings.categories: cat_settings = cat.get_settings(global_setting) items.append( Button(label=ctx.translate(cat.name), style=ButtonStyle.blurple if len(cat_settings) else ButtonStyle.gray, disabled=not len(cat_settings))) view = CategoryView(ctx, items, author=ctx.author, timeout=10) message = await ctx.respond( ctx.translate("std_command_settings_choose_category"), view=view) await view.wait() if view.category_len is None: return category = settings.categories[view.category_len] category_settings = category.get_settings(global_setting) count = len(category_settings) num_pages = math.ceil(count / PAGE_SIZE) if count > PAGE_SIZE else 1 settings_list = [] for setting in category_settings: if global_setting: value = (await setting.get_or_create())[0].value else: value = (await setting.get_or_create(guild_id=ctx.guild_id))[0].value settings_list.append( (f"{setting.key} [{value}]", ctx.translate(setting.description))) paginator = CmdsPaginator(ctx, settings_list, ctx.translate(category.name), is_global=global_setting, num_pages=num_pages, timeout=30, original_author=ctx.author, guild=ctx.guild) await paginator.attach(message)
async def process_dm(self, ctx: MrvnCommandContext): allow_dms = (await SettingAllowCommandsInDMs.get_or_create())[0].value if not allow_dms: await ctx.respond_embed( Style.ERROR, ctx.translate("mrvn_core_dm_commands_disabled")) return True guild_only = self.is_guild_only(ctx.command) if guild_only: await ctx.respond_embed( Style.ERROR, ctx.translate("mrvn_core_command_is_guild_only")) return guild_only
async def purge(ctx: MrvnCommandContext, number: Option(int, min_value=1, max_value=200), member: Member = None): try: deleted = await ctx.channel.purge( limit=number, check=lambda msg: member is None or msg.author == member, bulk=True) except Forbidden: await ctx.respond_embed( Style.ERROR, ctx.translate("moderation_bot_insufficient_perms")) else: await ctx.respond_embed( Style.OK, ctx.format("moderation_command_purge_messages_removed", ctx.author.mention, deleted))
async def convert(converter, ctx: MrvnCommandContext, argument: str) -> Union[SlashCommand, SlashCommandGroup]: try: command = next(filter(lambda it: it.name == argument, runtime.bot.application_commands)) except StopIteration: raise ArgumentParseException(ctx.translate("std_command_override_command_not_found")) return command
async def glados(ctx: MrvnCommandContext, text: ParseUntilEndsOption(str)): await ctx.defer() data = {"text": text, "character": "GLaDOS", "emotion": "Contextual"} session = ClientSession(timeout=ClientTimeout(20)) try: response = await session.post(TTS_URL, headers=TTS_HEADERS, json=data) except (asyncio.TimeoutError, ClientConnectionError): await ctx.respond_embed( Style.ERROR, ctx.translate("fun_stuff_command_glados_request_error")) return if response.status != 200: await ctx.respond_embed( Style.ERROR, ctx.translate("fun_stuff_command_glados_api_error")) return response_data = await response.json(content_type=None) response.close() result_url = AUDIO_URL + response_data["wavNames"][0] try: response = await session.get(result_url, headers=TTS_HEADERS) except (asyncio.TimeoutError, ClientConnectionError): await ctx.respond_embed( Style.ERROR, ctx.translate("fun_stuff_command_glados_download_error")) return buf = BytesIO(await response.read()) await session.close() response.close() await ctx.respond(file=File( buf, filename= f"GLaDOS_{ctx.message.id if not ctx.interaction else ctx.interaction.id}.wav" ))
async def vision(ctx: MrvnCommandContext, image: Option(Attachment)): if not image.content_type.startswith("image"): await ctx.respond_embed( Style.ERROR, ctx.translate("vision_command_vision_invalid_content_type")) return await ctx.defer() session = aiohttp.ClientSession(timeout=ClientTimeout(20)) try: response = await session.post( SERVICE_URL, data="userlink=%s" % parse.quote(image.url, safe=''), headers={ "Cookie": "textonly=true; imageonly=true; qronly=false", "Content-Type": "application/x-www-form-urlencoded" }) except (aiohttp.ClientConnectionError, asyncio.TimeoutError): await ctx.respond_embed( Style.ERROR, ctx.translate("vision_command_vision_connection_error")) return finally: await session.close() text = await response.text() response.close() soup = BeautifulSoup(text, "html.parser") results = soup.find_all("div", {"class": "success description"}) if not len(results): await ctx.respond_embed(Style.ERROR, ctx.translate("vision_command_vision_fail")) return embed = ctx.get_embed(Style.INFO, results[0].text, ctx.translate("vision_command_vision_title")) embed.set_image(url=image.url) await ctx.respond(embed=embed)
async def prefix_disable(ctx: MrvnCommandContext, command: command_option): override = (await CommandOverride.get_or_create(guild_id=ctx.guild_id, command_name=command.name))[0] override.prefix = "" await override.save() await ctx.respond_embed(Style.OK, ctx.format("std_command_custom_prefix_disabled", command.name))
async def command_set_enabled(ctx: MrvnCommandContext, command: command_option, enable: bool): override = (await CommandOverride.get_or_create(guild_id=ctx.guild_id, command_name=command.name))[0] override.disabled = not enable await override.save() await ctx.respond_embed(Style.OK, ctx.format( f"std_command_override_command_{'enabled' if enable else 'disabled'}", command.name))
async def execute(ctx: MrvnCommandContext, code: ParseUntilEndsOption(str)): if ctx.interaction is None: if len((splitted := ctx.message.content.split("```"))) == 3: code = splitted[1].rstrip() else: await ctx.respond_embed( Style.ERROR, ctx.translate("execute_command_execute_invalid_format")) return
async def info(ctx: MrvnCommandContext, command: command_option): override = await CommandOverride.get_or_none(guild_id=ctx.guild_id, command_name=command.name) if not override: await ctx.respond_embed(Style.ERROR, ctx.translate("std_command_override_no_override")) return empty = ctx.translate("std_command_override_empty") enabled = not override.disabled prefix = override.prefix if len(override.prefix) else empty perm_list = ", ".join(override.discord_permissions) if len(override.discord_permissions) else empty channel_list = ", ".join([channel.mention if (channel := runtime.bot.get_channel(x)) else str(x) for x in override.whitelist_channel_ids]) if len(override.whitelist_channel_ids) else empty await ctx.respond_embed(Style.INFO, ctx.format("std_command_override_info", enabled, prefix, perm_list, channel_list), ctx.format("std_command_override_info_title", command.name))
async def owner_list(ctx: MrvnCommandContext): owner_ids = list([x.user_id for x in await MrvnUser.filter(is_owner=True)]) app = await runtime.bot.application_info() # type: ignore if app.team: owner_ids.extend([m.id for m in app.team.members]) else: owner_ids.append(app.owner.id) if not len(owner_ids): await ctx.respond_embed(Style.ERROR, ctx.translate("std_command_owners_no_owners")) return owners = [ user.mention if (user := runtime.bot.get_user(x)) else ctx.translate("std_command_owners_unknown") for x in owner_ids ]
async def ban(ctx: MrvnCommandContext, member: Member): if member == runtime.bot.user: await ctx.respond_embed(Style.ERROR, ctx.translate("moderation_cant_do_this_to_bot")) return elif member == ctx.author: await ctx.respond_embed(Style.ERROR, ctx.translate("moderation_cant_do_this_to_self")) return elif ctx.author.top_role.position < member.top_role.position or ctx.author.guild_permissions < member.guild_permissions: await ctx.respond_embed(Style.ERROR, ctx.translate("moderation_cant_do_this_to_this_user")) return try: await member.ban(delete_message_days=0) except Forbidden: await ctx.respond_embed(Style.ERROR, ctx.translate("moderation_bot_insufficient_perms")) else: await ctx.respond_embed(Style.OK, ctx.format("moderation_command_ban_success", member.mention))
async def commands(ctx: MrvnCommandContext): await ctx.defer() command_entries = sorted(await StatsCommandEntry.filter(guild_id=ctx.guild_id), key=lambda k: k.count, reverse=True)[:10] command_stats = "\n".join([f"**{x.command_name}** - `{x.count}`" for x in command_entries]) user_entries = sorted(await StatsUserCommandsEntry.filter(guild_id=ctx.guild_id), key=lambda k: k.count, reverse=True)[:10] user_stats = "\n".join([f"{get_user_mention(x.user_id)} - `{x.count}`" for x in user_entries]) embed = ctx.get_embed(Style.INFO, title=ctx.translate("statistics_command_commands_title")) embed.description = f""" {ctx.translate("statistics_command_commands_command_top")} {command_stats} {ctx.translate("statistics_command_commands_user_top")} {user_stats} """ await ctx.respond(embed=embed)
async def messages_command(ctx: MrvnCommandContext): await ctx.defer() data = await get_monthly_messages(ctx.guild_id) legend_text = ctx.format("statistics_command_messages_legend", ctx.guild.name) result = await asyncio.get_event_loop().run_in_executor( None, functools.partial(plot.get_plot, data, legend_text)) await ctx.respond(file=File(result, filename="Chart.png"))
async def edit_(ctx: MrvnCommandContext, key: str, value: str, global_setting: bool): cls = GlobalSetting if global_setting else GuildSetting try: setting = next(filter(lambda it: it.key == key, cls.__subclasses__())) except StopIteration: await ctx.respond_embed( Style.ERROR, ctx.translate("std_command_settings_edit_invalid_key")) return if global_setting: model = (await setting.get_or_create())[0] else: model = (await setting.get_or_create(guild_id=ctx.guild_id))[0] value_type = type(model.value_field) if value_type == bool: value = value.lower() == "true" try: model.value = value except SettingsValueWriteError as e: await ctx.respond_embed( Style.ERROR, ctx.translate(e.message), ctx.translate("std_command_settings_edit_invalid_value")) return try: await model.save() except ValueError: await ctx.respond_embed( Style.ERROR, ctx.format("std_command_settings_edit_invalid_value_for_type", value_type.__name__)) return await ctx.respond_embed( Style.OK, ctx.translate("std_command_settings_edit_value_set"))
async def remove(ctx: MrvnCommandContext): entries = await BotStatusEntry.filter() if not len(entries): await ctx.respond_embed( Style.ERROR, ctx.translate("bot_status_command_remove_no_entries")) return view = EntriesView(ctx, await BotStatusEntry.filter(), timeout=40) await view.respond(ctx)
async def permissions_edit(ctx: MrvnCommandContext, command: command_option, permission: str, add_: bool): permission = permission.lower() if permission not in Permissions.VALID_FLAGS.keys(): await ctx.respond_embed(Style.ERROR, ctx.translate("std_command_override_invalid_permission")) return override = (await CommandOverride.get_or_create(guild_id=ctx.guild_id, command_name=command.name))[0] if add_ and permission not in override.discord_permissions: override.discord_permissions.append(permission) await override.save() elif not add_ and permission in override.discord_permissions: override.discord_permissions.remove(permission) await override.save() await ctx.respond_embed(Style.OK, ctx.format( f"std_command_override_permission_{'added' if add_ else 'removed'}", permission, command.name))
async def process_override(self, ctx: MrvnCommandContext, override: CommandOverride) -> bool: if override.disabled: await ctx.respond_embed( Style.ERROR, ctx.translate("mrvn_core_override_command_disabled")) return False elif len(override.whitelist_channel_ids ) and ctx.channel_id not in override.whitelist_channel_ids: channel_list = ", ".join([ channel.mention if (channel := self.get_channel(x)) else str(x) for x in override.whitelist_channel_ids ])
async def edit_owner(ctx: MrvnCommandContext, user: User, add_: bool): mrvn_user = (await MrvnUser.get_or_create(user_id=user.id))[0] if add_ and mrvn_user.is_owner: await ctx.respond_embed( Style.ERROR, ctx.translate("std_command_owner_already_an_owner")) return elif not add_ and not mrvn_user.is_owner: await ctx.respond_embed( Style.ERROR, ctx.translate("std_command_owner_not_an_owner")) return mrvn_user.is_owner = add_ await mrvn_user.save() await ctx.respond_embed( Style.OK, ctx.format( "std_command_owner_add" if add_ else "std_command_owner_remove", user.mention))
async def man(ctx: MrvnCommandContext, cmd_name: ParseUntilEndsOption(str)): cmd_split = iter(cmd_name.split()) name = next(cmd_split) for cmd in runtime.bot.application_commands: if isinstance(cmd, (SlashCommand, SlashCommandGroup) ) and cmd.name == name and ctx.guild_id in cmd.guild_ids: command = cmd break else: await ctx.respond_embed( Style.ERROR, ctx.translate("std_command_help_command_not_found")) return root = command try: while isinstance(command, SlashCommandGroup): sub_cmd_name = next(cmd_split) command = next( filter(lambda it: it.name == sub_cmd_name, command.subcommands)) except StopIteration: await ctx.respond_embed( Style.INFO, runtime.bot.get_command_desc(root, ctx, as_tree=True)) return embed = ctx.get_embed(Style.INFO) embed.add_field(name=runtime.bot.get_command_desc(command, ctx), value=command.description) await ctx.respond(embed=embed)
async def channel_edit(ctx: MrvnCommandContext, command: SlashCommand, channel: TextChannel, add_: bool): override = (await CommandOverride.get_or_create(guild_id=ctx.guild_id, command_name=command.name))[0] channel_id = channel.id if add_ and channel_id not in override.whitelist_channel_ids: override.whitelist_channel_ids.append(channel_id) await override.save() elif not add_ and channel_id in override.whitelist_channel_ids: override.whitelist_channel_ids.remove(channel_id) await override.save() await ctx.respond_embed(Style.OK, ctx.format( f"std_command_override_channel_{'added' if add_ else 'removed'}", channel.mention, command.name))
async def mute(ctx: MrvnCommandContext, member: Member, time: int, unit: Option(str, choices=[ OptionChoice("Seconds", "s"), OptionChoice("Minutes", "m"), OptionChoice("Hours", "h"), OptionChoice("Days", "d"), OptionChoice("Weeks", "w"), OptionChoice("Months", "mo"), OptionChoice("Years", "y") ])): if member == runtime.bot.user: await ctx.respond_embed( Style.ERROR, ctx.translate("moderation_cant_do_this_to_bot")) return elif member == ctx.author: await ctx.respond_embed( Style.ERROR, ctx.translate("moderation_cant_do_this_to_self")) return elif member.guild_permissions.administrator: await ctx.respond_embed( Style.ERROR, ctx.translate("moderation_command_mute_cant_mute_administrator")) return elif ctx.author.top_role.position < member.top_role.position or ctx.author.guild_permissions < member.guild_permissions: await ctx.respond_embed( Style.ERROR, ctx.translate("moderation_cant_do_this_to_this_user")) return timestamp = datetime.datetime.utcnow() + datetime.timedelta( 0, time * TIME_DICT[unit]) try: await member.edit(communication_disabled_until=timestamp) except Forbidden: await ctx.respond_embed( Style.ERROR, ctx.translate("moderation_bot_insufficient_perms")) else: await ctx.respond_embed( Style.OK, ctx.format("moderation_command_mute_successful", member.mention))