From 2d24ad86dbefee0b3750949ebfcd7b4fa42bd63b Mon Sep 17 00:00:00 2001 From: Hammy Date: Fri, 17 Sep 2021 13:59:25 +0100 Subject: [PATCH] Remove old python code --- bot/__init__.py | 622 ----------- bot/libs/__init__.py | 0 bot/libs/cache.py | 159 --- cogs/anime.py | 584 ---------- cogs/botlists.py | 77 -- cogs/error.py | 162 --- cogs/events.py | 313 ------ cogs/fun.py | 531 --------- cogs/guild.py | 543 --------- cogs/help.py | 803 -------------- cogs/info.py | 490 -------- cogs/interactive.py | 295 ----- cogs/libs/__init__.py | 0 cogs/libs/functions.py | 137 --- cogs/libs/modmail.py | 309 ------ cogs/libs/paginators.py | 395 ------- cogs/libs/starboard.py | 241 ---- cogs/moderation.py | 1354 ----------------------- cogs/owner.py | 246 ---- cogs/relationship.py | 293 ----- images/FunCommands/choking.txt | 20 - images/FunCommands/cuddling.txt | 82 -- images/FunCommands/digby.txt | 13 - images/FunCommands/hugging.txt | 43 - images/FunCommands/killing.txt | 22 - images/FunCommands/kissing.txt | 47 - images/FunCommands/patting.txt | 40 - images/FunCommands/slapping.txt | 40 - images/homies/AllMyHomies.jpg | Bin 68374 -> 0 bytes images/homies/impact/Impacted.ttf | Bin 107032 -> 0 bytes images/homies/impact/impact.ttf | Bin 136076 -> 0 bytes images/homies/impact/unicode.impact.ttf | Bin 77666 -> 0 bytes main.py | 37 - requirements.txt | 85 -- 34 files changed, 7983 deletions(-) delete mode 100644 bot/__init__.py delete mode 100644 bot/libs/__init__.py delete mode 100644 bot/libs/cache.py delete mode 100644 cogs/anime.py delete mode 100644 cogs/botlists.py delete mode 100644 cogs/error.py delete mode 100644 cogs/events.py delete mode 100644 cogs/fun.py delete mode 100644 cogs/guild.py delete mode 100644 cogs/help.py delete mode 100644 cogs/info.py delete mode 100644 cogs/interactive.py delete mode 100644 cogs/libs/__init__.py delete mode 100644 cogs/libs/functions.py delete mode 100644 cogs/libs/modmail.py delete mode 100644 cogs/libs/paginators.py delete mode 100644 cogs/libs/starboard.py delete mode 100644 cogs/moderation.py delete mode 100644 cogs/owner.py delete mode 100644 cogs/relationship.py delete mode 100644 images/FunCommands/choking.txt delete mode 100644 images/FunCommands/cuddling.txt delete mode 100644 images/FunCommands/digby.txt delete mode 100644 images/FunCommands/hugging.txt delete mode 100644 images/FunCommands/killing.txt delete mode 100644 images/FunCommands/kissing.txt delete mode 100644 images/FunCommands/patting.txt delete mode 100644 images/FunCommands/slapping.txt delete mode 100644 images/homies/AllMyHomies.jpg delete mode 100644 images/homies/impact/Impacted.ttf delete mode 100644 images/homies/impact/impact.ttf delete mode 100644 images/homies/impact/unicode.impact.ttf delete mode 100644 main.py delete mode 100644 requirements.txt diff --git a/bot/__init__.py b/bot/__init__.py deleted file mode 100644 index 9209de95..00000000 --- a/bot/__init__.py +++ /dev/null @@ -1,622 +0,0 @@ -# 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 os -import pathlib -import random - -import asyncpg as asyncpg -import discord -from decouple import config -from discord import Colour, Embed -from discord.ext import commands, tasks -from discord.ext.commands import when_mentioned_or - -from bot.libs.cache import MyCoolCache - -# Counter for cycling statuses -counter = 0 - -# Get DB information from .env -password = config('DB_PASS') -host = config('DB_HOST') -user = config('DB_USER') -port = config('DB_PORT') -db = config('DB_NAME') - -# Getting the bot token from environment variables -API_TOKEN = config('DISCORD_TOKEN') - - -class Bot(commands.Bot): - def __init__(self, **options): - - async def get_prefix(bot, message): - """Allow the commands to be used with mentioning the bot""" - - if message.guild is None: - return "." - return when_mentioned_or(self.get_prefix_for_guild(message.guild.id))(bot, message) - - intents = discord.Intents.default() - intents.members = True - super().__init__(intents=intents, command_prefix=get_prefix, case_insensitive=True, **options) - self.db = None - self.line_count = None - self.description = 'All current available commands within Ensō~Chan', - self.owner_id = 154840866496839680 # Your unique User ID - self.admin_colour = Colour(0x62167a) # Admin Embed Colour - self.version = "0.8.2" # Version number of Ensō~Chan - self.remove_command("help") # Remove default help command - - # Instance variables for Enso - self.hammyMention = '<@154840866496839680>' - self.hammy_role_ID = "<@&715412394968350756>" - self.blank_space = "\u200b" - self.enso_embedmod_colours = Colour(0x62167a) - self.enso_ensochancommands_Mention = "<#721449922838134876>" - self.enso_ensochancommands_ID = 721449922838134876 - self.enso_verification_ID = 728034083678060594 - self.enso_selfroles_ID = 722347423913213992 - self.enso_guild_ID = 663651584399507476 - self.enso_newpeople_ID = 669771571337887765 - self.enso_modmail_ID = 728083016290926623 - self.enso_feedback_ID = 739807803438268427 - - # Cross/Tick Emojis - self.cross = "<:xMark:746834944629932032>" - self.tick = "<:greenTick:746834932936212570>" - - # Instance variables for cache - self.enso_cache = {} - self.modmail_cache = {} - self.starboard_cache = {} - self.starboard_messages_cache = {} - self.member_cache = MyCoolCache(100) - - async def create_connection(): - """Setting up connection using asyncpg""" - - self.db = await asyncpg.create_pool( - host=host, - port=int(port), - user=user, - password=password, - database=db, - loop=self.loop) - - async def line_count(): - """Getting the line count of the project""" - - code = 0 - comments = 0 - blank = 0 - file_amount = 0 - ENV = "venv" - - for path, _, files in os.walk("."): - if ".local" in path: - continue - for name in files: - file_dir = str(pathlib.PurePath(path, name)) - # Ignoring the venv directory - if not name.endswith(".py") or ENV in file_dir: - continue - file_amount += 1 - with open(file_dir, "r", encoding="utf-8") as file: - for line in file: - if line.strip().startswith("#"): - comments += 1 - elif not line.strip(): - blank += 1 - else: - code += 1 - - # Adding up the total lines of code - total = comments + blank + code - - self.line_count = f"Code: {code}\n" \ - f"Commentary: {comments}\n" \ - f"Blank: {blank}\n" \ - f"Total: {total}\n" \ - f"Files: {file_amount}" - - async def startup_cache_log(): - """Store the guilds/modmail systems in cache from the database on startup""" - - # Setup up pool connection - pool = self.db - async with pool.acquire() as conn: - - # Query to get all records of guilds that the bot is in - try: - results = await conn.fetch("""SELECT * FROM guilds""") - - # Store the guilds information within cache - for row in results: - self.enso_cache[row["guild_id"]] = {"prefix": row["prefix"], - "modlogs": row["modlogs"], - "roles_persist": row["roles_persist"]} - # Catch errors - except asyncpg.PostgresError as e: - print("PostGres Error: Guild Records Could Not Be Loaded Into Cache On Startup", e) - - # Query to get all records of modmails within guilds - try: - results = await conn.fetch("""SELECT * FROM moderatormail""") - - # Store the information for modmail within cache - for row in results: - self.modmail_cache[row["guild_id"]] = {"modmail_channel_id": row["modmail_channel_id"], - "message_id": row["message_id"], - "modmail_logging_channel_id": - row["modmail_logging_channel_id"]} - # Catch errors - except asyncpg.PostgresError as e: - print("PostGres Error: Modmail Records Could Not Be Loaded Into Cache On Startup", e) - - # Query to get all records of starboards within guilds - try: - results = await conn.fetch("SELECT * FROM starboard") - - # Store the information for starboard within cache - for row in results: - self.starboard_cache[row["guild_id"]] = {"channel_id": row["channel_id"], - "min_stars": row["min_stars"]} - - # Catch errors - except asyncpg.PostgresError as e: - print("PostGres Error: Starboard Records Could Not Be Loaded Into Cache On Startup", e) - - # Query to get all records of starboard messages within guilds - try: - results = await conn.fetch("SELECT * FROM starboard_messages") - - for row in results: - self.starboard_messages_cache[(row["root_message_id"], row["guild_id"])] = { - "star_message_id": row["star_message_id"], - "stars": row["stars"]} - - # Catch errors - except asyncpg.PostgresError as e: - print("PostGres Error: Starboard Records Could Not Be Loaded Into Cache On Startup", e) - - # Establish Database Connection - self.loop.run_until_complete(create_connection()) - # Get line count of project - self.loop.run_until_complete(line_count()) - # Load Information Into Cache - self.loop.run_until_complete(startup_cache_log()) - - @tasks.loop(minutes=2, reconnect=True) - async def change_status(): - """Creating custom statuses as background task""" - - global counter - # Waiting for the bot to ready - await self.wait_until_ready() - - # Define array of statuses - looping_statuses = [ - discord.Activity( - type=discord.ActivityType.watching, - name=f"{len(self.users)} Weebs | {self.version}"), - discord.Activity( - type=discord.ActivityType.watching, - name=f"Hamothy | Real Life | {self.version}"), - discord.Activity( - type=discord.ActivityType.watching, - name=f"Hamothy Program | {self.version}"), - discord.Game(name=f".help | {self.version}") - ] - - # Check if the counter is at the end of the array - if counter == (len(looping_statuses) - 1): - # Reset the loop - counter = 0 - else: - # Increase the counter - counter += 1 - - # Display the next status in the loop - await self.change_presence(activity=looping_statuses[counter]) - - # Start the background task(s) - change_status.start() - - # --------------------------------------------!Cache Section!------------------------------------------------------- - - def store_cache(self, guild_id, prefix, modlogs, roles_persist): - """Storing guild information within cache""" - - self.enso_cache[guild_id] = {"prefix": prefix, - "modlogs": modlogs, - "roles_persist": roles_persist} - - def del_cache(self, guild_id): - """Deleting the entry of the guild within the cache""" - - del self.enso_cache[guild_id] - - async def check_cache(self, member_id, guild_id): - """Checks if member is in the member cache""" - - # Return key-value pair if member is already in the cache - if (member_id, guild_id) in self.member_cache.cache: - return self.member_cache.cache[member_id, guild_id] - - else: - # Setup pool connection - pool = self.db - async with pool.acquire() as conn: - - # Get the author's/members row from the Members Table - try: - select_query = """SELECT * FROM members WHERE member_id = $1 and guild_id = $2""" - result = await conn.fetchrow(select_query, member_id, guild_id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Member {member_id} From Guild {guild_id}" - "Record Could Not Be Retrieved When Checking Cache", e) - - # Store it in cache - else: - dict_items = {"married": result["married"], - "married_date": result["married_date"], - "muted_roles": result["muted_roles"], - "roles": result["roles"]} - self.member_cache.store_cache((member_id, guild_id), dict_items) - - return self.member_cache.cache[member_id, guild_id] - - # --------------------------------------------!End Cache Section!--------------------------------------------------- - - # --------------------------------------------!Starboard Section!--------------------------------------------------- - - def cache_store_starboard(self, guild_id, channel_id, min_stars): - """Storing starboard within cache""" - - self.starboard_cache[guild_id] = {"channel_id": channel_id, - "min_stars": min_stars} - - def get_starboard_channel(self, guild_id): - """Returning the starboard channel of the guild""" - - starboard = self.starboard_cache.get(guild_id) - return starboard.get("channel_id") if starboard else None - - def get_starboard_min_stars(self, guild_id): - """Returning the starboard minimum stars of the guild""" - - starboard = self.starboard_cache.get(guild_id) - return starboard.get("min_stars") if starboard else None - - def update_starboard_channel(self, guild_id, channel_id): - """Update the starboard channel""" - - self.starboard_cache[guild_id]["channel_id"] = channel_id - - def update_starboard_min_stars(self, guild_id, min_stars): - """Update the starboard minimum stars""" - - self.starboard_cache[guild_id]["min_stars"] = min_stars - - def delete_starboard(self, guild_id): - """Deleting the starboard of the guild""" - - del self.starboard_cache[guild_id] - - def delete_starboard_messages(self, in_guild_id): - - # Array to store keys to be removed - keys_to_remove = [] - - # For every starboard message in cache - for (root_msg_id, guild_id) in self.starboard_messages_cache: - # if the guild_id passed in is equal to the guild_id within the cache - if in_guild_id == guild_id: - # Store key within array - keys_to_remove.append((root_msg_id, guild_id)) - - # Iterate through the array and then pop the keys from cache - for key in keys_to_remove: - self.starboard_messages_cache.pop(key) - - def cache_store_starboard_message(self, root_message_id, guild_id, star_message_id): - """Store the starboard messages within cache""" - - self.starboard_messages_cache[root_message_id, guild_id] = {"star_message_id": star_message_id, - "stars": 1} - - def update_starboard_message_id(self, root_message_id, guild_id, star_message_id): - """Update the stored starboard message""" - - self.starboard_messages_cache[root_message_id, guild_id]["star_message_id"] = star_message_id - - def del_starboard_star_message_id(self, root_message_id, guild_id): - """Set the star message id to None""" - - self.starboard_messages_cache[root_message_id, guild_id]["star_message_id"] = None - - def update_starboard_message_stars(self, root_message_id, guild_id, reactions): - """Update the stored starboard message""" - - self.starboard_messages_cache[root_message_id, guild_id]["stars"] = reactions - - def check_root_message_id(self, root_message_id, guild_id): - """Check if the original message is stored within the cache""" - - # Return value if message is already in the cache - if (root_message_id, guild_id) in self.starboard_messages_cache: - return True - else: - return False - - async def check_starboard_messages_cache(self, root_message_id, guild_id): - """Check if the message is already in the cache""" - - # Return value if message is already in the cache - if (root_message_id, guild_id) in self.starboard_messages_cache: - return self.starboard_messages_cache[root_message_id, guild_id]["star_message_id"], \ - self.starboard_messages_cache[root_message_id, guild_id]["stars"] - - else: - # Setup pool connection - pool = self.db - async with pool.acquire() as conn: - - # Get the starboard row from the starboard_messages table - try: - select_query = """SELECT * FROM starboard_messages WHERE root_message_id = $1 AND guild_id = $2""" - result = await conn.fetchrow(select_query, root_message_id, guild_id) - - # Catch errors - except asyncpg.PostgresError as e: - print(e) - - # Store it in cache - else: - if result: - self.starboard_messages_cache[root_message_id, guild_id] = { - "star_message_id": result["star_message_id"], - "stars": result["stars"]} - - # Returning as separate variables for better readability - star_message_id = self.starboard_messages_cache[root_message_id, guild_id]["star_message_id"] - stars = self.starboard_messages_cache[root_message_id, guild_id]["stars"] - - return star_message_id, stars - else: - return None, 0 - - # --------------------------------------------!EndStarboard Section!------------------------------------------------- - - # --------------------------------------------!Modmail Section!----------------------------------------------------- - - def cache_store_modmail(self, guild_id, modmail_channel, message, modmail_logging_channel): - """Storing all modmail channels within cache""" - - self.modmail_cache[guild_id] = {"modmail_channel_id": modmail_channel, - "message_id": message, - "modmail_logging_channel_id": modmail_logging_channel} - - def get_modmail(self, guild_id): - """Returning the modmail system of the guild""" - - return self.modmail_cache.get(guild_id) - - def update_modmail(self, guild_id, channel_id): - """Update the modmail channel""" - - self.modmail_cache[guild_id]["modmail_logging_channel_id"] = channel_id - - def delete_modmail(self, guild_id): - """Deleting the modmail system of the guild within the Cache""" - - del self.modmail_cache[guild_id] - - # --------------------------------------------!EndModmail Section!-------------------------------------------------- - - # --------------------------------------------!RolePersist Section!------------------------------------------------- - - def get_roles_persist(self, guild_id): - """Returning rolespersist value of the guild""" - - role_persist = self.enso_cache.get(guild_id) - return role_persist.get("roles_persist") if role_persist else None - - async def update_role_persist(self, guild_id, value): - """Update the rolepersist value of the guild (Enabled or Disabled)""" - - # Setup up pool connection - pool = self.db - async with pool.acquire() as conn: - - # Query for updating rolepersist values For guilds - try: - update_query = """UPDATE guilds SET roles_persist = $1 WHERE guild_id = $2""" - await conn.execute(update_query, value, guild_id) - - # Catch error - except asyncpg.PostgresError as e: - print(f"PostGres Error: RolePersist For Guild {guild_id} Could Not Be Updated", e) - - # Store in cache - else: - self.enso_cache[guild_id]["roles_persist"] = value - - # --------------------------------------------!End RolePersist Section!--------------------------------------------- - - # --------------------------------------------!ModLogs Section!----------------------------------------------------- - - async def storage_modlog_for_guild(self, ctx, channel_id, setup): - """Updating the modlog within the dict and database""" - - # Setup up pool connection - pool = self.db - async with pool.acquire() as conn: - - # Query to update modlogs within the database - try: - update_query = """UPDATE guilds SET modlogs = $1 WHERE guild_id = $2""" - rowcount = await conn.execute(update_query, channel_id, ctx.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print("PostGres Error: Modlogs Value In Guilds Table Could Not Be Updated/Setup", e) - - # Let the user know that modlogs channel has been updated/setup - else: - if setup: - print(rowcount, f"Modlog channel for guild {ctx.guild} has been Setup") - await self.generate_embed(ctx, desc=f"**Modlogs Channel** successfully setup in <#{channel_id}>" + - f"\nPlease refer to **{ctx.prefix}help** for any information") - else: - print(rowcount, f"Modlog channel for guild {ctx.guild} has been Updated") - await self.generate_embed(ctx, - desc=f"Modlog Channel for **{ctx.guild}** has been updated to <#{channel_id}>") - - # Store in cache - self.enso_cache[ctx.guild.id]["modlogs"] = channel_id - - def remove_modlog_channel(self, guild_id): - """Remove the value of modlog for the guild specified""" - - self.enso_cache[guild_id]["modlogs"] = None - - def get_modlog_for_guild(self, guild_id): - """Get the modlog channel of the guild that the user is in""" - - modlogs = self.enso_cache.get(guild_id) - return modlogs.get("modlogs") if modlogs else None - - # --------------------------------------------!End ModLogs Section!------------------------------------------------- - - # --------------------------------------------!Prefixes Section!---------------------------------------------------- - - async def storage_prefix_for_guild(self, ctx, prefix): - """Updating the prefix within the dict and database when the method is called""" - - # Setup up pool connection - pool = self.db - async with pool.acquire() as conn: - - # Query to update the existing prefix within the database - try: - update_query = """UPDATE guilds SET prefix = $1 WHERE guild_id = $2""" - rowcount = await conn.execute(update_query, prefix, ctx.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Prefix For Guild {ctx.guild.id} Could Not Be Updated", e) - - # Let the user know that the guild prefix has been updated - else: - print(rowcount, f"Guild prefix has been updated for guild {ctx.guild}") - await self.generate_embed(ctx, desc=f"Guild prefix has been updated to **{prefix}**") - - # Store in cache - self.enso_cache[ctx.guild.id]["prefix"] = prefix - - def get_prefix_for_guild(self, guild_id): - """Get the prefix of the guild that the user is in""" - - prefix = self.enso_cache[guild_id]["prefix"] - if prefix: - return prefix - return "." - - # --------------------------------------------!End Prefixes Section!------------------------------------------------ - - # --------------------------------------------!Roles/Colour/Embed Section!------------------------------------------ - - @staticmethod - def random_colour(): - """Generate a random hex colour""" - - return Colour(random.randint(0, 0xFFFFFF)) - - async def generate_embed(self, ctx, desc): - """Generate embed""" - - embed = Embed(description=desc, - colour=self.admin_colour) - - await ctx.send(embed=embed) - - async def store_roles(self, target, ctx, member): - """Storing user roles within database""" - - role_ids = ", ".join([str(r.id) for r in target.roles]) - - # Setup up pool connection - pool = self.db - async with pool.acquire() as conn: - - # Query to store existing roles of the member within the database - try: - update_query = """UPDATE members SET muted_roles = $1 WHERE guild_id = $2 AND member_id = $3""" - rowcount = await conn.execute(update_query, role_ids, ctx.guild.id, member.id) - result = await self.check_cache(member.id, member.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Roles Could Not Be Stored For Member {member.id} in Guild {member.guild.id}", e) - - # Print success - # Update cache - else: - result["muted_roles"] = role_ids - print(rowcount, f"Roles Added For User {member} in {ctx.guild}") - - async def clear_roles(self, member): - """Clear the roles when the user has been unmuted""" - - # Setup up pool connection - pool = self.db - async with pool.acquire() as conn: - - # Query to clear the existing role of the member from the database - try: - update = """UPDATE members SET muted_roles = NULL WHERE guild_id = $1 AND member_id = $2""" - rowcount = await conn.execute(update, member.guild.id, member.id) - result = await self.check_cache(member.id, member.guild.id) - - # Catch error - except asyncpg.PostgresError as e: - print(f"PostGres Error: Roles Could Not Be Cleared for Member {member.id} in Guild {member.guild.id}", - e) - - # Print success - # Update cache - else: - result["muted_roles"] = None - print(rowcount, f"Roles Cleared For User {member} in {member.guild.name}") - - # --------------------------------------------!End Roles/Colour/Embed Section!-------------------------------------- - - def execute(self): - """Load the cogs and then run the bot""" - - for file in os.listdir(f'.{os.sep}cogs'): - if file.endswith('.py'): - self.load_extension(f"cogs.{file[:-3]}") - - # Run the bot, allowing it to come online - try: - self.run(API_TOKEN) - except discord.errors.LoginFailure as e: - print(e, "Login unsuccessful.") diff --git a/bot/libs/__init__.py b/bot/libs/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/bot/libs/cache.py b/bot/libs/cache.py deleted file mode 100644 index 09dffa9a..00000000 --- a/bot/libs/cache.py +++ /dev/null @@ -1,159 +0,0 @@ -# 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 . - -# Built by karan#7508 -# Edited by Hamothy#5619 - -# TODO: UPDATE ALL COMMENTARY TO REFLECT OUR WORK - -import threading - - -class CachingCircularQueue: - # When this lock is enabled, only the function it was called in can change - # The state of this class - threadLock = threading.Lock() - - def __init__(self, size): - # The current queue - self.values = [] - # The maximum size of the queue - self.MAX_SIZE = size - - def push(self, value): - # thread safe - with self.threadLock: - # If the current size is less than the max size: - if len(self.values) < self.MAX_SIZE: - # Add the value to the end of the queue - self.values.append(value) - # Return None as the default return value of Push - return None - else: - # If the queue is full, add the value to the end of the list - self.values.append(value) - # Then remove and return the item at the start of the list - # Therefore we don't need to touch the size - return self.values.pop(0) - - def pop(self): - # thread safe - with self.threadLock: - # should never try to pop an empty queue, you fucked up - assert len(self.values) > 0 - # decrement the size - # return the first value of the array - return self.values.pop(0) - - def remove(self, value): - # To my knowledge, this method can be called when deleting a single member and many members??? - # So this should only be called to remove a single member at a time - with self.threadLock: - # Remove the value inside the array (value will be a tuple that is passed in) - # PRECONDITION, VALUE EXISTS IN CACHE, SO SHOULD EXIST IN LIST - if value in self.values: - self.values.remove(value) - - -class MyCoolCache: - threadLock = threading.Lock() - - def __init__(self, size): - self.MAX_SIZE = size - self.queue = CachingCircularQueue(size) - self.cache = {} - - def get_size(self): - """Return size of cache and queue""" - - return self.MAX_SIZE, len(self.cache), len(self.queue.values) - - def change_array_size(self, input_size): - """Dynamically change the size of the array""" - - # Making it thread-safe - with self.threadLock: - - # Increase the size of the array and the cache to the new size - if input_size > self.MAX_SIZE: - self.queue.MAX_SIZE = input_size - self.MAX_SIZE = input_size - - # Increase the size of the array while - # it's greater than the queue and - # less than the max value of the cache - elif self.MAX_SIZE > input_size > len(self.queue.values): - self.queue.MAX_SIZE = input_size - self.MAX_SIZE = input_size - - # Decrease the size of the queue and pop values in cache - else: - # Split Array into 2 and iterate through the queue and delete things - for value in self.queue.values[input_size:]: - self.cache.pop(value) - - # Make sure only the records up until the size specified are stored - self.queue.values = self.queue.values[:input_size] - - # Set max size of queue and cache to be the length of the new queue - self.queue.MAX_SIZE = len(self.queue.values) - self.MAX_SIZE = input_size - - def store_cache(self, key, dict_item): - """Store the value in queue/cache""" - - with self.threadLock: - has_key = True - # Assume the key exists in the cache - if key in self.cache: - # If the key is None, aka removed - if self.cache[key] is None: - has_key = False - else: - # Or doesn't exist - has_key = False - - # Then we don't have the key. - # In this case, we have to check if adding a key will exceed max size - if not has_key: - key_to_delete = self.queue.push(key) - # If the key is not None, that means the queue was full. We must delete an item. - - if key_to_delete is not None: - self.cache[key_to_delete] = None - self.cache[key] = dict_item - - def remove_many(self, in_guild_id): - # This method is to be used for when the bot has left a guild - with self.threadLock: - # Array to store keys to be removed - keys_to_remove = [] - - # For every member within the cache - for (member_id, guild_id) in self.cache: - # if the guild_id passed in is equal to the guild_id within the cache - if in_guild_id == guild_id: - # When removing a value from the cache due to a guild leave, permanently remove all values - # Yes it is expensive, however as this can run concurrently and we won't need the data available - # For this guild, it doesn't matter how long it takes, and will save in memory in the long term - - # Store key within array - keys_to_remove.append((member_id, guild_id)) - # Remove the key from the queue - self.queue.remove((member_id, guild_id)) - - # Iterate through the array and then pop the keys from cache - for key in keys_to_remove: - self.cache.pop(key) diff --git a/cogs/anime.py b/cogs/anime.py deleted file mode 100644 index 6698fd1b..00000000 --- a/cogs/anime.py +++ /dev/null @@ -1,584 +0,0 @@ -# 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 aiohttp -from decouple import config -from discord import Embed -from discord.ext.commands import Cog, group, bot_has_permissions, command, BadArgument, MissingRequiredArgument - -from cogs.libs.paginators import SimpleMenu, MWLMenu - -my_waifu_list_auth = config('MYWAIFULIST_AUTH') - - -class WaifuCommandNotFound(Exception): - """Exception raised for errors when user does not use the right arguments for waifu command - - Attributes: - waifu -- input command which cause the error - message -- explanation of the error - """ - - def __init__(self, waifu, ctx): - self.command = waifu - self.bot = ctx.bot - self.message = f"Error! Use **{ctx.prefix}help {self.command}** to see {self.command} commands" - super().__init__(self.message) - - def __str__(self): - return f'{self.command} -> {self.message}' - - -def store_in_dict(_dict, api): - """Store the waifu data in dicts""" - - # Store all the shows with the name as the key - for item in api: - _dict[item["id"]] = {} - for value in item: - store_dict(_dict, item, value) - - -def store_dict(dict_, key, value): - """Method to store waifu's in the new dict""" - - dict_[key["id"]][value] = key[value] - - -async def get_from_api(self, ctx, url): - """Retrieving data from API""" - - url = f"https://mywaifulist.moe/api/v1/{url}" - - # Retrieve random or daily waifu from API - async with aiohttp.ClientSession() as session: - async with session.get(url, headers=self.headers) as resp: - - # Store waifu's in dict when request is successful, else send an error - if resp.status == 200: - api_dict = await resp.json() - _dict = api_dict["data"] - - # Send error if something went wrong internally/while grabbing data from API - else: - await self.bot.generate_embed(ctx, desc="**Something went wrong with MyWaifuList!**") - - return _dict - - -async def post_from_api(self, i, ctx, term, _dict, url): - """Posting information to the API and getting data back""" - - data = {"term": term, - 'content-type': "application/json"} - - # Searching API for waifu(s) - async with aiohttp.ClientSession() as session: - async with session.post(url, data=data, headers=self.headers) as resp: - # Store waifu's in dict when request is successful, else send an error - if resp.status == 200: - api_data = await resp.json() - - # Send error if something went wrong internally/while grabbing data from API - else: - await self.bot.generate_embed(ctx, desc="**Something went wrong with MyWaifuList!**") - - # As long as data is returned - # Store all data from the api in dict - if len(api_data["data"]) > 1: - for item in api_data["data"]: - _dict[item["id"]] = {} - for value in item: - store_dict(_dict, item, value) - - # Get the instance of the bot - bot = ctx.guild.get_member(self.bot.user.id) - # Get the permissions of the channel - perms = bot.permissions_in(ctx.message.channel) - - # Send the menu to the display - menu = MWLMenu(i, perms, _dict, self.bot) - - return menu - - # Don't bother with pagination if only 1 item is returned by the API - elif len(api_data["data"]) == 1: - if api_data["data"][0]["type"] in ["Waifu", "Husbando"]: - await ctx.send(embed=waifu_embed(self, api_data["data"][0], False)) - return False - else: - await ctx.send(embed=anime_embed(self, api_data["data"][0], False)) - return False - - # When no waifu has been retrieved, send error message to the user - else: - await self.bot.generate_embed(ctx, desc="**Waifu/Anime Not Found!\n" - "Tips for a good search:\n" - "- Use the full name!\n" - f"- Use `{ctx.prefix}bsearch` to try and get better results!**") - return False - - -def anime_embed(self, anime, detailed): - """Generate embed of single anime's""" - - # Get all the data to be displayed in the embed - name = anime["name"] - anime_id = anime["id"] - og_name = anime["original_name"] - picture = anime["display_picture"] - url = anime["url"] - _type = anime["type"] - romaji_name = anime["romaji_name"] - - # Only setting the description if original name is returned from the API - desc = og_name if og_name else Embed.Empty - embed = Embed(title=name, description=desc, - url=url, - colour=self.bot.random_colour()) - embed.set_author(name=f"{_type} | ID: {anime_id}") - embed.set_image(url=picture) - if romaji_name: - embed.set_footer(text=f"{romaji_name} | Powered by MyWaifuList") - else: - embed.set_footer(text="Powered By MyWaifuList") - - if detailed: - release_date = anime["release_date"] - airing_start = anime["airing_start"] - airing_end = anime["airing_end"] - episode_count = anime["episode_count"] - - # Only setting the series date information if they exist - rel_date = release_date if release_date else self.bot.cross - air_start = airing_start if airing_start else self.bot.cross - air_end = airing_end if airing_end else self.bot.cross - ep_count = episode_count if episode_count else self.bot.cross - - fields = [("Airing Start Date", air_start, True), - ("Airing End Date", air_end, True), - ("\u200b", "\u200b", True), - ("Release Date", rel_date, True), - ("Episode Count", ep_count, True), - ("\u200b", "\u200b", True)] - - # Add fields to the embed - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - - return embed - - -def waifu_embed(self, waifu, _type): - """Generate embed of single waifu's""" - - # Get all the data to be displayed in the embed - name = waifu["name"] - og_name = waifu["original_name"] - picture = waifu["display_picture"] - url = waifu["url"] - waifu_id = waifu["id"] - likes = waifu["likes"] - trash = waifu["trash"] - waifu_type = waifu["type"] - - # Set different values for description based on the command - if _type == "random": - author = f"Random {waifu_type} | ID: {waifu_id}" - elif _type == "daily": - author = f"Daily {waifu_type} | ID: {waifu_id}" - else: - author = f"{waifu_type} | ID: {waifu_id}" - - desc = og_name if waifu["original_name"] else Embed.Empty - embed = Embed(title=name, description=desc, - colour=self.bot.random_colour(), - url=url) - embed.set_author(name=author) - embed.set_image(url=picture) - embed.set_footer(text=f"❤️ {likes} 🗑️ {trash} | Powered by MyWaifuList") - - return embed - - -async def detailed_waifu_embed(self, waifu, author, ctx): - """Generate embed of single waifu's (detailed)""" - - not_found = "https://media.discordapp.net/attachments/741072426984538122/748586578074664980/DzEZ4UsXgAAcFjN.png?width=423&height=658" - - # Get all the data to be displayed in the embed - name = waifu["name"] - waifu_id = waifu["id"] - url = waifu["url"] - picture = waifu["display_picture"] - likes = waifu["likes"] - trash = waifu["trash"] - - og_name = waifu["original_name"] - romaji_name = waifu["romaji_name"] - age = waifu["age"] - b_day = waifu["birthday_day"] - b_month = waifu["birthday_month"] - b_year = waifu["birthday_year"] - popularity_rank = waifu["popularity_rank"] - like_rank = waifu["like_rank"] - trash_rank = waifu["trash_rank"] - - height = waifu["height"] - weight = waifu["weight"] - waist = waifu["waist"] - bust = waifu["bust"] - hip = waifu["hip"] - - # Only setting up description if waifu og_name has a value - desc = f"**Waifu ID:** {waifu_id}\n" - desc += f"**Original Name:** {og_name}\n" if og_name else f"**Original Name:** {self.bot.cross}\n" - desc += f"**Romaji Name:** {romaji_name}\n" if romaji_name else f"**Romaji Name:** {self.bot.cross}\n" - - desc += f"\n**Age:** {age}\n" if age else f"**Age:** {self.bot.cross}\n" - desc += f"**Birthday-Day:** {b_day}\n" if b_day else f"**Birthday-Day:** {self.bot.cross}\n" - desc += f"**Birthday-Month:** {b_month}\n" if b_month else f"**Birthday-Month:** {self.bot.cross}\n" - desc += f"**Birthday-Year:** {b_year}\n" if b_year else f"**Birthday-Year:** {self.bot.cross}\n" - - height = f"**Height:** {height}cm" if height != "0.00" else f"**Height:** {self.bot.cross}" - weight = f"**Weight:** {weight}kg" if weight != "0.00" else f"**Weight:** {self.bot.cross}" - waist = f"**Waist:** {waist}cm" if waist != "0.00" else f"**Waist:** {self.bot.cross}" - bust = f"**Bust:** {bust}cm" if bust != "0.00" else f"**Bust:** {self.bot.cross}" - hip = f"**Hip:** {hip}cm" if hip != "0.00" else f"**Hip:** {self.bot.cross}" - - # Only setting up the ranks if they are returned - pop_rank_string = f"**Popularity Rank:** {popularity_rank}\n" if popularity_rank else f"**Popularity Rank:** {self.bot.cross}\n" - like_rank_string = f"**Like Rank:** {like_rank}\n" if like_rank else f"**Like Rank:** {self.bot.cross}\n" - trash_rank_string = f"**Trash Rank:** {trash_rank}\n" if trash_rank else f"**Trash Rank:** {self.bot.cross}\n" - - fields = [("Measurements", - f"{height}" - f"\n{weight}" - f"\n{waist}" - f"\n{bust}" - f"\n{hip}", True), - - ("Ranks", - f"{pop_rank_string}" - f"{like_rank_string}" - f"{trash_rank_string}", True)] - - # Only using image if it can be displayed, else display 404 image - picture_url = picture if picture.endswith((".jpeg", ".png", ".jpg")) else not_found - # Different titles depending on if author was given or not - title = f"True Love | {name}" if author else f"Detailed Waifu | {name}" - - detailed = Embed(title=title, description=desc, - colour=self.bot.random_colour(), - url=url) - detailed.set_image(url=picture_url) - detailed.set_footer(text=f"❤️ {likes} 🗑️ {trash} | Powered by MyWaifuList") - - # Add fields to the embed - for name, value, inline in fields: - detailed.add_field(name=name, value=value, inline=inline) - - # Get the permissions of the channel - perms = ctx.guild.me.permissions_in(ctx.message.channel) - - if author: - menu = SimpleMenu(0, "User Information", perms, [author, detailed], self) - await menu.start(ctx) - else: - return detailed - - -async def user_embed(self, user, ctx): - """Generate embed of user profile information""" - - love = False - - # Get all the data to be displayed in the embed - name = user["name"] - avatar = user["avatar"] if user[ - "avatar"] else "https://media.discordapp.net/attachments/741072426984538122/748586578074664980/DzEZ4UsXgAAcFjN.png?width=423&height=658" - joined = user["joined"] - id = user["id"] - waifus_created = user["waifus_created"] - waifus_liked = user["waifus_liked"] - waifus_trashed = user["waifus_trashed"] - main_love = user["true_love"] - profile_url = f"https://mywaifulist.moe/user/{id}" - - date_time_obj = datetime.datetime.strptime(joined, '%Y-%m-%d %H:%M:%S') - joined_at = date_time_obj.strftime("%a, %b %d, %Y\n%I:%M:%S %p") - - desc = f"**Waifu's Created:** {waifus_created}" \ - f"\n**Waifu's Liked:** {waifus_liked}" \ - f"\n**Waifu's Trashed:** {waifus_trashed}" - author = Embed(title=name, description=desc, - colour=self.bot.random_colour(), - url=profile_url) - author.add_field(name="Joined Date", value=joined_at, inline=False) - author.set_thumbnail(url=avatar) - author.set_footer(text=f"User ID: {id} | Powered by MyWaifuList") - - if main_love["slug"]: - love = True - slug = main_love["slug"] - - # Get true love details from the API - true_love = await get_from_api(self, ctx, f"waifu/{slug}") - await detailed_waifu_embed(self, true_love, author, ctx) - - return author, love - - -class Anime(Cog): - """ - Search MyWaifuList for Waifu's, Anime's and more! - Please keep in mind that this API is in ALPHA (And it is a community driven website.) - Searches might not return fully detailed results and images may be missing - """ - - # TODO: ADD AIRING SHOWS BY SEASON COMMAND - # TODO: ADD USER WAIFUS COMMAND - - def __init__(self, bot): - self.bot = bot - self.headers = {'apikey': my_waifu_list_auth} - - @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-----") - - @group(name="airing", invoke_without_command=True, case_insensitive=True, usage="`anime|best|popular|trash`") - @bot_has_permissions(embed_links=True, add_reactions=True) - async def airing(self, ctx): - """ - Display's airing anime and waifu's within those anime's - """ - - error = WaifuCommandNotFound(ctx.command, ctx) - await self.bot.generate_embed(ctx, desc=error.message) - - @airing.command(name="trash", aliases=["worst", "garbage"]) - @bot_has_permissions(embed_links=True, add_reactions=True) - async def airing_trash(self, ctx): - """Get the worst waifu’s from the airing anime""" - - # Variables to set up the reaction menu - i = 0 - airing_trash = {} - trash_waifus = await get_from_api(self, ctx, "airing/trash") - store_in_dict(airing_trash, trash_waifus) - - # Get the instance of the bot - bot = ctx.guild.get_member(self.bot.user.id) - # Get the permissions of the channel - perms = bot.permissions_in(ctx.message.channel) - - # Send the menu to the display - menu = MWLMenu(i, perms, airing_trash, self.bot) - await menu.start(ctx) - - @airing.command(name="popular", aliases=["pop"]) - @bot_has_permissions(embed_links=True, add_reactions=True) - async def airing_popular(self, ctx): - """Get the most popular waifu’s from the airing anime""" - - # Variables to setup the reaction menu - i = 0 - airing_popular = {} - popular_waifus = await get_from_api(self, ctx, "airing/popular") - store_in_dict(airing_popular, popular_waifus) - - # Get the instance of the bot - bot = ctx.guild.get_member(self.bot.user.id) - # Get the permissions of the channel - perms = bot.permissions_in(ctx.message.channel) - - # Send the menu to the display - menu = MWLMenu(i, perms, airing_popular, self.bot) - await menu.start(ctx) - - @airing.command(name="best") - @bot_has_permissions(embed_links=True, add_reactions=True) - async def airing_best(self, ctx): - """Get the best waifu’s from the airing anime""" - - # Local Variable i to allow the pages to be modified - i = 0 - airing_best = {} - best_waifus = await get_from_api(self, ctx, "airing/best") - store_in_dict(airing_best, best_waifus) - - # Get the instance of the bot - bot = ctx.guild.get_member(self.bot.user.id) - # Get the permissions of the channel - perms = bot.permissions_in(ctx.message.channel) - - # Send the menu to the display - menu = MWLMenu(i, perms, airing_best, self.bot) - await menu.start(ctx) - - @airing.command(name="anime", aliases=["show", "series"]) - @bot_has_permissions(embed_links=True, add_reactions=True) - async def airing_anime(self, ctx): - """Display the current airing anime""" - - # Local Variable i to allow the pages to be modified - i = 0 - anime_dict = {} - animes = await get_from_api(self, ctx, "airing") - store_in_dict(anime_dict, animes) - - # Get the instance of the bot - bot = ctx.guild.get_member(self.bot.user.id) - # Get the permissions of the channel - perms = bot.permissions_in(ctx.message.channel) - - # Send the menu to the display - menu = MWLMenu(i, perms, anime_dict, self.bot) - await menu.start(ctx) - - @group(name="waifu", invoke_without_command=True, case_insensitive=True, usage="`daily|random`") - @bot_has_permissions(embed_links=True) - async def waifu(self, ctx): - """ - Waifu's that are retrieved from MyWaifuList - """ - - error = WaifuCommandNotFound(ctx.command, ctx) - await self.bot.generate_embed(ctx, desc=error.message) - - @waifu.command(name="daily") - @bot_has_permissions(embed_links=True) - async def daily_waifu(self, ctx): - """Returns the Daily Waifu from MyWaifuList""" - - waifu = await get_from_api(self, ctx, "meta/daily") - await ctx.send(embed=waifu_embed(self, waifu, "daily")) - - @waifu.command(name="random", aliases=["rnd"]) - @bot_has_permissions(embed_links=True) - async def random_waifu(self, ctx): - """Returning a Random Waifu from MyWaifuList""" - - waifu = await get_from_api(self, ctx, "meta/random") - await ctx.send(embed=waifu_embed(self, waifu, "random")) - - @group(name="anime", aliases=["series", "shows"], - invoke_without_command=True, case_insensitive=True, - usage="`|waifu `") - @bot_has_permissions(embed_links=True) - async def anime(self, ctx, term: int): - """Returning information about a given series (MWL ID ONLY)""" - - anime = await get_from_api(self, ctx, f"series/{term}") - await ctx.send(embed=anime_embed(self, anime, True)) - - @anime.command(name="waifu", usage="``") - @bot_has_permissions(embed_links=True) - async def anime_waifus(self, ctx, term: int): - """Return the waifu's of the given anime (MWL ID ONLY)""" - - i = 0 - anime_waifus = {} - waifus = await get_from_api(self, ctx, f"series/{term}/waifus") - - if len(waifus) > 0: - for item in waifus: - if item["type"] in ["Waifu", "Husbando"]: - anime_waifus[item["id"]] = {} - for value in item: - store_dict(anime_waifus, item, value) - - # Get the instance of the bot - bot = ctx.guild.get_member(self.bot.user.id) - # Get the permissions of the channel - perms = bot.permissions_in(ctx.message.channel) - - # Send the menu to the display - menu = MWLMenu(i, perms, anime_waifus, self.bot) - await menu.start(ctx) - - else: - await self.bot.generate_embed(ctx, desc="**No Waifu's/Husbando's To Be Displayed!**") - - @command("detailedwaifu", aliases=["dwaifu"], usage="``") - @bot_has_permissions(embed_links=True) - async def detailed_waifu(self, ctx, term: int): - """Returns detailed information about a waifu (MWL ID ONLY)""" - - waifu = await get_from_api(self, ctx, f"waifu/{term}") - embed = await detailed_waifu_embed(self, waifu, None, ctx) - await ctx.send(embed=embed) - - @command(name="profile", aliases=["user"], usage="``") - @bot_has_permissions(embed_links=True) - async def mwl_user_profile(self, ctx, term: int): - """Returning the MWL User Profile requested""" - - user = await get_from_api(self, ctx, f"user/{term}") - embed, love = await user_embed(self, user, ctx) - if not love: - await ctx.send(embed=embed) - - @command(name="search", aliases=["lookup"], usage="``") - @bot_has_permissions(embed_links=True, add_reactions=True) - async def search(self, ctx, *, term: str): - """Search the entire website! (Anime|Manga|Waifus|Husbandos)""" - - # Local Variable i to allow the index of the embeds to be modified - i = 0 - - anime_or_waifu = {} - url = "https://mywaifulist.moe/api/v1/search/" - - if menu := await post_from_api(self, i, ctx, term, anime_or_waifu, url): - await menu.start(ctx) - - @command(name="betasearch", aliases=["bsearch", "betalookup", "blookup"], usage="``") - @bot_has_permissions(embed_links=True, add_reactions=True) - async def beta_search(self, ctx, *, term: str): - """Search the entire website - more aggressive searching! (Anime|Manga|Waifus|Husbandos)""" - - # Local Variable i to allow the index of the embeds to be modified - i = 0 - - anime_or_waifu = {} - url = "https://mywaifulist.moe/api/v1/search/beta" - - if menu := await post_from_api(self, i, ctx, term, anime_or_waifu, url): - await menu.start(ctx) - - @anime.error - @anime_waifus.error - @mwl_user_profile.error - @detailed_waifu.error - async def mlsetup_command_error(self, ctx, exc): - """Catching error if ID is not recognised""" - - if isinstance(exc, BadArgument): - text = "**MyWaifuList ID Not Detected... Aborting Process**" - await self.bot.generate_embed(ctx, desc=text) - elif isinstance(exc, MissingRequiredArgument): - text = "Required Argument(s) Missing!" \ - f"\nUse **{ctx.prefix}help** to find how to use **{ctx.command}**" - await self.bot.generate_embed(ctx, desc=text) - - -def setup(bot): - bot.add_cog(Anime(bot)) diff --git a/cogs/botlists.py b/cogs/botlists.py deleted file mode 100644 index ee92b7c7..00000000 --- a/cogs/botlists.py +++ /dev/null @@ -1,77 +0,0 @@ -# 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 aiohttp -import dbl -import statcord -from decouple import config -from discord.ext import commands, tasks - -statcord_auth = config("STATCORD_AUTH") -disforge_auth = config('DISFORGE_AUTH') -disc_bots_gg_auth = config('DISCORD_BOTS_BOTS_AUTH') -top_gg_auth = config('TOP_GG_AUTH') - - -async def post_bot_stats(self): - """Update guild count on bot lists""" - - async with aiohttp.ClientSession() as session: - await session.post(f"https://discord.bots.gg/api/v1/bots/{self.user.id}/stats", - data={"guildCount": {len(self.guilds)}, - "Content-Type": "application/json"}, - headers={'Authorization': disc_bots_gg_auth}) - - await session.post(f"https://disforge.com/api/botstats/{self.user.id}", - data={"servers": {len(self.guilds)}}, - headers={'Authorization': disforge_auth}) - - -class BotLists(commands.Cog): - """Handles interactions with the top.gg API""" - - def __init__(self, bot): - self.bot = bot - self.token = top_gg_auth - self.dblpy = dbl.DBLClient(self.bot, self.token) - self.key = f"statcord.com-{statcord_auth}" - self.api = statcord.Client(self.bot, self.key) - self.api.start_loop() - - @tasks.loop(hours=1, reconnect=True) - async def post_updates(): - """Post updates to botlists""" - - await self.bot.wait_until_ready() - - try: - await post_bot_stats(self.bot) - await self.dblpy.post_guild_count() - except Exception as e: - print(e) - else: - print("Server count posted successfully") - - # Start the background task(s) - post_updates.start() - - @commands.Cog.listener() - async def on_command(self, ctx): - self.api.command_run(ctx) - - -def setup(bot): - bot.add_cog(BotLists(bot)) diff --git a/cogs/error.py b/cogs/error.py deleted file mode 100644 index 62cb005b..00000000 --- a/cogs/error.py +++ /dev/null @@ -1,162 +0,0 @@ -# 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 string - -from discord import Forbidden, Embed -from discord.ext import commands -from discord.ext.commands import Cog - - -async def send_error(ctx, perms, embed): - """ - Sending error message to the user - Only send error message if the channel permissions allow it - """ - - if perms.send_messages and perms.embed_links: - await ctx.send(embed=embed) - elif perms.send_messages: - await ctx.send( - "Error Message Not Sent.\nMake sure that the bot has the **Embed Links** Permission") - else: - print("Error: Error Handling Message Could Not Be Sent") - - -async def on_bot_forbidden(ctx, perms, args2): - """Handles Missing Bot Permissions Errors""" - - # Convert list into string of the missing permissions - missing_perms = string.capwords(", ".join(args2.missing_perms).replace("_", " ")) - - embed = Embed( - description=f"{ctx.bot.cross} I Need **{missing_perms}** Permission(s) to Execute This Command! {ctx.bot.cross}", - colour=ctx.bot.admin_colour) - - await send_error(ctx, perms, embed) - - -async def on_command_forbidden(ctx, perms): - """Handles Forbidden Error""" - - embed = Embed(description=f"**{ctx.bot.cross} I Don't Have Permissions To Execute This Command {ctx.bot.cross}**", - colour=ctx.bot.admin_colour) - - await send_error(ctx, perms, embed) - - -async def on_command_bad_argument(ctx, perms): - """Handles Bad Argument Errors (Argument can't be read properly)""" - - embed = Embed(description=f"**{ctx.bot.cross} Uh oh! Couldn't find anyone to mention! Try again! {ctx.bot.cross}**", - colour=ctx.bot.admin_colour) - - await send_error(ctx, perms, embed) - - -async def on_command_not_found(ctx, perms): - """Handles the command not found error""" - - embed = Embed(description=f"Command Not Found! {ctx.bot.cross} Please use **{ctx.prefix}help** to see all commands", - colour=ctx.bot.admin_colour) - - await send_error(ctx, perms, embed) - - -async def on_command_cooldown(ctx, perms, error): - """Handles Cooldown Errors""" - - embed = Embed(description=f"That command is on cooldown. Try again in **{error.retry_after:,.2f}** seconds", - colour=ctx.bot.admin_colour) - - await send_error(ctx, perms, embed) - - -async def on_command_permission(ctx, perms, args2): - """Handles User Missing Permissions Errors""" - - # Convert list into string of the missing permissions - missing_perms = string.capwords(", ".join(args2.missing_perms).replace("_", " ")) - - embed = Embed( - description=f"{ctx.bot.cross} Uh oh! You Need **{missing_perms}** Permission(s) To Execute This Command! {ctx.bot.cross}", - colour=ctx.bot.admin_colour) - - await send_error(ctx, perms, embed) - - -async def on_command_missing_argument(ctx, perms): - """Handles the missing argument error""" - - embed = Embed(description="Required Argument(s) Missing!" - f"\nUse **{ctx.prefix}help** to find how to use **{ctx.command}**", - colour=ctx.bot.admin_colour) - - await send_error(ctx, perms, embed) - - -async def on_not_owner(ctx, perms): - """Handles the error when the user is not the owner and tries to invoke owner only command""" - - embed = Embed(description=f"**{ctx.bot.cross} Owner Only Command {ctx.bot.cross}**", - colour=ctx.bot.admin_colour) - - await send_error(ctx, perms, embed) - - -class Errors(Cog): - """Global Error Event Handler!""" - - def __init__(self, bot): - self.bot = bot - - @Cog.listener() - async def on_command_error(self, ctx, args2): - """Event to detect and handle errors""" - - # Get permissions for the bot within - perms = ctx.guild.me.permissions_in(ctx.message.channel) - - # Ignore global event handler if command already has error handler event - if hasattr(ctx.command, "on_error"): return - - # if the user did not specify an user - if isinstance(args2, commands.MissingRequiredArgument): - await on_command_missing_argument(ctx, perms) - # if the user has spammed a command and invoked a cooldown - elif isinstance(args2, commands.CommandOnCooldown): - await on_command_cooldown(ctx, perms, args2) - # if the user tries to access a command that isn't available - elif isinstance(args2, commands.CommandNotFound): - await on_command_not_found(ctx, perms) - # if the user provides an argument that isn't recognised - elif isinstance(args2, commands.BadArgument): - await on_command_bad_argument(ctx, perms) - # if the user does not the correct permissions to call a command - elif isinstance(args2, commands.MissingPermissions): - await on_command_permission(ctx, perms, args2) - # if the bot is missing permissions needed - elif isinstance(args2, commands.BotMissingPermissions): - await on_bot_forbidden(ctx, perms, args2) - # if the bot is forbidden from performing the command - elif isinstance(args2, Forbidden): - await on_command_forbidden(ctx, perms) - # if the user tries to invoke a command that is only for the owner - elif isinstance(args2, commands.NotOwner): - await on_not_owner(ctx, perms) - - -def setup(bot): - bot.add_cog(Errors(bot)) diff --git a/cogs/events.py b/cogs/events.py deleted file mode 100644 index 2f58d188..00000000 --- a/cogs/events.py +++ /dev/null @@ -1,313 +0,0 @@ -import datetime - -import asyncpg -from discord.ext.commands import Cog - - -class Events(Cog): - """Handling all global events""" - - def __init__(self, bot): - self.bot = bot - - @Cog.listener() - async def on_ready(self): - """Display startup message""" - - print("UvU Senpaiii I'm ready\n") - - @Cog.listener() - async def on_guild_join(self, guild): - """ - Store users in a database - Store prefix/modlogs in the cache - """ - - # Store every single record into an array - records = [(member.id, None, None, guild.id, None, None, None, 0) for member in guild.members] - - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Insert the guild information into guilds table - try: - insert = """INSERT INTO guilds VALUES ($1, $2, $3, $4) ON CONFLICT (guild_id) DO NOTHING""" - rowcount = await conn.execute(insert, guild.id, ".", None, 0) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Guild {guild.id} Could Not Be Inserted Into Guilds Table", e) - - # Print success - else: - print(rowcount, f"Record(s) inserted successfully into {guild}") - self.bot.store_cache(guild.id, modlogs=None, prefix=".", roles_persist=0) - - # Query to insert all the member details to members table - try: - rowcount = await conn.copy_records_to_table("members", records=records) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Members Could Not Be Inserted Into Members Table For Guild {guild.id}", e) - - # Store in cache - else: - print(rowcount, f"Record(s) inserted successfully into Members from {guild}") - - @Cog.listener() - async def on_guild_remove(self, guild): - """ - Remove users in the database for the guild - Remove the modlogs/guild from the cache - """ - - # Setup pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Delete the guild information as the bot leaves the server - try: - delete = """DELETE FROM guilds WHERE guild_id = $1""" - rowcount = await conn.execute(delete, guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: On Guild Remove Event Record Was Not Deleted For {guild.id}", e) - - # Delete the key - value pair for the guild - else: - print(rowcount, f"Record deleted successfully from Guild {guild}") - self.bot.del_cache(guild.id) - - # Delete all records of members from that guild - try: - delete = """DELETE FROM members WHERE guild_id = $1""" - rowcount = await conn.execute(delete, guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: All Members Could Not Be Deleted From {guild.id}", e) - - # Print success - else: - print(rowcount, f"Record(s) deleted successfully from Members from {guild}") - # Remove any/all members stored in cache from that guild - self.bot.member_cache.remove_many(guild.id) - - # Delete any starboard information upon leaving the guild - try: - delete = """DELETE FROM starboard WHERE guild_id = $1""" - rowcount = await conn.execute(delete, guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print( - f"PostGres Error: On Guild Remove Event Starboard/Starboard Messages Were Not Deleted For {guild.id}", - e) - - # Delete all information about the starboard and any messages stored - else: - print(rowcount, f"Starboard deleted successfully from Guild {guild}") - if self.bot.get_starboard_channel(guild.id): - self.bot.delete_starboard(guild.id) - self.bot.delete_starboard_messages(guild.id) - - @Cog.listener() - async def on_member_join(self, member): - """ - Bot event to insert new members into the database - In the Enso guild, it will send an introduction embed - """ - - # Ignoring bots - if member.bot: return - - # Get the guild and role persist value of the guild - guild = member.guild - role_persist = self.bot.get_roles_persist(guild.id) - - # Setup pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Define the insert statement that will insert the user's information - # On conflict, set the left values to null - try: - - insert = """INSERT INTO members (guild_id, member_id) VALUES ($1, $2) - ON CONFLICT (guild_id, member_id) DO UPDATE SET left_at = NULL, has_left = 0""" - rowcount = await conn.execute(insert, member.guild.id, member.id) - - # Catch errors - except asyncpg.PostgresError as e: - print( - f"PostGres Error: {member} | {member.id} was not be able to be added to {member.guild} | {member.guild.id}", - e) - - # Print success - else: - print(rowcount, f"{member} Joined {member.guild}, Record Inserted Into Members") - print(rowcount, f"Roles Cleared For {member} in {member.guild}") - - # Get the roles of the user from the database - try: - select_query = """SELECT * FROM members WHERE guild_id = $1 AND member_id = $2""" - - user_joined = await conn.fetchrow(select_query, member.guild.id, member.id) - role_ids = user_joined["roles"] - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: {member} | {member.id} Record Not Found", e) - - # Give roles back to the user if role persist is enabled - else: - if role_persist == 1: - # Get Enso Chan - bot = guild.get_member(self.bot.user.id) - # Set flag for what value role_ids is - flag = role_ids - - # Check permissions of Enso - if bot.guild_permissions.manage_roles and flag: - # Get all the roles of the user before they were muted from the database - roles = [member.guild.get_role(int(id_)) for id_ in role_ids.split(", ") if len(id_)] - - # Give the member their roles back - await member.edit(roles=roles) - print(f"{member} Had Their Roles Given Back In {member.guild}") - - # Don't give roles if user has no roles to be given - elif bot.guild_permissions.manage_roles and not flag: - print(f"Member {member} | {member.id} Had No Roles To Be Given") - - # No permissions to give roles in the server - else: - print( - f"Insufficient Permissions to Add Roles to {member} | {member.id} in {member.guild} | {member.guild.id}") - - # Reset the roles entry for the database - try: - update_query = """UPDATE members SET roles = NULL WHERE guild_id = $1 AND member_id = $2""" - rowcount = await conn.execute(update_query, member.guild.id, member.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Clearing Member {member.id} Roles in Guild {member.guild.id}", e) - - # Print success - # Update cache - else: - print(rowcount, f"Roles Cleared For {member} in {member.guild}") - - @Cog.listener() - async def on_member_remove(self, member): - """Storing member roles within the database when the member leaves""" - - # Ignoring bots - if member.bot: return - - # Get the datetime of when the user left the guild - left_at = datetime.datetime.utcnow() - - # Store member roles within a string to insert into database - role_ids = ", ".join([str(r.id) for r in member.roles if not r.managed]) - - # Setup pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Store member roles within the database - try: - update = """UPDATE members SET roles = $1, left_at = $2, has_left = 1 - WHERE guild_id = $3 AND member_id = $4""" - rowcount = await conn.execute(update, role_ids, left_at, member.guild.id, member.id) - - # Catch Error - except asyncpg.PostgresError as e: - print(f"PostGres Error: Roles Could Not Be Added To {member} When Leaving {member.guild.id}", e) - - # Print success - else: - print(rowcount, f"{member} Left {member.guild.name}, Roles stored into Members") - - @Cog.listener() - async def on_guild_channel_delete(self, channel): - """Deleting modlogs/modmail channel if it's deleted in the guild""" - - # Get the modlogs channel (channel or none) - modlogs = self.bot.get_modlog_for_guild(channel.guild.id) - # Get the starboard (record or none) - starboard = self.bot.get_starboard_channel(channel.guild.id) - - # Get the modmail record - (normal and logging channels) - modmail_record = self.bot.get_modmail(channel.guild.id) - modmail_channel = modmail_record.get("modmail_channel_id") if modmail_record else None - modmail_logging_channel = modmail_record.get("modmail_logging_channel_id") if modmail_record else None - - # Get pool - pool = self.bot.db - - # Delete the modlogs system from the database when modlogs channel is deleted - if channel.id == modlogs: - # Setup pool connection - async with pool.acquire() as conn: - - # Set channel to none - try: - update = """UPDATE guilds SET modlogs = NULL WHERE guild_id = $1""" - await conn.execute(update, channel.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Guild Modlogs Could Not Be Deleted For {channel.guild.id}", e) - - # Delete channel from cache - else: - self.bot.remove_modlog_channel(channel.guild.id) - - # Delete all of the starboard information when the channel is deleted from the guild - if channel.id == starboard: - # Setup pool connection - async with pool.acquire() as conn: - - # Delete any starboard information upon leaving the guild - try: - delete = """DELETE FROM starboard WHERE guild_id = $1""" - rowcount = await conn.execute(delete, channel.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print( - f"PostGres Error: On Guild Remove Event Starboard/Starboard Messages Were Not Deleted For {channel.guild.id}", - e) - - # Delete all information about the starboard and any messages stored - else: - print(rowcount, f"Starboard deleted successfully from Guild {channel.guild}") - self.bot.delete_starboard(channel.guild.id) - self.bot.delete_starboard_messages(channel.guild.id) - - # If modmail channels are deleted, delete the entire system - if channel.id == modmail_channel or channel.id == modmail_logging_channel: - # Set up pool connection - async with pool.acquire() as conn: - - # Remove the moderatormail record from the database - try: - delete = """DELETE FROM moderatormail WHERE guild_id = $1""" - await conn.execute(delete, channel.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: ModeratorMail Record Could Not Be Deleted for Guild {channel.guild.id}", e) - - # Delete from cache - else: - self.bot.delete_modmail(channel.guild.id) - - -def setup(bot): - bot.add_cog(Events(bot)) diff --git a/cogs/fun.py b/cogs/fun.py deleted file mode 100644 index 09e45f5c..00000000 --- a/cogs/fun.py +++ /dev/null @@ -1,531 +0,0 @@ -# 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 random -import string -import textwrap -import urllib.parse -from typing import Optional - -import aiohttp -import discord -from PIL import Image, ImageDraw, ImageFont -from PIL.ImageOps import invert -from aiohttp import request -from discord import Member, Embed -from discord.ext import commands -from discord.ext.commands import BucketType, cooldown, command, Cog -from discord.ext.commands import is_owner, bot_has_permissions -from owotext import OwO - - -def generate_meme(image_path, top_text, bottom_text='', font_path='images/homies/impact/Impacted.ttf', font_size=9): - get_image = Image.open(image_path) - draw = ImageDraw.Draw(get_image) - image_width, image_height = get_image.size - - # Load font - font = ImageFont.truetype(font=font_path, size=int(image_height * font_size) // 100) - - # Convert text to uppercase - top_text = top_text.upper() - bottom_text = bottom_text.upper() - - # Text wrapping - char_width, char_height = font.getsize('A') - chars_per_line = image_width // char_width - top_lines = textwrap.wrap(top_text, width=chars_per_line) - bottom_lines = textwrap.wrap(bottom_text, width=chars_per_line) - - # Draw top lines - y = 10 - for line in top_lines: - line_width, line_height = font.getsize(line) - x = (image_width - line_width) / 2 - draw.text((x, y), line, fill='white', font=font) - y += line_height - - # Draw bottom lines - y = image_height - char_height * len(bottom_lines) - 15 - for line in bottom_lines: - line_width, line_height = font.getsize(line) - x = (image_width - line_width) / 2 - draw.text((x, y), line, fill='white', font=font) - y += line_height - - # Save meme as bytes - file = io.BytesIO() - get_image.save(file, format='PNG') - file.seek(0) - return file - - -# Set up the cog -class Fun(Cog): - """Fun Commands! (8ball, Doggo etc!)""" - - 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="attack", hidden=True) - @is_owner() - async def attack(self, ctx, member: Member): - """Throw Insults at Members""" - - # Set up array of insults to throw at people - responses = [ - f"{member.mention} is stinky", - f"{member.mention} is ugly", - f"{member.mention} has a gigantic nose", - f"{member.mention} gets no views on their tiktok", - f"{member.mention} is obviously compensating for something :eyes:", - f"{member.mention} DIE DIE DIE :knife: :skull:", - f"{member.mention} is so annoying smh :rolling_eyes:", - f"I'd say {member.mention} was dropped as a child but they would have to be held to be dropped in the first place", - f"I hate {member.mention}", - f"{member.mention} close your legs, it smells like clam chowder :face_vomiting: :face_vomiting: :nauseated_face: :nauseated_face:", - f"I bet {member.mention} can't reach the wall cabinets without a booster chair", - f"{member.mention} Browses 4Chan and Reddit all day looking for love", - f"{member.mention} Your forehead could be used as a landing pad", - f"I bet {member.mention} likes eating watermelon with the rind.", - f"{member.mention} You were the first creation to make god say oops", - f"{member.mention} You have delusions of adequacy", - f"{member.mention} I treasure the time I don't spend with you", - f"Don't be ashamed of yourself {member.mention}, that's your parent's job", - f"I don't have the energy to pretend I like {member.mention} today", - f"I know this was made for me to insult but it’s kinda hard to be a hateful cunt like {member.mention} :star_struck::star_struck:", - f"#{member.mention}IsOverParty", - f"I hope {member.mention} drops dead with a curable disease that doctors simply didn’t feel like curing :)", - f"{member.mention} You know there's no vaccine for stupidity right?", - f"{member.mention} You are not very epic at all", - f"You make Kpop Fancams 24/7 for validation on the internet {member.mention}", - f"Your mother wanted to drop you on the head when you were little {member.mention}", - f"{member.mention} You're the CEO of Racism", - f"{member.mention} has no common sense" - ] - - # Sending out a random insult from the array "responses" - await ctx.send(random.choice(responses)) - - @command(name="compliment", aliases=["comp"]) - async def compliment(self, ctx, member: Member): - """Give Compliments to Members""" - - # Set up array of compliments to throw at people - responses = [ - f"{member.mention} is the most adorable uwu <:awie:676201100793085952> <:awie:676201100793085952> <:awie:676201100793085952>", - f"{member.mention} You have my ENTIRE HEART <:blushlook1:677310734123663363> <:blushlook2:679524467248201769>", - f"{member.mention} Hun you're CUTE uwu :pleading_face: :flushed: :pleading_face: :flushed: :pleading_face:", - f"I love {member.mention} so so much :heartbeat: :heartbeat: :heartbeat: ", - f"My heart is full of love for you {member.mention} <:Kawaii:676203363922214953> <:Kawaii:676203363922214953>", - f"{member.mention} I admire your greatness so much that I consider making a fan club to become your #1 fan (´꒳`)", - f"{member.mention} has no flaws, only special effects :))", - f"{member.mention}'s smile is brighter than sunlight, so smile more often ( ◠‿◠ )", - f"{member.mention} Your smile is so beautiful it blinds me :heart_eyes: :heart_eyes:", - f"Being on a journey all my life, I will never meet a person as amazing as you are {member.mention}", - f"Such a pleasure to be on the same server with {member.mention} <:boneappleteeth:676202300573876252> <:boneappleteeth:676202300573876252>", - f"With {member.mention}, even the worst day will be filled with joy <:hug:718248629034549299> <:hug:718248629034549299>", - f"There's no better antidepressant than {member.mention}", - f"{member.mention} You're great, keep going Σd(˘ꇴ˘๑)", - f"I'd simp for {member.mention} anyday :flushed: :heart_eyes: :flushed: ", - f"{member.mention} Even the ugliest clothes won't ruin your look (。•̀ᴗ -)☆", - f"{member.mention} You’re that “nothing” when people ask me what I’m thinking about <:Kawaii:676203363922214953> <:Kawaii:676203363922214953>", - f"{member.mention} Somehow you make time stop and fly at the same time <:awie:676201100793085952> <:blushlook1:677310734123663363>", - f"{member.mention} is a whole ass SWAGMEAL <:Kawaii:676203363922214953> <:Kawaii:676203363922214953>", - f"After meeting {member.mention}, I couldn't imagine living my life without them", - f"Take me into your arms and tell me you love me <:blushlook1:677310734123663363> <:blushlook2:679524467248201769> {member.mention}", - f"{member.mention} I would spend eternity cuddling with you :flushed: :flushed:", - f"Would you want to go on an e-date together? :pleading_face: :point_right: :point_left: {member.mention}", - f"Let me shoot my shot to you :see_no_evil: :see_no_evil: {member.mention}", - f"Your existence makes me feel so much better {member.mention}", - f"You're so hot, even hotter than hell :heart_eyes: {member.mention}", - f"{member.mention} You’re so cute that Taz will simp for you anytime :flushed: :heart_eyes: :flushed:", - f"{member.mention} The thought of you leaving me is too much to bear. Stay with me forever :pleading_face: :pleading_face:", - f"You're... You're SHREKTACULAR :heart_eyes: :flushed: :heart_eyes: {member.mention}", - f"{member.mention} Your beauty renders me speechless... :heart_eyes: :heart_eyes:", - f"Your taste in music is impeccable {member.mention}", - f"{member.mention} I can't stop thinking about you :see_no_evil: :see_no_evil:", - f"{member.mention} Your wedding will be wonderful, but the y is silent ", - f"{member.mention} I would give up my lifelong goals just to have a chance with you ", - f"{member.mention} Will you be the **yee** to my **haw**? :pleading_face: :pleading_face:", - f"{member.mention} is the definition of perfection :heart_eyes: :heart_eyes:", - f"{member.mention} My love for you is bigger than the amount of code Hammy has written <:Kawaii:676203363922214953> <:Kawaii:676203363922214953> <:Kawaii:676203363922214953>", - f"{member.mention} Why jump off a cliff when you can jump into my arms :flushed: :flushed:" - ] - - # Sending out a random compliment from the array "responses" - await ctx.send(random.choice(responses)) - - @command(name="flip") - async def flip(self, ctx): - """Flip a Coin (Huge pp/Smol pp)""" - - # Define array with only 2 entries to create 50/50 chance - pp_array = ["Smol pp", "Huge pp"] - - # Send out one of the responses stored in the array - await ctx.send(f"{ctx.author.mention} {random.choice(pp_array)}") - - @command(name="digby", hidden=True) - @bot_has_permissions(embed_links=True) - async def digby(self, ctx): - """Pictures of Digby!""" - - # Surround with try/except to catch any exceptions that may occur - try: - - # Open the file containing the digby images - with open('images/FunCommands/digby.txt') as file: - # Store content of the file in digby_array - digby_array = file.readlines() - - # Set member as the author - member = ctx.message.author - # Get the member avatar - userAvatar = member.avatar_url - - # Set up the embed to display a random image of digby - embed = Embed( - title=f"**A cute picture of Digby!**", - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_image(url=random.choice(digby_array)) - embed.set_footer(text=f"Requested by {member}", icon_url=userAvatar) - - # Send the embedded message to the user - await ctx.send(embed=embed) - - except FileNotFoundError as e: - print(e) - - @command(name="doggo") - @bot_has_permissions(embed_links=True) - @cooldown(1, 1, BucketType.user) - async def doggo(self, ctx, breed: Optional[str] = None): - """Pictures of Doggos!""" - - # Set member as the author - member = ctx.message.author - # Get the member avatar - userAvatar = member.avatar_url - - # Initialise array to store doggo pics - b_list = [] - - # If a breed if specified - if breed: - # Get the lowercase string input - lowercase_breed = breed.lower() - - # If the user wants to know what breeds there are - if lowercase_breed == "breeds": - # Get the list of breeds - breed_url = "https://dog.ceo/api/breeds/list/all" - - # Using API, retrieve the full list of breeds available - async with request("GET", breed_url, headers={}) as response: - if response.status == 200: - data = await response.json() - breed_link = data["message"] - - # Store every Doggo in an array - for doggo in breed_link: - b_list.append(doggo) - - # Join together all the breeds into a string - doggo_string = string.capwords(", ".join(b_list)) - - # Tell the user to try the breeds listed below - desc = f"Try the Breeds listed below!\n{doggo_string}" - await self.bot.generate_embed(ctx, desc=desc) - - # If no breed has been specified - else: - - # Grab a random image of a doggo with the breed specified - image_url = f"https://dog.ceo/api/breed/{lowercase_breed}/images/random" - - # Using API, retrieve the image of a doggo of the breed specified - async with request("GET", image_url, headers={}) as response: - if response.status == 200: - data = await response.json() - image_link = data["message"] - - # Set up the embed for a doggo image - doggo_embed = Embed( - title=f"**It's a {lowercase_breed.capitalize()} Doggo!!** ", - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - doggo_embed.set_image(url=image_link) - doggo_embed.set_footer(text=f"Requested by {member}", icon_url=userAvatar) - - # Send the doggo image - await ctx.send(embed=doggo_embed) - - else: - - # Send error message that Doggo was not found! - desc = f"Doggo Not Found!\nPlease do **{ctx.prefix}doggo breeds** to see the full list of Doggos!" - await self.bot.generate_embed(ctx, desc=desc) - else: - - # Grab a random image of a doggo of any breed - image_url = "https://dog.ceo/api/breeds/image/random" - - # Using API, retrieve the image of a doggo of any breed - async with request("GET", image_url, headers={}) as response: - if response.status == 200: - data = await response.json() - image_link = data["message"] - - # Set up the embed for a random doggo image - doggo_embed = Embed( - title=f"**Doggo!** ", - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - doggo_embed.set_image(url=image_link) - doggo_embed.set_footer(text=f"Requested by {member}", icon_url=userAvatar) - - # Send random doggo image to the channel - await ctx.send(embed=doggo_embed) - - else: - # Send error message that Doggo was not found! - desc = f"Doggo Not Found!\nPlease do **{ctx.prefix}doggo breeds** to see the full list of Doggos!" - await self.bot.generate_embed(ctx, desc=desc) - - @command(name="8ball") - async def _8ball(self, ctx, *, question): - """8ball Responses!""" - - try: - # Make the text readable to the api - eightball_question = urllib.parse.quote(question) - - # Using API, make a connection to 8ball API - async with request("GET", f"https://8ball.delegator.com/magic/JSON/{eightball_question}", - headers={}) as response: - - # With a successful connection - # Get the answer - if response.status == 200: - data = await response.json() - api_question = data["magic"] - api_answer = api_question["answer"] - - await ctx.send(api_answer) - - except commands.BadArgument as e: - raise e - - @command(name="kpop") - async def get_kpop_member(self, ctx): - """Retrieve a random kpop member""" - - # Request profile information from API - url = "https://apis.duncte123.me/kpop" - async with aiohttp.ClientSession() as session: - async with await session.get(url=url) as response: - # When successful, read data from json - if response.status == 200: - kpop = await response.json() - - name = kpop["data"]["name"] - band = kpop["data"]["band"] - image_url = kpop["data"]["img"] - id = kpop["data"]["id"] - - elif response.status == 422 or response.status == 404: - await self.bot.generate_embed(ctx, desc="**Kpop Member Not Found!**") - return - - elif response.status == 429: - await self.bot.generate_embed(ctx, - desc="**You are being rate limited! You have spammed it too much :(**") - return - - embed = Embed(title=name, - description=band, - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_image(url=image_url) - embed.set_footer(text=f"Internal ID: {id}") - - await ctx.send(embed=embed) - - @command(name="insta", aliases=["instagram"]) - async def insta_info(self, ctx, *, user_name): - """Retrieve a persons Instagram profile information""" - - with ctx.typing(): - # Request profile information from API - url = f"https://apis.duncte123.me/insta/{user_name}" - async with aiohttp.ClientSession() as session: - async with await session.get(url=url) as response: - - # When successful, read data from json - if response.status == 200: - insta = await response.json() - - data = insta["user"] - images = insta["images"] - private = data["is_private"] - verified = data["is_verified"] - - full_name = data["full_name"] - username = data["username"] - pfp = data["profile_pic_url"] - - followers = data["followers"]["count"] - following = data["following"]["count"] - uploads = data["uploads"]["count"] - biography = data["biography"] - - # When profile isn't private, grab the information of their last post - if not private: - image_url = images[0]["url"] - image_caption = images[0]["caption"] - - # Send error if no instagram profile was found with given username - elif response.status == 422: - await self.bot.generate_embed(ctx, desc="**Instagram Username Not Found!**") - return - - elif response.status == 429: - await self.bot.generate_embed(ctx, - desc="**You are being rate limited! You have spammed it too much :(**") - return - - # Setting bools to ticks/cross emojis - verif = self.bot.tick if verified else self.bot.cross - priv = self.bot.tick if private else self.bot.cross - - # Set the page url to the last post or the profile based on privacy settings - page_url = images[0]["page_url"] if not private else f"https://www.instagram.com/{username}/" - - desc = f"**Full Name:** {full_name}" \ - f"\n**Bio:** {biography}" \ - f"\n\n**Verified?:** {verif} | **Private?:** {priv}" \ - f"\n**Following:** {following} | **Followers:** {followers}" \ - f"\n**Upload Count:** {uploads}" - embed = discord.Embed(title=f"{username}'s Instagram", - description=desc, - url=page_url, - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_thumbnail(url=pfp) - embed.set_footer(text=f"Requested By {ctx.author}", icon_url=ctx.author.avatar_url) - - # When profile is not private, display the last post with the caption - if not private: - embed.add_field(name="My Latest Post", - value=f"**Caption:** {image_caption}", - inline=False) - embed.set_image(url=image_url) - - await ctx.send(embed=embed) - - @command(name="homies") - @cooldown(1, 5, BucketType.user) - @bot_has_permissions(attach_files=True) - async def homies(self, ctx, *, text): - """Summoning the Homies""" - - # Make sure the text entered is less than 20 characters - if len(text) >= 20: - await self.bot.generate_embed(ctx, desc="Please make sure the prompt is below **20** characters!") - else: - - # Define the text to be drawn on the top and the bottom - top_text = f"Ayo fuck {text}" - bottom_text = f"All my homies hate {text}" - - # Call the method to generate the image - file = generate_meme('images/homies/AllMyHomies.jpg', top_text=top_text, bottom_text=bottom_text) - - # Send the bytes object as an image file - await ctx.send(file=discord.File(file, "homies.png")) - - @command(name="grayscale", aliases=["gs"]) - @cooldown(1, 2, BucketType.user) - @bot_has_permissions(attach_files=True) - async def grayscale(self, ctx): - """Display grayscale version of image uploaded""" - - if ctx.message.attachments: - for attachments in ctx.message.attachments: - attach = await attachments.read() - image = Image.open(io.BytesIO(attach)).convert('LA') - - file = io.BytesIO() - image.save(file, format='PNG') - file.seek(0) - - await ctx.message.delete() - await ctx.send(file=discord.File(file, "gs.png")) - - else: - await self.bot.generate_embed(ctx, desc="**Image Not Detected!**") - - @command(name="invert", aliases=["negative"]) - @cooldown(1, 2, BucketType.user) - @bot_has_permissions(attach_files=True) - async def invert(self, ctx): - """Display inverted version of image uploaded""" - - if ctx.message.attachments: - for attachments in ctx.message.attachments: - attach = await attachments.read() - image = Image.open(io.BytesIO(attach)).convert('RGB') - inverted = invert(image) - - # Save new grayscale image as bytes - file = io.BytesIO() - inverted.save(file, format='PNG') - file.seek(0) - - await ctx.message.delete() - # Send Grayscale Image - await ctx.send(file=discord.File(file, "inverted.png")) - - else: - await self.bot.generate_embed(ctx, desc="**Image Not Detected!**") - - @command(name="owo", aliases=["uwu"]) - @bot_has_permissions(manage_messages=True) - async def owo(self, ctx, *, text): - """Converts given text to 'OwO' format""" - - # Delete the message sent by the user - await ctx.message.delete() - - # Convert to "OwO" text - uwu = OwO() - owo = uwu.whatsthis(text) - - # Send the text back - await ctx.message.channel.send(owo) - - -def setup(bot): - bot.add_cog(Fun(bot)) diff --git a/cogs/guild.py b/cogs/guild.py deleted file mode 100644 index 8f977d05..00000000 --- a/cogs/guild.py +++ /dev/null @@ -1,543 +0,0 @@ -# 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 string - -import asyncpg -from discord import Embed, TextChannel -from discord.ext.commands import has_permissions, Cog, group, bot_has_permissions, BadArgument, MissingRequiredArgument, \ - command, MissingPermissions - -from cogs.libs.modmail import Modmail -from cogs.libs.starboard import Starboard - - -async def get_starboard_from_db(self, ctx, action): - """Get the starboard record from DB""" - - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Get the row of the guild from the starboard table - try: - select_query = """SELECT * FROM starboard WHERE guild_id = $1""" - result = await conn.fetchrow(select_query, ctx.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print("PostGres Error: Starboard Record Could Not Be Retrieved For Starboard", e) - - # Throw error if the guild already exists - else: - if action == "setup" and result: - text = "**Starboard** Already Setup!" \ - f"\nDo **{ctx.prefix}help starboard** to find out more!" - await self.bot.generate_embed(ctx, desc=text) - return None - elif (action == "update" or action == "delete") and not result: - text = "**Starboard** Not Setup!" \ - f"\nDo **{ctx.prefix}help starboard** to find out more!" - await self.bot.generate_embed(ctx, desc=text) - return None - - return not None - - -async def get_modlogs_from_db(self, ctx, action): - """Get the starboard record from DB""" - - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Get the entire row of the guild from the guilds table - try: - select_query = """SELECT * FROM guilds WHERE guild_id = $1""" - result = await conn.fetchrow(select_query, ctx.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print("PostGres Error: Modlog Record Could Not Be Retrieved For Modlogs", e) - - # Throw error if the modlogs already exists - else: - if action == "setup" and result["modlogs"]: - text = "**Modlogs** Already Setup!" \ - f"\nDo **{ctx.prefix}help modlogs** to find out more!" - await self.bot.generate_embed(ctx, desc=text) - return None - elif (action == "update" or action == "delete") and not result["modlogs"]: - text = "**Modlogs** Not Setup!" \ - f"\nDo **{ctx.prefix}help modlogs** to find out more!" - await self.bot.generate_embed(ctx, desc=text) - return None - - return not None - - -async def get_modmail_from_db(self, ctx, action): - """Get the starboard record from DB""" - - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Get the entire row of the guild from the guilds table - try: - select_query = """SELECT * FROM moderatormail WHERE guild_id = $1""" - result = await conn.fetchrow(select_query, ctx.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print("PostGres Error: Modlog Record Could Not Be Retrieved For Modlogs", e) - - # Throw error if the modlogs already exists - else: - if action == "setup" and result: - text = "**Modmail** Already Setup!" \ - f"\nDo **{ctx.prefix}help modmail** to find out more!" - await self.bot.generate_embed(ctx, desc=text) - return None - elif (action == "update" or action == "delete") and not result: - text = "**Modmail** Not Setup!" \ - f"\nDo **{ctx.prefix}help modmail** to find out more!" - await self.bot.generate_embed(ctx, desc=text) - return None - - return not None - - -# Set up the Cog -class Guild(Cog): - """All Guild Systems (Modmail/Modlogs/RolePersist)""" - - def __init__(self, bot): - self.bot = bot - self.modmail = Modmail(self.bot) - self.starboard = Starboard(self.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="modstatus", aliases=["logsstatus"]) - @bot_has_permissions(embed_links=True) - async def all_statuses(self, ctx): - """Status of all the moderation systems (Modlogs/Modmail/RolePersist)""" - desc = "" - - # Get status of mod - if self.bot.get_roles_persist(ctx.guild.id) == 0: - desc += f"**{self.bot.cross} Role Persist**\n" - else: - desc += f"**{self.bot.tick} Role Persist**\n" - - # Get status of modlogs - if ml_channel := self.bot.get_modlog_for_guild(ctx.guild.id): - channel = ctx.guild.get_channel(ml_channel) - desc += f"**{self.bot.tick} Modlogs | {channel.mention}**\n" - else: - desc += f"**{self.bot.cross} Modlogs**\n" - - # Get status of modmail - if modmail := self.bot.get_modmail(ctx.guild.id): - modmail_channel = ctx.guild.get_channel(modmail["modmail_channel_id"]) - modmail_logging = ctx.guild.get_channel(modmail["modmail_logging_channel_id"]) - - desc += f"**{self.bot.tick} Modmail | Channel: {modmail_channel.mention} | Logging: {modmail_logging.mention}**\n" - else: - desc += f"**{self.bot.cross} Modmail**\n" - - if starboard := self.bot.get_starboard_channel(ctx.guild.id): - channel = self.bot.get_channel(starboard) - min_stars = self.bot.get_starboard_min_stars(ctx.guild.id) - desc += f"**{self.bot.tick} Starboard | Channel: {channel.mention} | Minimum Stars: {min_stars} :star:**\n" - else: - desc += f"**{self.bot.cross} Starboard**\n" - - embed = Embed(title="Moderation Systems", - description=desc, - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_footer(text=f"Requested by {ctx.author}", icon_url=ctx.author.avatar_url) - - await ctx.send(embed=embed) - - @group(name="rolepersist", case_insensitive=True, usage="``") - @has_permissions(manage_guild=True) - @bot_has_permissions(manage_roles=True) - async def roles_persist(self, ctx): - """Role Persist! Keep user roles when they leave/join!""" - - @roles_persist.command(name="enable") - async def rp_enable(self, ctx): - """Enabling role persist within the guild""" - - if self.bot.get_roles_persist(ctx.guild.id) == 0: - await self.bot.update_role_persist(ctx.guild.id, value=1) - await self.bot.generate_embed(ctx, desc=f"**Role Persist has been enabled within {ctx.guild}!**") - else: - await self.bot.generate_embed(ctx, desc=f"**Role Persist is already enabled within {ctx.guild}!**") - - @roles_persist.command(name="disable") - async def rp_disable(self, ctx): - """Disabling role persist within the guild""" - - if self.bot.get_roles_persist(ctx.guild.id) == 1: - await self.bot.update_role_persist(ctx.guild.id, value=0) - await self.bot.generate_embed(ctx, desc=f"**Role Persist has been disabled within {ctx.guild}!**") - else: - await self.bot.generate_embed(ctx, desc=f"**Role Persist is already disabled within {ctx.guild}!**") - - @group(name="starboard", case_insensitive=True, usage="``") - @bot_has_permissions(embed_links=True) - @has_permissions(manage_guild=True) - async def starboard(self, ctx): - """ - Starboard! Let the community star messages! - """ - - @starboard.command(name="stars") - @bot_has_permissions(embed_links=True) - async def sb_min_stars(self, ctx, stars: int): - """Update the minimum amount of stars needed for the message to appear on the starboard""" - - if await get_starboard_from_db(self, ctx, "update") and stars > 0: - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Update the starboard min_stars in the database - try: - update_query = """UPDATE starboard SET min_stars = $1 WHERE guild_id = $2""" - await conn.execute(update_query, stars, ctx.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Starboard Record Could Not Be Updated For Guild {ctx.guild.id}", e) - - # Send confirmation that the channel has been updated - else: - star_channel = self.bot.get_starboard_channel(ctx.guild.id) - channel = self.bot.get_channel(star_channel) - text = "**Minimum Stars Updated!**" \ - f"\nMessages will now need {stars} :star: to appear in {channel.mention}" - await self.bot.generate_embed(ctx, desc=text) - - # Update cache - self.bot.update_starboard_min_stars(ctx.guild.id, stars) - - elif stars <= 0: - await self.bot.generate_embed(ctx, desc="Minimum Stars Must Be Over or Equal to 1!") - - @starboard.command(name="setup", usage="`<#channel>`") - @bot_has_permissions(embed_links=True) - async def sb_setup(self, ctx, starboard_channel: TextChannel): - """ - Setup Starboard - First Argument: Input Channel(Mention or ID) where starred messages will be sent - """ - - if await get_starboard_from_db(self, ctx, "setup"): - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Insert the information about the starboard into database - try: - insert_query = """INSERT INTO starboard (guild_id, channel_id) VALUES ($1, $2)""" - await conn.execute(insert_query, ctx.guild.id, starboard_channel.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Starboard Record Could Not Be Inserted For Guild {ctx.guild.id}", e) - - # Send confirmation message - else: - text = "**Starboard** is successfully set up!" \ - f"\nRefer to **{ctx.prefix}help starboard** for more information" - await self.bot.generate_embed(ctx, desc=text) - - # Store into cache - self.bot.cache_store_starboard(ctx.guild.id, starboard_channel.id, 1) - - @starboard.command(name="update", usage="``") - @bot_has_permissions(embed_links=True) - async def sb_update(self, ctx, starboard_channel: TextChannel): - """ - Update the channel that the starred messages are sent to - You can Mention or use the Channel ID - """ - - if await get_starboard_from_db(self, ctx, "update"): - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Update the starboard channel in the database - try: - update_query = """UPDATE starboard SET channel_id = $1 WHERE guild_id = $2""" - await conn.execute(update_query, starboard_channel.id, ctx.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Starboard Record Could Not Be Updated For Guild {ctx.guild.id}", e) - - # Send confirmation that the channel has been updated - else: - text = "**Channel Updated**" \ - f"\nNew Starred Messages will be sent to {starboard_channel.mention}" - await self.bot.generate_embed(ctx, desc=text) - - # Update cache - self.bot.update_starboard_channel(ctx.guild.id, starboard_channel.id) - - @starboard.command(name="delete") - @bot_has_permissions(embed_links=True) - async def sb_delete(self, ctx): - """Delete the starboard from the guild""" - - if await get_starboard_from_db(self, ctx, "delete"): - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Remove the starboard record from the database - try: - delete_query = """DELETE FROM starboard WHERE guild_id = $1""" - await conn.execute(delete_query, ctx.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Starboard Record Could Not Be Deleted for Guild {ctx.guild.id}", e) - - # Sending confirmation message that the starboard has been deleted - else: - text = "**Starboard** successfully deleted!" \ - f"\nDo **{ctx.prefix}help starboard** to find out more!" - await self.bot.generate_embed(ctx, desc=text) - - # Delete from cache - self.bot.delete_starboard(ctx.guild.id) - - @group(name="modlogs", case_insensitive=True, usage="``") - @has_permissions(manage_guild=True) - @bot_has_permissions(embed_links=True) - async def modlogs(self, ctx): - """ - Log updates in your server! (Nicknames/Deleted Msgs/etc!) - """ - - @modlogs.command(name="setup", usage="`<#channel>`") - async def mlsetup(self, ctx, user_channel: TextChannel): - """Setup a channel for Kick/Ban/Mute actions to be logged""" - - if await get_modlogs_from_db(self, ctx, "setup"): - # Set up the modlogs channel within the guild - mod_log_setup = True - await self.bot.storage_modlog_for_guild(ctx, user_channel.id, mod_log_setup) - - @modlogs.command(name="update", usage="`<#channel>`") - async def mlupdate(self, ctx, user_channel: TextChannel): - """Change the channel that your modlogs are sent to""" - - if await get_modlogs_from_db(self, ctx, "update"): - # Update the modlog channel within the database and cache - mod_log_setup = False - await self.bot.storage_modlog_for_guild(ctx, user_channel.id, mod_log_setup) - - @modlogs.command("delete") - async def mldelete(self, ctx): - """Delete the existing modlogs channel""" - - if await get_modlogs_from_db(self, ctx, "delete"): - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Update the existing modlogs for guild - try: - update = """UPDATE guilds SET modlogs = NULL WHERE guild_id = $1""" - await conn.execute(update, ctx.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Guild Modlogs Could Not Be Deleted For {ctx.guild.id}", e) - - # Delete channel from cache - else: - text = "**Modlogs System** successfully deleted!" \ - f"\nDo **{ctx.prefix}help modlogs** to setup Modlogs again!" - await self.bot.generate_embed(ctx, desc=text) - - self.bot.remove_modlog_channel(ctx.guild.id) - - @group(name="modmail", case_insensitive=True, usage="``") - @bot_has_permissions(manage_channels=True, embed_links=True, add_reactions=True, manage_messages=True, - attach_files=True, read_message_history=True, manage_roles=True) - @has_permissions(manage_guild=True) - async def mod_mail(self, ctx): - """ - Modmail! Allow your members to send mail to the staff team! - """ - - @mod_mail.command(name="setup") - async def mmsetup(self, ctx, modmail: TextChannel, modmail_logging: TextChannel): - """ - Setup Modmail System - First Argument: Input Channel(Mention or ID) where members can send modmail - Second Argument: Input Channel(Mention or ID) where the members mail should be sent - """ - - if await get_modmail_from_db(self, ctx, "setup"): - - # Set up embed to let the user how to start sending modmail - desc = "React to this message if you want to send a message to the Staff Team!" \ - "\n\n**React with ✅**" \ - "\n\nWe encourage all suggestions/thoughts and opinions on the server!" \ - "\nAs long as it is **valid** criticism." \ - "\n\n\n**Purely negative feedback will not be considered.**" - - ModMail = Embed(title="**Welcome to Modmail!**", - description=desc, - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - ModMail.set_thumbnail(url=self.bot.user.avatar_url) - - # Send modmail embed to the specified channel and auto add the ✅ reaction - modmail_message = await modmail.send(embed=ModMail) - await modmail_message.add_reaction('✅') - - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Insert the information about the modmail system into database - try: - insert_query = """INSERT INTO moderatormail (guild_id, modmail_channel_id, message_id, modmail_logging_channel_id) - VALUES ($1, $2, $3, $4)""" - await conn.execute(insert_query, ctx.guild.id, modmail.id, modmail_message.id, modmail_logging.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Modmail System Record Could Not Be Inserted For Guild {ctx.guild.id}", e) - - # Send confirmation message - else: - text = "**Modmail System** is successfully set up!" \ - f"\nRefer to **{ctx.prefix}help modmail** for more information" - await self.bot.generate_embed(ctx, desc=text) - - # Store into cache - self.bot.cache_store_modmail(ctx.guild.id, modmail.id, modmail_message.id, modmail_logging.id) - - @mod_mail.command(name="update") - async def mmupdate(self, ctx, modmail_logging_channel: TextChannel): - """ - Update the Channel that the Modmail is logged to - You can Mention or use the Channel ID - """ - - if await get_modmail_from_db(self, ctx, "update"): - - pool = self.bot.db - async with pool.acquire() as conn: - # Update the modmail channel in the database - try: - update_query = """UPDATE moderatormail SET modmail_logging_channel_id = $1 WHERE guild_id = $2""" - await conn.execute(update_query, modmail_logging_channel.id, ctx.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Modmail System Record Could Not Be Updated For Guild {ctx.guild.id}", e) - - # Send confirmation that the channel has been updated - else: - text = "**Channel Updated**" \ - f"\nNew Modmail will be sent to {modmail_logging_channel.mention}" - await self.bot.generate_embed(ctx, desc=text) - # Update cache - self.bot.update_modmail(ctx.guild.id, modmail_logging_channel.id) - - @mod_mail.command(name="delete") - async def mmdelete(self, ctx): - """Delete the entire modmail system from the guild""" - - if await get_modmail_from_db(self, ctx, "delete"): - - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Remove the moderatormail record from the database - try: - delete_query = """DELETE FROM moderatormail WHERE guild_id = $1""" - await conn.execute(delete_query, ctx.guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: ModeratorMail Record Could Not Be Deleted for Guild {ctx.guild.id}", e) - - # Sending confirmation message that the modmail system has been deleted - else: - text = "**Modmail System** successfully deleted!" \ - f"\nDo **{ctx.prefix}help modmail** to find out more!" - await self.bot.generate_embed(ctx, desc=text) - - # Delete from cache - self.bot.delete_modmail(ctx.guild.id) - - @Cog.listener() - async def on_raw_reaction_add(self, payload): - """Listening for reactions relating to modmail/starboard""" - - await self.modmail.modmail(payload) - await self.starboard.send_starboard_and_update_db(payload, action="added") - - @Cog.listener() - async def on_raw_reaction_remove(self, payload): - """Editing the message if a star reaction was removed""" - - await self.starboard.send_starboard_and_update_db(payload, action="removed") - - @mlsetup.error - @mlupdate.error - @mmsetup.error - @mmupdate.error - @sb_setup.error - @sb_update.error - async def mlsetup_command_error(self, ctx, exc): - """Catching error if channel is not recognised""" - - if isinstance(exc, BadArgument): - text = "**Channel Not Detected... Aborting Process**" - await self.bot.generate_embed(ctx, desc=text) - elif isinstance(exc, MissingRequiredArgument): - text = "Required Argument(s) Missing!" \ - f"\nUse **{ctx.prefix}help** to find how to use **{ctx.command}**" - await self.bot.generate_embed(ctx, desc=text) - elif isinstance(exc, MissingPermissions): - missing_perms = string.capwords(", ".join(exc.missing_perms).replace("_", " ")) - - text = f"{self.bot.cross} Uh oh! You Need **{missing_perms}** Permission(s) To Execute This Command! {self.bot.cross}" - await self.bot.generate_embed(ctx, desc=text) - - -def setup(bot): - bot.add_cog(Guild(bot)) diff --git a/cogs/help.py b/cogs/help.py deleted file mode 100644 index 69819f52..00000000 --- a/cogs/help.py +++ /dev/null @@ -1,803 +0,0 @@ -# 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 . - - -# Help paginator by Rapptz -# Edited by F4stZ4p -# Edited by Hamothy - -import asyncio -import datetime -from typing import Optional - -import discord -from discord import Embed, DMChannel -from discord.ext import commands -from discord.ext.commands import Cog, command, has_permissions, guild_only, bot_has_permissions - - -class CannotPaginate(Exception): - pass - - -class Pages: - """Implements a paginator that queries the user for the - pagination interface. - - Pages are 1-index based, not 0-index based. - - If the user does not reply within 2 minutes then the pagination - interface exits automatically. - - Parameters - ------------ - ctx: Context - The context of the command. - entries: List[str] - A list of entries to paginate. - per_page: int - How many entries show up per page. - show_entry_count: bool - Whether to show an entry count in the footer. - - Attributes - ----------- - embed: discord.Embed - The embed object that is being used to send pagination info. - Feel free to modify this externally. Only the description, - footer fields, and colour are internally modified. - permissions: discord.Permissions - Our permissions for the channel. - """ - - def __init__(self, ctx, *, entries, per_page=8, show_entry_count=True): - - self.bot = ctx.bot - self.prefix = ctx.prefix - self.entries = entries - self.message = ctx.message - self.channel = ctx.channel - self.author = ctx.author - self.per_page = per_page - pages, left_over = divmod(len(self.entries), self.per_page) - if left_over: - pages += 1 - self.maximum_pages = pages - self.embed = discord.Embed(colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - self.paginating = len(entries) > per_page - self.show_entry_count = show_entry_count - self.reaction_emojis = [ - ('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', self.first_page), - ('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page), - ('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page), - ('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', self.last_page), - ('\N{INPUT SYMBOL FOR NUMBERS}', self.numbered_page), - ('\N{BLACK SQUARE FOR STOP}', self.stop_pages), - ('\N{INFORMATION SOURCE}', self.show_help), - ] - - if ctx.guild is not None: - self.permissions = self.channel.permissions_for(ctx.guild.me) - else: - self.permissions = self.channel.permissions_for(ctx.bot.user) - - if not self.permissions.embed_links: - raise CannotPaginate('Bot does not have Embed Links permission') - - if not self.permissions.send_messages: - raise CannotPaginate('Bot Cannot Send Messages') - - if self.paginating: - # verify we can actually use the pagination session - if not self.permissions.add_reactions: - raise CannotPaginate('Bot does not have Add Reactions permission') - - if not self.permissions.read_message_history: - raise CannotPaginate('Bot does not have Read Message History permission') - - def get_page(self, page): - base = (page - 1) * self.per_page - return self.entries[base:base + self.per_page] - - async def show_page(self, page, *, first=False): - self.current_page = page - entries = self.get_page(page) - p = [] - for index, entry in enumerate(entries, 1 + ((page - 1) * self.per_page)): - p.append(f'{index}. {entry}') - - if self.maximum_pages > 1: - if self.show_entry_count: - text = f'Page {page}/{self.maximum_pages} ({len(self.entries)} entries)' - else: - text = f'Page {page}/{self.maximum_pages}' - - self.embed.set_footer(text=text) - - if not self.paginating: - self.embed.description = '\n'.join(p) - return await self.channel.send(embed=self.embed) - - if not first: - self.embed.description = '\n'.join(p) - await self.message.edit(embed=self.embed) - return - - p.append('') - p.append('Confused? React with \N{INFORMATION SOURCE} for more info.') - self.embed.description = '\n'.join(p) - self.message = await self.channel.send(embed=self.embed) - for (reaction, _) in self.reaction_emojis: - if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'): - # no |<< or >>| buttons if we only have two pages - # we can't forbid it if someone ends up using it but remove - # it from the default set - continue - - await self.message.add_reaction(reaction) - - async def checked_show_page(self, page): - if page != 0 and page <= self.maximum_pages: - await self.show_page(page) - - async def first_page(self): - """Show First Page""" - await self.show_page(1) - - async def last_page(self): - """Show Last Page""" - await self.show_page(self.maximum_pages) - - async def next_page(self): - """Show Next Page""" - await self.checked_show_page(self.current_page + 1) - - async def previous_page(self): - """Show Previous Page""" - await self.checked_show_page(self.current_page - 1) - - async def show_current_page(self): - if self.paginating: - await self.show_page(self.current_page) - - async def numbered_page(self): - """Go to Given Page""" - to_delete = [] - to_delete.append(await self.channel.send('What page do you want to go to?')) - - def message_check(m): - return m.author == self.author and \ - self.channel == m.channel and \ - m.content.isdigit() - - try: - msg = await self.bot.wait_for('message', check=message_check, timeout=30.0) - except asyncio.TimeoutError: - to_delete.append(await self.channel.send('Took too long.')) - await asyncio.sleep(5) - else: - page = int(msg.content) - to_delete.append(msg) - if page != 0 and page <= self.maximum_pages: - await self.show_page(page) - else: - to_delete.append(await self.channel.send(f'Invalid page given. ({page}/{self.maximum_pages})')) - await asyncio.sleep(5) - - try: - await self.channel.delete_messages(to_delete) - except Exception: - pass - - async def show_help(self): - """shows this message""" - messages = ['Welcome to the interactive paginator!\n', - 'This interactively allows you to see pages of text by navigating with ' - 'reactions. They are as follows:\n'] - - for (emoji, func) in self.reaction_emojis: - messages.append(f'{emoji} {func.__doc__}') - - self.embed.description = '\n'.join(messages) - self.embed.clear_fields() - self.embed.set_footer(text=f'We were on page {self.current_page} before this message.') - await self.message.edit(embed=self.embed) - - async def go_back_to_current_page(): - await asyncio.sleep(60.0) - await self.show_current_page() - - self.bot.loop.create_task(go_back_to_current_page()) - - async def stop_pages(self): - """Deletes Help Message""" - await self.message.delete() - self.paginating = False - - def react_check(self, reaction, user): - if user is None or user.id != self.author.id: - return False - - if reaction.message.id != self.message.id: - return False - - for (emoji, func) in self.reaction_emojis: - if reaction.emoji == emoji: - self.match = func - return True - return False - - async def paginate(self): - """Actually paginate the entries and run the interactive loop if necessary.""" - first_page = self.show_page(1, first=True) - if not self.paginating: - await first_page - else: - # allow us to react to reactions right away if we're paginating - self.bot.loop.create_task(first_page) - - while self.paginating: - try: - reaction, user = await self.bot.wait_for('reaction_add', check=self.react_check, timeout=120.0) - except asyncio.TimeoutError: - self.paginating = False - try: - await self.message.clear_reactions() - except: - pass - finally: - break - - try: - await self.message.remove_reaction(reaction, user) - except: - pass # can't remove it so don't bother doing so - - await self.match() - - -class FieldPages(Pages): - """Similar to Pages except entries should be a list of - tuples having (key, value) to show as embed fields instead. - """ - - async def show_page(self, page, *, first=False): - self.current_page = page - entries = self.get_page(page) - - self.embed.clear_fields() - self.embed.description = discord.Embed.Empty - - for key, value in entries: - self.embed.add_field(name=key, value=value, inline=False) - - if self.maximum_pages > 1: - if self.show_entry_count: - text = f'Page {page}/{self.maximum_pages} ({len(self.entries)} entries)' - else: - text = f'Page {page}/{self.maximum_pages}' - - self.embed.set_footer(text=text) - - if not self.paginating: - return await self.channel.send(embed=self.embed) - - if not first: - await self.message.edit(embed=self.embed) - return - - self.message = await self.channel.send(embed=self.embed) - for (reaction, _) in self.reaction_emojis: - if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'): - # no |<< or >>| buttons if we only have two pages - # we can't forbid it if someone ends up using it but remove - # it from the default set - continue - - await self.message.add_reaction(reaction) - - -import itertools -import inspect -import re - -# ?help -# ?help Cog -# ?help command -# -> could be a subcommand - -_mention = re.compile(r'<@!?([0-9]{1,19})>') - - -def cleanup_prefix(bot, prefix): - m = _mention.match(prefix) - if m: - user = bot.get_user(int(m.group(1))) - if user: - return f'@{user.name} ' - return prefix - - -async def _can_run(cmd, ctx): - try: - return await cmd.can_run(ctx) - except: - return False - - -def _command_signature(cmd): - # this is modified from discord.py source - # which I wrote myself lmao - - result = [cmd.qualified_name] - if cmd.usage: - result.append(cmd.usage) - return ' '.join(result) - - params = cmd.clean_params - if not params: - return ' '.join(result) - - for name, param in params.items(): - if param.default is not param.empty: - # We don't want None or '' to trigger the [name=value] case and instead it should - # do [name] since [name=None] or [name=] are not exactly useful for the user. - should_print = param.default if isinstance(param.default, str) else param.default is not None - if should_print: - result.append(f'[{name}={param.default!r}]') - else: - result.append(f'`[{name}]`') - elif param.kind == param.VAR_POSITIONAL: - result.append(f'`[{name}...]`') - else: - result.append(f'`<{name}>`') - - return ' '.join(result) - - -class HelpPaginator(Pages): - def __init__(self, ctx, entries, *, per_page=10): - super().__init__(ctx, entries=entries, per_page=per_page) - self.reaction_emojis.append(('\N{WHITE QUESTION MARK ORNAMENT}', self.show_bot_help)) - self.total = len(entries) - - @classmethod - async def from_cog(cls, ctx, cog): - cog_name = cog.__class__.__name__ - - if ctx.guild is None: - icon = ctx.author.avatar_url - else: - icon = ctx.guild.icon_url - - # get the commands - entries = sorted(Cog.get_commands(cog), key=lambda c: c.name) - - # remove the ones we can't run - entries = [cmd for cmd in entries if not cmd.hidden] - - self = cls(ctx, entries) - self.title = f'(っ◔◡◔)っ {cog_name} (っ◔◡◔)っ' - self.embed.set_thumbnail(url=icon) - self.description = inspect.getdoc(cog) - self.prefix = cleanup_prefix(ctx.bot, ctx.prefix) - - return self - - @classmethod - async def from_command(cls, ctx, command): - try: - entries = sorted(command.commands, key=lambda c: c.name) - except AttributeError: - entries = [] - else: - entries = [cmd for cmd in entries if not cmd.hidden] - - self = cls(ctx, entries) - if not isinstance(command, discord.ext.commands.Group): - if command.aliases: - aliases = " | ".join(command.aliases) - if command.usage: - self.title = f"{command.qualified_name} | {aliases} {command.signature}" - elif command.signature: - self.title = f"{command.qualified_name} | {aliases} `{command.signature}`" - else: - self.title = f"{command.qualified_name} | {aliases}" - else: - if command.usage: - self.title = f"{command.qualified_name} | {command.signature}" - elif command.signature: - self.title = f"{command.qualified_name} `{command.signature}`" - else: - self.title = f"{command.qualified_name}" - - else: - if command.aliases: - aliases = " | ".join(command.aliases) - self.title = f"{command.name} | {aliases}" - else: - self.title = command.name - - if command.description: - self.description = f'{command.description}\n\n{command.help}' - else: - self.description = command.help or 'No help given.' - - self.prefix = cleanup_prefix(ctx.bot, ctx.prefix) - return self - - @classmethod - async def from_bot(cls, ctx): - def key(c): - return c.cog_name or '\u200bMisc' - - entries = sorted(ctx.bot.commands, key=key) - nested_pages = [] - per_page = 8 - - # 0: (cog, desc, commands) (max len == 9) - # 1: (cog, desc, commands) (max len == 9) - # ... - - for cog, commands in itertools.groupby(entries, key=key): - plausible = [cmd for cmd in commands if not cmd.hidden] - if len(plausible) == 0: - continue - - description = ctx.bot.get_cog(cog) - if description is None: - description = discord.Embed.Empty - else: - description = inspect.getdoc(description) or discord.Embed.Empty - - nested_pages.extend( - (cog, description, plausible[i:i + per_page]) for i in range(0, len(plausible), per_page)) - - if ctx.guild is None: - icon = ctx.author.avatar_url - else: - icon = ctx.guild.icon_url - - self = cls(ctx, nested_pages, per_page=1) # this forces the pagination session - self.prefix = cleanup_prefix(ctx.bot, ctx.prefix) - self.embed.set_thumbnail(url=icon) - - # swap the get_page implementation with one that supports our style of pagination - self.get_page = self.get_bot_page - self._is_bot = True - - # replace the actual total - self.total = sum(len(o) for _, _, o in nested_pages) - return self - - def get_bot_page(self, page): - cog, description, commands = self.entries[page - 1] - self.title = f'{cog} Commands' - self.description = description - return commands - - async def show_page(self, page, *, first=False): - self.current_page = page - entries = self.get_page(page) - - self.embed.clear_fields() - self.embed.description = self.description - self.embed.title = self.title - - self.embed.set_footer(text=f'"{self.prefix}help command | module" For More Information!') - - signature = _command_signature - - for entry in entries: - self.embed.add_field(name=signature(entry), value=entry.short_doc or "No help given", inline=False) - - if self.maximum_pages: - self.embed.set_author(name=f'Page {page}/{self.maximum_pages} ({self.total} commands)') - - if not self.paginating: - return await self.channel.send(embed=self.embed) - - if not first: - await self.message.edit(embed=self.embed) - return - - self.message = await self.channel.send(embed=self.embed) - for (reaction, _) in self.reaction_emojis: - if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'): - # no |<< or >>| buttons if we only have two pages - # we can't forbid it if someone ends up using it but remove - # it from the default set - continue - - await self.message.add_reaction(reaction) - - async def show_help(self): - """Shows This Message""" - - self.embed.title = 'Paginator help' - self.embed.description = 'Hello! Welcome to the help page.' - - messages = [f'{emoji} {func.__doc__}' for emoji, func in self.reaction_emojis] - self.embed.clear_fields() - self.embed.add_field(name='What are these reactions for?', value='\n'.join(messages), inline=False) - - self.embed.set_footer(text=f'We were on page {self.current_page} before this message.') - await self.message.edit(embed=self.embed) - - async def go_back_to_current_page(): - await asyncio.sleep(30.0) - await self.show_current_page() - - self.bot.loop.create_task(go_back_to_current_page()) - - async def show_bot_help(self): - """Information On The Bot""" - - self.embed.title = 'Using Ensō~Chan' - self.embed.description = 'Hiya! This is the Help Page!' - self.embed.clear_fields() - - entries = ( - ('``', 'This means the argument is **required**.'), - ('`[argument]`', 'This means the argument is **optional**.'), - ('`[A|B]`', 'This means the it can be **either A or B**.'), - ('`[argument...]`', 'This means you can have multiple arguments.\n' \ - 'Now that you know the basics, it should be noted that...\n' \ - '**You do not type in the brackets!**') - ) - - self.embed.add_field(name='How do I use Ensō~Chan', value='Reading the signature is pretty straightforward') - - for name, value in entries: - self.embed.add_field(name=name, value=value, inline=False) - - self.embed.set_footer(text=f'We were on page {self.current_page} before this message.') - await self.message.edit(embed=self.embed) - - async def go_back_to_current_page(): - await asyncio.sleep(30.0) - await self.show_current_page() - - self.bot.loop.create_task(go_back_to_current_page()) - - -def send_feedback(self, message, author): - """Preparing Embed to send to the support server""" - - embed = Embed(title="Feedback!", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - - embed.set_thumbnail(url=author.avatar_url) - embed.set_footer(text=f"Send By {author}") - - fields = [("Member", f"{author.mention} | {author}", False), - ("Message", message.content, False)] - - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - - return embed - - -def message_sent_confirmation(self): - """Preparing Embed to be sent to the user after the message has been received successfully""" - - ConfirmationEmbed = Embed(title="Thank you for your feedback!", - description=f"**Message relayed to {self.bot.hammyMention}**", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - ConfirmationEmbed.set_footer(text=f"Thanks Once Again! ~ Hammy") - - return ConfirmationEmbed - - -def error_handling(self, author): - """Preparing embed to send if the message is not suitable""" - - ErrorHandlingEmbed = Embed( - title="Uh Oh! Please make sure the message is below **1024** characters!", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - - ErrorHandlingEmbed.set_footer(text=f"Sent To {author}") - - return ErrorHandlingEmbed - - -# Set up the Cog -class Help(Cog): - """Help Commands!""" - - def __init__(self, bot): - self.bot = bot - - @command(name='help', usage="`[command|cog]`") - async def _help(self, ctx, *, cmd: Optional[str] = None): - """Shows help about a command or the bot""" - - try: - if cmd is None: - p = await HelpPaginator.from_bot(ctx) - else: - entity = ctx.bot.get_cog(cmd) or ctx.bot.get_command(cmd) - - if entity is None: - clean = cmd.replace('@', '@\u200b') - return await self.bot.generate_embed(ctx, desc=f"Command or Category **{clean}** Not Found.") - elif isinstance(entity, commands.Command): - p = await HelpPaginator.from_command(ctx, entity) - else: - p = await HelpPaginator.from_cog(ctx, entity) - - await p.paginate() - except Exception as ex: - await ctx.send(f"**{ex}**") - - @command(name="prefix") - @guild_only() - @has_permissions(manage_guild=True) - async def change_prefix(self, ctx, new: Optional[str] = None): - """View/Change Guild Prefix""" - - # As long as a new prefix has been given and is less than 5 characters - if new and len(new) <= 5: - # Store the new prefix in the dictionary and update the database - await self.bot.storage_prefix_for_guild(ctx, new) - - # Making sure that errors are handled if prefix is above 5 characters - elif new and len(new) > 5: - await self.bot.generate_embed(ctx, desc="The guild prefix must be less than or equal to **5** characters!") - - # if no prefix was provided - elif not new: - # Grab the current prefix for the guild within the cached dictionary - prefix = self.bot.get_prefix_for_guild(ctx.guild.id) - await self.bot.generate_embed(ctx, desc=f"**The current guild prefix is `{prefix}`**") - - @command(name="invite", aliases=["inv"]) - @has_permissions(embed_links=True) - async def get_invite(self, ctx): - """Give the invite of the bot to the user""" - - embed = Embed(title="Invite For Ensō~Chan", - description="Click the link above to invite Ensō~Chan into your server!", - url="https://discord.com/oauth2/authorize?client_id=716701699145728094&permissions=1543892087&scope=bot", - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_footer(text="Thank you for considering to invite Ensō~Chan!") - - await ctx.send(embed=embed) - - @command(name="source") - @bot_has_permissions(embed_links=True) - async def _bot_source(self, ctx): - """Link to the source code for Enso!""" - - embed = Embed(title=f"<:github:741000905364603010> Source Code | Ensō~Chan {self.bot.version}", - description="**Click above me to view my source code!**", - url="https://github.com/sgoudham/Enso-Bot", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_thumbnail(url=self.bot.user.avatar_url) - embed.add_field(name="Developer", value=f"{self.bot.hammyMention} | Hamothy#5619", inline=False) - embed.set_footer(text=f"Requested by {ctx.author}", icon_url=ctx.author.avatar_url) - - await ctx.send(embed=embed) - - @command(name="vote", aliases=["upvote"]) - @bot_has_permissions(embed_links=True) - async def upvote(self, ctx): - """Upvote the bot on top.gg!""" - - desc = "Click the link above to upvote me!\nIt would greatly help me out as it allows the bot to be " \ - "noticed more on the website!\nIt's free and takes a maximum of 30 seconds to do. Thanks so much!" - embed = Embed(title="Upvote me on top.gg!", - description=desc, - url="https://top.gg/bot/716701699145728094/vote", - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_thumbnail(url=self.bot.user.avatar_url) - embed.add_field(name="Developer", value=f"{self.bot.hammyMention} | Hamothy#5619", inline=False) - - await ctx.send(embed=embed) - - @command(name="support") - @bot_has_permissions(embed_links=True) - async def support(self, ctx): - """Joining Support Server And Sending Feedback""" - - embed = Embed(title="Support Server!", - description=f"Do **{ctx.prefix}feedback** to send me feedback about the bot and/or report any issues " - f"that you are having!", - url="https://discord.gg/SZ5nexg", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_thumbnail(url=ctx.bot.user.avatar_url) - embed.set_footer(text=f"Requested by {ctx.author}", icon_url=ctx.author.avatar_url) - fields = [("Developer", f"{self.bot.hammyMention} | Hamothy#5619", False), - ("Data Collection", - "\nData Stored:" + - "\n- User ID" + - "\n- Guild ID" + - "\n\n If you wish to delete this data being stored about you. Follow steps outlined below:" + - "\n\n1) **Enable Developer Mode**" + - "\n2) Note down your **User ID** and the **Guild ID** (You must have left this guild or are planning to leave)" + - f"\n3) Join support server and notify me or use **{ctx.prefix}feedback** to notify me", 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="feedback") - @bot_has_permissions(embed_links=True) - async def feedback(self, ctx): - """Sending Feedback to Support Server""" - - # Get the #feedback channel within the support server - channel = self.bot.get_channel(self.bot.enso_feedback_ID) - - embed = Embed(title="Provide Feedback!", - description=f"Hiya! Please respond to this message with the feedback you want to provide!" - f"\n(You have **5 minutes** to respond. Make sure it is a **single message** and under **1024** characters!)", - url="https://discord.gg/SZ5nexg", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_footer(text=f"Requested by {ctx.author}", icon_url=ctx.author.avatar_url) - - helper = await ctx.author.send(embed=embed) - - def check(m): - """Ensure that the feedback received is from the author's DMs""" - return m.author == ctx.author and isinstance(m.channel, DMChannel) - - try: - # Wait for feedback from author - msg = await ctx.bot.wait_for('message', check=check, timeout=300.0) - - # Make sure sure that the message is below 1024 characters - while len(msg.content) > 1024 and check(msg): - await ctx.author.send(embed=error_handling(self, ctx.author)) - - # Wait for feedback again - msg = await ctx.bot.wait_for('message', check=check, timeout=300.0) - - # Once message is below 1024 characters - # Send confirmation message to author to let them know feedback has been sent - # Send the feedback entered from the author into support server - if len(msg.content) < 1024 and check(msg): - await ctx.author.send(embed=message_sent_confirmation(self)) - - await channel.send(embed=send_feedback(self, msg, ctx.author)) - - # Edit current embed to show error that the feedback timed out - except asyncio.TimeoutError: - - embed = Embed(title="(。T ω T。) You waited too long", - description=f"Do **{ctx.prefix}feedback** to try again!", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_footer(text=f"Sent To {ctx.author}", icon_url=ctx.author.avatar_url) - - # Send out an error message if the user waited too long - await helper.edit(embed=embed) - - -def setup(bot): - bot.add_cog(Help(bot)) diff --git a/cogs/info.py b/cogs/info.py deleted file mode 100644 index 178bd613..00000000 --- a/cogs/info.py +++ /dev/null @@ -1,490 +0,0 @@ -# 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(colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=f"{member}'s Avatar", icon_url=member.avatar_url, url=userAvatar) - 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, "greyscale.png") - embed = Embed(colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=f"{member}'s Avatar | Greyscale", icon_url=member.avatar_url) - embed.set_image(url="attachment://greyscale.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", "inverted"]) - @bot_has_permissions(embed_links=True) - @cooldown(1, 2, BucketType.user) - async def invert_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 - - # Invert the image - 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, "inverted.png") - embed = Embed(colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=f"{member}'s Avatar | Inverted", icon_url=member.avatar_url) - embed.set_image(url="attachment://inverted.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)) diff --git a/cogs/interactive.py b/cogs/interactive.py deleted file mode 100644 index 8ba6a0e2..00000000 --- a/cogs/interactive.py +++ /dev/null @@ -1,295 +0,0 @@ -# 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 random - -from discord import Embed, Member -from discord.ext.commands import cooldown, command, BucketType, bot_has_permissions, Cog - - -def random_line(file): - """Return a random line from the chosen file""" - - lines = open(f'images/FunCommands/{file}.txt').read().splitlines() - return random.choice(lines) - - -# Gets the member and user avatar -def getMember(ctx): - # Set member as the author - member = ctx.message.author - # Get the member avatar - userAvatar = member.avatar_url - - return member, userAvatar - - -# Set up the Cog -class Interactive(Cog): - """Interactive Commands! (E.G Kiss/Hug/Cuddle)""" - - 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="kiss") - @bot_has_permissions(embed_links=True) - @cooldown(1, 1, BucketType.user) - async def kiss(self, ctx, member: Member): - """Kiss your partner""" - - # Get the guild - guild = ctx.guild - - # Error handling to make sure that the user can kiss themselves - if member.id == ctx.author.id: - kiss = False - title = f":kissing_heart: :kissing_heart: | **{ctx.author.display_name}** kissed **themselves**" - else: - kiss = True - title = f":kissing_heart: :kissing_heart: | **{ctx.author.display_name}** kissed **{member.display_name}**" - - # Get author record from cache/database - result = await self.bot.check_cache(ctx.author.id, guild.id) - - married_user = result["married"] - if married_user is None and kiss: - await self.bot.generate_embed(ctx, - desc="Σ(‘◉⌓◉’) You need to be married in order to use this command! Baka!") - return - elif not member.id == married_user and kiss: - await self.bot.generate_embed(ctx, desc="Σ(‘◉⌓◉’) You can only kiss your partner! Baka!") - return - - # Get the member and the userAvatar - member, userAvatar = getMember(ctx) - - # Set up the embed to display a random kissing gif - embed = Embed( - title=title, - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_image(url=random_line("kissing")) - embed.set_footer(text=f"Requested by {member}", icon_url=userAvatar) - - # Send the embedded message to the user - await ctx.send(embed=embed) - - @command(name="cuddle") - @bot_has_permissions(embed_links=True) - @cooldown(1, 1, BucketType.user) - async def cuddle(self, ctx, member: Member): - """Cuddle your partner""" - - # Get the guild - guild = ctx.guild - - # Error handling to make sure that the user can cuddle themselves - if member.id == ctx.author.id: - cuddle = False - title = f":blush: :blush: | **{ctx.author.display_name}** cuddled **themselves**" - else: - cuddle = True - title = f":blush: :blush: | **{ctx.author.display_name}** cuddled **{member.display_name}**" - - # Get author record from cache/database - result = await self.bot.check_cache(ctx.author.id, guild.id) - - married_user = result["married"] - if married_user is None and cuddle: - await self.bot.generate_embed(ctx, - desc="Σ(‘◉⌓◉’) You need to be married in order to use this command! Baka!") - return - elif not member.id == married_user and cuddle: - await self.bot.generate_embed(ctx, desc="Σ(‘◉⌓◉’) You can only cuddle your partner! Baka!") - return - - # Get the member and the userAvatar - member, userAvatar = getMember(ctx) - - # Set up the embed to display a random cuddling gif - embed = Embed( - title=title, - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_image(url=random_line("cuddling")) - embed.set_footer(text=f"Requested by {member}", icon_url=userAvatar) - - # Send the embedded message to the user - await ctx.send(embed=embed) - - @command(name="kill") - @bot_has_permissions(embed_links=True) - @cooldown(1, 1, BucketType.user) - async def kill(self, ctx, member: Member): - """Kill a member""" - - if member is ctx.author: - title = f":scream: :scream: | **{ctx.author.display_name}** killed **themselves**" - else: - title = f":scream: :scream: | **{ctx.author.display_name}** killed **{member.display_name}**" - - # Get the member and the userAvatar - member, userAvatar = getMember(ctx) - - # Set up the embed to display a random killing gif - embed = Embed( - title=title, - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_image(url=random_line("killing")) - embed.set_footer(text=f"Requested by {member}", icon_url=userAvatar) - - # Send the embedded message to the user - await ctx.send(embed=embed) - - @command(name="slap") - @bot_has_permissions(embed_links=True) - @cooldown(1, 1, BucketType.user) - async def slap(self, ctx, member: Member): - """Slap a member""" - - if member is ctx.author: - title = f":cold_sweat: :cold_sweat: | **{ctx.author.display_name}** slapped **themselves**" - else: - title = f":cold_sweat: :cold_sweat: | **{ctx.author.display_name}** slapped **{member.display_name}**" - - # Get the member and the userAvatar - member, userAvatar = getMember(ctx) - - # Set up the embed to display a random slapping gif - embed = Embed( - title=title, - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_image(url=random_line("slapping")) - embed.set_footer(text=f"Requested by {member}", icon_url=userAvatar) - - # Send the embedded message to the user - await ctx.send(embed=embed) - - @command(name="pat") - @bot_has_permissions(embed_links=True) - @cooldown(1, 1, BucketType.user) - async def pat(self, ctx, member: Member): - """Pat a member""" - - if member is ctx.author: - title = f"👉 👈 | **{ctx.author.display_name}** patted **themselves**" - else: - title = f"👉 👈 | **{ctx.author.display_name}** patted **{member.display_name}**" - - # Get the member and the userAvatar - member, userAvatar = getMember(ctx) - - # Set up the embed to display a random patting gif - embed = Embed( - title=title, - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_image(url=random_line("patting")) - embed.set_footer(text=f"Requested by {member}", icon_url=userAvatar) - - # Send the embedded message to the user - await ctx.send(embed=embed) - - @command(name="lemon") - @bot_has_permissions(embed_links=True) - @cooldown(1, 1, BucketType.user) - async def lemon(self, ctx, member: Member): - """Give Lemon to member""" - - if member is ctx.author: - title = f":relaxed: :relaxed: | **{ctx.author.display_name}** gave a lemon to **themselves**" - else: - title = f":relaxed: :relaxed: | **{ctx.author.display_name}** gave a lemon to **{member.display_name}**" - - lemon_array = ["https://media.discordapp.net/attachments/669812887564320769/720093589056520202/lemon.gif", - "https://media.discordapp.net/attachments/669812887564320769/720093575492272208/lemon2.gif", - "https://media.discordapp.net/attachments/718484280925224981/719629805263257630/lemon.gif"] - - # Get the member and the userAvatar - member, userAvatar = getMember(ctx) - - # Set up the embed to display a random lemon gif - embed = Embed( - title=title, - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_image(url=random.choice(lemon_array)) - embed.set_footer(text=f"Requested by {member}", icon_url=userAvatar) - - # Send the embedded message to the user - await ctx.send(embed=embed) - - @command(name="choke") - @bot_has_permissions(embed_links=True) - @cooldown(1, 1, BucketType.user) - async def choke(self, ctx, member: Member): - """Choke a member""" - - if member is ctx.author: - title = f":confounded: :confounded: | **{ctx.author.display_name}** choked **themselves**" - else: - title = f":confounded: :confounded: | **{ctx.author.display_name}** choked **{member.display_name}**" - - # Get the member and the userAvatar - member, userAvatar = getMember(ctx) - - # Set up the embed to display a random choking gif - embed = Embed( - title=title, - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_image(url=random_line("choking")) - embed.set_footer(text=f"Requested by {member}", icon_url=userAvatar) - - # Send the embedded message to the user - await ctx.send(embed=embed) - - @command(name="hug") - @bot_has_permissions(embed_links=True) - @cooldown(1, 1, BucketType.user) - async def hug(self, ctx, member: Member): - """Hug a member""" - - if member is ctx.author: - title = f":smiling_face_with_3_hearts: :smiling_face_with_3_hearts: | **{ctx.author.display_name}** hugged **themselves**" - else: - title = f":smiling_face_with_3_hearts: :smiling_face_with_3_hearts: | **{ctx.author.display_name}** hugged **{member.display_name}**" - - # Get the member and the userAvatar - member, userAvatar = getMember(ctx) - - # Set up the embed to display a random hugging gif - embed = Embed( - title=title, - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_image(url=random_line("hugging")) - embed.set_footer(text=f"Requested by {member}", icon_url=userAvatar) - - # Send the embedded message to the user - await ctx.send(embed=embed) - - -def setup(bot): - bot.add_cog(Interactive(bot)) diff --git a/cogs/libs/__init__.py b/cogs/libs/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cogs/libs/functions.py b/cogs/libs/functions.py deleted file mode 100644 index d0ef07c6..00000000 --- a/cogs/libs/functions.py +++ /dev/null @@ -1,137 +0,0 @@ -# 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 . - -# Using frozenset -# Permissions to filter through -perms = frozenset( - { - "create instant invite", - "add reactions", - "view audit log", - "priority speaker", - "stream", - "read messages", - "send messages", - "send tts messages", - "embed links", - "attach links", - "read message history", - "external emojis", - "view guild insights", - "connect", - "speak", - "use voice activation", - "change nickname" - } -) - -# List of regions mapped to emojis -region = { - "eu-central": ":flag_eu: Central Europe", - "europe": ":flag_eu: Central Europe", - "singapore": ":flag_sg: Singapore", - "india": ":flag_in: India", - "japan": ":flag_jp: Japan", - "us-central": ":flag_us: U.S. Central", - "sydney": ":flag_au: Sydney", - "us-east": ":flag_us: U.S. East", - "us-south": ":flag_us: U.S. South", - "us-west": ":flag_us: U.S. West", - "eu-west": ":flag_eu: Western Europe", - "vip-us-east": ":flag_us: VIP U.S. East", - "london": ":flag_gb: London", - "amsterdam": ":flag_nl: Amsterdam", - "hongkong": ":flag_hk: Hong Kong", - "russia": ":flag_ru: Russia", - "southafrica": ":flag_za: South Africa", - "brazil": ":flag_br: Brazil" -} - -# List of content filters and their descriptions -filters = { - "disabled": "<:xMark:746834944629932032>", - "no_role": "<:greenTick:746834932936212570> For Members Without Roles", - "all_members": "<:greenTick:746834932936212570> For All Members" -} - -# List of default notifications settings for guild -notifs = { - "all_messages": "<:greenTick:746834932936212570> For All Messages", - "only_mentions": "<:greenTick:746834932936212570> For All Mentions" -} - - -def detect_perms(message, fset): - """Filter out permissions that are not important""" - - # Split the message individual permissions - message = message.split(",") - - # Filter the permission out if it's in the frozenset - filtered = filter(lambda perm: perm not in fset, message) - return ", ".join(filtered) - - -def string_list(types, n, instance): - """Return objects in nicely formatted strings""" - - if len(types) > n: - # Retrieve the length of the remaining roles - length = len(types) - n - - if instance == "Emoji": - # Store the first 20 emojis in a string - string = f"{' '.join(map(str, (types[:n])))} and **{length}** more..." - else: - # Store the first n roles/members in a string called "roles" (highest to lowest) - string = f"{' **|** '.join(_type.mention for _type in list(reversed(types))[:n])} and **{length}** more" - - else: - if instance == "Role": - # Display all roles as it is lower than n provided - string = f"{' **|** '.join(role.mention for role in list(reversed(types[1:])))}" - elif instance == "Emoji": - # Display all the emojis in the server as it is less than 20 - string = " ".join(map(str, types)) - else: - # Display all members as it is lower than n provided - string = f"{' **|** '.join(role.mention for role in list(reversed(types)))}" - - return string - - -def get_region(disc_region): - """Return Nicer Looking Region String""" - - for key in region: - if key == disc_region: - return region[key] - - -def get_content_filter(content_filter): - """Return nicer looking content filter string""" - - for key in filters: - if key == content_filter: - return filters[key] - - -def get_notifs(notif): - """Return nicer looking notification string""" - - for key in notifs: - if key == notif: - return notifs[key] diff --git a/cogs/libs/modmail.py b/cogs/libs/modmail.py deleted file mode 100644 index 21d63eec..00000000 --- a/cogs/libs/modmail.py +++ /dev/null @@ -1,309 +0,0 @@ -# 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 asyncio -import datetime -import io -import random - -import discord -from discord import Embed, File - - -class Modmail: - """Methods for sending modmail!""" - - def __init__(self, bot): - self.bot = bot - self.anon = None - self.avatars = ["https://cdn.discordapp.com/embed/avatars/0.png", - "https://cdn.discordapp.com/embed/avatars/1.png", - "https://cdn.discordapp.com/embed/avatars/2.png", - "https://cdn.discordapp.com/embed/avatars/3.png", - "https://cdn.discordapp.com/embed/avatars/4.png"] - - def anon_or_not(self, author): - """Method to ask the user if they want to be anonymous or not""" - - # Set up embed to let the user how to start sending modmail - AnonModMailEmbed = Embed(title="**Want to send it Anonymously?**", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - - AnonModMailEmbed.set_thumbnail(url=author.avatar_url) - AnonModMailEmbed.set_footer(text=f"Sent by {author}") - - fields = [(self.bot.blank_space, "**We understand that for some things," - "you may want to remain Anonymous." - "\nUse the reactions below to choose!**", False), - (self.bot.blank_space, "**Use :white_check_mark: for** `Yes`", True), - (self.bot.blank_space, "**Use :x: for** `No`", True), - (self.bot.blank_space, self.bot.blank_space, True), - (self.bot.blank_space, - "The Staff will not know who is sending this" - "\nPurely negative feedback will not be considered.", True)] - - for name, value, inline in fields: - AnonModMailEmbed.add_field(name=name, value=value, inline=inline) - - return AnonModMailEmbed - - def send_instructions(self, author): - """Method to send an embed to to let the user know to type into chat""" - - SendModMailEmbed = Embed(title="**Please enter a message for it to be sent to the staff!**", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - - SendModMailEmbed.set_thumbnail(url=author.avatar_url) - SendModMailEmbed.set_footer(text=f"Sent by {author}") - - fields = [("**Make sure that the message is above **50** and below **1024** characters!**", - "**Include as much detail as possible :P**", - False)] - - for name, value, inline in fields: - SendModMailEmbed.add_field(name=name, value=value, inline=inline) - - return SendModMailEmbed - - def error_handling(self, author): - """Method to let the user know that the message must be above 50 characters""" - - ErrorHandlingEmbed = Embed( - title="Uh Oh! Please make sure the message is above **50** and below **1024** characters!", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - - ErrorHandlingEmbed.set_thumbnail(url=author.avatar_url) - ErrorHandlingEmbed.set_footer(text=f"Sent by {author}") - - fields = [("Please enter in a message which is above **50** and below **1024** characters!", - "**This helps us reduce spam and allows you to include more detail in your mail!**", - False)] - - for name, value, inline in fields: - ErrorHandlingEmbed.add_field(name=name, value=value, inline=inline) - - return ErrorHandlingEmbed - - def message_sent_confirmation(self, author): - """Method to send an embed into chat to let the user know that their mail has been sent successfully""" - - ConfirmationEmbed = Embed(title="**Message relayed to Staff!!**", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - - ConfirmationEmbed.set_thumbnail(url=author.avatar_url) - ConfirmationEmbed.set_footer(text=f"Sent by {author}") - - fields = [("Thank you for your input! The staff team appreciate it very much!", - f"\n As mentioned previously, please don't be hesitant to DM the Staff for anything! :P", - False)] - - for name, value, inline in fields: - ConfirmationEmbed.add_field(name=name, value=value, inline=inline) - - return ConfirmationEmbed - - def send_modmail(self, msg, author): - """Method to actually allow the message to be sent to modmail logging channel""" - - embed = Embed(title="Modmail", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - - if self.anon: - - embed.set_thumbnail(url=random.choice(self.avatars)) - embed.set_footer(text=f"Sent By Anon Member") - - fields = [("Member", "Anon Member", False), - ("Message", msg.content, False)] - else: - - embed.set_thumbnail(url=author.avatar_url) - embed.set_footer(text=f"Sent By {author}") - - fields = [("Member", author, False), - ("Message", msg.content, False)] - - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - - return embed - - async def wait_for_msg(self, check, user_channel): - """ - Method to check if the user actually types in a message - If not, delete the channel - """ - - try: - # Wait for the message from the author - mod_message = await self.bot.wait_for('message', check=check, timeout=300.0) - - # Delete channel if user does not send a message within 5 minutes - except asyncio.TimeoutError: - await user_channel.delete() - return None - else: - return mod_message - - async def modmail(self, payload): - """Listen for reactions for modmail channel/starboard""" - - # Don't count reactions that are made by the bot - # Don't count other reactions other than ✅ and ❌ - if payload.member.bot or str(payload.emoji) not in ['✅', '❌']: - return - - # Get the modmail information from cache - modmail = self.bot.get_modmail(payload.guild_id) - if modmail: - channel_id = modmail["modmail_channel_id"] - message_id = modmail["message_id"] - modmail_channel_id = modmail["modmail_logging_channel_id"] - else: - return - - # Bunch of checks to make sure it has the right guild, channel, message and reaction - if payload.channel_id == channel_id and payload.message_id == message_id and payload.emoji.name == "✅": - - # Get the guild - guild = self.bot.get_guild(payload.guild_id) - # Get the member - member = guild.get_member(payload.user_id) - # Get the setup modmail channel - channel = guild.get_channel(payload.channel_id) - # Get the modmail logging channel - modmail_channel = guild.get_channel(modmail_channel_id) - - # Fetch the message and remove the reaction - reaction = await channel.fetch_message(message_id) - await reaction.remove_reaction('✅', member) - - # Setting up the channel permissions for the new channel that will be created - overwrites = { - guild.default_role: discord.PermissionOverwrite(read_messages=False, send_messages=False), - guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True, embed_links=True, - add_reactions=True, manage_messages=True), - member: discord.PermissionOverwrite(read_messages=True, send_messages=True) - } - - # Saving this for later within when discord.py 1.4 comes out - # user_channel = await guild.create_category_channel("Member", overwrites=overwrites, position=7) - - # Create the text channel - user_channel = await guild.create_text_channel("Member", overwrites=overwrites, - position=0) - - # Mention the user to make sure that they get pinged - mention = await user_channel.send(member.mention) - await mention.delete() - - try: - - # Send the embed if they want to remain anonymous or not - Anon_or_Not = await user_channel.send(embed=self.anon_or_not(member)) - # Add reactions to the message - await Anon_or_Not.add_reaction('✅') - await Anon_or_Not.add_reaction('❌') - - # Checking if the user reacted with ✅ with response to sending staff a message - def emoji_check(reaction, user): - return user == member and str(reaction.emoji) in ['✅', '❌'] - - try: - # Wait for the user to add a reaction - reaction, user = await self.bot.wait_for('reaction_add', check=emoji_check, timeout=60.0) - - # Delete channel if user does not react within 60 seconds - except asyncio.TimeoutError as ex: - print(ex) - await user_channel.delete() - else: - - # Making sure that the reply is from the author - def check(m): - return m.author == payload.member and user_channel.id == instructions.channel.id - - # Checking if user wants to be Anonymous or not - if str(reaction.emoji) == "✅": - self.anon = True - - if str(reaction.emoji) == "❌": - self.anon = False - - # Delete the old embed - await Anon_or_Not.delete() - - # Tell the user to type their mail into the chat - instructions = await user_channel.send(embed=self.send_instructions(member)) - - # Wait for the message from the author - msg = await self.wait_for_msg(check, user_channel) - if not msg: return - - # Making sure that the message is below 50 characters and the message was sent in the channel - while len(msg.content) <= 50 and msg.channel == user_channel: - await user_channel.send(embed=self.error_handling(member)) - - # Wait for the message from the author - msg = await self.wait_for_msg(check, user_channel) - if not msg: return - - # As long as the message is above 50 characters and in the correct channel - if len(msg.content) > 50 and msg.channel == user_channel: - # Delete the previous embed - await instructions.delete() - - # Store all text in the channel in a bytesio object - text = "" - async for message in user_channel.history(limit=300): - text += "".join(f"{message.created_at} : {message.content}\n") - text_bytes = str.encode(text) - - file = io.BytesIO(text_bytes) - file_name = "Anon.txt" if self.anon else f"{member.name}.txt" - - # Send the message to the modmail channel - await modmail_channel.send(embed=self.send_modmail(msg, member), - file=File(file, file_name)) - - # Make sure the user knows that their message has been sent - await user_channel.send(embed=self.message_sent_confirmation(member)) - - # Let the user read the message for 5 seconds - await asyncio.sleep(5) - - # Delete the channel and then stop the function - await user_channel.delete() - - # If the user types anywhere else, delete the channel - else: - await user_channel.delete() - - except Exception as ex: - print(ex) - - # Send out an error message if the user waited too long - await user_channel.send( - "Sorry! Something seems to have gone wrong and the modmail will be aborting." - "\nRemember to make sure it's under **1024** characters!!") - - await asyncio.sleep(5) - await user_channel.delete() diff --git a/cogs/libs/paginators.py b/cogs/libs/paginators.py deleted file mode 100644 index 242b2212..00000000 --- a/cogs/libs/paginators.py +++ /dev/null @@ -1,395 +0,0 @@ -# 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 asyncio -import datetime - -from discord import Embed -from discord.ext import menus - - -def search(self, bot): - """Method to generate embed of multiple waifu's""" - - not_found = "https://media.discordapp.net/attachments/741072426984538122/748586578074664980/DzEZ4UsXgAAcFjN.png?width=423&height=658" - - embeds = [] - for key in self._dict.values(): - - # Only setting up description if waifu og_name has a value - desc = key['original_name'] if key["original_name"] else Embed.Empty - # Only using image if it can be displayed, else display 404 image - url = key["display_picture"] if key["display_picture"].endswith((".jpeg", ".png", ".jpg")) else not_found - - embed = Embed(title=key["name"], description=desc, - colour=bot.random_colour(), - url=key["url"]) - embed.set_image(url=url) - embed.set_author(name=f"{key['type']} | ID: {key['id']}") - - if key["type"] in ["Waifu", "Husbando"]: - embed.set_footer(text=f"❤️ {key['likes']} 🗑️ {key['trash']} | Powered by MyWaifuList") - else: - embed.set_footer(text=f"{key['romaji_name']} | Powered by MyWaifuList" if key[ - "romaji_name"] else "Powered by MyWaifuList") - - embeds.append(embed) - - return embeds - - -class SimpleMenu(menus.Menu): - def __init__(self, i, item, perms, embeds, bot): - super().__init__(timeout=125.0, clear_reactions_after=True) - self.i = i - self.perms = perms - self.dicts = embeds - self.type = item - self.bot = bot - - async def remove_reaction(self, reaction): - """Remove the reaction given""" - - if self.perms.manage_messages: - await self.message.remove_reaction(reaction, self.ctx.author) - - @staticmethod - def check(m, payload): - """Simple check to make sure that the reaction is performed by the user""" - - return m.author == payload.member and m.channel.id == payload.channel_id - - @staticmethod - def get_page(self): - """ - Return the current page index - """ - - cur_page = self.i + 1 - pages = len(self.dicts) - - return cur_page, pages - - async def send_initial_message(self, ctx, channel): - """Set the first embed to the first element in the pages[]""" - - initial = self.dicts[self.i] - - cur_page, pages = self.get_page(self) - pages = len(self.dicts) - initial.set_author(name=f"{self.type} | Page {cur_page}/{pages}") - - # Send embed - return await channel.send(embed=initial) - - @menus.button('\N{LEFTWARDS BLACK ARROW}') - async def on_left_arrow(self, payload): - """Reaction to allow user to go to the previous page in the embed""" - - # Do nothing if the check does not return true - if self.check(self.ctx, payload): - # Set self.i to (i - 1) remainder length of the array - self.i = (self.i - 1) % len(self.dicts) - prev_page = self.dicts[self.i] - - cur_page, pages = self.get_page(self) - prev_page.set_author(name=f"{self.type} | Page {cur_page}/{pages}") - - # Send the embed and remove the reaction of the user - await self.message.edit(embed=prev_page) - await self.remove_reaction("⬅") - - @menus.button('\N{BLACK RIGHTWARDS ARROW}') - async def on_right_arrow(self, payload): - """Reaction to allow user to go to the next page in the embed""" - - # Do nothing if the check does not return true - if self.check(self.ctx, payload): - # Set self.i to (i + 1) remainder length of the array - self.i = (self.i + 1) % len(self.dicts) - next_page = self.dicts[self.i] - - cur_page, pages = self.get_page(self) - next_page.set_author(name=f"{self.type} | Page {cur_page}/{pages}") - - # Send the embed and remove the reaction of the user - await self.message.edit(embed=next_page) - await self.remove_reaction("➡") - - @menus.button('\N{BLACK SQUARE FOR STOP}\ufe0f') - async def on_stop(self, payload): - """Reaction to allow user to make the embed disappear""" - - # Do nothing if the check does not return true - if self.check(self.ctx, payload): - # Edit the embed and tell the member that the session has been closed - embed = Embed(description="**Pagination Session Has Been Closed**", - colour=self.bot.random_colour()) - await self.message.edit(embed=embed) - self.stop() - - -class MWLMenu(menus.Menu): - """Setup menus for MyWaifuList results""" - - def __init__(self, i, perms, _dict, bot): - super().__init__(timeout=125.0, clear_reactions_after=True) - self.i = i - self.perms = perms - self._dict = _dict - self.bot = bot - self.dicts = search(self, bot) - - async def remove_reaction(self, reaction): - """Remove the reaction given""" - - if self.perms.manage_messages: - await self.message.remove_reaction(reaction, self.ctx.author) - - @staticmethod - def check(m, payload): - """Simple check to make sure that the reaction is performed by the user""" - - return m.author == payload.member and m.channel.id == payload.channel_id - - @staticmethod - def get_page(self): - """ - Return the current page index - """ - - cur_page = self.i + 1 - pages = len(self.dicts) - - return cur_page, pages - - @staticmethod - def set_author(embed, cur_page, pages): - """ - Returns the author for the first initial embed - - The reason why it's different is because I need to retrieve the previous author that I set for the - embed (to get the type from the API) - - """ - - __type = embed.author.name - embed.remove_author() - return embed.set_author(name=f"{__type} | Page {cur_page}/{pages}") - - @staticmethod - def set_author_after(embed, cur_page, pages): - """ - Returns the author for all the pages when the user reacts to go back and forwards - - This needs to be another method because the previous author is gonna be different to the one - specified at the start "multiple_dict_generators()" - """ - - author = embed.author.name - tv_type = author.split("|") - __type = f"{tv_type[0].strip()} | {tv_type[1].strip()}" - return embed.set_author(name=f"{__type} | Page {cur_page}/{pages}") - - async def send_initial_message(self, ctx, channel): - """Set the first embed to the first element in the pages[]""" - - initial = self.dicts[self.i] - - cur_page, pages = self.get_page(self) - initial = self.set_author(initial, cur_page, pages) - - # Send embed - return await channel.send(embed=initial) - - @menus.button('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}') - async def on_first_page_arrow(self, payload): - """Reaction to allow the user to return to the first page""" - - # Do nothing if the check does not return true - if self.check(self.ctx, payload): - - # Send the embed and remove the reaction of the user - if self.i == 0: - await self.remove_reaction("\U000023ee") - return - - self.i = 0 % len(self.dicts) - first_page = self.dicts[self.i] - - cur_page, pages = self.get_page(self) - first_page = self.set_author_after(first_page, cur_page, pages) - - await self.message.edit(embed=first_page) - await self.remove_reaction("\U000023ee") - - @menus.button('\N{LEFTWARDS BLACK ARROW}') - async def on_left_arrow(self, payload): - """Reaction to allow user to go to the previous page in the embed""" - - # Do nothing if the check does not return true - if self.check(self.ctx, payload): - # Set self.i to (i - 1) remainder length of the array - self.i = (self.i - 1) % len(self.dicts) - prev_page = self.dicts[self.i] - - cur_page, pages = self.get_page(self) - prev_page = self.set_author_after(prev_page, cur_page, pages) - - # Send the embed and remove the reaction of the user - await self.message.edit(embed=prev_page) - await self.remove_reaction("⬅") - - @menus.button('\N{BLACK RIGHTWARDS ARROW}') - async def on_right_arrow(self, payload): - """Reaction to allow user to go to the next page in the embed""" - - # Do nothing if the check does not return true - if self.check(self.ctx, payload): - # Set self.i to (i + 1) remainder length of the array - self.i = (self.i + 1) % len(self.dicts) - next_page = self.dicts[self.i] - - cur_page, pages = self.get_page(self) - next_page = self.set_author_after(next_page, cur_page, pages) - - # Send the embed and remove the reaction of the user - await self.message.edit(embed=next_page) - await self.remove_reaction("➡") - - @menus.button('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}') - async def on_last_page_arrow(self, payload): - """Reaction to allow the user to go to the last page in the embed""" - - # Do nothing if the check does not return true - if self.check(self.ctx, payload): - - # Send the embed and remove the reaction of the user - if self.i == len(self.dicts) - 1: - await self.remove_reaction("\U000023ed") - return - - self.i = len(self.dicts) - 1 - last_page = self.dicts[self.i] - - cur_page, pages = self.get_page(self) - last_page = self.set_author_after(last_page, cur_page, pages) - - await self.message.edit(embed=last_page) - await self.remove_reaction("\U000023ed") - - @menus.button('\N{INPUT SYMBOL FOR NUMBERS}') - async def on_numbered_page(self, payload): - """Reaction to allow users to input page numbers""" - - # Do nothing if the check does not return true - if self.check(self.ctx, payload): - - embed = Embed(description="**What Page Would You Like To Go To? (Only Numbers!)**", - colour=self.bot.random_colour()) - message = await self.ctx.send(embed=embed) - - def check(m): - """Simple check to make sure that the reaction is performed by the user""" - return m.author == payload.member and m.channel.id == payload.channel_id - - try: - # Wait for the message from the mentioned user - msg = await self.bot.wait_for('message', check=check, timeout=20.0) - - # Catch timeout error - except asyncio.TimeoutError as ex: - print(ex) - - await self.remove_reaction("\U0001f522") - - embed = Embed(description="**You Waited Too Long D:**", - colour=self.bot.random_colour()) - await message.edit(embed=embed) - - await asyncio.sleep(2.5) - await message.delete() - - else: - # As long as the number entered is within the page numbers, go to that page - try: - if 0 < int(msg.content) <= len(self.dicts): - await message.delete() - await msg.delete() - - self.i = int(msg.content) - 1 - number_page = self.dicts[self.i] - - cur_page, pages = self.get_page(self) - last_page = self.set_author_after(number_page, cur_page, pages) - - await self.message.edit(embed=last_page) - await self.remove_reaction("\U0001f522") - - # Delete the message and remove the reaction if out of bounds - else: - await message.delete() - await msg.delete() - await self.remove_reaction("\U0001f522") - except Exception as e: - print(e) - await message.delete() - await msg.delete() - await self.remove_reaction("\U0001f522") - - @menus.button('\N{INFORMATION SOURCE}') - async def on_information(self, payload): - """Show's information about the pagination session""" - - # Do nothing if the check does not return true - if self.check(self.ctx, payload): - - messages = ['Welcome to the Waifu/Anime Pagination Session!', - 'This interactively allows you to see pages of text by navigating with ' - 'reactions. They are as follows:\n'] - - reaction_emojis = [ - ('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', "Takes You To The First Page"), - ('\N{BLACK LEFT-POINTING TRIANGLE}', "Takes You To The Previous Page"), - ('\N{BLACK RIGHT-POINTING TRIANGLE}', "Takes You To The Next Page"), - ('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', "Takes You To The Last Page"), - ('\N{INPUT SYMBOL FOR NUMBERS}', "Enter Page Number To Go To"), - ('\N{INFORMATION SOURCE}', "Shows This Message"), - ('\N{BLACK SQUARE FOR STOP}', "Closes The Pagination Session") - ] - - for value, func in reaction_emojis: - messages.append(f"{value}, {func}") - - embed = Embed(description='\n'.join(messages), - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_footer(text=f'We Were On Page {self.i + 1} Before This Message') - - await self.message.edit(embed=embed) - await self.remove_reaction("\U00002139") - - @menus.button('\N{BLACK SQUARE FOR STOP}\ufe0f') - async def on_stop(self, payload): - """Reaction to allow user to make the embed disappear""" - - # Do nothing if the check does not return true - if self.check(self.ctx, payload): - # Edit the embed and tell the member that the session has been closed - embed = Embed(description="**Waifu/Anime Reaction Session Has Been Closed**", - colour=self.bot.random_colour()) - await self.message.edit(embed=embed) - self.stop() diff --git a/cogs/libs/starboard.py b/cogs/libs/starboard.py deleted file mode 100644 index 743af4c1..00000000 --- a/cogs/libs/starboard.py +++ /dev/null @@ -1,241 +0,0 @@ -# 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 asyncpg -from discord import Embed - - -class Starboard: - def __init__(self, bot): - self.bot = bot - - async def send_starboard_message(self, payload, new_stars, msg_id, channel, message, embed): - """Send the message to the starboard for the first time""" - - # When the message stars is larger than the minimum, send to the starboard and store in database/cache - if new_stars >= self.bot.get_starboard_min_stars(payload.guild_id) and not msg_id: - star_message = await channel.send(embed=embed) - - # Only insert the record into database when it doesn't exist in cache - if not self.bot.check_root_message_id(message.id, payload.guild_id): - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Insert the starboard message in the database - try: - insert = """INSERT INTO starboard_messages (root_message_id, guild_id, star_message_id) - VALUES ($1, $2, $3)""" - await conn.execute(insert, message.id, payload.guild_id, star_message.id) - - # Catch errors - except asyncpg.PostgresError as e: - print( - f"PostGres Error: Starboard_Message Record Could Not Be Inserted For Guild {payload.guild_id}", - e) - - # Update cache - else: - self.bot.cache_store_starboard_message(message.id, payload.guild_id, star_message.id) - else: - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Update the stars that the message has in the database and then store the message id's - try: - update = """UPDATE starboard_messages SET stars = $1, star_message_id = $2 WHERE root_message_id = $3 AND guild_id = $4""" - await conn.execute(update, new_stars, star_message.id, message.id, payload.guild_id) - - # Catch errors - except asyncpg.PostgresError as e: - print( - f"PostGres Error: Starboard_Message Record Could Not Be Updated For Guild {payload.guild_id}", - e) - - # Update cache - else: - self.bot.update_starboard_message_id(message.id, payload.guild_id, star_message.id) - self.bot.update_starboard_message_stars(message.id, payload.guild_id, new_stars) - - # Store the message into the database so the reactions can be tracked - else: - # Only insert into the database when the message doesn't exist in cache - if not self.bot.check_root_message_id(message.id, payload.guild_id): - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Insert the starboard message in the database - try: - insert = """INSERT INTO starboard_messages (root_message_id, guild_id) VALUES ($1, $2)""" - await conn.execute(insert, message.id, payload.guild_id) - - # Catch errors - except asyncpg.PostgresError as e: - print( - f"PostGres Error: Starboard_Message Record Could Not Be Inserted For Guild {payload.guild_id}", - e) - - # Update cache - else: - self.bot.cache_store_starboard_message(message.id, payload.guild_id, None) - - # When the message is already in the database/cache, update the amount of reactions - else: - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Update the stars that the message has in the database and then store the message id's - try: - update = """UPDATE starboard_messages SET stars = $1 WHERE root_message_id = $2 AND guild_id = $3""" - await conn.execute(update, new_stars, message.id, payload.guild_id) - - # Catch errors - except asyncpg.PostgresError as e: - print( - f"PostGres Error: Starboard_Message Record Could Not Be Updated For Guild {payload.guild_id}", - e) - - # Update cache - else: - self.bot.update_starboard_message_stars(message.id, payload.guild_id, new_stars) - - async def edit_starboard_message(self, payload, new_stars, msg_id, channel, message, embed): - """Edit the message which is already on the starboard""" - - min_stars = self.bot.get_starboard_min_stars(payload.guild_id) - - # When the message has finally reached the minimum amount of stars, send the message to the starboard - if new_stars >= min_stars and not msg_id: - star_message = await channel.send(embed=embed) - - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Update the stars that the message has in the database and then store the message id's - try: - update = """UPDATE starboard_messages SET stars = $1, star_message_id = $2 - WHERE root_message_id = $3 AND guild_id = $4""" - await conn.execute(update, new_stars, star_message.id, message.id, payload.guild_id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Starboard_Message Record Could Not Be Updated For Guild {payload.guild_id}", - e) - - # Update cache - else: - self.bot.cache_store_starboard_message(message.id, payload.guild_id, star_message.id) - self.bot.update_starboard_message_stars(message.id, payload.guild_id, new_stars) - - elif new_stars < min_stars and msg_id: - star_message = await channel.fetch_message(msg_id) - await star_message.delete() - - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Update the stars that the message has in the database and set the star message id to None - try: - update = """UPDATE starboard_messages - SET stars = $1, star_message_id = NULL - WHERE root_message_id = $2 AND guild_id = $3""" - await conn.execute(update, new_stars, message.id, payload.guild_id) - - # Catch errors - except asyncpg.PostgresError as e: - print( - f"PostGres Error: Starboard_Message Record Could Not Be Updated For Guild {payload.guild_id}", - e) - - # Update cache - else: - self.bot.del_starboard_star_message_id(message.id, payload.guild_id) - self.bot.update_starboard_message_stars(message.id, payload.guild_id, new_stars) - - # When the message already exists on the starboard but doesn't have enough reactions anymore - elif msg_id: - star_message = await channel.fetch_message(msg_id) - await star_message.edit(embed=embed) - - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Update the stars that the message has in the database and set the star message id to None - try: - update = """UPDATE starboard_messages SET stars = $1 WHERE root_message_id = $2 AND guild_id = $3""" - await conn.execute(update, new_stars, message.id, payload.guild_id) - - # Catch errors - except asyncpg.PostgresError as e: - print( - f"PostGres Error: Starboard_Message Record Could Not Be Updated For Guild {payload.guild_id}", - e) - - # Update cache - else: - self.bot.update_starboard_message_id(message.id, payload.guild_id, star_message.id) - self.bot.update_starboard_message_stars(message.id, payload.guild_id, new_stars) - - elif not msg_id: - self.bot.update_starboard_message_stars(message.id, payload.guild_id, new_stars) - - async def send_starboard_and_update_db(self, payload, action): - """Send the starboard embed and update database/cache""" - - if (starboard := self.bot.get_starboard_channel(payload.guild_id)) and payload.emoji.name == "⭐": - message = await self.bot.get_channel(payload.channel_id).fetch_message(payload.message_id) - - if not message.author.bot and payload.user_id != message.author.id: - channel = self.bot.get_channel(starboard) - msg_id, stars = await self.bot.check_starboard_messages_cache(message.id, payload.guild_id) - new_stars = stars + 1 if action == "added" else stars - 1 - - embed = Embed(title=f"Starred Message | {new_stars} :star:", - description=f"{message.content or 'View Attachment'}", - colour=message.author.colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=message.author, icon_url=message.author.avatar_url) - embed.set_footer(text=f"ID: {message.id}") - embed.add_field(name="Original", - value=f"**Channel:** {message.channel.mention}\n[Jump To Message]({message.jump_url})", - inline=False) - - # Send spoiler attachments as links - if message.attachments: - file = message.attachments[0] - spoiler = file.is_spoiler() - if not spoiler and file.url.endswith(('png', 'jpeg', 'jpg', 'gif', 'webp')): - embed.set_image(url=file.url) - elif spoiler: - embed.add_field(name='Attachment', value=f'||[{file.filename}]({file.url})||', inline=False) - else: - embed.add_field(name='Attachment', value=f'[{file.filename}]({file.url})', inline=False) - - # When the message has no previous stars, send a new starboard message or update the amount of stars it has - if not stars: - await self.send_starboard_message(payload, new_stars, msg_id, channel, message, embed) - - # When the message has stars already from the cache/database. Delete or edit the message - else: - await self.edit_starboard_message(payload, new_stars, msg_id, channel, message, embed) diff --git a/cogs/moderation.py b/cogs/moderation.py deleted file mode 100644 index 639a1ede..00000000 --- a/cogs/moderation.py +++ /dev/null @@ -1,1354 +0,0 @@ -# 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 string -from datetime import timedelta -from typing import Optional - -import discord -from discord import Member, Embed, DMChannel, NotFound, User -from discord.ext.commands import command, guild_only, has_guild_permissions, bot_has_guild_permissions, Greedy, \ - cooldown, BucketType, Cog - -from cogs.libs.functions import string_list, get_region, get_content_filter, get_notifs - -# TODO: CREATE A BITARRAY SO THAT THE MODLOG EVENTS ARE TOGGLEABLE -# TODO: MAKE SURE THAT THE BITARRAY IS ONLY IMPLEMENTED AFTER ALL EVENTS ARE CODED - -# Defining a dictionary of the statuses to emojis -member_status = { - "online": "", - "idle": "", - "dnd": "", - "offline": "" -} - - -async def send_to_modlogs(self, ctx, target, reason, action): - """ - Function to send the moderation actions to modlogs channel - - On top of the normal logging, this function sends another embed - if the user has used the inbuilt mute command which allows for a - reason to be given - - """ - - # Get the channel of the modlog within the guild - if modlog := self.bot.get_modlog_for_guild(ctx.guild.id): - channel = ctx.guild.get_channel(modlog) - - if isinstance(target, User): - desc = f"**User -->** {target}\n**ID -->** {target.id}" \ - f"\n\n**Auctioned By --> {ctx.author.mention} | {ctx.author}\nID -->** {ctx.author.id}" - embed = Embed(title=f"User {action}", - description=desc, - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_thumbnail(url=target.avatar_url) - embed.add_field(name="Reason", value=reason, inline=False) - embed.set_footer(text=f"User {action}") - else: - desc = f"**Member --> {target.mention} |** {target}\n**ID -->** {target.id}" \ - f"\n\n**Auctioned By --> {ctx.author.mention} | {ctx.author}\nID -->** {ctx.author.id}" - embed = Embed(title=f"Member {action}", - description=desc, - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_thumbnail(url=target.avatar_url) - embed.add_field(name="Reason", value=reason, inline=False) - embed.set_footer(text=f"Member {action}") - - await channel.send(embed=embed) - - -async def check(ctx, members): - """ - Check Function - - - Checks if all arguments are given - - Checks if user mentions themselves - - Error will be thrown in these two cases - """ - - if not len(members): - desc = f"Not Correct Syntax!\nUse **{ctx.prefix}help** to find how to use **{ctx.command}**" - await ctx.bot.generate_embed(ctx, desc=desc) - return True - - elif ctx.author in members: - await ctx.bot.generate_embed(ctx, desc=f"**{ctx.bot.cross} Forbidden Action {ctx.bot.cross}**") - return True - - -async def ummute_members(self, ctx, targets, reason): - """ - - Method to allow members to be unmuted - - 2 embeds will be sent, one to the channel that the user is in - And if the user has the modlogs channel setup, an embed will be logged there -c3 - """ - - for target in targets: - if (ctx.guild.me.top_role.position > target.top_role.position - and not target.guild_permissions.administrator): - - # Get the roles of the user - result = await self.bot.check_cache(target.id, ctx.guild.id) - - # Get muted roles of the user from cache/database and give them back - role_ids = result["muted_roles"] - roles = [ctx.guild.get_role(int(id_)) for id_ in role_ids.split(", ") if len(id_)] - - # Clear all the roles of the user - await self.bot.clear_roles(member=target) - - await target.edit(roles=roles) - - # Send confirmation to the channel that the user is in - await self.bot.generate_embed(ctx, desc=f"{ctx.bot.tick} **{target}** Was Unmuted! {ctx.bot.tick}") - - await send_to_modlogs(self, ctx, target, reason, action="Unmuted") - - # Send error message if the User could not be muted - else: - desc = f"**{target.mention} Could Not Be Unmuted!**" - await self.bot.generate_embed(ctx, desc=desc) - - -async def mute_members(self, ctx, targets, reason, muted): - """ - - Method to allow members to be muted - - 2 embeds will be sent, one to the channel that the user is in - And if the user has the modlogs channel setup, an embed will be logged there - - """ - - for target in targets: - - # When user is already muted, send error message - if muted in target.roles: - await self.bot.generate_embed(ctx, desc=f"**{ctx.bot.cross} User Is Already Muted! {ctx.bot.cross}**") - - else: - - # Store managed roles into the list of roles to be added to the user (including muted) - roles = [role for role in target.roles if role.managed] - roles.append(muted) - - if (ctx.guild.me.top_role.position > target.top_role.position - and not target.guild_permissions.administrator): - - # Store the current roles of the user within database - await self.bot.store_roles(target=target, ctx=ctx, member=target) - # Give the user the muted role (and any integration roles if need be) - await target.edit(roles=roles, reason=reason) - - # Send confirmation to the channel that the user is in - embed = Embed(description=f"{ctx.bot.tick} **{target}** Was Muted! {ctx.bot.tick}", - colour=self.bot.admin_colour) - - if self.bot.get_roles_persist(ctx.guild.id) == 0: - embed.add_field(name="**WARNING: ROLE PERSIST NOT ENABLED**", - value="The bot **will not give** the roles back to the user if they leave the server." - "\nAllowing the user to bypass the Mute by leaving and rejoining." - f"\nPlease enable Role Persist by doing **{ctx.prefix}rolepersist enable**", - inline=True) - - await ctx.send(embed=embed) - - await send_to_modlogs(self, ctx, target, reason, action="Muted") - - # Send error message if the User could not be muted - else: - await self.bot.generate_embed(ctx, desc=f"**{target.mention} Could Not Be Muted!**") - - -async def ban_members(self, ctx, users, reason): - """ - - Method to allow members to be banned - - 2 embeds will be sent, one to the channel that the user is in - And if the user has the modlogs channel setup, an embed will be logged there - - """ - - # Get the list of banned users from the server - bans = await ctx.guild.bans() - banned_members = list(map(lambda m: m.user, bans)) - - for user in users: - # Make sure that the user not banned already - if user in banned_members: - await self.bot.generate_embed(ctx, desc=f"{ctx.bot.cross} **Member Is Already Banned!** {ctx.bot.cross}") - continue - - # Ban - if user in ctx.guild.members: - member = ctx.guild.get_member(user.id) - if (ctx.guild.me.top_role.position > member.top_role.position - and not member.guild_permissions.administrator): - - await member.ban(reason=reason) - await self.bot.generate_embed(ctx, desc=f"{ctx.bot.tick} **{member}** Was Banned! {ctx.bot.tick}") - - await send_to_modlogs(self, ctx, member, reason, action="Banned") - - # Send error message if the User could not be banned - else: - await self.bot.generate_embed(ctx, desc=f"**{member} Could Not Be Banned!**") - else: - await ctx.guild.ban(discord.Object(id=user.id), reason=reason) - - # Send confirmation to the channel that the user is in - await self.bot.generate_embed(ctx, - desc=f"{self.bot.tick} **{user}** Was Power Banned! {self.bot.tick}") - - await send_to_modlogs(self, ctx, user, reason, action="Power Banned") - - -async def unban_members(self, ctx, users, reason): - """ - - Method to allow members to be unbanned - - 2 embeds will be sent, one to the channel that the user is in - And if the user has the modlogs channel setup, an embed will be logged there - - """ - - # Get the list of banned users from the server - bans = await ctx.guild.bans() - ban_users = list(map(lambda m: m.user, bans)) - - for user in users: - if user not in ban_users: - await self.bot.generate_embed(ctx, desc=f"{ctx.bot.cross} **Member Is Not Banned!** {ctx.bot.cross}") - - else: - await ctx.guild.unban(discord.Object(id=user.id), reason=reason) - - # Send confirmation to the channel that the user is in - await self.bot.generate_embed(ctx, desc=f"{ctx.bot.tick} **{user}** Was Unbanned! {ctx.bot.tick}") - - await send_to_modlogs(self, ctx, user, reason, action="Unbanned") - - -async def kick_members(self, ctx, targets, reason): - """ - - Method to allow the kick member log to be sent to the modlog channel - - 2 embeds will be sent, one to the channel that the user is in - And if the user has the modlogs channel setup, an embed will be logged there - - """ - - for target in targets: - if (ctx.guild.me.top_role.position > target.top_role.position - and not target.guild_permissions.administrator): - - await target.kick(reason=reason) - - await self.bot.generate_embed(ctx, desc=f"{ctx.bot.tick} **{target}** Was Kicked! {ctx.bot.tick}") - - await send_to_modlogs(self, ctx, target, reason, action="Kicked") - - # Send error message if the User could not be kicked - else: - await self.bot.generate_embed(ctx, desc=f"**{target.mention} Could Not Be Kicked!**") - - -class Moderation(Cog): - """Moderation Commands! (Kick/Ban/Mute etc)""" - - def __init__(self, bot): - self.bot = bot - - @Cog.listener() - async def on_ready(self): - print(f"{self.__class__.__name__} Cog has been loaded\n-----") - - @command(name="kick", usage="`...` `[reason]`") - @guild_only() - @has_guild_permissions(kick_members=True) - @bot_has_guild_permissions(kick_members=True) - @cooldown(1, 1, BucketType.user) - async def kick_member(self, ctx, members: Greedy[Member], *, reason: Optional[str] = "No Reason Given"): - """ - Kick Member(s) from Server - Multiple Members can be Kicked at Once - """ - - if not await check(ctx, members): - with ctx.typing(): - # Send embed of the kicked member - await kick_members(self, ctx, members, reason) - - @command(name="mute", usage="`...` `[reason]`") - @has_guild_permissions(manage_roles=True) - @bot_has_guild_permissions(manage_roles=True) - @cooldown(1, 1, BucketType.user) - async def mute(self, ctx, members: Greedy[Member], *, reason: Optional[str] = "No Reason Given"): - """ - Mute Member(s) from Server - Multiple Members can be Muted At Once - """ - - if not await check(ctx, members): - with ctx.typing(): - - # Get muted role from the server - role = discord.utils.get(ctx.guild.roles, name="Muted") - - # Create muted role when no muted role exists and mute member(s) - if not role: - muted = await ctx.guild.create_role(name="Muted") - for channel in ctx.guild.channels: - await channel.set_permissions(muted, send_messages=False) - - await mute_members(self, ctx, members, reason, muted) - - else: - await mute_members(self, ctx, members, reason, role) - - @command(name="unmute", usage="`...` `[reason]`") - @has_guild_permissions(manage_roles=True) - @bot_has_guild_permissions(manage_roles=True) - @cooldown(1, 1, BucketType.user) - async def unmute(self, ctx, members: Greedy[Member], *, reason: Optional[str] = "No Reason Given"): - """ - Unmute Member(s) from Server - Multiple Members can be unmuted at once - """ - unmute = False - - if not await check(ctx, members): - with ctx.typing(): - role = discord.utils.get(ctx.guild.roles, name="Muted") - if not role: - desc = f"**{self.bot.cross} No Muted Role Was Found! {self.bot.cross}**" - await self.bot.generate_embed(ctx, desc=desc) - - else: - for member in members: - if role in member.roles: - await ummute_members(self, ctx, members, reason) - unmute = True - if role not in member.roles and unmute is False: - desc = f"**{self.bot.cross} {member.mention} Is Not Muted! {self.bot.cross}**" - await self.bot.generate_embed(ctx, desc=desc) - - @command(name="ban", usage="`...` `[reason]`") - @guild_only() - @has_guild_permissions(ban_members=True) - @bot_has_guild_permissions(ban_members=True) - @cooldown(1, 1, BucketType.user) - async def ban(self, ctx, members: Greedy[User], *, reason: Optional[str] = "No Reason Given"): - """ - Ban Member(s) from Server - Multiple Members can be banned at once - """ - - if not await check(ctx, members): - with ctx.typing(): - await ban_members(self, ctx, members, reason) - - @command(name="unban", usage="`...` `[reason]`") - @guild_only() - @has_guild_permissions(ban_members=True) - @bot_has_guild_permissions(ban_members=True) - @cooldown(1, 1, BucketType.user) - async def unban(self, ctx, members: Greedy[User], *, reason: Optional[str] = "No Reason Given"): - """ - Unban Member(s) from Server - Multiple Members can be Unbanned At Once - """ - - if not await check(ctx, members): - with ctx.typing(): - await unban_members(self, ctx, members, reason) - - @command(name="purge") - @guild_only() - @has_guild_permissions(manage_messages=True) - @bot_has_guild_permissions(manage_messages=True, read_message_history=True) - @cooldown(1, 1, BucketType.user) - async def purge(self, ctx, amount: int = None): - """ - Purge Messages from Channel - (No Amount Will Default to 50 Messages Deleted) - """ - - # When an amount is specified and is between 0 and 100 - if amount: - if 0 < amount <= 100: - - # Delete the message sent and then the amount specified - # (Only messages sent within the last 14 days) - - await ctx.message.delete() - deleted = await ctx.channel.purge(limit=amount, - after=datetime.datetime.utcnow() - timedelta(days=14)) - - await ctx.send(f"Deleted **{len(deleted):,}** messages.", delete_after=5) - - # Send error if amount is not between 0 and 100 - else: - await self.bot.generate_embed(ctx, desc="The amount provided is not between **0** and **100**") - - # Delete the last 50 messages if no amount is given - else: - - # Delete the message sent and then the amount specified - # (Only messages sent within the last 14 days) - - await ctx.message.delete() - deleted = await ctx.channel.purge(limit=50, - after=datetime.datetime.utcnow() - timedelta(days=14)) - - await ctx.send(f"Deleted **{len(deleted):,}** messages.", delete_after=5) - - @ban.error - @unban.error - async def ban_command_error(self, ctx, exc): - """Catching error if channel is not recognised""" - - error = getattr(exc, "original", exc) - - if isinstance(error, NotFound): - text = f"**{self.bot.cross} User Not Detected... Aborting Process** {self.bot.cross}" - await self.bot.generate_embed(ctx, desc=text) - - @Cog.listener() - async def on_raw_bulk_message_delete(self, payload): - """Logging Bulk Message Deletion from Server""" - - if modlogs := self.bot.get_modlog_for_guild(payload.guild_id): - modlogs_channel = self.bot.get_channel(modlogs) - - deleted_msgs_channel = self.bot.get_channel(payload.channel_id) - desc = f"**Bulk Delete in {deleted_msgs_channel.mention} | {len(payload.message_ids)} messages deleted**" - - # Set up embed showing the messages deleted and what channel they were deleted in - embed = Embed( - description=desc, - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=deleted_msgs_channel.guild.name, icon_url=deleted_msgs_channel.guild.icon_url) - embed.set_footer(text="Bulk Message Deletion") - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_member_remove(self, member): - """Log Member Leaves from Server""" - - if member == self.bot.user: return - - removed_at = datetime.datetime.utcnow() - if modlogs := self.bot.get_modlog_for_guild(member.guild.id): - modlogs_channel = self.bot.get_channel(modlogs) - - embed = Embed(title="Member Left", - description=f"**Member --> {member.mention} |** {member}" - f"\n**ID -->** {member.id}", - colour=self.bot.admin_colour, - timestamp=removed_at) - embed.add_field(name="Account Creation Date", - value=member.created_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), inline=True) - embed.add_field(name="Member Left Date", value=removed_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), - inline=True) - embed.set_thumbnail(url=member.avatar_url) - embed.set_footer(text=f"Member Left") - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_member_join(self, member): - """Log Member Joins to Server""" - - if member == self.bot.user: return - - joined_at = datetime.datetime.utcnow() - if modlogs := self.bot.get_modlog_for_guild(member.guild.id): - modlogs_channel = self.bot.get_channel(modlogs) - - embed = Embed(title="Member Joined", - description=f"**Member --> {member.mention} |** {member}" - f"\n**ID -->** {member.id}", - colour=self.bot.admin_colour, - timestamp=joined_at) - embed.add_field(name="Account Creation Date", - value=member.created_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), inline=True) - embed.add_field(name="Member Joined Date", value=joined_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), - inline=True) - embed.set_thumbnail(url=member.avatar_url) - embed.set_footer(text=f"Member Joined") - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_member_ban(self, guild, user): - """Logs Member Bans to Server""" - - if user == self.bot.user: return - - banned_at = datetime.datetime.utcnow() - if modlogs := self.bot.get_modlog_for_guild(guild.id): - modlogs_channel = self.bot.get_channel(modlogs) - - embed = Embed(title="Member Banned", - description=f"**Member --> {user.mention} |** {user}" - f"\n**ID -->** {user.id}", - colour=self.bot.admin_colour, - timestamp=banned_at) - embed.add_field(name="Account Creation Date", - value=user.created_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), inline=True) - embed.add_field(name="Member Banned Date", value=banned_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), - inline=True) - embed.set_footer(text="Member Banned") - embed.set_thumbnail(url=user.avatar_url) - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_member_unban(self, guild, user): - """Logs Member Unbans to Server""" - - unbanned_at = datetime.datetime.utcnow() - if modlogs := self.bot.get_modlog_for_guild(guild.id): - modlogs_channel = self.bot.get_channel(modlogs) - - embed = Embed(title="Member Unbanned", - description=f"**Member --> {user.mention} |** {user}" - f"\n**ID -->** {user.id}", - colour=self.bot.admin_colour, - timestamp=unbanned_at) - embed.add_field(name="Account Creation Date", - value=user.created_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), inline=True) - embed.add_field(name="Member Unbanned Date", value=unbanned_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), - inline=True) - embed.set_footer(text="Member Unbanned") - embed.set_thumbnail(url=user.avatar_url) - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_member_update(self, before, after): - """Logging Member Profile Updates""" - - if modlogs := self.bot.get_modlog_for_guild(after.guild.id): - modlogs_channel = self.bot.get_channel(modlogs) - - # Logging nickname changes or custom activity updates - if before.nick != after.nick: - - # Get the status of the member - after_status = member_status[str(after.status)] - # Getting activity - after_activity = f"{after.activity.emoji or '' if after.activity.type == discord.ActivityType.custom else ''}" \ - f"{after.activity.name}" if after.activity else None - - fields = [("Nickname Before", - f"{before.nick or None}", False), - ("Nickname After", - f"{after.nick or None}", False)] - - embed = Embed(title="Member Nickname Updated", - description=f"**Member --> {after.mention} |** {after}" - f"\n**ID -->** {after.id}" - f"\n\n**Activity -->** {after_activity}" - f"\n**Status -->** {after_status}", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after, icon_url=after.avatar_url) - embed.set_footer(text="Member Nickname Updated") - - # Add fields to the embed - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - - await modlogs_channel.send(embed=embed) - - # Logging Role additions/removals from Members - if before.roles != after.roles: - - # Grab total list of roles that the user has after additions/removal - role = string_list(after.roles, 30, "Role") - - # Retrieve the roles that were added/removed to/from the Member - new_roles = [roles for roles in after.roles if roles not in before.roles] - old_roles = [roles for roles in before.roles if roles not in after.roles] - - # As long as roles were added to the Member, log the role(s) that were given - if len(new_roles) >= 1: - new_roles_string = " **|** ".join(r.mention for r in new_roles) - - # Change the description of the embed depending on how many roles were added - if len(new_roles) == 1: - field = ("Member Role Added", new_roles_string, False) - footer = "Member Role Added" - else: - field = ("Member Roles Added", new_roles_string, False) - footer = "Member Roles Added" - - embed = Embed(title=footer, - description=f"**Member --> {after.mention} |** {after}" - f"\n**ID -->** {after.id}", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after, icon_url=after.avatar_url) - embed.add_field(name=field[0], value=field[1], inline=field[2]) - embed.add_field(name="All Roles", value=role or "No Roles", inline=False) - embed.set_footer(text=footer) - - await modlogs_channel.send(embed=embed) - - # As long as roles were removed from the member, log the role(s) that were removed - if len(old_roles) >= 1: - old_roles_string = " **|** ".join(r.mention for r in old_roles) - - # Change the description of the embed depending on how many roles were removed - if len(old_roles) == 1: - field = ("Member Role Removed", old_roles_string, False) - footer = "Member Role Removed" - else: - field = ("Member Roles Removed", old_roles_string, False) - footer = "Member Roles Removed" - - embed = Embed(title=footer, - description=f"**Member --> {after.mention} |** {after}" - f"\n**ID -->** {after.id}", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after, icon_url=after.avatar_url) - embed.add_field(name=field[0], value=field[1], inline=field[2]) - embed.add_field(name="All Roles", value=role or "No Roles", inline=False) - embed.set_footer(text=footer) - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_message_edit(self, before, after): - """Logging Message Edits (Within Cache)""" - - msg_channel = self.bot.get_channel(after.channel.id) - - # Get the channel within the cache - if not isinstance(msg_channel, DMChannel): - # Get the channel within the cache - channel = self.bot.get_modlog_for_guild(after.guild.id) - else: - return - - # When no modlogs channel is returned, do nothing - if channel: - modlogs_channel = self.bot.get_channel(channel) - - # Logging Message Content Edits - # Not logging any message edits from bots - if before.content != after.content and not after.author.bot: - desc = f"**Channel --> {after.channel.mention} |** #{after.channel}" \ - f"\n**Message ID -->** {after.id}" \ - f"\n**Edited Message -->** [Jump To Message]({after.jump_url})" - - # Allowing messages of all sizes to be logged - def message_fields(status): - if status == "Before": - if len(before.content) <= 1024: - fields = [(f"Edited Message Before", before.content, False)] - else: - fields = [(f"Before Message Content #1", before.content[:1000], False), - (f"Before Message Content #2", before.content[1000:], False)] - else: - if len(after.content) <= 1024: - fields = [(f"Edited Message After", after.content, False)] - else: - fields = [(f"After Message Content #1", after.content[:1000], False), - (f"After Message Content #2", after.content[1000:], False)] - - # Add fields to the embed - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - - embed = Embed(title="Message Edited", - description=desc, - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after.author, icon_url=after.author.avatar_url) - message_fields("Before") - message_fields("After") - embed.set_footer(text="Message Edited") - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_raw_message_edit(self, payload): - """Logging Message Edits Not Stored Within Internal Cache""" - - msg_channel = self.bot.get_channel(int(payload.data["channel_id"])) - - # Get the channel within the cache - if not isinstance(msg_channel, DMChannel): - channel = self.bot.get_modlog_for_guild(int(payload.data["guild_id"])) - else: - return - - # Only log this message edit when the message does not exist within the internal cache - # and modlogs channel is set up - if channel and not payload.cached_message: - modlogs_channel = self.bot.get_channel(channel) - - desc = f"**Channel --> {msg_channel.mention} |** #{msg_channel}" \ - f"\n**Message ID -->** {payload.message_id}" \ - f"\n**Message Content -->** N/A" - embed = Embed(title="Raw Message Edited", - description=desc, - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=modlogs_channel.guild.name, icon_url=modlogs_channel.guild.icon_url) - embed.set_footer(text="Raw Message Edited") - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_message_delete(self, message): - """Logging Message Deletions (Within Cache)""" - - # Get the channel within the cache - channel = self.bot.get_modlog_for_guild(message.guild.id) - - # When no modlogs channel is returned, do nothing - if channel and not message.author.bot: - modlogs_channel = self.bot.get_channel(channel) - - # Allowing messages of all sizes to be logged - if len(message.content) <= 1024: - fields = [("Deleted Message Content", message.content or "View Attachment", False)] - else: - fields = [("Deleted Message Content #1", message.content[:1000], False), - ("Deleted Message Content #2", message.content[1000:], False)] - - if not message.attachments: - desc = f"**Channel --> {message.channel.mention} |** #{message.channel}" \ - f"\n**Author ID -->** {message.author.id}" \ - f"\n**Message ID -->** {message.id}" - else: - attach_string = "".join(f"[Here]({attach.proxy_url})" for attach in message.attachments) - desc = f"**Channel --> {message.channel.mention}**" \ - f"\n**Author ID -->** {message.author.id}" \ - f"\n**Message ID -->** {message.id}" - fields += [("Attachment Link(s)", attach_string, False)] - - embed = Embed(title="Message Deleted", - description=desc, - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=message.author, icon_url=message.author.avatar_url) - embed.set_footer(text="Message Deleted") - - # Add fields to the embed - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_raw_message_delete(self, payload): - """Logging Message Deletions Not Stored Within Internal Cache""" - - msg_channel = self.bot.get_channel(payload.channel_id) - - # Get the channel within the cache - if not isinstance(msg_channel, DMChannel): - channel = self.bot.get_modlog_for_guild(payload.guild_id) - else: - return - - # Only log this message deletion when the message does not exist within the internal cache - # and modlogs channel is set up - if channel and not payload.cached_message: - modlogs_channel = self.bot.get_channel(channel) - - desc = f"**Channel --> {msg_channel.mention} |** #{msg_channel}" \ - f"\n**Message ID -->** {payload.message_id}" \ - f"\n**Message Content -->** N/A" - embed = Embed(title="Raw Message Deleted", - description=desc, - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=modlogs_channel.guild.name, icon_url=modlogs_channel.guild.icon_url) - embed.set_footer(text="Raw Message Deleted") - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_guild_channel_delete(self, channel): - """Logging channel deletions within the guild""" - - deleted_at = datetime.datetime.utcnow() - - if modlogs := self.bot.get_modlog_for_guild(channel.guild.id): - modlogs_channel = self.bot.get_channel(modlogs) - - desc = f"**Channel Deleted |** #{channel.name}\n" \ - f"**Category |** {channel.category or self.bot.cross}\n" \ - f"**Position |** #{channel.position} / {len(channel.guild.channels)}\n" - embed = Embed(description=desc, - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.add_field(name="Created Date", - value=channel.created_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), - inline=True) - embed.add_field(name="Deleted Date", - value=deleted_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), - inline=True) - embed.set_author(name=modlogs_channel.guild.name, icon_url=modlogs_channel.guild.icon_url) - embed.set_footer(text="Channel Deleted") - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_guild_channel_create(self, channel): - """Logging channel creations within the guild""" - - if modlogs := self.bot.get_modlog_for_guild(channel.guild.id): - modlogs_channel = self.bot.get_channel(modlogs) - - desc = f"**Channel Created |** {channel.mention}\n" \ - f"**Category |** {channel.category or self.bot.cross}\n" \ - f"**Position |** #{channel.position} / {len(channel.guild.channels)}\n" - embed = Embed(description=desc, - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.add_field(name="Created Date", - value=channel.created_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), - inline=True) - embed.set_author(name=modlogs_channel.guild.name, icon_url=modlogs_channel.guild.icon_url) - embed.set_footer(text="Channel Created") - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_guild_channel_update(self, before, after): - """Logging guild channel updates""" - - if modlogs := self.bot.get_modlog_for_guild(after.guild.id): - modlogs_channel = self.bot.get_channel(modlogs) - - # Logging Channel Name/Category/Position Updates - if before.name != after.name or before.category != after.category: - fields = [("Channel Before", - f"**Channel Updated -->** #{before}\n" - f"**Category -->** {before.category or self.bot.cross}\n" - f"**Position -->** #{before.position} / {len(before.guild.channels)}\n", False), - ("Channel After", - f"**Channel Updated -->** #{after}\n" - f"**Category -->** {after.category or self.bot.cross}\n" - f"**Position -->** #{after.position} / {len(after.guild.channels)}\n", False)] - - embed = Embed(title="Channel Updated", - description=f"**ID -->** {after.id}", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after.guild, icon_url=after.guild.icon_url) - embed.set_footer(text="Channel Updated") - - # Add fields to the embed - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - - await modlogs_channel.send(embed=embed) - - # Logging any roles added/removed from channel permissions - if before.changed_roles != after.changed_roles: - new_roles = [roles for roles in after.changed_roles] - old_roles = [roles for roles in before.changed_roles] - - # Get total new_roles in the channel - new_role_string = string_list(new_roles, 20, "Role") - # Get total old_roles in the channel - old_role_string = string_list(old_roles, 20, "Role") - - embed = Embed(title="Role Overrides Updated", - description=f"**Channel -->** {after.mention}\n" - f"**ID -->** {after.id}", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after.guild, icon_url=after.guild.icon_url) - embed.add_field(name="Channel Roles Before", - value=old_role_string or after.guild.default_role.mention, inline=False) - embed.add_field(name="Channel Roles After", - value=new_role_string or after.guild.default_role.mention, inline=False) - embed.set_footer(text="Role Overrides Updated") - - await modlogs_channel.send(embed=embed) - - # TODO: REMEMBER TO TRY AND LOG CHANNEL OVERWRITES - - @Cog.listener() - async def on_guild_update(self, before, after): - """Logging guild updates""" - - if modlogs := self.bot.get_modlog_for_guild(after.id): - modlogs_channel = self.bot.get_channel(modlogs) - - attributes = ["name", "verification_level", "afk_channel", "mfa_level", - "default_notifications", "region", "explicit_content_filter"] - if any(getattr(before, x) != getattr(after, x) for x in attributes): - - fields = [("Guild Before", - f"**Guild Name -->** {before}\n" - f"**Region -->** {get_region(str(before.region))}\n\n" - - f"**2-Factor Authentication -->** {self.bot.tick if before.mfa_level == 1 else self.bot.cross}\n" - f"**Explicit Content Filter -->** {get_content_filter(before.explicit_content_filter.name)}\n" - f"**Verification Level -->** {before.verification_level.name.capitalize()}\n\n" - - f"**Default Notifications -->** {get_notifs(before.default_notifications)}\n" - f"**AFK Channel -->** {before.afk_channel.mention if before.afk_channel else '#N/A'} **|** {before.afk_timeout}s\n", - True), - ("\u200b", "\u200b", True), - ("Guild After", - f"**Guild Name -->** {after}\n" - f"**Region -->** {get_region(str(after.region))}\n\n" - - f"**2-Factor Authentication -->** {self.bot.tick if after.mfa_level == 1 else self.bot.cross}\n" - f"**Explicit Content Filter -->** {get_content_filter(after.explicit_content_filter.name)}\n" - f"**Verification Level -->** {after.verification_level.name.capitalize()}\n\n" - - f"**Default Notifications -->** {get_notifs(after.default_notifications)}\n" - f"**AFK Channel -->** {after.afk_channel.mention if after.afk_channel else '#N/A'} **|** {after.afk_timeout}s\n", - True)] - - embed = Embed(title="Guild Updated", - description=f"**Owner --> {after.owner.mention} |** {after.owner}\n" - f"**ID -->** {after.id}", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after, icon_url=after.icon_url) - embed.set_footer(text="Guild Updated") - - # Add fields to the embed - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - - await modlogs_channel.send(embed=embed) - - if before.banner_url != after.banner_url: - embed = Embed(title="Banner Updated", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after, icon_url=after.icon_url) - embed.add_field(name="New Banner", - value=f"[Link To New Banner]({after.banner_url})", inline=False) - embed.set_image(url=after.banner_url) - embed.set_footer(text="Banner Updated") - - await modlogs_channel.send(embed=embed) - - if before.discovery_splash_url != after.discovery_splash_url: - embed = Embed(title="Discovery Splash Updated", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after, icon_url=after.icon_url) - embed.add_field(name="New Discovery Splash", - value=f"[Link To New Discovery Splash]({after.discovery_splash_url})", inline=False) - embed.set_image(url=after.discovery_splash_url) - embed.set_footer(text="Discovery Splash Updated") - - await modlogs_channel.send(embed=embed) - - if before.splash_url != after.splash_url: - embed = Embed(title="Splash Updated", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after, icon_url=after.icon_url) - embed.add_field(name="New Splash", - value=f"[Link To New Splash]({after.splash_url})", inline=False) - embed.set_image(url=after.splash_url) - embed.set_footer(text="Splash Updated") - - await modlogs_channel.send(embed=embed) - - if before.icon_url != after.icon_url: - embed = Embed(title="Icon Updated", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after, icon_url=after.icon_url) - embed.add_field(name="New Icon", - value=f"[Link To Icon]({after.icon_url})", inline=False) - embed.set_image(url=after.icon_url) - embed.set_footer(text="Icon Updated") - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_guild_emojis_update(self, guild, before, after): - """Logging any emoji updates""" - - if modlogs := self.bot.get_modlog_for_guild(guild.id): - modlogs_channel = self.bot.get_channel(modlogs) - - # Logging emoji additions/deletions - if before != after: - # Retrieve the emoji that were removed/added to the guild - new_emojis = [emojis for emojis in after if emojis not in before] - old_emojis = [emojis for emojis in before if emojis not in after] - - # Determining whether emoji was added or removed - if len(new_emojis) == 1: - text = "Emoji Added" - emoji_id = new_emojis[0].id - name = new_emojis[0].name - animated = self.bot.tick if new_emojis[0].animated else self.bot.cross - managed = self.bot.tick if new_emojis[0].managed else self.bot.cross - url = new_emojis[0].url - if len(old_emojis) == 1: - text = "Emoji Removed" - emoji_id = old_emojis[0].id - name = old_emojis[0].name - animated = self.bot.tick if old_emojis[0].animated else self.bot.cross - managed = self.bot.tick if old_emojis[0].managed else self.bot.cross - url = old_emojis[0].url - - # Get total emojis - emojis = string_list(after, 30, "Emoji") - embed = Embed(title=text, - description=f"**ID -->** {emoji_id}" - f"\n**Name -->** {name}" - f"\n**Animated? -->** {animated}" - f"\n**Managed? -->** {managed}", - colour=self.bot.admin_colour, - url=str(url), - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=guild, icon_url=guild.icon_url) - embed.add_field(name=f"All Emojis --> {len(guild.emojis)}", value=emojis or "No Emojis", inline=False) - embed.set_thumbnail(url=str(url)) - embed.set_footer(text=text) - - await modlogs_channel.send(embed=embed) - - # Log emoji name updates - elif before is not after: - for b, a in zip(before, after): - if b.name != a.name: - animated = self.bot.tick if a.animated else self.bot.cross - managed = self.bot.tick if a.managed else self.bot.cross - - embed = Embed(title="Emoji Name Updated", - description=f"**ID -->** {a.id}" - f"\n**Name -->** {a.name}" - f"\n**Animated? -->** {animated}" - f"\n**Managed? -->** {managed}", - colour=self.bot.admin_colour, - url=str(a.url), - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=guild, icon_url=guild.icon_url) - embed.set_thumbnail(url=str(a.url)) - embed.set_footer(text="Emoji Name Updated") - - await modlogs_channel.send(embed=embed) - break - - @Cog.listener() - async def on_guild_role_create(self, role): - """Logging role creations""" - - if modlogs := self.bot.get_modlog_for_guild(role.guild.id): - modlogs_channel = self.bot.get_channel(modlogs) - - # 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 - permission = string.capwords(_perms) - - # 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} **<-- Colour:** {str(role.colour)}" \ - f"\n**Position -->** #{role.position} / {len(role.guild.roles)}" \ - f"\n**ID -->** {role.id}" - - # Set up Embed - embed = Embed(title=f"Role Created", - description=desc, - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=role.guild, icon_url=role.guild.icon_url) - embed.set_footer(text=f"Role Created") - - # Setting up fields - fields = [ - ("Creation At", role.created_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), True), - - (f"Misc", - f"\nMentionable?: {mentionable}" - f"\nHoisted?: {hoisted}" - f"\nManaged?: {managed}", True), - - ("All Permissions", permission or "No Permissions", False) - ] - - # Add fields to the embed - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_guild_role_delete(self, role): - """Logging role deletions""" - - if modlogs := self.bot.get_modlog_for_guild(role.guild.id): - deleted_at = datetime.datetime.utcnow() - modlogs_channel = self.bot.get_channel(modlogs) - - # 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(_perms) - - # 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} **<-- Colour:** {str(role.colour)}" \ - f"\n**Position -->** #{role.position} / {len(role.guild.roles)}" \ - f"\n**ID -->** {role.id}" - - # Set up Embed - embed = Embed(title=f"Role Deleted", - description=desc, - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=role.guild, icon_url=role.guild.icon_url) - embed.set_footer(text=f"Role Deleted") - - # Setting up fields - fields = [ - ("Creation At", role.created_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), True), - ("Deletion At", deleted_at.strftime("%a, %b %d, %Y\n%I:%M:%S %p"), True), - - (f"Misc", - f"\nMentionable?: {mentionable}" - f"\nHoisted?: {hoisted}" - f"\nManaged?: {managed}", True), - - ("All Permissions Before Deletion", permission or "No Permissions", False) - ] - - # Add fields to the embed - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - - await modlogs_channel.send(embed=embed) - - @Cog.listener() - async def on_guild_role_update(self, before, after): - """Logging any updates to roles""" - - if modlogs := self.bot.get_modlog_for_guild(after.guild.id): - modlogs_channel = self.bot.get_channel(modlogs) - - attributes = ["name", "hoist", "managed", "mentionable", "colour"] - if any(getattr(before, x) != getattr(after, x) for x in attributes): - - # Using emotes to represent bools - b_mentionable = self.bot.tick if before.mentionable else self.bot.cross - b_hoisted = self.bot.tick if before.hoist else self.bot.cross - b_managed = self.bot.tick if before.managed else self.bot.cross - # Using emotes to represent bools - a_mentionable = self.bot.tick if after.mentionable else self.bot.cross - a_hoisted = self.bot.tick if after.hoist else self.bot.cross - a_managed = self.bot.tick if after.managed else self.bot.cross - - fields = [("Role Before", - f"**Name -->** @{before}" - f"\n**Colour -->** {str(before.colour)}" - f"\n\n**Mentionable? -->** {b_mentionable}" - f"\n**Hoisted? -->** {b_hoisted}" - f"\n**Managed? -->** {b_managed}", - True), - ("Role After", - f"**Name -->** @{after}" - f"\n**Colour -->** {str(after.colour)}" - f"\n\n**Mentionable? -->** {a_mentionable}" - f"\n**Hoisted? -->** {a_hoisted}" - f"\n**Managed? -->** {a_managed}", - True)] - - # Set up Embed - embed = Embed(title=f"Role Updated", - description=f"**Name --> {after.mention} |** @{after}" - f"**\nID -->** {after.id}", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after.guild, icon_url=after.guild.icon_url) - embed.set_footer(text=f"Role Updated") - - # Add fields to the embed - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - - await modlogs_channel.send(embed=embed) - - # Logging permission changes to roles - if before.permissions != after.permissions: - # Returns the permissions that the role has within the guild - after_filtered = list(filter(lambda x: x[1], after.permissions)) - before_filtered = list(filter(lambda x: x[1], before.permissions)) - after_perms = string.capwords(", ".join(map(lambda x: x[0].replace("_", " "), after_filtered))) - - # Retrieve the permissions that were enabled/disabled from the role - new_perm = [perms for perms in after_filtered if perms not in before_filtered] - old_perm = [perms for perms in before_filtered if perms not in after_filtered] - - # Log the new permissions that were enabled for the role - if len(new_perm) >= 1 and not old_perm: - new_perm_string = string.capwords(", ".join(map(lambda x: x[0].replace("_", " "), new_perm))) - - # Change the description of the embed depending on how many permissions were enabled - if len(new_perm) == 1: - field = ("Role Permission Added", new_perm_string, False) - footer = "Role Permission Added" - else: - field = ("Role Permissions Added", new_perm_string, False) - footer = "Role Permissions Added" - - embed = Embed(title=footer, - description=f"{after.mention} **|** @{after} **<-- Colour:** {str(after.colour)}" - f"\n**Position -->** #{after.position} / {len(after.guild.roles)}" - f"\n** ID -->** {after.id}", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after.guild, icon_url=after.guild.icon_url) - embed.add_field(name=field[0], value=field[1], inline=field[2]) - embed.add_field(name="All Permissions", value=after_perms or "No Permissions", inline=False) - embed.set_footer(text=footer) - - await modlogs_channel.send(embed=embed) - - # As long as permissions were disabled, log them - if len(old_perm) >= 1 and not new_perm: - old_perm_string = string.capwords(", ".join(map(lambda x: x[0].replace("_", " "), old_perm))) - - # Change the description of the embed depending on how many permissions were disabled - if len(old_perm) == 1: - field = ("Role Permission Removed", old_perm_string, False) - footer = "Role Permission Removed" - else: - field = ("Role Permissions Removed", old_perm_string, False) - footer = "Role Permissions Removed" - - embed = Embed(title=footer, - description=f"{after.mention} **|** @{after} **<-- Colour:** {str(after.colour)}" - f"\n**Position -->** #{after.position} / {len(after.guild.roles)}" - f"\n** ID -->** {after.id}", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after.guild, icon_url=after.guild.icon_url) - embed.add_field(name=field[0], value=field[1], inline=field[2]) - embed.add_field(name="All Permissions", value=after_perms or "No Permissions", inline=False) - embed.set_footer(text=footer) - - await modlogs_channel.send(embed=embed) - - # Log the different permissions that were enabled and disabled - if len(old_perm) >= 1 and len(new_perm) >= 1: - new_perm_string = string.capwords(", ".join(map(lambda x: x[0].replace("_", " "), new_perm))) - old_perm_string = string.capwords(", ".join(map(lambda x: x[0].replace("_", " "), old_perm))) - - # Change the description of the embed depending on how many permissions were enabled - if len(new_perm) == 1: - new_field = ("Role Permission Added", new_perm_string, False) - else: - new_field = ("Role Permissions Added", new_perm_string, False) - - # Change the description of the embed depending on how many permissions were disabled - if len(old_perm) == 1: - old_field = ("Role Permission Removed", old_perm_string, False) - else: - old_field = ("Role Permissions Removed", old_perm_string, False) - - embed = Embed(title="Role Permissions Updated", - description=f"{after.mention} **|** @{after} **<-- Colour:** {str(after.colour)}" - f"\n**Position -->** #{after.position} / {len(after.guild.roles)}" - f"\n** ID -->** {after.id}", - colour=self.bot.admin_colour, - timestamp=datetime.datetime.utcnow()) - embed.set_author(name=after.guild, icon_url=after.guild.icon_url) - embed.add_field(name=new_field[0], value=new_field[1], inline=new_field[2]) - embed.add_field(name=old_field[0], value=old_field[1], inline=old_field[2]) - embed.add_field(name="All Permissions", value=after_perms or "No Permissions", inline=False) - embed.set_footer(text="Role Permissions Updated") - - await modlogs_channel.send(embed=embed) - - -def setup(bot): - bot.add_cog(Moderation(bot)) - - -""" - -@Cog.listener() - async def on_guild_integrations_update(self, guild): - Logging updates to integrations - - if modlogs := self.bot.get_modlog_for_guild(guild.id): - modlogs_channel = self.bot.get_channel(modlogs) - -print(before.overwrites) -print(after.overwrites) - -before_diffkeys = [k for k in before.overwrites if - before.overwrites[k].pair() != after.overwrites[k].pair()] -after_diffkeys = [k for k in after.overwrites if after.overwrites[k].pair() != before.overwrites[k].pair()] - -before_array = [] -after_array = [] -for k in before_diffkeys: - for pair in k.permissions: - before_array += [pair] - -for k in after_diffkeys: - for pair in k.permissions: - after_array += [pair] - -old_roles = [p for p in before_array if p in after_array] -after_roles = [p for p in after_array if p in before_array] - -print(old_roles) -print(after_roles) -""" diff --git a/cogs/owner.py b/cogs/owner.py deleted file mode 100644 index 6e40049d..00000000 --- a/cogs/owner.py +++ /dev/null @@ -1,246 +0,0 @@ -# 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 asyncio -import inspect -import io -import textwrap -import traceback -from contextlib import redirect_stdout -from typing import Optional - -import asyncpg -from discord import Member -from discord.ext.commands import Cog, command, is_owner - - -def cleanup_code(content): - """Automatically removes code blocks from the code.""" - # remove ```py\n``` - if content.startswith('```') and content.endswith('```'): - return '\n'.join(content.split('\n')[1:-1]) - - # remove `foo` - return content.strip('` \n') - - -def paginate(text: str): - """Simple generator that paginates text.""" - last = 0 - pages = [] - for curr in range(0, len(text)): - if curr % 1980 == 0: - pages.append(text[last:curr]) - last = curr - appd_index = curr - if appd_index != len(text) - 1: - pages.append(text[last:curr]) - return list(filter(lambda a: a != '', pages)) - - -def get_syntax_error(e): - if e.text is None: - return f'```py\n{e.__class__.__name__}: {e}\n```' - return f'```py\n{e.text}{"^":>{e.offset}}\n{e.__class__.__name__}: {e}```' - - -class Owner(Cog): - """Commands for Ensō server""" - - def __init__(self, bot): - self.bot = bot - - @command(name="dm", hidden=True) - @is_owner() - async def dm(self, ctx, member: Member, *, text): - """DM users""" - - # Delete the message sent instantly - await ctx.message.delete() - # Send the message typed the mentioned user - await member.send(text) - - @command(name="leave", hidden=True) - @is_owner() - async def leave(self, ctx): - """Leaves the guild""" - - await self.bot.generate_embed(ctx, desc="**Leaving the guild... Bye Bye uvu**") - await ctx.guild.leave() - - @command(name="forceprefix", hidden=True) - @is_owner() - async def override_prefix(self, ctx, new: Optional[str] = None): - """Override the prefix in any given guild (Owner only)""" - - # As long as a new prefix has been given and is less than 5 characters - if new and len(new) <= 5: - # Store the new prefix in the dictionary and update the database - await self.bot.storage_prefix_for_guild(ctx, new) - - # Making sure that errors are handled if prefix is above 5 characters - elif new and len(new) > 5: - await self.bot.generate_embed(ctx, desc="The guild prefix must be less than or equal to **5** characters!") - - # if no prefix was provided - elif not new: - # Grab the current prefix for the guild within the cached dictionary - prefix = self.bot.get_prefix_for_guild(ctx.guild.id) - await self.bot.generate_embed(ctx, desc=f"**The current guild prefix is `{prefix}`**") - - @command(name="restart", hidden=True) - @is_owner() - async def restart(self, ctx): - """Restart the bot""" - - # Close the database connection - try: - await asyncio.wait_for(self.bot.db.close(), timeout=1.0) - - # Catch errors - except asyncio.TimeoutError: - await self.bot.generate_embed(ctx, desc="**Database Connection Timed Out!") - - # Shutdown the bot - else: - await self.bot.generate_embed(ctx, desc="**Success Senpai!" - "\nMy Reboot Had No Problems** ") - await self.bot.logout() - - @command(name="reloadusers", hidden=True) - @is_owner() - async def reload_db(self, ctx): - """Reloads the database by inserting/updating all the records""" - - # Store every single record into an array - records = [(ctx.guild.id, member.id) for member in ctx.guild.members] - - # Setup up pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Define the insert statement that will insert the user's information - try: - insert_query = """INSERT INTO members (guild_id, member_id) VALUES ($1, $2) - ON CONFLICT (guild_id, member_id) DO NOTHING""" - await conn.executemany(insert_query, records) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: Member(s) were not be able to be added to Guild", e) - - # Print success - else: - print(f"Record(s) Inserted Into Members") - - @command(name="cache", hidden=True) - @is_owner() - async def set_cache(self, ctx, size: Optional[int]): - """Allow me to dynamically set the cache max size""" - - # Change the size of the cache - if size: - try: - self.bot.member_cache.change_array_size(size) - - # Catch errors - except Exception as e: - print(e) - - # Let me that it's successful - else: - await self.bot.generate_embed(ctx, desc=f"Cache Now Storing To **{size}** Records") - - # Display the length of the current queue and cache and the max length of the cache - else: - max_cache_len, cache_len, queue_len = self.bot.member_cache.get_size() - await self.bot.generate_embed(ctx, desc=f"Current Records Stored Within Cache: **{cache_len}**" - f"\nCurrent Queue Length: **{queue_len}**" - f"\nMax Size Of Cache: **{max_cache_len}**") - - @command(name="eval", hidden=True) - @is_owner() - async def _eval(self, ctx, *, body): - """ - Evaluates python code - Gracefully yoinked from (https://github.com/fourjr/eval-bot)""" - - env = { - 'ctx': ctx, - 'self': self, - 'bot': self.bot, - 'channel': ctx.channel, - 'author': ctx.author, - 'guild': ctx.guild, - 'message': ctx.message, - 'source': inspect.getsource - } - - env.update(globals()) - - body = cleanup_code(body) - stdout = io.StringIO() - err = out = None - - to_compile = f'async def func():\n{textwrap.indent(body, " ")}' - - try: - exec(to_compile, env) - except Exception as e: - err = await ctx.send(f'```py\n{e.__class__.__name__}: {e}\n```') - return await ctx.message.add_reaction('\u2049') - - func = env['func'] - try: - with redirect_stdout(stdout): - ret = await func() - except Exception as e: - value = stdout.getvalue() - err = await ctx.send(f'```py\n{value}{traceback.format_exc()}\n```') - else: - value = stdout.getvalue() - if ret is None: - if value: - try: - out = await ctx.send(f'```py\n{value}\n```') - except: - paginated_text = paginate(value) - for page in paginated_text: - if page == paginated_text[-1]: - out = await ctx.send(f'```py\n{page}\n```') - break - await ctx.send(f'```py\n{page}\n```') - else: - try: - out = await ctx.send(f'```py\n{value}{ret}\n```') - except: - paginated_text = paginate(f"{value}{ret}") - for page in paginated_text: - if page == paginated_text[-1]: - out = await ctx.send(f'```py\n{page}\n```') - break - await ctx.send(f'```py\n{page}\n```') - - if out: - await ctx.message.add_reaction('\u2705') # tick - elif err: - await ctx.message.add_reaction('\u2049') # x - else: - await ctx.message.add_reaction('\u2705') - - -def setup(bot): - bot.add_cog(Owner(bot)) diff --git a/cogs/relationship.py b/cogs/relationship.py deleted file mode 100644 index b57670ba..00000000 --- a/cogs/relationship.py +++ /dev/null @@ -1,293 +0,0 @@ -# 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 asyncio -import datetime - -import asyncpg -from discord import Member, Embed -from discord.ext.commands import command, bot_has_permissions, Cog - - -# Sets up the embed for the marriage info -def marriageInfo(self, target, marriedUser, marriedDate, currentDate, married): - # Make sure that non-users can still use the marriage - if not married: - # Set up the fields for the embed - fields = [("Married To", "No One", False), - ("Marriage Date", "N/A", False), - ("Days Married", "N/A", False)] - else: - # Calculate the days married - marriedTime = datetime.datetime.strptime(marriedDate, "%a, %b %d, %Y") - currentTime = datetime.datetime.strptime(currentDate, "%a, %b %d, %Y") - delta = currentTime - marriedTime - - # Set up the fields for the embed - fields = [("Married To", marriedUser.mention, False), - ("Marriage Date", marriedDate, False), - ("Days Married", delta.days, False)] - - # Set the title, colour, timestamp and thumbnail - embed = Embed(title=f"{target.name}'s Marriage Information", - colour=self.bot.random_colour(), - timestamp=datetime.datetime.utcnow()) - embed.set_thumbnail(url=target.avatar_url) - - # Add fields to the embed - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - - return embed - - -# Set up the Cog -class Relationship(Cog): - """Marry/Divorce etc!""" - - 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="marry") - async def marry(self, ctx, member: Member): - """Wed your Lover!""" - - # Getting the guild of the user - guild = ctx.guild - - # Make sure that the user cannot marry themselves - if member.id == ctx.author.id: - await self.bot.generate_embed(ctx, desc="**Senpaii! ˭̡̞(◞⁎˃ᆺ˂)◞*✰ You can't possibly marry yourself!**") - return - - # Get the author from the cache - db_author = await self.bot.check_cache(ctx.author.id, ctx.guild.id) - married_user = db_author["married"] - - # Make sure that the person is not already married to someone else within the server - if married_user: - member = guild.get_member(married_user) - await self.bot.generate_embed(ctx, desc=f"**((╬◣﹏◢)) You're already married to {member.mention}!**") - return - - # Get the member mentioned from the cache - db_member = await self.bot.check_cache(member.id, ctx.guild.id) - target_user = db_member["married"] - - if target_user: - member = guild.get_member(target_user) - await self.bot.generate_embed(ctx, desc=f"**Sorry! That user is already married to {member.mention}**") - return - - # Send a message to the channel mentioning the author and the person they want to wed. - await self.bot.generate_embed(ctx, desc=f"{ctx.author.mention} **Proposes To** {member.mention}" - f"\n**Do you accept??**" - f"\nRespond with [**Y**es/**N**o]") - - # A check that makes sure that the reply is not from the author - # and that the reply is in the same channel as the proposal - def check(m): - return m.author == member and m.channel == ctx.channel - - try: - # Wait for the message from the mentioned user - msg = await self.bot.wait_for('message', check=check, timeout=90.0) - - # if the person says yes - if msg.content.lower() in ['y', 'yes', 'yea']: - - # Setup pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Get the time of marriage - message_time = msg.created_at.strftime("%a, %b %d, %Y") - - # Update the existing records in the database with the user that they are marrying along with the time of the accepted proposal - try: - update_query = """UPDATE members SET married = $1, married_date = $2 WHERE member_id = $3 AND guild_id = $4""" - await conn.execute(update_query, member.id, message_time, ctx.author.id, guild.id) - await conn.execute(update_query, ctx.author.id, message_time, member.id, guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: {member.id} and {ctx.author.id} Could Not Be Married", e) - - # Update cache new details - else: - db_author["married"] = member.id - db_author["married_date"] = message_time - - db_member["married"] = ctx.author.id - db_member["married_date"] = message_time - - # Congratulate them! - desc = f"**Congratulations! 。゚( ゚^∀^゚)゚。 {ctx.author.mention} and {member.mention} are now married to each other!**" - await self.bot.generate_embed(ctx, desc=desc) - - # if the person says no - elif msg.content.lower() in ['n', 'no', 'nah']: - - # Try to console the person and wish them the best in their life - desc = f"**{ctx.author.mention} It's okay king. Pick up your crown and move on (◕‿◕✿)**" - await self.bot.generate_embed(ctx, desc=desc) - else: - # Abort the process as the message sent did not make sense - await self.bot.generate_embed(ctx, desc="**Senpaiiii! (。╯︵╰。) Speak English Please**") - - except asyncio.TimeoutError as ex: - print(ex) - - # Send out an error message if the user waited too long - await self.bot.generate_embed(ctx, desc=f"**(。T ω T。) {member.mention} waited too long**") - - @command(name="divorce") - async def divorce(self, ctx, member: Member): - """Divorce your Partner!""" - - # Getting the guild of the user - guild = ctx.guild - - # Make sure that the user cannot divorce themselves - if member.id == ctx.author.id: - await self.bot.generate_embed(ctx, desc="**Senpaii! ˭̡̞(◞⁎˃ᆺ˂)◞*✰ You can't possibly divorce yourself!**") - return - - # Get the author from the cache - db_author = await self.bot.check_cache(ctx.author.id, ctx.guild.id) - married_user = db_author["married"] - - # Make sure that the person trying to divorce is actually married to the user - if married_user is None: - desc = "**((╬◣﹏◢)) You must be married in order to divorce someone! Baka!**" - await self.bot.generate_embed(ctx, desc=desc) - return - - # Make sure the person is married to the person that they're trying to divorce - elif married_user != member.id: - member = guild.get_member(married_user) - - desc = f"**(ノ ゜口゜)ノ You can only divorce the person that you're married!" \ - f"\n That person is {member.mention}**" - await self.bot.generate_embed(ctx, desc=desc) - return - - # Send a message to the channel mentioning the author and the person they want to wed. - await self.bot.generate_embed(ctx, desc=f"{ctx.author.mention} **Wishes to Divorce** {member.mention}" - f"\n**Are you willing to break this sacred bond?**" - f"\nRespond with [**Y**es/**N**o]") - - # A check that makes sure that the reply is not from the author - # and that the reply is in the same channel as the proposal - def check(m): - return m.author == member and m.channel == ctx.channel - - # Surround with try/except to catch any exceptions that may occur - try: - # Wait for the message from the mentioned user - msg = await self.bot.wait_for('message', check=check, timeout=90.0) - - # if the person says yes - if msg.content.lower() in ['y', 'yes', 'yea']: - - # Setup pool connection - pool = self.bot.db - async with pool.acquire() as conn: - - # Update the existing records in the database that the author and member are divorced - try: - update_query = """UPDATE members SET married = NULL, married_date = NULL WHERE member_id = $1 and guild_id = $2""" - await conn.execute(update_query, ctx.author.id, guild.id) - await conn.execute(update_query, member.id, guild.id) - - # Catch errors - except asyncpg.PostgresError as e: - print(f"PostGres Error: {member.id} and {ctx.author.id} Could Not Be Divorced", e) - - # Update cache with new details - else: - db_author["married"] = None - db_author["married_date"] = None - - db_member = await self.bot.check_cache(member.id, ctx.guild.id) - db_member["married"] = None - db_member["married_date"] = None - - # Congratulate them! - desc = f"**૮( ´⁰▱๋⁰ )ა {ctx.author.mention} and {member.mention} are now divorced." \ - f"\nI hope you two can find happiness in life with other people**" - await self.bot.generate_embed(ctx, desc=desc) - - # if the person says no - elif msg.content.lower() in ['n', 'no', 'nah']: - - # Try to console the person and wish them the best in their life - desc = f"**Sorry {ctx.author.mention} but you're gonna need {member.mention}'s consent to move forward with this!**" - await self.bot.generate_embed(ctx, desc=desc) - - else: - # Abort the process as the message sent did not make sense - await self.bot.generate_embed(ctx, desc="**Senpaiiii! (。╯︵╰。) Speak English Please**") - - except asyncio.TimeoutError as ex: - print(ex) - - # Send out an error message if the user waited too long - await self.bot.generate_embed(ctx, desc=f"**(。T ω T。) {member.mention} waited too long**") - - @command(name="marriageinfo", aliases=["minfo"]) - @bot_has_permissions(embed_links=True) - async def m_info(self, ctx, member: Member = None): - """Marriage Information!""" - - # Choose author if no other user has been mentioned - member = ctx.author if not member else member - - # Getting the guild of the user - guild = member.guild - - # Get the author from the cache - result = await self.bot.check_cache(member.id, ctx.guild.id) - - user = result["married"] - marriage_date = result["married_date"] - - # Set empty values for non-married users - if not user: - married = False - marriedUser = "" - marriedDate = "" - # Set the member, date married and setting married status - else: - married = True - marriedUser = guild.get_member(user) - marriedDate = marriage_date - - # Get the current date of the message sent by the user - currentDate = ctx.message.created_at.strftime("%a, %b %d, %Y") - - # Get the marriage info embed and then send it to the display - embed = marriageInfo(self, member, marriedUser, marriedDate, currentDate, married) - await ctx.send(embed=embed) - - -def setup(bot): - bot.add_cog(Relationship(bot)) diff --git a/images/FunCommands/choking.txt b/images/FunCommands/choking.txt deleted file mode 100644 index f866f2e4..00000000 --- a/images/FunCommands/choking.txt +++ /dev/null @@ -1,20 +0,0 @@ -https://cdn.discordapp.com/attachments/716424509858644018/729562288624762971/unknown.gif -https://cdn.discordapp.com/attachments/716424509858644018/729563027531104336/tenor_2.gif -https://cdn.discordapp.com/attachments/716424509858644018/729562295134322718/unknown.gif -https://cdn.discordapp.com/attachments/716424509858644018/729565034547642409/giphy_1.gif -https://cdn.discordapp.com/attachments/716424509858644018/729565035013341195/DBvbJ4grTZuOgR3YyWCliY7znqMF1sehfeBTvW4pd3yUZF6Uy-1Ad36eR_Ho11Im1eEWqB8TQcM6mCjpd3LhLg.gif -https://cdn.discordapp.com/attachments/716424509858644018/729565035226988595/giphy_2.gif -https://cdn.discordapp.com/attachments/716424509858644018/729565197110345818/d4qelwv-b77885ba-b71e-463b-baf1-82146cf2eddd.gif -https://cdn.discordapp.com/attachments/716424509858644018/729565431261691954/tenor_3.gif -https://cdn.discordapp.com/attachments/716424509858644018/729566244579049533/0b7033dafc792ff9-anime-choking-gifs-tenor.gif -https://cdn.discordapp.com/attachments/716424509858644018/729566778954350672/SourGrizzledGavial-size_restricted.gif -https://cdn.discordapp.com/attachments/716424509858644018/729567686551404644/He_didnt_strangle_her_.gif -https://cdn.discordapp.com/attachments/716424509858644018/729568394508238918/tenor_5.gif -https://cdn.discordapp.com/attachments/716424509858644018/729568521083945042/tenor_6.gif -https://cdn.discordapp.com/attachments/716424509858644018/729569562399604786/tumblr_myp7nzBpkB1torue8o1_500.gif -https://cdn.discordapp.com/attachments/716424509858644018/729569673561112646/2uCh694.gif -https://cdn.discordapp.com/attachments/716424509858644018/731279769848643744/original.gif -https://cdn.discordapp.com/attachments/716424509858644018/731281689971523594/tenor_9.gif -https://cdn.discordapp.com/attachments/716424509858644018/731281690319913020/79o.gif -https://cdn.discordapp.com/attachments/716424509858644018/731282061024952340/tumblr_n3xfkdCYN71ssisofo2_500.gif -https://images-ext-2.discordapp.net/external/jgTnvfjNup8F8F-Yj7jrOFfhCVocYePg-LVxOt4VFHE/https/zippy.gfycat.com/AmazingIdealAustralianfreshwatercrocodile.mp4 \ No newline at end of file diff --git a/images/FunCommands/cuddling.txt b/images/FunCommands/cuddling.txt deleted file mode 100644 index 2e0e3731..00000000 --- a/images/FunCommands/cuddling.txt +++ /dev/null @@ -1,82 +0,0 @@ -https://media.tenor.co/images/012cc6d6cb65c3c98bd5505ab2e1c42a/tenor.gif -https://media.tenor.co/images/3b205574d0352d4d61687f835276566d/tenor.gif -https://media.tenor.co/images/864d525effe7da722203e209efafafec/tenor.gif -https://media.tenor.co/images/9af57b60dca6860724a0ff6c1689c246/tenor.gif -https://media.tenor.co/images/9c055cf13ecd8255834775c0af48f2c3/tenor.gif -https://media.tenor.co/images/826fedbb36f0e844089e10b37fa41643/tenor.gif -https://media.tenor.co/images/75f0c4deb31845f1e6ec33e3d7611095/tenor.gif -https://media.tenor.co/images/52471e5ee4d2c953127091efac41de23/tenor.gif -https://media.tenor.co/images/c445e2665d12cfda0921291d919cbe9a/tenor.gif -https://media.tenor.co/images/8cbe0edadc12ca1056d5eb685a4c27f6/tenor.gif -https://media.tenor.co/images/9af57b60dca6860724a0ff6c1689c246/tenor.gif -https://media.tenor.co/images/c51d8a4505e1dfef709efd4739d09faa/tenor.gif -https://media.tenor.co/images/1a65319302b9e1c86a99a39e9a81084e/tenor.gif -https://media.tenor.co/images/8f8ba3baeecdf28f3e0fa7d4ce1a8586/tenor.gif -https://media.tenor.co/images/f349135baa0c6b0bf72d1746d4b949bf/tenor.gif -https://media.tenor.co/images/9fa3577b5aeb65dc898f5cc75b82a659/tenor.gif -https://media.tenor.co/images/d0c2e7382742f1faf8fcb44db268615f/tenor.gif -https://media.tenor.co/images/826fedbb36f0e844089e10b37fa41643/tenor.gif -https://media.tenor.co/images/adeb030aaa5a2a3d16abdc58be4d1448/tenor.gif -https://media.tenor.co/images/c51d8a4505e1dfef709efd4739d09faa/tenor.gif -https://media.tenor.co/images/6d73b0a9cadef5310be4b6160d2f959a/tenor.gif -https://media.tenor.co/images/7b8f5e852e6a53c1fe36816d3ccf9326/tenor.gif -https://media.tenor.co/images/ec14f1673479a72db6083c0be2bee335/tenor.gif -https://media.tenor.co/images/f65bd4bb3441b9d647d86b04eee6542e/tenor.gif -https://media.tenor.com/images/d309f16ec3299431393db58b3b7935e5/tenor.gif -https://media.tenor.com/images/a8cbc11ee331c62aaf03420d99696da0/tenor.gif -https://media.tenor.com/images/b2010973cddee5baf2d3ee369897709c/tenor.gif -https://media.tenor.com/images/acdf6187a51161b74c88e2fad6991041/tenor.gif -https://media.tenor.com/images/43fce3d874179afb2d9d74a7402dcff4/tenor.gif -https://media.tenor.com/images/3b8a361475bb17f157c1b4c2988b1d79/tenor.gif -https://media.tenor.com/images/4179058caa9eef3e7c6b21b8888b9cc9/tenor.gif -https://media.tenor.com/images/bba22a37d85c5760a64c2497d5fdd4d2/tenor.gif -https://media.tenor.com/images/26105169827cd8ca702a809d32db38c9/tenor.gif -https://media.tenor.com/images/4f6bf30970f0ffdd755981d43eee3f7c/tenor.gif -https://media.tenor.com/images/48f3a68df98b66f72d3c6b01efd6c6fd/tenor.gif -https://media.tenor.com/images/8cab4f4c73547d077c56066461c40a5e/tenor.gif -https://media.tenor.com/images/b43e90f82f1b7f85683a9c6610f0045e/tenor.gif -https://media.tenor.com/images/ea1dc7c4ad4835b02f7d8f4062d76006/tenor.gif -https://media.tenor.com/images/26f3a326707440dc8bf65c0573826e22/tenor.gif -https://media.tenor.com/images/a3862661163e420d57538b1b08aa5972/tenor.gif -https://media.tenor.com/images/1d771bdc0c22e534e5f675eba5ec3618/tenor.gif -https://media.tenor.com/images/cb2d293a76ebfb4baebc717807177c98/tenor.gif -https://media.tenor.com/images/324a1822660c452851a134f4e9b25802/tenor.gif -https://media.tenor.com/images/8df294439f420646327ead09a1789912/tenor.gif -https://media.tenor.com/images/e486734428257a7b10dbe0f5a8fa7903/tenor.gif -https://media.tenor.com/images/e07473d3cf372dbc24c760527740b85e/tenor.gif -https://media.tenor.com/images/0569ee4ea490a197b0b38e92c3eede9e/tenor.gif -https://media.tenor.com/images/7ffa5f3b6fe7a3ddd009abb0f2c62ff9/tenor.gif -https://media.tenor.com/images/3e647c2df4183920ba3b036f6d057dae/tenor.gif -https://media.tenor.com/images/2e3c72a7aaf0fe3405d4c5ba9cee9bbd/tenor.gif -https://media.tenor.com/images/a3627bd9e42008a899ca315ab51c0553/tenor.gif -https://media.tenor.com/images/545e4ea926ca2a5fd215525963bd7360/tenor.gif -https://media.tenor.com/images/4781b66b378c1566d812c699f2a09661/tenor.gif -https://media.tenor.com/images/7e7ddfd89c6aac968f33334fda3730b8/tenor.gif -https://media.tenor.com/images/7d9209867acfafa378cef022ffd3abfa/tenor.gif -https://media.tenor.com/images/f65bd4bb3441b9d647d86b04eee6542e/tenor.gif -https://media.tenor.com/images/3c295a01cf438f45d07ec5f8b1f9dd4e/tenor.gif -https://media.tenor.com/images/29b6b20fc3c729e56184b0c92a86d842/tenor.gif -https://media.tenor.com/images/218aed7cb7c6b37183cff6baed7166b8/tenor.gif -https://media.tenor.com/images/012cc6d6cb65c3c98bd5505ab2e1c42a/tenor.gif -https://media.tenor.com/images/9787de95d7d307e63f6e1a8ddb5d2ba2/tenor.gif -https://media.tenor.com/images/cf2607b5940170eac383b41355d956fa/tenor.gif -https://media.tenor.com/images/6d73b0a9cadef5310be4b6160d2f959a/tenor.gif -https://media.tenor.com/images/2db3419bd925a54ff17e44fa5d708ca0/tenor.gif -https://media.tenor.com/images/91af20b99c5f0d209cec13b3f7b4ca3a/tenor.gif -https://media.tenor.com/images/e000639b39298e8e5022f80adc8768b4/tenor.gif -https://media.tenor.com/images/4781b66b378c1566d812c699f2a09661/tenor.gif -https://media.tenor.com/images/85f48bfe2846e8cad93009ca8dba0e9a/tenor.gif -https://media.tenor.com/images/85c92e585c30685d62768d735349af43/tenor.gif -https://media.tenor.com/images/68cc73bdd66f0467ceb3e49ce5967dbc/tenor.gif -https://media.tenor.com/images/34603799afdf0b57800704f2de31ecd0/tenor.gif -https://media.tenor.com/images/3b205574d0352d4d61687f835276566d/tenor.gif -https://media.tenor.com/images/3ddd35e50f7d0500efddae755ecea3d1/tenor.gif -https://media.tenor.com/images/6ae2e8f2d7cb4762ddd489f26f6b0b47/tenor.gif -https://media.tenor.com/images/b2c300f095efb7d38108c6e7309f13de/tenor.gif -https://media.tenor.com/images/20ecc3af6a5523872854a7bc2c083b7e/tenor.gif -https://media.tenor.com/images/35641bbc12957f3a8a0b2196c965b483/tenor.gif -http://i.imgur.com/zG60zPk.gif -http://i.imgur.com/xWTyaKY.gif -http://i.imgur.com/ct76LIg.gif -http://i.imgur.com/xWTyaKY.gif -http://i.imgur.com/Asnv32U.gif \ No newline at end of file diff --git a/images/FunCommands/digby.txt b/images/FunCommands/digby.txt deleted file mode 100644 index 05e7e45f..00000000 --- a/images/FunCommands/digby.txt +++ /dev/null @@ -1,13 +0,0 @@ -https://cdn.discordapp.com/attachments/625393597231005786/726208352727597139/image3.jpg -https://cdn.discordapp.com/attachments/625393597231005786/726208352014827570/image2.jpg -https://cdn.discordapp.com/attachments/625393597231005786/726208351393808414/image1.jpg -https://cdn.discordapp.com/attachments/625393597231005786/726208350773182594/image0.jpg -https://cdn.discordapp.com/attachments/625393597231005786/726208962134933575/image0.jpg -https://cdn.discordapp.com/attachments/625393597231005786/726209091437068448/image0.jpg -https://cdn.discordapp.com/attachments/625393597231005786/726209145883066368/image0.jpg -https://cdn.discordapp.com/attachments/625393597231005786/726209317371641865/image0.jpg -https://cdn.discordapp.com/attachments/625393597231005786/726209476771971134/image0.jpg -https://cdn.discordapp.com/attachments/625393597231005786/726211884922961960/image0.jpg -https://cdn.discordapp.com/attachments/625393597231005786/726212026493567056/image0.jpg -https://cdn.discordapp.com/attachments/625393597231005786/726212027009335406/image1.jpg -https://cdn.discordapp.com/attachments/625393597231005786/726212027886075975/image2.jpg \ No newline at end of file diff --git a/images/FunCommands/hugging.txt b/images/FunCommands/hugging.txt deleted file mode 100644 index f97592a7..00000000 --- a/images/FunCommands/hugging.txt +++ /dev/null @@ -1,43 +0,0 @@ -https://media.tenor.com/images/324fad6fdc3463105a404949d84bcfae/tenor.gif -https://media.tenor.com/images/35641bbc12957f3a8a0b2196c965b483/tenor.gif -https://media.tenor.com/images/9cdd31459981c41b6aa282f753273e60/tenor.gif -https://media.tenor.com/images/2cb9ed69aa1c49cbc1f5974cc9a76ed5/tenor.gif -https://media.tenor.com/images/579fdfefae8935a61b6e9614b16cfb3d/tenor.gif -https://media.tenor.com/images/81f693db5e5265c9ae21052d55ab7b3d/tenor.gif -https://media.tenor.com/images/b3a0e4968bd1555a82355bce760d8925/tenor.gif -https://media.tenor.com/images/b82d06e14a81a61a27a1c997fe57cd62/tenor.gif -https://media.tenor.com/images/57349c49dd49d24b707f506dcfaef9e0/tenor.gif -https://media.tenor.com/images/7af8d954d9b046758233c0f554b23729/tenor.gif -https://media.tenor.com/images/ec7d95ad0f77741e0b7980704351b835/tenor.gif -https://media.tenor.com/images/e7c53cbaaaf7124541f85ac421ec3d60/tenor.gif -https://media.tenor.com/images/b035aadbfbbfac443563f2d8d12372dc/tenor.gif -https://media.tenor.com/images/bb3ef2d15f0021c7dcfd688f4a6aadeb/tenor.gif -https://media.tenor.com/images/d3dca2dec335e5707e668b2f9813fde5/tenor.gif -https://media.tenor.com/images/a10f5769702e5fd50b59b8e965ef5f49/tenor.gif -https://media.tenor.com/images/c0d1624352bd64461e02238db242ed75/tenor.gif -https://media.tenor.com/images/ca2a1d63583bc23224ce45607f40703e/tenor.gif -https://media.tenor.com/images/0f837fac66cc7d00cef44675cccf9238/tenor.gif -https://media.tenor.com/images/85cab793748744ed4872d1c10aaeffd2/tenor.gif -https://media.tenor.com/images/44b4b9d5e6b4d806b6bcde2fd28a75ff/tenor.gif -https://media.tenor.com/images/749e89b235225a32bdda5211263edd14/tenor.gif -https://media.tenor.com/images/6d40d82e71dc167fd4a247704285fab7/tenor.gif -https://media.tenor.com/images/4be3396644e87d3c201f8965104e57b7/tenor.gif -https://media.tenor.com/images/9dd8b460be160d79123a9ca97d2502d6/tenor.gif -https://media.tenor.com/images/df8ba47cbd32dbebf41e465b1d5ed907/tenor.gif -https://media.tenor.com/images/9df38618eefe4998c7c6a2398808bef4/tenor.gif -https://media.tenor.com/images/62048cf3073b2670e176c470aa1d2714/tenor.gif -https://media.tenor.com/images/0570dca0f27a3aa7cc048ce829a7f53b/tenor.gif -https://media.tenor.com/images/29bd2a0cf5d863c3c803b3f4ab8d31f5/tenor.gif -https://media.tenor.com/images/b3f7b2e24f8605ed106fdeff6f03df62/tenor.gif -https://media.tenor.com/images/b5077daebe4c679dd12212e276c869ad/tenor.gif -https://media.tenor.com/images/6db54c4d6dad5f1f2863d878cfb2d8df/tenor.gif -https://media.tenor.com/images/194b5dc188fb0282636970391f1cd10b/tenor.gif -https://media.tenor.com/images/d23720aa94f1fd10dabe5255df778fb3/tenor.gif -https://media.tenor.com/images/fc26c413b99a2b571cef77d52e61fad1/tenor.gif -https://media.tenor.com/images/f8c810e24acbdfde36d1908e10e39c28/tenor.gif -https://media.tenor.com/images/fae49449a3bd6db7a3a6ec1bcade2622/tenor.gif -https://media.tenor.com/images/38e6c2549798384639533da90470e26d/tenor.gif -https://media.tenor.com/images/0e78b8d8e174fa48bd2808093360f1d5/tenor.gif -http://i.imgur.com/7C36d39.gif -http://i.imgur.com/xWTyaKY.gif -http://i.imgur.com/MrEMpE6.gif \ No newline at end of file diff --git a/images/FunCommands/killing.txt b/images/FunCommands/killing.txt deleted file mode 100644 index 18e41d2c..00000000 --- a/images/FunCommands/killing.txt +++ /dev/null @@ -1,22 +0,0 @@ -https://cdn.discordapp.com/attachments/713822779622948884/723601025377894403/kill10.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601049444810834/kill14.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601071481552956/kill19.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601071317975070/kill7.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601076619706378/kill8.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601076997324830/kill17.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601078733635685/kill15.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601107888242728/kill13.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601083590639616/kill21.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601116163604541/kill2.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601127320584192/kill11.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601130814439534/kill18.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601135973171220/kill6.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601138229837864/kill5.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601147213906100/kill12.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601146962509844/kill3.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601149164519574/kill20.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601149772693584/kill9.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601149659447396/kill16.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601197889617920/kill22.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601204319486053/kill4.gif -https://cdn.discordapp.com/attachments/713822779622948884/723601204193525791/kill1.gif \ No newline at end of file diff --git a/images/FunCommands/kissing.txt b/images/FunCommands/kissing.txt deleted file mode 100644 index 28971259..00000000 --- a/images/FunCommands/kissing.txt +++ /dev/null @@ -1,47 +0,0 @@ -https://media.giphy.com/media/G3va31oEEnIkM/giphy.gif -https://media.giphy.com/media/vHISzfc8dcVG0/giphy.gif -https://media.giphy.com/media/jLBwuKcJQcCpq/giphy.gif -https://media.giphy.com/media/Gj8bn4pgTocog/giphy.gif -https://media.giphy.com/media/TkDX9bkIROf8k/giphy.gif -https://media.giphy.com/media/bm2O3nXTcKJeU/giphy.gif -https://media.giphy.com/media/QGc8RgRvMonFm/giphy.gif -https://media.giphy.com/media/pcui5ohH3X96M/giphy.gif -https://media.giphy.com/media/nyGFcsP0kAobm/giphy.gif -https://media.giphy.com/media/dP8ONh1mN8YWQ/giphy.gif -https://media.giphy.com/media/wOtkVwroA6yzK/giphy.gif -https://media.giphy.com/media/zkppEMFvRX5FC/giphy.gif -https://media.giphy.com/media/ZL0G3c9BDX9ja/giphy.gif -https://media.giphy.com/media/12VXIxKaIEarL2/giphy.gif -https://media.giphy.com/media/hnNyVPIXgLdle/giphy.gif -https://media.giphy.com/media/iseq9MQgxo4aQ/giphy.gif -https://media.giphy.com/media/xdXIoBwYnBC0w/giphy.gif -https://cdn.discordapp.com/attachments/716823227409760337/718539083881644062/anime-kissin-7.gif -https://media.giphy.com/media/11rWoZNpAKw8w/giphy.gif -https://media.giphy.com/media/gy84qPkNTkgU/giphy.gif -https://media.giphy.com/media/fHtb1JPbfph72/giphy.gif -https://media.giphy.com/media/w2OMjUGV7mCSQ/giphy.gif -https://media.giphy.com/media/7pzeSkzilmVZS/giphy.gif -https://media.discordapp.net/attachments/716823227409760337/718597550671921213/tumblr_n5r2spwEYQ1rygh4xo1_500.gif -https://media.discordapp.net/attachments/716823227409760337/718598000406167652/tumblr_mwo343m7tK1sv72vno1_500.gif -https://cdn.discordapp.com/attachments/716823227409760337/718598371056812102/tumblr_n5h2m2u6SW1sn5ot7o2_500.gif -https://images-ext-2.discordapp.net/external/160FCOVxC5bjA5hGNUVp8RGfH-Lb9GdoTAJ4S8K7tVM/https/i.gifer.com/2q5t.gif -https://images-ext-2.discordapp.net/external/5_zU60yCUbwaBaYne1BYTa7JwfOscBLOOcBa2vnckrg/https/i.gifer.com/XrqL.gif -https://cdn.discordapp.com/attachments/652999909989023784/731574134668263454/kiss.gif -https://cdn.discordapp.com/attachments/652999909989023784/731575159823007764/BJMX2TuPb.gif -https://media.tenor.com/images/230e9fd40cd15e3f27fc891bac04248e/tenor.gif -https://media.tenor.com/images/b13ddaa73cf589346d6b6ca172873a9e/tenor.gif -https://media.tenor.com/images/5d2b53028b305f56ae753d6a7a988f6b/tenor.gif -https://media.tenor.com/images/c50aec5a3e97cce71466b06d05fe9058/tenor.gif -https://media.tenor.com/images/3cdfc0cce845667ec5184c3d2d4f9eff/tenor.gif -https://media.tenor.com/images/68d31e1a49f53b1e48a1331bf07e4655/tenor.gif -https://media.tenor.com/images/1cea57aa12dbe8e9e08e498ff57dda47/tenor.gif -https://media.tenor.com/images/5f6d2bbbbd6f5587c86865459e0a53ce/tenor.gif -https://media.tenor.com/images/6eba381d2764ea05a0fb4da0675a95db/tenor.gif -https://media.tenor.com/images/cd0d06e057bf6a01cd5f9bb5d7971b08/tenor.gif -https://media.tenor.com/images/ef9f2f0a8d16ce62ef8c2322ad6ddef9/tenor.gif -https://media.tenor.com/images/8a1dc34ab019887c21f814bb9e2fb3f2/tenor.gif -https://media.tenor.com/images/518ae408ab8a48ddf4d9504cfde88a72/tenor.gif -https://media.tenor.com/images/db79d17d7a5e08bf64e55a63eea5976f/tenor.gif -https://media.tenor.com/images/05bcc042f90c2ec3387bb27b35e0355f/tenor.gif -https://media.tenor.com/images/77b755b246126dca1a1c6880cb0a0174/tenor.gif -https://media.tenor.com/images/778d51aca07848160ad9b52e6df37b30/tenor.gif \ No newline at end of file diff --git a/images/FunCommands/patting.txt b/images/FunCommands/patting.txt deleted file mode 100644 index 24d20dc3..00000000 --- a/images/FunCommands/patting.txt +++ /dev/null @@ -1,40 +0,0 @@ -https://media.tenor.com/images/2b2f9c5d046ea2cdaca41dfdc4356eea/tenor.gif -https://media.tenor.com/images/70a1188a3db8fb41b9d539d05d685293/tenor.gif -https://media.tenor.com/images/ae1f3447e350116988d58da47d3ef778/tenor.gif -https://media.tenor.com/images/f79a9ec48bde0e592e55447b17ecfbad/tenor.gif -https://media.tenor.com/images/c22558035a2b09f1538935162dead992/tenor.gif -https://media.tenor.com/images/8331ba63516b37eb6987dcd45c4c0f66/tenor.gif -https://media.tenor.com/images/dae02df4433b91feff5c2cc74b412bec/tenor.gif -https://media.tenor.com/images/a9d0fd4bb985bb38a82a9af843fa2480/tenor.gif -https://media.tenor.com/images/196c72ab7342ac159b50c98559d81269/tenor.gif -https://media.tenor.com/images/f250100c8dbc02ca7f5a50056c8c6d33/tenor.gif -https://media.tenor.com/images/5466adf348239fba04c838639525c28a/tenor.gif -https://media.tenor.com/images/cf9a587a3fc4ef2e8f9f92bae63cb0d0/tenor.gif -https://media.tenor.com/images/3e0f3a22d4b2ca4fe91bf5839feb646b/tenor.gif -https://media.tenor.com/images/0ea33070f2294ad89032c69d77230a27/tenor.gif -https://media.tenor.com/images/d3c117054fb924d66c75169ff158c811/tenor.gif -https://media.tenor.com/images/3e94b77bfc7d4e240bb530b347a84008/tenor.gif -https://media.tenor.com/images/c61cc63503c21c8e69452639f068ad7f/tenor.gif -https://media.tenor.com/images/0339f275fff983e083997b3cfff81ae8/tenor.gif -https://media.tenor.com/images/d9e575861bb2f6389cec93da6cbdfa1f/tenor.gif -https://media.tenor.com/images/b8b0fe3f429e6d921274de333a9e2dfc/tenor.gif -https://media.tenor.com/images/c4bf2e3cba3d3e18514f635ae8b86f17/tenor.gif -https://media.tenor.com/images/57e98242606d651cc992b9525d3de2d8/tenor.gif -https://media.tenor.com/images/93ef9b0e6d1f89a92faf17e2c9db9622/tenor.gif -https://media.tenor.com/images/c9b5197227668f7fe5bca4627c539b20/tenor.gif -https://media.tenor.com/images/418caaf5331da97c2f70b4fc4bc9655d/tenor.gif -https://media.tenor.com/images/f0e0a23c4ec0aa610c69ca273f1bf541/tenor.gif -https://media.tenor.com/images/affa531884b2e7936a6bc563168f8c34/tenor.gif -https://media.tenor.com/images/4ebafc0f89234c97131e88e67adbdd58/tenor.gif -https://media.tenor.com/images/fee6bd69efbf19041c84c01381ce021e/tenor.gif -https://media.tenor.com/images/76d3b063b91ac22600d5604003047c01/tenor.gif -https://media.tenor.com/images/f4574b9bef2b67bf6f31eb8fe6617887/tenor.gif -https://media.tenor.com/images/d3c117054fb924d66c75169ff158c811/tenor.gif -https://media.tenor.com/images/220babfd5f8b629cc16399497ed9dd96/tenor.gif -https://media.tenor.com/images/e626aaa7a704c20a17e418272add6af6/tenor.gif -https://media.tenor.com/images/68d981347bf6ee8c7d6b78f8a7fe3ccb/tenor.gif -https://media.tenor.com/images/7a21fbeac0b890e3f38db405e303cb94/tenor.gif -https://media.tenor.com/images/f1d89b997e0f4165af9abe72af7f67ca/tenor.gif -https://media.tenor.com/images/6b0036b38dbc8e5b40c256ffed664829/tenor.gif -https://media.tenor.com/images/662051affffdd394e8540807b07d0fd9/tenor.gif -https://media.tenor.com/images/f5faaccf8cc78a9c6138b3a8f8d875b6/tenor.gif \ No newline at end of file diff --git a/images/FunCommands/slapping.txt b/images/FunCommands/slapping.txt deleted file mode 100644 index d180fd00..00000000 --- a/images/FunCommands/slapping.txt +++ /dev/null @@ -1,40 +0,0 @@ -https://media.tenor.com/images/d0d4bc024b256d86c740a5804e4bcfc8/tenor.gif -https://media.tenor.com/images/49cc731f0d76650a83351618b495805b/tenor.gif -https://media.tenor.com/images/1387554d4c1cf903c06cd276b9f32361/tenor.gif -https://media.tenor.com/images/5eeb1a7d7d0e9b2d6ed7d7125e31667e/tenor.gif -https://media.tenor.com/images/6df057c42393deb1c483d14b9b312495/tenor.gif -https://media.tenor.com/images/c03828c5fac2d1342fa5a5462c3a5ac7/tenor.gif -https://media.tenor.com/images/106a19ebd266594f3ec70321811c6c60/tenor.gif -https://media.tenor.com/images/9d8f45c7a54b44dccf5302768182bf57/tenor.gif -https://media.tenor.com/images/b150f67bd4ba4aff38fe6f38830b4eb8/tenor.gif -https://media.tenor.com/images/33a5b1d6a3e316f12720ab3060f683cf/tenor.gif -https://media.tenor.com/images/de289d8f02fc810eb1389aca8df6a417/tenor.gif -https://media.tenor.com/images/30c9c3185e1a105571eda9b417986509/tenor.gif -https://media.tenor.com/images/d94acb5413ad36fd304b87fbd6c65c42/tenor.gif -https://media.tenor.com/images/f1ffc9863f7b43d1e171482b9d1503d2/tenor.gif -https://media.tenor.com/images/b6f922d588883687b473dcaa9e1ff312/tenor.gif -https://media.tenor.com/images/d94be9c942b034e4a00a66d1e277ba84/tenor.gif -https://media.tenor.com/images/40a27297ca170bd4456c121add83faf0/tenor.gif -https://media.tenor.com/images/71d49049a41f85331e38ad56467db093/tenor.gif -https://media.tenor.com/images/d0eafbb07e1ae4c5d8681f0239bae82b/tenor.gif -https://media.tenor.com/images/ff7adfeff2359efac2fb324e289babf7/tenor.gif -https://media.tenor.com/images/ce295550c5436b7b61edac89b3a49b07/tenor.gif -https://media.tenor.com/images/cf881d79afde980ea7cf58219167bfb7/tenor.gif -https://media.tenor.com/images/4db51ef6f447abb161a649221a88b40d/tenor.gif -https://media.tenor.com/images/d46abc46d314b30177d3e2fcc1121af6/tenor.gif -https://media.tenor.com/images/b6d8a83eb652a30b95e87cf96a21e007/tenor.gif -https://media.tenor.com/images/9d907ed56fa1c8c011791e494b1d6ce0/tenor.gif -https://media.tenor.com/images/50f65d05d91bbac8cbe15b46215fe7a0/tenor.gif -https://media.tenor.com/images/f8d4f44c6206933341da60ebaf3b147c/tenor.gif -https://media.tenor.com/images/6f5c1f380b4cb313f412f57f4508c7e9/tenor.gif -https://media.tenor.com/images/23533677bc97359b318a9cbf156905d0/tenor.gif -https://media.tenor.com/images/e8f880b13c17d61810ac381b2f6a93c3/tenor.gif -https://media.tenor.com/images/984c0dc6e682d4978504874cc75485c8/tenor.gif -https://media.tenor.com/images/26b39008b7080fbcc8d644f7237d515e/tenor.gif -https://media.tenor.com/images/13844a6bc3d247b571e2cee25651d1a1/tenor.gif -https://media.tenor.com/images/e1c6feaeb3a64be5777b746c00f2eb18/tenor.gif -https://media.tenor.com/images/f0aa023d6422ad071f91e7a825a072fa/tenor.gif -https://media.tenor.com/images/f4044eb30d1f480d705288de2f13a2de/tenor.gif -https://media.tenor.com/images/2971bb1dde784a82020f9f2fed107612/tenor.gif -https://media.tenor.com/images/4ceedd1da542cccbc32a7a49e52ce6e6/tenor.gif -https://media.tenor.com/images/b632ce9619d275dbf091c16cdb85c9bd/tenor.gif \ No newline at end of file diff --git a/images/homies/AllMyHomies.jpg b/images/homies/AllMyHomies.jpg deleted file mode 100644 index 8b7bf277d062946b6557db9c301c2e85cacc4cac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68374 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 Yo6Wh(FBZ>W(+*LYw9+e;O5%V2*`Y)8#{d8T diff --git a/images/homies/impact/Impacted.ttf b/images/homies/impact/Impacted.ttf deleted file mode 100644 index c099baaa4697a0542ec66ea89055e66f22e73b5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 107032 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 HqvXE;;Xzsr diff --git a/images/homies/impact/impact.ttf b/images/homies/impact/impact.ttf deleted file mode 100644 index 114e6c1968b663086409144e50e7a592bff1d6c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136076 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 VQlm?y$QD;A35 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 diff --git a/images/homies/impact/unicode.impact.ttf b/images/homies/impact/unicode.impact.ttf deleted file mode 100644 index 12ea83ee8dff7fae92e000999d43b0e90ccc88ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77666 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 y zD6d@Mc2yxRszSa&g}88qeBlbW8x`_ZRVeRJAz!&ddF2W(R2AZ)D&!kfhznQ97q0L^ zqe8x_3gsOtCyirxFTU0G?xN3ES=XIv=ip?0t%;zgM>r5SA@YEHX?UFLS zs>hg->N)%(AH>yoa)YeM3C5UDfHSBHj5VIX1K<^O2|s{4)*(EW@RbX2i(X|u${YS1 zZd}Iq)Nkr(^s4zde^NfBzA2yLkE$E>lm*o38)e%Z8%-kd78AKqHEbh)7Mhjovh$<3 zI@PoC7o(N@G}kO2$BhN7*A>+clW1MhtQ#b6FlxrH>+MEsoq@*MjkenPX|7p5jvJU% z-U{1_#8KUs)R$0Wtpo&kw!^nODY5zUG&0ZM%1`PHdvvziR2-Sx{S>==#4P C#PAyc diff --git a/main.py b/main.py deleted file mode 100644 index 1909845b..00000000 --- a/main.py +++ /dev/null @@ -1,37 +0,0 @@ -# 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 . - -from bot import Bot - -# Initiating Bot Object As Client -client = Bot() - - -@client.event -async def on_message(message): - """Make sure bot messages are not tracked""" - - # Ignoring messages that start with 2 .. - if message.content.startswith("..") or message.author.bot: - return - - # Processing the message - await client.process_commands(message) - - -# Run the bot -client.execute() diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 27c7a454..00000000 --- a/requirements.txt +++ /dev/null @@ -1,85 +0,0 @@ -aiofiles==0.5.0 -aiohttp==3.7.4 -aiomysql==0.0.20 -Animapy==1.5.3.1 -appdirs==1.4.4 -APScheduler==3.6.3 -asgiref==3.2.7 -async-timeout==3.0.1 -asyncpg==0.21.0 -attrs==19.3.0 -beautifulsoup4==4.9.1 -bitarray==1.5.2 -buttons==0.1.9 -certifi==2020.4.5.1 -cffi==1.14.0 -changelogs==0.14.0 -chardet==3.0.4 -Click==7.0 -config==0.5.0.post0 -decorator==4.4.2 -dill==0.3.1.1 -discord==1.0.1 -discord-ext-menus==1.0.0a22+gcc108be -discord.py==1.4.0 -distlib==0.3.1 -Django==3.1.12 -dnspython==1.16.0 -docopt==0.6.2 -dsdev-utils==1.0.4 -filelock==3.0.12 -futures==3.1.1 -gitdb==4.0.5 -GitPython==3.1.7 -greenlet==0.4.15 -idna==2.8 -idna-ssl==1.1.0 -importlib-metadata==1.7.0 -importlib-resources==3.0.0 -Jinja2==2.11.3 -lxml==4.6.3 -MarkupSafe==1.1.1 -multidict==4.7.6 -mysqlclient==2.0.1 -numpy==1.19.1 -opencv-python==4.3.0.38 -owotext==1.0.2 -packaging==20.4 -Paginator==0.5.1 -pbr==5.4.5 -Pillow==8.2.0 -protobuf==3.12.2 -psutil==5.7.2 -pycodestyle==2.6.0 -pycparser==2.20 -PyMySQL==0.9.2 -PyNaCl==1.3.0 -pyparsing==2.4.7 -pytesseract==0.3.5 -python-aiml==0.9.3 -python-changelog==0.2.0 -python-dateutil==2.8.1 -python-decouple==3.3 -python-dotenv==0.13.0 -pytz==2020.1 -requests==2.22.0 -six==1.15.0 -smmap==3.0.4 -soupsieve==2.0.1 -South==1.0.2 -sqlparse==0.3.1 -statcord.py==2.2.0 -stevedore==3.2.0 -tenorpy==1.0.3 -typing-extensions==3.7.4.2 -tzlocal==2.1 -urllib3==1.26.5 -validators==0.17.1 -virtualenv==20.0.27 -virtualenv-clone==0.5.4 -virtualenvwrapper==4.8.4 -Voice==0.1.0 -websockets==9.1 -yarl==1.4.2 -youtube-dl==2020.6.6 -zipp==3.1.0 \ No newline at end of file