async def log_error( self, ctx, error, to_log_channel: bool = True, to_owner: bool = False, to_context: bool = False, ): if ctx.guild is None: return embed = text.SafeEmbed(title=f"{config.NO} Command Error") embed.add_field( name="Error", value=f"```{error.__class__.__name__}: {error}```", inline=False, ) embed.add_field(name="Channel", value=ctx.channel.mention, inline=True) embed.add_field(name="Context", value=f"[Jump]({ctx.message.jump_url})", inline=True) caused_by = (ctx.message.clean_content if len(ctx.message.clean_content) < 500 else "*too long*") embed.add_field(name="Caused by", value=caused_by, inline=False) if to_context: local_embed = text.SafeEmbed( title=":warning: Something went wrong", description= f"An unexpected error occurred while performing this command. " f"The developer has been notified. Sorry!\n\n" f"```{error.__class__.__name__}: {error}```", ) await ctx.send(embed=local_embed) if to_log_channel: log_channel = await self.get_logging_channel(ctx.guild) if log_channel is not None and await self.get_guild_setting( ctx.guild.id, "logging_enabled"): await log_channel.send(embed=embed) if to_owner: embed.add_field(name="Guild", value=f"{ctx.guild.name} ({ctx.guild.id})", inline=False) pretty_traceback = "".join( traceback.format_exception(type(error), error, error.__traceback__)) embed.add_field(name="Traceback", value=f"```py\n{pretty_traceback}```") await self.owner.send(embed=embed)
async def on_guild_join(self, guild: discord.Guild): if len(self.guilds) >= 70: if guild.system_channel: embed = text.SafeEmbed( title=":pensive: Sorry!", description= "Discord requires bot developers to hand over their passport " "once their bot reaches about ~75 servers. If they refuse to, " f"the bot cannot be invited anymore and Discord takes away some " f"critical features that the Democraciv Bot needs in order " f"to work properly. I **do not want to give Discord my " f"passport.**\n\nThis is the 70th server the bot is in, " f"and as a precaution **the bot is leaving this server** again.", ) await guild.system_channel.send(embed=embed) await guild.leave() return introduction_channel = guild.system_channel or guild.text_channels[0] # Alert owner of this bot that the bot was invited to some place await self.owner.send( f"{config.HINT} I was added to {guild.name} ({guild.id}).") p = config.BOT_PREFIX introduction_message = ( f"You can check `{p}help` or `{p}commands` to get more " f"information about me.\nUse the `{p}server` command to configure me for this server." f"\n\n__**Icon Guide**__\n" f'{config.YES} - Confirm, Accept or "Success"\n' f'{config.NO} - Cancel, Decline or "Error"\n' f"{config.HINT} - Hint, Tip or additional information to explain something\n" f"{config.USER_INTERACTION_REQUIRED} - User reply is needed. You should reply with something, or confirm something" f"\n\nIf you have any questions or suggestions, send a DM to {self.owner.mention} ({self.owner})." ) # Send introduction message to random guild channel embed = text.SafeEmbed(title="Thanks for inviting me!", description=introduction_message) # Add new guild to database await self.db.execute( "INSERT INTO guild (id) VALUES ($1) ON CONFLICT DO NOTHING ", guild.id) await self.update_guild_config_cache() try: await introduction_channel.send(embed=embed) except discord.Forbidden: pass
async def possible_alt_listener(self, member): if member.guild != self.bot.dciv: return if member.bot: return chance, details = await self.calculate_alt_chance(member) if chance >= 0.2: embed = text.SafeEmbed(title="Possible Alt Account Joined", description="") embed.add_field(name="Person", value=f"{member.mention} ({member.id})", inline=False) embed.add_field( name="Chance", value= f"There is a **{(chance * 100):.0f}%** chance that {member} is an alt.", inline=False, ) if details: embed.add_field(name="Factors", value=details, inline=False) await self.bot.get_democraciv_channel( mk.DemocracivChannel.MODERATION_NOTIFICATIONS_CHANNEL ).send(embed=embed)
async def exportlaws(self, ctx: context.CustomContext): """Generate a Legal Code as a Google Docs document from the list of active laws in {NATION_NAME}""" doc_url = await ctx.input( f"{config.USER_INTERACTION_REQUIRED} Reply with an **edit** link to a Google Docs " "document you created. I will then fill that document to make it an up-to-date Legal Code.\n" ":warning: Note that I will replace the entire content of your Google Docs document if it " "isn't empty.", delete_after=True, ) if not doc_url: ctx.command.reset_cooldown(ctx) return if not self.is_google_doc_link(doc_url): ctx.command.reset_cooldown(ctx) return await ctx.send( f"{config.NO} That doesn't look like a Google Docs URL." ) await ctx.send( f"{config.YES} I will generate an up-to-date Legal Code." f"\n:arrows_counterclockwise: This may take a few minutes..." ) async with ctx.typing(): all_laws = await self.bot.db.fetch( "SELECT id, name, link FROM bill WHERE status = $1 ORDER BY id;", models.BillIsLaw.flag.value, ) ugly_laws = [dict(r) for r in all_laws] date = discord.utils.utcnow().strftime("%B %d, %Y at %H:%M") result = await self.bot.run_apps_script( script_id="MMV-pGVACMhaf_DjTn8jfEGqnXKElby-M", function="generate_legal_code", parameters=[ doc_url, {"name": self.bot.mk.NATION_FULL_NAME, "date": date}, ugly_laws, ], ) embed = text.SafeEmbed( title=f"Generated Legal Code", description="This Legal Code is not guaranteed to be correct. Its " f"content is based entirely on the list of Laws " f"in `{config.BOT_PREFIX}laws`." "\n\nRemember to change the edit link you " "gave me earlier to not be public.", ) embed.add_field( name="Link to the Legal Code", value=result["response"]["result"]["view"], inline=False, ) await ctx.send(embed=embed)
async def format_page(self, menu, cogs): prefix = config.BOT_PREFIX description = ( f"Use `{prefix}help <thing>` for more info on a category or command." ) embed = text.SafeEmbed(title="All Categories | Help", description=description) for cog in cogs: if cog.hidden: continue commands = self.commands.get(cog) if commands: # value = self.format_commands(cog, commands) if cog.description: short_doc = cog.description.split("\n", 1)[0] + "\n" else: short_doc = "No help found.\n" value = f"{short_doc}`{config.BOT_PREFIX}help {cog.qualified_name}`" embed.add_field(name=cog.qualified_name, value=value, inline=True) maximum = self.get_max_pages() embed.set_footer(text=f"Page {menu.current_page + 1}/{maximum}") return embed
async def ping(self, ctx: context.CustomContext): """Pong!""" title = "Pong!" if ctx.invoked_with == "ping" else "Ping!" start = time.perf_counter() message = await ctx.send(":arrows_counterclockwise: Ping...") end = time.perf_counter() discord_http = (end - start) * 1000 try: start = time.perf_counter() await self.bot.api_request("GET", "") end = time.perf_counter() api_http = (end - start) * 1000 except exceptions.DemocracivBotAPIError: api_http = None embed = text.SafeEmbed( title=f":ping_pong: {title}", description= "**[status.discord.com](https://status.discord.com/)**\n\n", ) embed.add_field( name="Discord", value= f"HTTP API: {discord_http:.0f}ms\nGateway Websocket: {self.bot.ping}ms\n", inline=False, ) embed.add_field( name="Internal API", value=f"{api_http:.0f}ms" if api_http else "*not running*", ) await message.edit(content=None, embed=embed)
async def addme(self, ctx): """Invite this bot to your Discord server""" invite_url = discord.utils.oauth_url( self.bot.user.id, permissions=discord.Permissions(8)) await ctx.send(embed=text.SafeEmbed( title="Add this bot to your own Discord server", description=invite_url))
async def format_page(self, menu, commands): embed = text.SafeEmbed(title=self.title, description=self.description) for command in commands: try: is_allowed = await command.can_run(menu.ctx) except Exception: is_allowed = False if is_allowed: signature = f"__{config.BOT_PREFIX}{command.qualified_name} {command.signature}__" else: default_sig = f"__*{config.BOT_PREFIX}{command.qualified_name} {command.signature}" if default_sig.endswith(" "): default_sig = default_sig[:-1] signature = f"{default_sig}*__" embed.add_field( name=signature, value=command.short_doc or "No help given.", inline=False, ) maximum = self.get_max_pages() if maximum > 1: embed.set_footer( text=f"Page {menu.current_page + 1}/{maximum} ({len(self.entries)} commands)" ) return embed
async def bank(self, ctx): """The Bank of Democraciv""" embed = text.SafeEmbed( description= f"The [{self.BANK_NAME}](https://democracivbank.com) provides the international community " f"with free financial " f"services: Personal & Shared Bank Accounts with complete transaction records, a " f"corporate registry, personalized notifications, support for multiple currencies and a " f"deep integration into the Democraciv Discord Bot.\n\nYou can register at " f"[democracivbank.com](https://democracivbank.com) and open as many bank accounts as you want. " f"We don't have, give out or loan any money ourselves, so to actually get some " f"money you need to consult with our third-party partners (usually governments) " f"that issue the currencies.\n\n" f"Connect your Discord Account on " f"[democracivbank.com/me/discord](https://democracivbank.com/me/discord) to get access to " f"the `{config.BOT_PREFIX}bank` commands, like " f"`{config.BOT_PREFIX}bank accounts` to view all of your balances right here in Discord, " f"and to enable personalized " f"notifications in real-time whenever someone sends you money.\n\nSee `{config.BOT_PREFIX}help " f"bank` for a list of all bank related commands here on Discord.") embed.set_author(name=self.BANK_NAME, icon_url=self.BANK_ICON_URL) embed.set_image( url= "https://cdn.discordapp.com/attachments/738903909535318086/837833943377903616/bank.PNG" ) await ctx.send(embed=embed)
async def _log_npc_usage( self, npc, original_message: discord.Message, npc_message_url: str, npc_message_content: str, ): if not await self.bot.get_guild_setting( original_message.guild.id, "logging_enabled" ): return log_channel = await self.bot.get_logging_channel(original_message.guild) if not log_channel: return embed = text.SafeEmbed(title=":disguised_face: NPC Used") embed.add_field(name="NPC", value=npc["name"], inline=False) embed.add_field( name="Real Author", value=f"{original_message.author.mention} {original_message.author} " f"({original_message.author.id})", inline=False, ) embed.add_field( name="Context", value=f"[Jump to Message]({npc_message_url})", inline=False ) embed.add_field(name="Message", value=npc_message_content) await log_channel.send(embed=embed)
async def role_info(self, ctx, role: discord.Role): if role is None: return await ctx.send( f"{config.NO} `role` is neither a role on this server, nor on the Democraciv server." ) if role.guild.id != ctx.guild.id: description = f":warning: This role is from the {self.bot.dciv.name} server, not from this server!" role_name = role.name else: description = "" role_name = f"{role.name} {role.mention}" if role != role.guild.default_role: role_members = ("\n".join( [f"{member.mention} {member}" for member in role.members]) or "-") else: role_members = "*Too long to display.*" embed = text.SafeEmbed(title="Role Information", description=description, colour=role.colour) embed.add_field(name="Role", value=role_name, inline=False) embed.add_field(name="ID", value=role.id, inline=False) embed.add_field(name="Created on", value=role.created_at.strftime("%B %d, %Y"), inline=True) embed.add_field(name="Colour", value=role.colour, inline=True) embed.add_field(name=f"Members ({len(role.members)})", value=role_members, inline=False) await ctx.send(embed=embed)
async def admin(self, ctx): """What is a Nation Admin?""" p = config.BOT_PREFIX embed = text.SafeEmbed( description=f"Nation Admins are allowed to make roles and " f"channels on the {self.bot.dciv.name} server that are " f"specific for their nation (`{p}help Nation`).\n\nAdditionally, they are " f"allowed to create, edit and delete political parties " f"(`{p}help Political Parties`).\n\nNation Admins can also pin messages " f"in every category that belongs to their nation.") embed.set_author(name=self.bot.mk.NATION_NAME, icon_url=self.bot.mk.safe_flag) try: role = self.bot.get_democraciv_role(mk.DemocracivRole.NATION_ADMIN) if role: fmt = [m.mention for m in role.members] or ["-"] embed.add_field(name="Nation Admins", value="\n".join(fmt)) except exceptions.RoleNotFoundError: pass await ctx.send(embed=embed)
async def mod_request_listener(self, message): # If it's a command, ignore if (await self.bot.get_context(message)).valid: return if message.guild != self.bot.dciv: return if message.author.bot: return try: mod_role = self.bot.get_democraciv_role( mk.DemocracivRole.MODERATION) except exceptions.RoleNotFoundError: return if mod_role is None: return try: mod_channel = self.bot.get_democraciv_channel( mk.DemocracivChannel.MODERATION_NOTIFICATIONS_CHANNEL) except exceptions.ChannelNotFoundError: return if mod_role in message.role_mentions: embed = text.SafeEmbed( title=f":pushpin: New Request in #{message.channel.name}", description=message.content, ) embed.add_field(name="From", value=message.author.mention) embed.add_field(name="Original", value=f"[Jump to Message]({message.jump_url})") await mod_channel.send(embed=embed)
async def modguidelines(self, ctx): """Link to DerJonas' Democraciv Moderation Guidelines""" link = token.MOD_GUIDELINES or "https://hastebin.com/afijavahox.coffeescript" embed = text.SafeEmbed( title="DerJonas' Democraciv Moderation Guidelines & Procedures", url=link) await self.safe_send_mod_links(ctx, embed)
async def statistics(self, ctx): """See how much of every currency is currently in circulation and other statistics""" response = await self.request(BankRoute("GET", f"currencies/")) currencies = await response.json() stat_response = await self.request(BankRoute("GET", f"statistics/")) stats = await stat_response.json() embed = text.SafeEmbed( description= f"\n\nThere are a total of {stats['total_bank_accounts']} open bank accounts across " f"all currencies with a total of {stats['total_transactions']} transactions between " f"all of them.\n\nThe shown circulation of a currency does not include any currency reserves that " f"were provided by the {self.BANK_NAME} when this currency " f"was originally created.\n\nThe velocity is calculated as the amount of currency transferred " f"in the last 7 days divided by its total circulation.") embed.set_author(name=self.BANK_NAME, icon_url=self.BANK_ICON_URL) for currency in currencies["result"]: as_object = self.get_currency(currency["code"]) stat = stats["currencies"]["detail"][currency["code"].upper()] value = ( f"Circulation: {as_object.with_amount(currency['circulation'])}\n" f"Transactions: {stat['transactions']}\n" f"Bank Accounts: {stat['bank_accounts']}\n" f"Velocity in the last 7 days: {stat['velocity']:.3f}") embed.add_field(name=currency["name"], value=value) await ctx.send(embed=embed)
async def dog(self, ctx): """Just for Tay: A random image or video of a dog""" async with self.bot.session.get("https://random.dog/woof") as resp: if resp.status != 200: return await ctx.send(f"{config.NO} No dog found :(") filename = await resp.text() url = f"https://random.dog/{filename}" filesize = ctx.guild.filesize_limit if ctx.guild else 8388608 if filename.endswith((".mp4", ".webm")): async with ctx.typing(): async with self.bot.session.get(url) as other: if other.status != 200: return await ctx.send( f"{config.NO} Could not download dog video :(") if int(other.headers["Content-Length"]) >= filesize: return await ctx.send( f"{config.NO} Video was too big to upload, watch it here instead: {url}" ) fp = io.BytesIO(await other.read()) await ctx.send(file=discord.File(fp, filename=filename) ) else: embed = text.SafeEmbed(title="Random Dog") embed.set_image(url=url) embed.set_footer(text="Just for Taylor.") await ctx.send(embed=embed)
async def tag_listener(self, message): if message.author.bot: return ctx: context.CustomContext = await self.bot.get_context(message) if ctx.valid or not ctx.prefix: return tag_name = message.content[len(ctx.prefix) :] tag_details = await self.resolve_tag_name(tag_name, message.guild) if tag_details is None: return tag_content_type = self.get_tag_content_type(tag_details["content"]) if tag_details["is_embedded"]: if tag_content_type is TagContentType.IMAGE: # invisible colour=0x2F3136 embed = text.SafeEmbed(title=tag_details["title"]) embed.set_image(url=tag_details["content"]) try: await message.channel.send(embed=embed) except discord.HTTPException: await message.channel.send( discord.utils.escape_mentions(tag_details["content"]) ) elif tag_content_type is TagContentType.VIDEO: # discord doesn't allow videos in embeds await message.channel.send( discord.utils.escape_mentions(tag_details["content"]) ) else: embed = text.SafeEmbed( title=tag_details["title"], description=tag_details["content"] ) await message.channel.send(embed=embed) else: await message.channel.send( discord.utils.escape_mentions(tag_details["content"]) )
async def taginfo(self, ctx: context.CustomContext, *, tag: Fuzzy[Tag]): """Info about a tag""" pretty_aliases = ( ", ".join([f"`{config.BOT_PREFIX}{alias}`" for alias in tag.aliases]) ) or "-" embed = text.SafeEmbed(title=tag.title) is_global = "Yes" if tag.is_global else "No" is_embedded = "Embed" if tag.is_embedded else "Plain Text" if isinstance(tag.author, discord.Member): embed.add_field(name="Author", value=tag.author.mention, inline=False) embed.set_author( name=tag.author.name, icon_url=tag.author.display_avatar.url, ) elif isinstance(tag.author, discord.User): embed.add_field( name="Author", value=f"*The author of this tag left this server.*\n" f"*You can claim this tag to make it yours with*\n" f"`{config.BOT_PREFIX}tag claim {tag.name}`", inline=False, ) embed.set_author( name=tag.author.name, icon_url=tag.author.display_avatar.url, ) elif tag.author is None: embed.add_field( name="Author", value=f"*The author of this tag left this server.*\n" f"*You can claim this tag to make it yours with*\n" f"`{config.BOT_PREFIX}tag claim {tag.name}`", inline=False, ) embed.add_field(name="Global Tag", value=is_global, inline=True) embed.add_field(name="Tag Format", value=is_embedded, inline=True) embed.add_field(name="Uses", value=tag.uses, inline=False) embed.add_field( name="Collaborators", value="\n".join( [f"{c.mention} {c}" for c in tag.collaborators] or [ f"*The owner of this tag can add other people as " f"collaborators for this tag, so that they can " f"edit and add & " f"remove aliases, with " f"`{config.BOT_PREFIX}tag share {tag.name}`.*\n\n-" ] ), ) embed.add_field(name="Aliases", value=pretty_aliases, inline=False) await ctx.send(embed=embed)
async def on_resumed(self): logging.info("Resumed connection to Discord") channel = self.get_channel(config.BOT_TECHNICAL_NOTIFICATIONS_CHANNEL) if channel: embed = text.SafeEmbed( title=f"{config.YES} Resumed connection to Discord") await channel.send(embed=embed)
async def allcmds(self, ctx): """List all commands""" description_text = [] field_text = [] amounts = 0 i = 0 p = config.BOT_PREFIX for name, cog in sorted(self.bot.cogs.items()): if cog.hidden: continue cog_cmds = sorted( [ command for command in cog.walk_commands() if not command.hidden ], key=lambda c: c.qualified_name, ) amounts += len(cog_cmds) commands_list = [] for command in cog_cmds: if not command.hidden: commands_list.append(f"`{p}{command.qualified_name}`") if i == 0: description_text.append(f"**__{cog.qualified_name}__**\n") description_text.append("\n".join(commands_list)) description_text.append("\n") elif i < 8: description_text.append(f"\n**__{cog.qualified_name}__**\n") description_text.append("\n".join(commands_list)) description_text.append("\n") else: field_text.append(f"\n**__{cog.qualified_name}__**\n") field_text.append("\n".join(commands_list)) field_text.append("\n") i += 1 embed = text.SafeEmbed( title=f"All Commands ({amounts})", description= f"For more detailed explanations and example usage of commands, " f"use `{p}help`, `{p}help <Category>`, " f"or `{p}help <command>`." f"\n\n{' '.join(description_text)}", ) if field_text: embed.add_field(name="\u200b", value=" ".join(field_text)) await ctx.send(embed=embed)
async def tagcreation(self, ctx: context.CustomContext): """Allow everyone to make tags on this server, or just Administrators""" settings = await self.ensure_guild_settings(ctx.guild.id) is_allowed = settings["tag_creation_allowed"] pretty_is_allowed = "Only Administrators" if not is_allowed else "Everyone" embed = text.SafeEmbed( description=f"React with the {config.GUILD_SETTINGS_GEAR} emoji" f" to change this setting.", ) embed.set_author(name=f"Tag Creation on {ctx.guild.name}", icon_url=ctx.guild_icon) embed.add_field(name="Allowed Tag Creators", value=pretty_is_allowed) info_embed = await ctx.send(embed=embed) if await ctx.ask_to_continue( message=info_embed, emoji=config.GUILD_SETTINGS_GEAR, label="Change Settings", ): view = SelectTagCreationView(ctx) await ctx.send( f"{config.USER_INTERACTION_REQUIRED} Who should be able to create new tags " f"on this server with `{config.BOT_PREFIX}tag add`?", view=view, ) result = await view.prompt() if not result: return if result == "Everyone": await self.bot.db.execute( "UPDATE guild SET tag_creation_allowed = true WHERE id = $1", ctx.guild.id, ) await ctx.send( f"{config.YES} Everyone can now make tags with `{config.BOT_PREFIX}tag add` on this server." ) elif result == "Administrators": await self.bot.db.execute( "UPDATE guild SET tag_creation_allowed = false WHERE id = $1", ctx.guild.id, ) await ctx.send( f"{config.YES} Only Administrators can now make tags with " f"`{config.BOT_PREFIX}tag add` on this server.") await self.bot.update_guild_config_cache()
async def send_command_help(self, command): embed = text.SafeEmbed() await self.common_command_formatting(embed, command) parent_name = f"{command.full_parent_name} " if command.full_parent_name else "" aliases = [f"`{config.BOT_PREFIX}{parent_name}{a}`" for a in command.aliases] if aliases: embed.add_field(name="Aliases", value=", ".join(aliases)) await self.context.send(embed=embed)
async def time(self, ctx, *, zone: str): """Displays the current time of a specified time zone""" # catch bird time - otherwise 'msk' would be matched to Asia/Omsk, not Moscow if zone.lower() == "msk": zone = "Europe/Moscow" try: tz = pytz.timezone(zone) except pytz.UnknownTimeZoneError: match = process.extract(zone, pytz.all_timezones, limit=5) menu = text.FuzzyChoose( ctx, question="Which time zone did you mean?", choices=[zone for zone, _ in match], ) zone = await menu.prompt() if not zone: return tz = pytz.timezone(zone) title = str(tz) # blame POSIX for this # https://stackoverflow.com/a/4009126 if "+" in zone: title = zone fixed = zone.replace("+", "-") tz = pytz.timezone(fixed) elif "-" in zone: title = zone fixed = zone.replace("-", "+") tz = pytz.timezone(fixed) utc_now = discord.utils.utcnow() date = utc_now.astimezone(tz) embed = text.SafeEmbed(title=f":clock1: Current Time in {title}") embed.add_field(name="Date", value=date.strftime("%A, %B %d %Y"), inline=False) embed.add_field( name="Time (12-Hour Clock)", value=date.strftime("%I:%M:%S %p"), inline=False, ) embed.add_field(name="Time (24-Hour Clock)", value=date.strftime("%H:%M:%S"), inline=False) await ctx.send(embed=embed)
async def send_initial_message(self, ctx, channel): if not self.send_intro: return await super().send_initial_message(ctx, channel) embed = text.SafeEmbed(title="Welcome to the Democraciv Bot") embed.description = BOT_INTRO embed.set_author( name=f"Made by {ctx.bot.owner}", icon_url=ctx.bot.owner.display_avatar.url, ) self.current_page = -1 return await channel.send(embed=embed)
async def lyrics(self, ctx, *, query: str): """Find lyrics for a song **Usage** `{PREFIX}{COMMAND} <query>` to search for lyrics that match your query """ if len(query) < 3: return await ctx.send( f"{config.NO} The query to search for has to be more than 3 characters!" ) async with ctx.typing(): async with self.bot.session.get( f"https://some-random-api.ml/lyrics?title={query}" ) as response: if response.status == 200: lyrics = await response.json() else: return await ctx.send( f"{config.NO} Genius could not suggest me anything related to `{query}`." ) try: lyrics["lyrics"] = lyrics["lyrics"].replace("[", "**[").replace( "]", "]**") if len(lyrics["lyrics"]) <= 2048: embed = text.SafeEmbed( title=lyrics["title"], description=lyrics["lyrics"], colour=0x2F3136, url=lyrics["links"]["genius"], ) embed.set_author(name=lyrics["author"]) embed.set_thumbnail(url=lyrics["thumbnail"]["genius"]) return await ctx.send(embed=embed) pages = paginator.SimplePages( entries=lyrics["lyrics"].splitlines(), title=lyrics["title"], title_url=lyrics["links"]["genius"], author=lyrics["author"], thumbnail=lyrics["thumbnail"]["genius"], colour=0x2F3136, ) except KeyError: return await ctx.send( f"{config.NO} Genius could not suggest me anything related to `{query}`." ) await pages.start(ctx)
async def automatic(self, ctx: CustomContext): """Automatically write as an NPC in a specific channel or channel category without having to use its trigger phrase""" automatic_channel = await self.bot.db.fetch( "SELECT npc_automatic_mode.npc_id, npc_automatic_mode.channel_id FROM npc_automatic_mode " "WHERE npc_automatic_mode.user_id = $1 " "AND npc_automatic_mode.guild_id = $2", ctx.author.id, ctx.guild.id, ) grouped_by_npc = collections.defaultdict(list) pretty = [ f"If you want to automatically speak as an NPC in a certain channel or channel category " f"without having to use the trigger phrase, use `{config.BOT_PREFIX}npc automatic " f"on <npc>`, or disable it with " f"`{config.BOT_PREFIX}npc automatic off <npc>`.\n\nYou can only have one " f"automatic NPC per channel.\n\nIf you have one NPC as automatic in an entire category, " f"but a different NPC in a single channel that is that same category, and you write " f"something in that channel, you will only speak as the NPC for that " f"specific channel, and not as both NPCs.\n\n" ] for record in automatic_channel: grouped_by_npc[record["npc_id"]].append( ctx.guild.get_channel(record["channel_id"]) ) for k, v in grouped_by_npc.items(): npc = self._npc_cache[k] pretty_chan = [ f"- {c.mention if type(c) is discord.TextChannel else f'{c.name} Category'}" for c in v ] pretty_chan = "\n".join(pretty_chan) pretty.append(f"**__{npc['name']}__**\n{pretty_chan}\n") if len(pretty) > 1: pages = paginator.SimplePages( entries=pretty, icon=ctx.guild_icon, per_page=15, author=f"{ctx.author.display_name}'s Automatic NPCs", ) await pages.start(ctx) else: embed = text.SafeEmbed(description=pretty[0]) embed.set_author( name=f"{ctx.author.display_name}'s Automatic NPCs", icon_url=ctx.guild_icon, ) await ctx.send(embed=embed)
async def close(self): """Closes the aiohttp ClientSession, the connection pool to the PostgreSQL database and the bot itself.""" logging.info("Closing bot...") channel = self.get_channel(config.BOT_TECHNICAL_NOTIFICATIONS_CHANNEL) if channel: embed = text.SafeEmbed( title=f"{config.YES} Bot is shutting down...") await channel.send(embed=embed) await super().close() await self.session.close() await self.db.close()
async def cat(self, ctx): """A random cat""" async with self.bot.session.get( "https://api.thecatapi.com/v1/images/search") as response: if response.status != 200: return await ctx.send(f"{config.NO} No cat found :(") js = await response.json() embed = text.SafeEmbed(title="Random Cat") embed.set_image(url=js[0]["url"]) await ctx.send(embed=embed)
async def send_initial_message(self, ctx, channel): read = "Deny" if self.overwrites.read_messages else "Allow" send = "Deny" if self.overwrites.send_messages else "Allow" embed = text.SafeEmbed( title= f"{config.USER_INTERACTION_REQUIRED} Which Permissions in #{self.channel.name} do you want " f"to change?", description= f"Select as many things as you want, then click the {config.YES} button to continue, " f"or {config.NO} to cancel.\n\n" f":one: {read} Read Messages Permission for `{self.role}` in {self.channel.mention}\n" f":two: {send} Send Messages Permission for `{self.role}` in {self.channel.mention}", ) return await ctx.send(embed=embed)
async def _person_stats(self, ctx, person): amount = await self.bot.db.fetch( "SELECT COUNT(name) FROM tag WHERE author = $1 " "UNION ALL " "SELECT COUNT(name) FROM tag WHERE author = $1 AND guild_id = $2 " "UNION ALL " "SELECT COUNT(name) FROM tag WHERE author = $1 AND global = true ", person.id, ctx.guild.id, ) top_tags = await self.bot.db.fetch( "SELECT name, uses FROM tag WHERE author = $1 AND guild_id = $2 " "ORDER BY uses DESC LIMIT 5", person.id, ctx.guild.id, ) top_global_tags = await self.bot.db.fetch( "SELECT name, uses FROM tag WHERE author = $1 AND global = true " "ORDER BY uses DESC LIMIT 5", person.id, ) embed = text.SafeEmbed() embed.set_author(name=person.display_name, icon_url=person.display_avatar.url) embed.add_field(name="Amount of Tags from any Server", value=amount[0]["count"]) embed.add_field( name="Amount of Global Tags from any Server", value=amount[2]["count"] ) embed.add_field( name="Amount of Tags from this Server", value=amount[1]["count"], inline=False, ) embed.add_field( name="Top Tags from this Server (Global and Local)", value=self._fmt_stats(top_tags), inline=False, ) embed.add_field( name="Top Global Tags from any Server", value=self._fmt_stats(top_global_tags), inline=False, ) await ctx.send(embed=embed)