# Ensō~Chan - A Multi Purpose Discord Bot That Has Everything Your Server Needs! # Copyright (C) 2020 Hamothy # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . import datetime import io import string from asyncio.subprocess import Process from platform import python_version from time import time from typing import Optional, Union from PIL import Image from PIL.ImageOps import invert from discord import Embed, Role, File from discord import Member, TextChannel from discord import __version__ as discord_version from discord.ext.commands import bot_has_permissions, guild_only, Cog, group, cooldown, BucketType from discord.ext.commands import command from psutil import Process, virtual_memory from cogs.libs.functions import string_list, get_region, perms, detect_perms from cogs.libs.paginators import SimpleMenu def add_perms(embed, _list): """Add all the permission in the list to embed fields""" i = 0 while i < len(_list): embed.add_field(name=str(_list[i].split(":")[0]).strip(), value=f"<{_list[i].split('<')[1]}", inline=True) i += 1 return embed class Info(Cog): """(User/Server/Bot etc) Information!""" def __init__(self, bot): self.bot = bot @Cog.listener() async def on_ready(self): """Printing out that Cog is ready on startup""" print(f"{self.__class__.__name__} Cog has been loaded\n-----") @command(name="ping") async def ping(self, ctx): """Latency of the Bot (ms)""" await self.bot.generate_embed(ctx, desc=f"Pong! **{round(self.bot.latency * 1000)}ms**") @command(name="permissions", aliases=["perms"], usage="`[member|role]`") @guild_only() @bot_has_permissions(embed_links=True, add_reactions=True) async def perms(self, ctx, *, item: Optional[Union[Member, Role]]): """View all permissions for any Member/Role!""" # Defaults to author if no argument is given item = item if item else ctx.author if isinstance(item, Member): # Iterating through list of perms perms = [f"{perm.title().replace('_', ' ')}: {self.bot.tick if value else self.bot.cross}" for perm, value in item.guild_permissions] else: # Iterating through list of perms perms = [f"{perm.title().replace('_', ' ')}: {self.bot.tick if value else self.bot.cross}" for perm, value in item.permissions] middle = len(perms) // 2 f_half = perms[:middle] s_half = perms[middle:] first_page = Embed(description=f"**Item:** {item}", colour=self.bot.admin_colour, timestamp=datetime.datetime.utcnow()) first_page.set_footer(text=f"ID: {item.id}") second_page = Embed(description=f"**Item:** {item}", colour=self.bot.admin_colour, timestamp=datetime.datetime.utcnow()) second_page.set_footer(text=f"ID: {item.id}") # Add permissions to both of the embeds first = add_perms(first_page, f_half) second = add_perms(second_page, s_half) # Get the permissions of the channel perms = ctx.guild.me.permissions_in(ctx.message.channel) menu = SimpleMenu(0, item, perms, [first, second], self) await menu.start(ctx) @command(name="roleinfo", aliases=["ri"]) @guild_only() async def role_info(self, ctx, *, role: Role): """Retrieve information about any role!""" # Returns the permissions that the role has within the guild filtered = filter(lambda x: x[1], role.permissions) # Replace all "_" with " " in each item and join them together _perms = ",".join(map(lambda x: x[0].replace("_", " "), filtered)) # Capitalise every word in the array and filter out the permissions that are defined within the frozenset permission = string.capwords("".join(detect_perms(_perms, perms))) # Get all members within role member = string_list(role.members, 30, "Member") # Using emotes to represent bools mentionable = self.bot.tick if role.mentionable else self.bot.cross hoisted = self.bot.tick if role.hoist else self.bot.cross managed = self.bot.tick if role.managed else self.bot.cross # Description of the embed desc = f"{role.mention} **|** @{role} **<-- Colour:** {str(role.colour)}" \ f"\n**Position -->** #{role.position} / {len(ctx.guild.roles)}" \ f"\n** ID -->** {role.id}" # Set up Embed embed = Embed(title=f"@{role.name} Information", description=desc, colour=role.colour, timestamp=datetime.datetime.utcnow()) embed.set_thumbnail(url=ctx.guild.icon_url) embed.set_footer(text=f"ID: {role.id}") # Setting up fields fields = [ ("Creation At", role.created_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), True), (f"Members ({len(role.members)})", f"\nHumans: {len(list(filter(lambda m: not m.bot, role.members)))}" + f"\nBots: {len(list(filter(lambda m: m.bot, role.members)))}", True), (f"Misc", f"\nMentionable?: {mentionable}" f"\nHoisted?: {hoisted}" f"\nManaged?: {managed}", True), (f"List of Members ({len(role.members)})", member or "No Members In Role", False), ("Key Permissions", permission or "No Key Permissions", False) ] # Add fields to the embed for name, value, inline in fields: embed.add_field(name=name, value=value, inline=inline) await ctx.send(embed=embed) @command(name="rolelist", aliases=["rl"]) @guild_only() async def role_list(self, ctx): """Retrieve list of all roles in the server!""" # More readable name guild_roles = ctx.guild.roles # Get all guild roles role = string_list(guild_roles, 50, "Role") embed = Embed(title=f"{ctx.guild}'s Roles --> {len(ctx.guild.roles)}", description=role or "Guild Has No Roles", color=self.bot.random_colour(), timestamp=datetime.datetime.utcnow()) embed.set_footer(text=f"Guild ID: {ctx.guild.id}", icon_url=ctx.guild.icon_url) await ctx.send(embed=embed) @command(name="userinfo", aliases=["ui"]) @guild_only() @bot_has_permissions(embed_links=True) async def user_info(self, ctx, member: Optional[Member] = None): """User Information! (Created At/Joined/Roles etc)""" # Use member when mentioned # Use author if no member is mentioned member = ctx.author if not member else member # Get the member avatar userAvatar = member.avatar_url # Get total member roles role = string_list(member.roles, 20, "Role") # Returns the permissions that the user has within the guild filtered = filter(lambda x: x[1], member.guild_permissions) # Replace all "_" with " " in each item and join them together perms = ",".join(map(lambda x: x[0].replace("_", " "), filtered)) # Capitalise every word in the array and filter out the permissions that are defined within the frozenset permission = string.capwords("".join(map(str, detect_perms(perms, perms)))) embed = Embed( title=f"**User Information**", colour=self.bot.random_colour(), timestamp=datetime.datetime.utcnow()) embed.set_thumbnail(url=userAvatar) embed.set_footer(text=f"ID: {member.id}", icon_url=userAvatar) embed_fields = [("Name", member.mention, True), ("Tag", member.name, True), ("Discrim", f"#{member.discriminator}", True), ("Registered", member.created_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), True), ("Joined", member.joined_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), True), ("Top Role", member.top_role.mention, False), ("Roles", role or "No Roles", False), ("Key Permissions", permission or "No Key Permissions", False), ("Status", str(member.status).title(), True), ("Boosting Server", bool(member.premium_since), True), ("Bot", member.bot, True)] # Add fields to the embed for name, value, inline in embed_fields: embed.add_field(name=name, value=value, inline=inline) await ctx.send(embed=embed) @command(name="serverinfo", aliases=["si", "guildinfo", "gi"]) @guild_only() @bot_has_permissions(embed_links=True) async def server_info(self, ctx): """Guild Information! (Owner/Roles/Emojis etc)""" # Define guild icon and id guild_icon = ctx.guild.icon_url guild_id = ctx.guild.id # Getting permissions of the bot within the channel perms = ctx.guild.me.permissions_in(ctx.message.channel) # Retrieve the top role of the guild top_role = ctx.guild.roles[-1] # Get total guild roles role_string = string_list(ctx.guild.roles, 20, "Role") # Get total emojis emojis = string_list(ctx.guild.emojis, 20, "Emoji") # Defining a dictionary of the statuses member_status = { "online": 0, "idle": 0, "dnd": 0, "offline": 0 } # Iterating over the members and then storing the numbers in the dictionary for m in ctx.guild.members: member_status[str(m.status)] += 1 # Storing the statuses in an array statuses = [member_status["online"], member_status["idle"], member_status["dnd"], member_status["offline"]] # Set up embed to display all the server information embed = Embed(title="**Server Information**", colour=self.bot.random_colour(), timestamp=datetime.datetime.utcnow()) embed.set_thumbnail(url=guild_icon) embed.set_footer(text=f"ID: {guild_id}", icon_url=guild_icon) # Get the list of banned users from the server bans = len(await ctx.guild.bans()) if perms.ban_members else f"No Perms {self.bot.cross}" # Get the list of invites created for the server invites = len(await ctx.guild.invites()) if perms.manage_guild else f"No Perms {self.bot.cross}" # Define fields to be added into the embed fields = [("Owner", ctx.guild.owner.mention, True), ("Created", ctx.guild.created_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), False), ("Region", get_region(str(ctx.guild.region)), False), ("Statuses", f" {statuses[0]} " f" {statuses[1]} " f" {statuses[2]} " f" {statuses[3]} ", False), (f"Members ({len(ctx.guild.members)})", f"\nHumans: {len(list(filter(lambda m: not m.bot, ctx.guild.members)))}" f"\nBots: {len(list(filter(lambda m: m.bot, ctx.guild.members)))}" f"\nBanned: {bans}", True), (f"Channels ({len(ctx.guild.channels)})", f"\nText: {len(ctx.guild.text_channels)}" f"\nVoice: {len(ctx.guild.voice_channels)}" f"\nCategories: {len(ctx.guild.categories)}", True), ("Misc", f"Invites: {invites}" f"\nVerif Level: {ctx.guild.verification_level.name.capitalize()}" f"\nNitro Boosters: {len(ctx.guild.premium_subscribers)}", True), ("Top Role", top_role.mention, False), (f"Roles ({len(ctx.guild.roles)})", role_string or "No Roles In Guild", True), (f"Emojis ({len(ctx.guild.emojis)})", emojis or "No Emojis In Guild", False)] # Add fields to the embed for name, value, inline in fields: embed.add_field(name=name, value=value, inline=inline) await ctx.send(embed=embed) @command(name="channelinfo", aliases=["chinfo"]) @guild_only() @bot_has_permissions(embed_links=True) async def channel_info(self, ctx, channel: Optional[TextChannel] = None): """Channel Statistics! (Category/Created At etc)""" # Get information about the channel channel = ctx.channel if not channel else channel perms_synced = self.bot.tick if channel.permissions_synced else self.bot.cross nsfw = self.bot.tick if channel.is_nsfw() else self.bot.cross # Set up Embed desc = f"**Guild -->** {ctx.guild}" \ f"\n**Position -->** {f'#{channel.position} / {len(ctx.guild.channels)}'}" embed = Embed(title=f"Statistics For #{channel.name}", description=desc, timestamp=datetime.datetime.utcnow(), colour=self.bot.random_colour()) embed.set_thumbnail(url=ctx.guild.icon_url) embed.set_footer(text=f"ID: {channel.id}") # Setting up fields fields = [("Category", channel.category or self.bot.cross, True), ("Topic", channel.topic or self.bot.cross, True), ("\u200b", "\u200b", True), ("Perms Synced?", perms_synced, True), ("Nsfw?", nsfw, True), ("Creation At", channel.created_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), False)] # Add fields to the embed for name, value, inline in fields: embed.add_field(name=name, value=value, inline=inline) await ctx.send(embed=embed) @command(name="about") @bot_has_permissions(embed_links=True) async def checking_bot_stats(self, ctx): """Bot Statistics! (CPU/Mem Usage etc)""" stats = Embed(title=f"<:github:741000905364603010> Source Code | Ensō~Chan {self.bot.version}", url="https://github.com/sgoudham/Enso-Bot", colour=self.bot.admin_colour, timestamp=datetime.datetime.utcnow()) stats.set_thumbnail(url=self.bot.user.avatar_url) stats.set_footer(text=f"Requested by {ctx.author}", icon_url=ctx.author.avatar_url) # Grabbing technical statistics of the bot proc = Process() with proc.oneshot(): uptime = datetime.timedelta(seconds=time() - proc.create_time()) mem_total = virtual_memory().total / (1024 ** 2) mem_of_total = proc.memory_percent() mem_usage = mem_total * (mem_of_total / 100) uptime_hours, uptime_remainder = divmod(uptime.seconds, 3600) uptime_minutes, uptime_seconds = divmod(uptime_remainder, 60) frmt_uptime = f'{int(uptime_hours):01} Hour(s), {int(uptime_minutes):01} Minute(s), {int(uptime_seconds):01} Second(s)' # Grabbing total number of channels across all guilds in which the bot is present in channels = map(lambda m: len(m.channels), self.bot.guilds) # Setting up fields fields = [ ("Developer", f"{self.bot.hammyMention} | Hamothy#5619", False), ("Language | Library", f"<:python:747224674319990895> Python {python_version()} | <:discord:747224665553895544> Discord.py {discord_version}", False), ("<:discord:747224665553895544> Support Server", "[Here!](https://discord.com/invite/SZ5nexg)", True), ("<:invite:740998357643952139> Invite Link", "[Here!](https://top.gg/bot/716701699145728094)", True), ("❗ Current Prefix", ctx.prefix, True), ("Discord Stats", f"Guilds: {len(self.bot.guilds)}" f"\nChannels: {sum(list(channels))}" f"\nEmojis: {len(self.bot.emojis)}" f"\nCommands: {len(self.bot.commands)}" f"\nUsers: {len(self.bot.users):,}", True), ("Line Count", self.bot.line_count, True), ("Uptime", frmt_uptime, False), ("Memory Usage", f"{mem_usage:,.2f} / {mem_total:,.2f} MiB ({mem_of_total:.2f}%)", False)] # Add fields to the embed for name, value, inline in fields: stats.add_field(name=name, value=value, inline=inline) await ctx.send(embed=stats) @group(name="avatar", invoke_without_command=True, case_insensitive=True, usage="`[member]|greyscale|invert`") @bot_has_permissions(embed_links=True) async def get_user_avatar(self, ctx, *, member: Optional[Member] = None): """ Displaying Member's Avatar Member can be mentioned and their avatar will be displayed """ # Get member mentioned or set to author member = ctx.author if not member else member # Get the member avatar userAvatar = str(member.avatar_url) embed = Embed(title=f"{member}'s Avatar", url=userAvatar, colour=self.bot.admin_colour, timestamp=datetime.datetime.utcnow()) embed.set_image(url=userAvatar) embed.set_footer(text=f"Requested by {ctx.author}", icon_url=ctx.author.avatar_url) await ctx.send(embed=embed) @get_user_avatar.command(name="greyscale", aliases=["gs"]) @bot_has_permissions(embed_links=True) @cooldown(1, 2, BucketType.user) async def greyscale_user_avatar(self, ctx, *, member: Optional[Member] = None): """Get the greyscale avatar of the member""" # Get member mentioned or set to author member = ctx.author if not member else member attach = await member.avatar_url.read() image = Image.open(io.BytesIO(attach)).convert('LA') # Save new grayscale image as bytes file = io.BytesIO() image.save(file, format='PNG') file.seek(0) # Send image in an embed f = File(file, "image.png") embed = Embed(title=f"{member}'s Avatar | Greyscale", colour=self.bot.admin_colour, timestamp=datetime.datetime.utcnow()) embed.set_image(url="attachment://image.png") embed.set_footer(text=f"Requested by {ctx.author}", icon_url=ctx.author.avatar_url) await ctx.send(file=f, embed=embed) @get_user_avatar.command(name="invert", aliases=["negative"]) @bot_has_permissions(embed_links=True) @cooldown(1, 2, BucketType.user) async def greyscale_user_avatar(self, ctx, *, member: Optional[Member] = None): """Get the inverted avatar of the member""" # Get member mentioned or set to author member = ctx.author if not member else member attach = await member.avatar_url.read() image = Image.open(io.BytesIO(attach)).convert('RGB') inverted = invert(image) # Save new inverted image as bytes file = io.BytesIO() inverted.save(file, format='PNG') file.seek(0) # Send image in an embed f = File(file, "image.png") embed = Embed(title=f"{member}'s Avatar | Inverted", colour=self.bot.admin_colour, timestamp=datetime.datetime.utcnow()) embed.set_image(url="attachment://image.png") embed.set_footer(text=f"Requested by {ctx.author}", icon_url=ctx.author.avatar_url) await ctx.send(file=f, embed=embed) def setup(bot): bot.add_cog(Info(bot))