async def snippetclear(self, ctx): async with self.bot.pool.acquire() as conn: await conn.execute("DELETE FROM snippet WHERE guild=$1", ctx.guild.id) await ctx.send(Embed("All snippets were removed successfully."))
async def select_guild(bot, message, msg): guilds = {} user_guilds = await get_user_guilds(bot, message.author) if user_guilds is None: embed = Embed( f"Please [click here]({bot.config.BASE_URI}/login?redirect=/authorized) to verify. " "This will allow the bot to see your servers and is required for the bot to function. " "Then, close the page and return back here." ) await msg.edit(embed) await bot.state.set( f"user_select:{message.author.id}", { "message": message._data, "msg": msg._data, }, ) return for guild in user_guilds: guild = await bot.get_guild(guild) if guild is None: continue channel = None for text_channel in await guild.text_channels(): if is_modmail_channel(text_channel, message.author.id): channel = text_channel if not channel: guilds[str(guild.id)] = (guild.name, False) else: guilds[str(guild.id)] = (guild.name, True) if len(guilds) == 0: await message.channel.send(ErrorEmbed("Oops, something strange happened. No server found.")) return embeds = [] for chunk in [list(guilds.items())[i : i + 10] for i in range(0, len(guilds), 10)]: embed = Embed( "Select Server", "Please select the server you want to send this message to. You can do so by reacting " "with the corresponding emote.", ) embed.set_footer("Use the reactions to flip pages.") for guild, value in chunk: embed.add_field( f"{len(embed.fields) + 1}: {value[0]}", f"{'Create a new ticket.' if value[1] is False else 'Existing ticket.'}\n" f"Server ID: {guild}", ) embeds.append(embed) await msg.edit(embeds[0]) await msg.add_reaction("◀️") await msg.add_reaction("▶️") for reaction in ["1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣", "🔟"][ : len(embeds[0].fields) ]: await msg.add_reaction(reaction) await bot.state.set( f"reaction_menu:{msg.channel.id}:{msg.id}", { "kind": "selection", "end": int(time.time()) + 180, "data": { "msg": message._data, "page": 0, "all_pages": [embed.to_dict() for embed in embeds], }, }, ) await bot.state.sadd("reaction_menu_keys", f"reaction_menu:{msg.channel.id}:{msg.id}")
async def restart(self, ctx): await ctx.send(Embed("Restarting...")) await self.bot.session.post(f"{self.bot.http_uri}/restart")
async def serverinfo(self, ctx): guild = await self.bot.get_guild(ctx.guild.id) embed = Embed(title="Server Information") embed.add_field("Name", guild.name) embed.add_field("ID", guild.id) embed.add_field("Owner", f"<@{guild.owner_id}>" if guild.owner_id else "Unknown") embed.add_field( "Icon", f"[Link]({guild.icon_url_as(static_format='png')})" if guild.icon else "*Not set*", ) embed.add_field("Server Created", guild.created_at.replace(microsecond=0)) embed.add_field("Members", guild.member_count) embed.add_field("Channels", str(len(await guild.channels()))) embed.add_field("Roles", str(len(await guild.roles()))) embed.add_field("Emojis", str(len(await guild.emojis()))) if guild.icon: embed.set_thumbnail(guild.icon_url) await ctx.send(embed)
async def on_raw_reaction_add(self, payload): if payload.user_id == self.bot.id: return if payload.member: return if payload.emoji.name in ["✅", "🔁", "❌"]: menu, channel, msg = await tools.get_reaction_menu( self.bot, payload, "confirmation") if menu is None: return guild = await self.bot.get_guild(menu["data"]["guild"]) message = Message(state=self.bot.state, channel=channel, data=menu["data"]["msg"]) if payload.emoji.name == "✅": await self.send_mail(message, guild) await msg.delete() else: if payload.emoji.name == "🔁": await msg.edit(Embed("Loading servers...")) self.bot.loop.create_task( tools.select_guild(self.bot, message, msg)) elif payload.emoji.name == "❌": await msg.edit( ErrorEmbed("Request cancelled successfully.")) for reaction in ["✅", "🔁", "❌"]: await msg.remove_reaction(reaction, self.bot.user) await self.bot.state.delete(f"reaction_menu:{channel.id}:{msg.id}") await self.bot.state.srem( "reaction_menu_keys", f"reaction_menu:{channel.id}:{msg.id}", ) return numbers = ["1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣", "🔟"] arrows = ["◀️", "▶️"] if payload.emoji.name in numbers + arrows: menu, channel, msg = await tools.get_reaction_menu( self.bot, payload, "selection") if menu is None: return page = menu["data"]["page"] all_pages = menu["data"]["all_pages"] if payload.emoji.name not in arrows: chosen = numbers.index(payload.emoji.name) await msg.delete() fields = all_pages[page]["fields"] if chosen > len(fields): return guild = await self.bot.get_guild( fields[chosen]["value"].split()[-1]) message = Message(state=self.bot.state, channel=channel, data=menu["data"]["msg"]) await self.send_mail(message, guild) await self.bot.state.delete( f"reaction_menu:{channel.id}:{msg.id}") await self.bot.state.srem( "reaction_menu_keys", f"reaction_menu:{channel.id}:{msg.id}", ) return if payload.emoji.name == "◀️" and page > 0: page -= 1 new_page = Embed.from_dict(all_pages[page]) await msg.edit(new_page) for reaction in numbers[:len(new_page.fields)]: await msg.add_reaction(reaction) if payload.emoji.name == "▶️" and page < len(all_pages) - 1: page += 1 new_page = Embed.from_dict(all_pages[page]) await msg.edit(new_page) for reaction in numbers[len(new_page.fields):]: try: await msg.remove_reaction(reaction, self.bot.user) except discord.NotFound: pass menu["data"]["page"] = page menu["end"] = int(time.time()) + 180 await self.bot.state.set(f"reaction_menu:{channel.id}:{msg.id}", menu)
async def on_message(self, message): if message.author.bot or not isinstance(message.channel, discord.DMChannel): return for prefix in [ f"<@{self.bot.id}> ", f"<@!{self.bot.id}> ", self.bot.config.DEFAULT_PREFIX ]: if message.content.startswith(prefix): return if await tools.is_user_banned(self.bot, message.author): await message.channel.send( ErrorEmbed("You are banned from this bot.")) return guild = None async for msg in message.channel.history(limit=30): if (msg.author.id == self.bot.id and len(msg.embeds) > 0 and msg.embeds[0].title in ["Message Received", "Message Sent"]): guild = msg.embeds[0].footer.text.split()[-1] guild = await self.bot.get_guild(int(guild)) break settings = await tools.get_user_settings(self.bot, message.author.id) confirmation = True if settings is None or settings[ 0] is True else False if guild and confirmation is True: embed = Embed( "Confirmation", f"You're sending this message to **{guild.name}** (ID: {guild.id}). React with ✅ " "to confirm.\nWant to send to another server? React with 🔁.\nTo cancel this " "request, react with ❌.", ) embed.set_footer( "Tip: You can disable confirmation messages with the " f"{self.bot.config.DEFAULT_PREFIX}confirmation command.") msg = await message.channel.send(embed) await msg.add_reaction("✅") await msg.add_reaction("🔁") await msg.add_reaction("❌") await self.bot.state.set( f"reaction_menu:{msg.channel.id}:{msg.id}", { "kind": "confirmation", "end": int(time.time()) + 180, "data": { "guild": guild.id, "msg": message._data, }, }, ) await self.bot.state.sadd( "reaction_menu_keys", f"reaction_menu:{msg.channel.id}:{msg.id}", ) elif guild: await self.send_mail(message, guild) else: msg = await message.channel.send(Embed("Loading servers...")) await tools.select_guild(self.bot, message, msg)
async def invite(self, ctx): await ctx.send(Embed("Invite Link", f"{self.bot.config.BASE_URI}/invite"))
async def help(self, ctx, *, command: str = None): if command: command = self.bot.get_command(command.lower()) if not command: await ctx.send( embed=Embed( description=f"That command does not exist. Use `{ctx.prefix}help` to see all the commands.", ) ) return embed = Embed(title=command.name, description=command.description) usage = "\n".join([ctx.prefix + x.strip() for x in command.usage.split("\n")]) embed.add_field(name="Usage", value=f"```{usage}```", inline=False) if len(command.aliases) > 1: embed.add_field(name="Aliases", value=f"`{'`, `'.join(command.aliases)}`") elif len(command.aliases) > 0: embed.add_field(name="Alias", value=f"`{command.aliases[0]}`") await ctx.send(embed=embed) return bot_user = await self.bot.real_user() all_pages = [] page = Embed( title=f"{bot_user.name} Help Menu", description="Thank you for using ModMail! Please direct message me if you wish to contact staff. You can " "also invite me to your server with the link below, or join our support server if you need further help.\n" f"\nDon't forget to check out our partners with the `{ctx.prefix}partners` command!", ) page.set_thumbnail(url=bot_user.avatar_url) page.set_footer(text="Use the reactions to flip pages.") page.add_field(name="Invite", value="https://modmail.xyz/invite", inline=False) page.add_field(name="Support Server", value="https://discord.gg/wjWJwJB", inline=False) all_pages.append(page) page = Embed(title=f"{bot_user.name} Help Menu") page.set_thumbnail(url=bot_user.avatar_url) page.set_footer(text="Use the reactions to flip pages.") page.add_field( name="About ModMail", value="ModMail is a feature-rich Discord bot designed to enable your server members to contact staff " "easily. A new channel is created whenever a user messages the bot, and the channel will serve as a shared " "inbox for seamless communication between staff and the user.", inline=False, ) page.add_field( name="Getting Started", value="Follow these steps to get the bot all ready to serve your server!\n1. Invite the bot with " f"[this link](https://modmail.xyz/invite)\n2. Run `{ctx.prefix}setup`, there will be an interactive guide." f"\n3. All done! For a full list of commands, see `{ctx.prefix}help`.", inline=False, ) all_pages.append(page) for cog_name in self.bot.cogs: if cog_name in ["Owner", "Admin"]: continue cog = self.bot.get_cog(cog_name) cog_commands = cog.get_commands() if len(cog_commands) == 0: continue page = Embed( title=cog_name, description=f"My prefix is `{ctx.prefix}`. Use `{ctx.prefix}help <command>` for more information on a " "command.", ) page.set_author(name=f"{bot_user.name} Help Menu", icon_url=bot_user.avatar_url) page.set_thumbnail(url=bot_user.avatar_url) page.set_footer(text="Use the reactions to flip pages.") for cmd in cog_commands: page.add_field(name=cmd.name, value=cmd.description, inline=False) all_pages.append(page) for page in range(len(all_pages)): all_pages[page].set_footer(text=f"Use the reactions to flip pages. (Page {page + 1}/{len(all_pages)})") await tools.create_paginator(self.bot, ctx, all_pages)
async def stats(self, ctx): total_seconds = int((datetime.utcnow() - await self.bot.started()).total_seconds()) hours, remainder = divmod(total_seconds, 3600) minutes, seconds = divmod(remainder, 60) days, hours = divmod(hours, 24) bot_user = await self.bot.real_user() embed = Embed( "ModMail Statistics", f"Visit the bot status page [here]({self.bot.config.BASE_URI}/status) for more " "information.", ) embed.add_field("Owner", "CHamburr#2591") embed.add_field("Bot Version", self.bot.version) if days: embed.add_field("Uptime", f"{days}d {hours}h {minutes}m {seconds}s") else: embed.add_field("Uptime", f"{hours}h {minutes}m {seconds}s") embed.add_field("Clusters", self.bot.cluster_count) if ctx.guild: embed.add_field("Shards", f"{ctx.guild.shard_id + 1}/{await self.bot.shard_count()}") else: embed.add_field("Shards", f"0/{await self.bot.shard_count()}") embed.add_field("Servers", str(await self.bot.state.scard("guild_keys"))) embed.add_field("CPU Usage", f"{psutil.cpu_percent(interval=None)}%") embed.add_field("RAM Usage", f"{psutil.virtual_memory().percent}%") embed.add_field("Python Version", platform.python_version()) embed.set_thumbnail(bot_user.avatar_url) await ctx.send(embed)
async def partners(self, ctx): await ctx.send(Embed("Partners", f"{self.bot.config.BASE_URI}/partners"))
async def close_channel(self, ctx, reason, anon: bool = False): await ctx.send(Embed("Closing channel...")) data = await tools.get_data(self.bot, ctx.guild.id) if data[7] is True: messages = await ctx.channel.history(limit=10000).flatten() try: await ctx.channel.delete() except discord.Forbidden: await ctx.send( ErrorEmbed("Missing permissions to delete this channel.")) return embed = ErrorEmbed( "Ticket Closed", reason if reason else "No reason was provided.", timestamp=True, ) embed.set_author( str(ctx.author) if anon is False else "Anonymous#0000", ctx.author.avatar_url if anon is False else "https://cdn.discordapp.com/embed/avatars/0.png", ) embed.set_footer(f"{ctx.guild.name} | {ctx.guild.id}", ctx.guild.icon_url) try: member = await ctx.guild.fetch_member( tools.get_modmail_user(ctx.channel).id) except discord.NotFound: member = None else: dm_channel = tools.get_modmail_channel(self.bot, ctx.channel) if data[6]: embed2 = Embed( "Closing Message", tools.tag_format(data[6], member), colour=0xFF4500, timestamp=True, ) embed2.set_footer(f"{ctx.guild.name} | {ctx.guild.id}", ctx.guild.icon_url) try: await dm_channel.send(embed2) except discord.Forbidden: pass try: await dm_channel.send(embed) except discord.Forbidden: pass if data[4] is None: return channel = await ctx.guild.get_channel(data[4]) if channel is None: return if member is None: try: member = await self.bot.fetch_user( tools.get_modmail_user(ctx.channel)) except discord.NotFound: pass if member: embed.set_footer(f"{member} | {member.id}", member.avatar_url) else: embed.set_footer( "Unknown#0000 | 000000000000000000", "https://cdn.discordapp.com/embed/avatars/0.png", ) embed.set_author( str(ctx.author) if anon is False else f"{ctx.author} (Anonymous)", ctx.author.avatar_url, ) if data[7] is True: history = "" for message in messages: if message.author.bot and ( message.author.id != self.bot.id or len(message.embeds) <= 0 or message.embeds[0].title not in ["Message Received", "Message Sent"]): continue if not message.author.bot and message.content == "": continue if message.author.bot: if not message.embeds[0].author.name: author = f"{' '.join(message.embeds[0].footer.text.split()[:-2])} (User)" elif message.embeds[0].author.name.endswith( " (Anonymous)"): author = f"{message.embeds[0].author.name[:-12]} (Staff)" else: author = f"{message.embeds[0].author.name} (Staff)" description = message.embeds[0].description for attachment in [ field.value for field in message.embeds[0].fields if field.name.startswith("Attachment ") ]: if not description: description = f"(Attachment: {attachment})" else: description += f" (Attachment: {attachment})" else: author = f"{message.author} (Comment)" description = message.content history = ( f"[{str(message.created_at.replace(microsecond=0))}] {author}: {description}\n" + history) history = io.BytesIO(history.encode()) file = discord.File( history, f"modmail_log_{tools.get_modmail_user(ctx.channel).id}.txt") try: msg = await channel.send(embed, file=file) except discord.Forbidden: return log_url = msg.attachments[0].url[39:-4] log_url = log_url.replace("modmail_log_", "") log_url = [hex(int(some_id))[2:] for some_id in log_url.split("/")] log_url = f"{self.bot.config.BASE_URI}/logs/{'-'.join(log_url)}" embed.add_field("Message Logs", log_url, False) await asyncio.sleep(0.5) await msg.edit(embed) return try: await channel.send(embed) except discord.Forbidden: pass
async def blacklistclear(self, ctx): async with self.bot.pool.acquire() as conn: await conn.execute("UPDATE data SET blacklist=$1 WHERE guild=$2", [], ctx.guild.id) await ctx.send(Embed("The blacklist is cleared successfully."))
async def viewsnippet(self, ctx, *, name: str = None): if name: async with self.bot.pool.acquire() as conn: res = await conn.fetchrow( "SELECT name, content FROM snippet WHERE name=$1 AND guild=$2", name.lower(), ctx.guild.id, ) if not res: await ctx.send(embed=ErrorEmbed( description="A snippet with that name was not found.")) return embed = Embed(title="Snippet") embed.add_field(name="Name", value=res[0], inline=False) embed.add_field(name="Content", value=res[1], inline=False) await ctx.send(embed=embed) return async with self.bot.pool.acquire() as conn: res = await conn.fetch( "SELECT name, content FROM snippet WHERE guild=$1", ctx.guild.id) if not res: await ctx.send(embed=Embed( description="No snippet has been added yet.")) return all_pages = [] for chunk in [res[i:i + 10] for i in range(0, len(res), 10)]: page = Embed(title="Snippets") for snippet in chunk: page.add_field( name=snippet[0], value=snippet[1][:97] + "..." if len(snippet[1]) > 100 else snippet[1], inline=False, ) page.set_footer(text="Use the reactions to flip pages.") all_pages.append(page) if len(all_pages) == 1: embed = all_pages[0] embed.set_footer(text=discord.Embed.Empty) await ctx.send(embed=embed) return await tools.create_paginator(self.bot, ctx, all_pages)
async def stats(self, ctx): total_seconds = int((datetime.utcnow() - await self.bot.started()).total_seconds()) hours, remainder = divmod(total_seconds, 3600) minutes, seconds = divmod(remainder, 60) days, hours = divmod(hours, 24) bot_user = await self.bot.real_user() embed = Embed( title=f"{bot_user.name} Statistics", description="Visit the bot status page [here](https://status.modmail.xyz) for more information.", ) embed.add_field(name="Owner", value="CHamburr#2591") embed.add_field(name="Bot Version", value=self.bot.version) if days: embed.add_field(name="Uptime", value=f"{days}d {hours}h {minutes}m {seconds}s") else: embed.add_field(name="Uptime", value=f"{hours}h {minutes}m {seconds}s") embed.add_field(name="Clusters", value=self.bot.cluster_count) if ctx.guild: embed.add_field(name="Shards", value=f"{ctx.guild.shard_id + 1}/{await self.bot.shard_count()}") else: embed.add_field(name="Shards", value=f"0/{await self.bot.shard_count()}") embed.add_field(name="Servers", value=str(await self.bot.state.scard("guild_keys"))) embed.add_field(name="Channels", value=str(await self.bot.state.scard("channel_keys"))) embed.add_field(name="Users", value=str(await self.bot.state.scard("user_keys"))) embed.add_field(name="CPU Usage", value=f"{psutil.cpu_percent(interval=None)}%") embed.add_field(name="RAM Usage", value=f"{psutil.virtual_memory().percent}%") embed.add_field(name="Platform", value=" ".join(distro.linux_distribution()[:2])) embed.add_field(name="Python Version", value=platform.python_version()) embed.set_thumbnail(url=bot_user.avatar_url) await ctx.send(embed=embed)
async def support(self, ctx): await ctx.send(Embed("Support Server", "https://discord.gg/wjWJwJB"))
async def partners(self, ctx): all_pages = [] page = Embed( title="Discord Templates", description="Discord Templates is the place for you to discover a huge variety of Discord server templates " "for all purposes.", ) page.add_field(name="Link", value="https://discordtemplates.me") page.set_thumbnail( url="https://cdn.discordapp.com/icons/696179394057732237/cf54e042456638eba2ea5abddfc7910e.png" ) all_pages.append(page) page = Embed( title="Call of Duty Mobile", description="The Activision-supported, community-run discord for the Call of Duty: Mobile Community.", ) page.add_field(name="Link", value="https://discord.gg/codmobile") page.set_thumbnail( url="https://cdn.discordapp.com/icons/619762818266431547/a_cce3e6b3b6e64dcf7bbb6fa92c9fc4e6.gif" ) all_pages.append(page) page = Embed( title="Eden of Gaming", description="Eden of Gaming is a global gaming community that aims to share knowledge and build " "relationships between members and fellow global gaming communities.", ) page.add_field(name="Link", value="https://discord.gg/edenofgaming") page.set_thumbnail( url="https://cdn.discordapp.com/icons/457151179072339978/a_6b2bf427b3f07f209386dcf85ea94a9a.gif" ) all_pages.append(page) page = Embed( title="Homework Help", description="Got assignments? Need help? Then come join Discord's premier hub for students, scholars, " "professionals, and hobbyists interested in discussions, challenges, as well as news, views, and reviews " "that runs the gamut of academic disciplines.", ) page.add_field(name="Link", value="https://discord.gg/homework") page.set_thumbnail( url="https://cdn.discordapp.com/icons/238956364729155585/468ac0a7dc84db45d018e0c442fe8447.png" ) all_pages.append(page) page = Embed( title="Otzdarva's Dungeon", description="Otzdarva's Dungeon is a community for the Dead by Daylight streamer Otzdarva, also known for " "being a PUBG and Dark Souls YouTuber in the past.", ) page.add_field(name="Link", value="https://discord.gg/otzdarva") page.set_thumbnail( url="https://cdn.discordapp.com/icons/227900298549657601/a_74313704119f88dc252e9b0b98c3ab25.gif" ) all_pages.append(page) page = Embed( title="DOOM", description="Hell’s armies have invaded Earth. Become the Slayer in an epic single-player campaign to " "conquer demons across dimensions and stop the final destruction of humanity. The only thing they fear... " "is you. RAZE HELL in DOOM Eternal!", ) page.add_field(name="Link", value="https://discord.gg/doom") page.set_thumbnail( url="https://cdn.discordapp.com/icons/162891400684371968/a_4363040f917b4920a2e78da1e302d9dc.gif" ) all_pages.append(page) page = Embed( title="Sea of Thieves", description="One of the longest running and largest community-run Sea of Thieves Discord servers. A great " "and most of all welcoming place to chat about Sea of Thieves and maybe find a few crew mates along the " "way.", ) page.add_field(name="Link", value="https://discord.gg/seaofthievescommunity") page.set_thumbnail( url="https://cdn.discordapp.com/icons/209815380946845697/a_04c8ae80dce6e6ef1e3d574dca61b4a2.png" ) all_pages.append(page) page = Embed( title="Underlords", description="Underlords Discord server acts as a secondary platform to r/Underlords where users can have " "casual chit-chat, give suggestions, share tactics and discuss everything related to Underlords.", ) page.add_field(name="Link", value="https://discord.gg/underlords") page.set_thumbnail( url="https://cdn.discordapp.com/icons/580534040692654101/a_0a6f7616c7d9b98f740809dbea272967.gif" ) all_pages.append(page) page = Embed( title="CH's amburr", description="CH's amburr is my personal community server. It is a fun and friendly place where you can " "talk about everything cool.", ) page.add_field(name="Link", value="https://discord.gg/TYe3U4w") page.set_thumbnail( url="https://cdn.discordapp.com/icons/447732123340767232/5a1064a156540e36e22a38abc527c737.png" ) all_pages.append(page) page = Embed( title="Member Count", description="Member Count is another bot that I am actively developing on. It shows stats on your server " "using channel names.", ) page.add_field(name="Link", value="https://discordbots.org/bot/membercount") page.set_thumbnail( url="https://cdn.discordapp.com/icons/496964682972659712/0b61c5cb7b9ace8f8f5e2fef37cacb5b.png" ) all_pages.append(page) for page in range(len(all_pages)): bot_user = await self.bot.real_user() all_pages[page].set_author(name=f"{bot_user.name} partners", icon_url=bot_user.avatar_url) all_pages[page].set_footer(text=f"Use the reactions to flip pages. (Page {page + 1}/{len(all_pages)})") await tools.create_paginator(self.bot, ctx, all_pages)
async def website(self, ctx): await ctx.send(Embed("Website", f"{self.bot.config.BASE_URI}"))
async def send_mail_mod(self, message, prefix, anon=False, snippet=False): self.bot.prom.tickets_message.inc({}) data = await tools.get_data(self.bot, message.guild.id) user = tools.get_modmail_user(message.channel) if user.id in data[9]: await message.channel.send( ErrorEmbed( "That user is blacklisted from sending a message here. You need to whitelist " "them before you can send them a message.")) return try: member = await message.guild.fetch_member(user.id) except discord.NotFound: await message.channel.send( ErrorEmbed( f"The user was not found. Use `{prefix}close [reason]` to close this channel." )) return if snippet is True: message.content = tools.tag_format(message.content, member) embed = Embed("Message Received", message.content, colour=0xFF4500, timestamp=True) embed.set_author( str(message.author) if anon is False else "Anonymous#0000", message.author.avatar_url if anon is False else "https://cdn.discordapp.com/embed/avatars/0.png", ) embed.set_footer(f"{message.guild.name} | {message.guild.id}", message.guild.icon_url) files = [] for file in message.attachments: saved_file = io.BytesIO() await file.save(saved_file) files.append(discord.File(saved_file, file.filename)) dm_channel = tools.get_modmail_channel(self.bot, message.channel) try: dm_message = await dm_channel.send(embed, files=files) except discord.Forbidden: await message.channel.send( ErrorEmbed( "The message could not be sent. The user might have disabled Direct Messages." )) return embed.title = "Message Sent" embed.set_author( str(message.author) if anon is False else f"{message.author} (Anonymous)", message.author.avatar_url, ) embed.set_footer(f"{member} | {member.id}", member.avatar_url) for count, attachment in enumerate( [attachment.url for attachment in dm_message.attachments], start=1): embed.add_field(f"Attachment {count}", attachment, False) for file in files: file.reset() await message.channel.send(embed, files=files) try: await message.delete() except (discord.Forbidden, discord.NotFound): pass
async def source(self, ctx): await ctx.send(Embed("GitHub Repository", "https://github.com/chamburr/modmail"))
async def send_mail(self, message, guild): self.bot.prom.tickets_message.inc({}) if not guild: await message.channel.send(ErrorEmbed("The server was not found.")) return try: member = await guild.fetch_member(message.author.id) except discord.NotFound: await message.channel.send( ErrorEmbed( "You are not in that server, and the message is not sent.") ) return data = await tools.get_data(self.bot, guild.id) category = await guild.get_channel(data[2]) if not category: await message.channel.send( ErrorEmbed( "A ModMail category is not found. The bot is not set up properly in the server." )) return if message.author.id in data[9]: await message.channel.send( ErrorEmbed( "That server has blacklisted you from sending a message there." )) return channel = None for text_channel in await guild.text_channels(): if tools.is_modmail_channel(text_channel, message.author.id): channel = text_channel break if channel is None: self.bot.prom.tickets.inc({}) name = "".join([ x for x in message.author.name.lower() if x not in string.punctuation and x.isprintable() ]) if name: name = name + f"-{message.author.discriminator}" else: name = message.author.id try: channel = await guild.create_text_channel( name=name, category=category, topic= f"ModMail Channel {message.author.id} {message.channel.id} (Please do " "not change this)", ) except discord.HTTPException as e: await message.channel.send( ErrorEmbed( "An HTTPException error occurred. Please contact an admin on the server " f"with the following information: {e.text} ({e.code})." )) return log_channel = await guild.get_channel(data[4]) if log_channel: embed = Embed( title="New Ticket", colour=0x00FF00, timestamp=True, ) embed.set_footer( f"{message.author.name}#{message.author.discriminator} | " f"{message.author.id}", message.author.avatar_url, ) try: await log_channel.send(embed) except discord.Forbidden: pass prefix = await tools.get_guild_prefix(self.bot, guild) embed = Embed( "New Ticket", "Type a message in this channel to reply. Messages starting with the server prefix " f"`{prefix}` are ignored, and can be used for staff discussion. Use the command " f"`{prefix}close [reason]` to close this ticket.", timestamp=True, ) embed.add_field("User", f"<@{message.author.id}> ({message.author.id})") embed.add_field( "Roles", "*None*" if len(member._roles) == 0 else " ".join( [f"<@&{x}>" for x in member._roles]), ) embed.set_footer(f"{message.author} | {message.author.id}", message.author.avatar_url) roles = [] for role in data[8]: if role == guild.id: roles.append("@everyone") elif role == -1: roles.append("@here") else: roles.append(f"<@&{role}>") try: await channel.send(" ".join(roles), embed=embed) except discord.HTTPException: await message.channel.send( ErrorEmbed( "The bot is missing permissions. Please contact an admin on the server." )) return if data[5]: embed = Embed( "Greeting Message", tools.tag_format(data[5], message.author), colour=0xFF4500, timestamp=True, ) embed.set_footer(f"{guild.name} | {guild.id}", guild.icon_url) await message.channel.send(embed) embed = Embed("Message Sent", message.content, colour=0x00FF00, timestamp=True) embed.set_footer(f"{guild.name} | {guild.id}", guild.icon_url) files = [] for file in message.attachments: saved_file = io.BytesIO() await file.save(saved_file) files.append(discord.File(saved_file, file.filename)) dm_message = await message.channel.send(embed, files=files) embed.title = "Message Received" embed.set_footer( f"{message.author.name}#{message.author.discriminator} | {message.author.id}", message.author.avatar_url, ) for count, attachment in enumerate( [attachment.url for attachment in dm_message.attachments], start=1): embed.add_field(f"Attachment {count}", attachment, False) for file in files: file.reset() try: await channel.send(embed, files=files) except discord.Forbidden: await dm_message.delete() await message.channel.send( ErrorEmbed( "The bot is missing permissions. Please contact an admin on the server." ))
async def help(self, ctx, *, command: str = None): if command: command = self.bot.get_command(command.lower()) if not command: await ctx.send( Embed( f"That command does not exist. Use `{ctx.prefix}help` to see all the " "commands.", ) ) return embed = Embed(command.name, command.description) usage = "\n".join([ctx.prefix + x.strip() for x in command.usage.split("\n")]) embed.add_field("Usage", f"```{usage}```", False) if len(command.aliases) > 1: embed.add_field("Aliases", f"`{'`, `'.join(command.aliases)}`") elif len(command.aliases) > 0: embed.add_field("Alias", f"`{command.aliases[0]}`") await ctx.send(embed) return bot_user = await self.bot.real_user() all_pages = [] page = Embed( "ModMail Help Menu", "ModMail is a feature-rich Discord bot designed to enable your server members to " "contact staff easily.\n\nPlease direct message me if you wish to contact staff. You " "can also invite me to your server with the link below, or join our support server if " f"you need further help.\n\nTo setup the bot, run `{ctx.prefix}setup`.", ) page.set_thumbnail(bot_user.avatar_url) page.set_footer("Use the reactions to flip pages.") page.add_field("Invite", f"{self.bot.config.BASE_URI}/invite", False) page.add_field("Support Server", "https://discord.gg/wjWJwJB", False) all_pages.append(page) for cog_name in self.bot.cogs: if cog_name in ["Owner", "Admin"]: continue cog = self.bot.get_cog(cog_name) cog_commands = cog.get_commands() if len(cog_commands) == 0: continue page = Embed( cog_name, f"My prefix is `{ctx.prefix}`. Use `{ctx.prefix}help <command>` for more " "information on a command.", ) page.set_author("ModMail Help Menu", bot_user.avatar_url) page.set_thumbnail(bot_user.avatar_url) page.set_footer("Use the reactions to flip pages.") for cmd in cog_commands: page.add_field(cmd.name, cmd.description, False) all_pages.append(page) for page in range(len(all_pages)): all_pages[page].set_footer( f"Use the reactions to flip pages. (Page {page + 1}/{len(all_pages)})" ) await tools.create_paginator(self.bot, ctx, all_pages)
async def new(self, ctx, *, message: str): ctx.message.content = message msg = await ctx.send(Embed("Loading servers...")) await tools.select_guild(self.bot, ctx.message, msg)
async def closingmessage(self, ctx, *, text: str = None): async with self.bot.pool.acquire() as conn: await conn.execute("UPDATE data SET goodbye=$1 WHERE guild=$2", text, ctx.guild.id) await ctx.send(Embed("The closing message is set successfully."))
async def userinfo(self, ctx, *, member: MemberConverter = None): if member is None: member = ctx.message.member roles = [f"<@&{role}>" for role in member._roles] if len(roles) == 0: roles.append("*No roles*") embed = Embed(title="User Information") embed.add_field("Name", str(member)) embed.add_field("ID", member.id) embed.add_field("Nickname", member.nick if member.nick else "*Not set*") embed.add_field("Avatar", f"[Link]({member.avatar_url_as(static_format='png')})") embed.add_field( "Joined Server", member.joined_at.replace(microsecond=0) if member.joined_at else "Unknown", ) embed.add_field("Account Created", member.created_at.replace(microsecond=0)) embed.add_field( "Roles", f"{len(roles)} roles" if len(", ".join(roles)) > 1000 else ", ".join(roles), ) embed.set_thumbnail(member.avatar_url) await ctx.send(embed)
async def viewconfig(self, ctx): data = await tools.get_data(self.bot, ctx.guild.id) category = await ctx.guild.get_channel(data[2]) logging_channel = await ctx.guild.get_channel(data[4]) access_roles = [] for role in data[3]: access_roles.append(f"<@&{role}>") ping_roles = [] for role in data[8]: if role == -1: ping_roles.append("@here") elif role == ctx.guild.id: ping_roles.append("@everyone") else: ping_roles.append(f"<@&{role}>") greeting = data[5] if greeting and len(greeting) > 1000: greeting = greeting[:997] + "..." closing = data[6] if closing and len(closing) > 1000: closing = closing[:997] + "..." embed = Embed(title="Server Configurations") embed.add_field("Prefix", ctx.prefix) embed.add_field("Category", "*Not set*" if category is None else category.name) embed.add_field( "Access Roles", "*Not set*" if len(access_roles) == 0 else " ".join(access_roles), ) embed.add_field("Ping Roles", "*Not set*" if len(ping_roles) == 0 else " ".join(ping_roles)) embed.add_field( "Logging", "*Not set*" if logging_channel is None else f"<#{logging_channel.id}>", ) embed.add_field("Advanced Logging", "Enabled" if data[7] is True else "Disabled") embed.add_field("Anonymous Messaging", "Enabled" if data[10] is True else "Disabled") embed.add_field("Greeting Message", "*Not set*" if greeting is None else greeting, False) embed.add_field("Closing message", "*Not set*" if closing is None else closing, False) await ctx.send(embed)
async def userinfo(self, ctx, *, member: MemberConverter = None): if member is None: member = ctx.message.member roles = [role.name for role in await member.roles()] embed = Embed(title="User Information") embed.add_field(name="Name", value=str(member)) embed.add_field(name="ID", value=member.id) embed.add_field(name="Nickname", value=member.nick if member.nick else "*Not set*") embed.add_field( name="Avatar", value=f"[Link]({member.avatar_url_as(static_format='png')})") embed.add_field(name="Joined Server", value=member.joined_at.replace( microsecond=0) if member.joined_at else "Unknown") embed.add_field(name="Account Created", value=member.created_at.replace(microsecond=0)) embed.add_field(name="Roles", value=f"{len(roles)} roles" if len(", ".join(roles)) > 1000 else ", ".join(roles)) embed.set_thumbnail(url=member.avatar_url) await ctx.send(embed=embed)
async def select_guild(self, message, msg=None): guilds = {} for guild in await self.bot.state.smembers(f"user:{message.author.id}" ): guild = await self.bot.get_guild(int(guild)) channel = None for text_channel in await guild.text_channels(): if tools.is_modmail_channel(text_channel, message.author.id): channel = text_channel if not channel: guilds[str(guild.id)] = (guild.name, False) else: guilds[str(guild.id)] = (guild.name, True) if len(guilds) == 0: await message.channel.send(embed=ErrorEmbed( description= "Oops, no server found. Please change your Discord status to online and try again." )) return embeds = [] for chunk in [ list(guilds.items())[i:i + 10] for i in range(0, len(guilds), 10) ]: embed = Embed( title="Select Server", description= "Please select the server you want to send this message to. You can do so by reacting " "with the corresponding emote.", ) embed.set_footer(text="Use the reactions to flip pages.") for guild, value in chunk: embed.add_field( name=f"{len(embed.fields) + 1}: {value[0]}", value= f"{'Create a new ticket.' if value[1] is False else 'Existing ticket.'}\nServer ID: {guild}", ) embeds.append(embed) if msg: msg = await msg.edit(embed=embeds[0]) else: msg = await message.channel.send(embed=embeds[0]) await msg.add_reaction("◀") await msg.add_reaction("▶") for reaction in [ "1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣", "🔟" ][:len(embeds[0].fields)]: await msg.add_reaction(reaction) await self.bot.state.sadd( "reaction_menus", { "kind": "selection", "channel": msg.channel.id, "message": msg.id, "end": int(time.time()) + 180, "data": { "msg": message._data, "page": 0, "all_pages": [embed.to_dict() for embed in embeds], }, }, )