diff options
Diffstat (limited to 'cogs')
-rwxr-xr-x | cogs/admin.py | 315 | ||||
-rwxr-xr-x[-rw-r--r--] | cogs/general.py | 723 | ||||
-rw-r--r-- | cogs/help.py | 49 | ||||
-rwxr-xr-x | cogs/logs.py | 61 | ||||
-rw-r--r-- | cogs/moderator.py | 138 | ||||
-rw-r--r-- | cogs/player.py | 444 | ||||
-rwxr-xr-x | cogs/src.py | 293 | ||||
-rwxr-xr-x | cogs/trans.py | 51 | ||||
-rwxr-xr-x | cogs/twitter.py | 50 | ||||
-rwxr-xr-x[-rw-r--r--] | cogs/utils.py | 277 | ||||
-rwxr-xr-x | cogs/webserver.py | 57 | ||||
-rw-r--r-- | cogs/welcome.py | 25 |
12 files changed, 2221 insertions, 262 deletions
diff --git a/cogs/admin.py b/cogs/admin.py new file mode 100755 index 0000000..8fcf43a --- /dev/null +++ b/cogs/admin.py @@ -0,0 +1,315 @@ +from discord.ext import commands +import discord +import asyncio +import json +import git +import os + + +class Admin(commands.Cog): + def __init__(self, bot): + self.bot = bot + + async def is_mod(ctx): + return ctx.author.guild_permissions.manage_channels + + async def is_botmaster(ctx): + return ctx.author.id in ctx.bot.config[str( + ctx.message.guild.id)]["bot_masters"] + + @commands.command(aliases=['deleteEverything'], hidden=True) + @commands.check(is_botmaster) + async def purge(self, ctx): + await ctx.message.channel.purge(limit=500) + + @commands.command(aliases=['quit'], hidden=True) + @commands.check(is_botmaster) + async def forceexit(self, ctx): + await ctx.send('Self Destructing') + await ctx.bot.close() + + @commands.command() + @commands.check(is_mod) + async def pull(self, ctx): + """Update the bot from github""" + g = git.cmd.Git(os.getcwd()) + try: + await ctx.send(f"Probably pulled.\n```bash\n{g.pull()}```") + except git.exc.GitCommandError as e: + await ctx.send(f"An error has occured when pulling```bash\n{e}```") + + @commands.command(aliases=['addcommand', 'newcommand']) + @commands.check(is_mod) + async def setcommand(self, ctx, command, *, message): + """Add a new simple command""" + self.bot.custom_commands[ctx.prefix + command] = message + with open('custom_commands.json', 'w') as f: + json.dump(self.bot.custom_commands, f, indent=4) + + await ctx.send(f"Set message for command {command}") + + @commands.command(aliases=['deletecommand']) + @commands.check(is_mod) + async def removecommand(self, ctx, command): + """Remove a simple command""" + del self.bot.custom_commands[ctx.prefix + command] + with open('custom_commands.json', 'w') as f: + json.dump(self.bot.custom_commands, f, indent=4) + + await ctx.send(f"Removed command {command}") + + @commands.command(name='reload', hidden=True, usage='<extension>') + @commands.check(is_mod) + async def _reload(self, ctx, ext): + """Reloads an extension""" + try: + self.bot.reload_extension(f'cogs.{ext}') + await ctx.send(f'The extension {ext} was reloaded!') + except commands.ExtensionNotFound: + await ctx.send(f'The extension {ext} doesn\'t exist.') + except commands.ExtensionNotLoaded: + await ctx.send( + f'The extension {ext} is not loaded! (use {ctx.prefix}load)') + except commands.NoEntryPointError: + await ctx.send( + f'The extension {ext} doesn\'t have an entry point (try adding the setup function) ' + ) + except commands.ExtensionFailed: + await ctx.send( + f'Some unknown error happened while trying to reload extension {ext} (check logs)' + ) + self.bot.logger.exception(f'Failed to reload extension {ext}:') + + @commands.command(name='load', hidden=True, usage='<extension>') + @commands.check(is_mod) + async def _load(self, ctx, ext): + """Loads an extension""" + try: + self.bot.load_extension(f'cogs.{ext}') + await ctx.send(f'The extension {ext} was loaded!') + except commands.ExtensionNotFound: + await ctx.send(f'The extension {ext} doesn\'t exist!') + except commands.ExtensionAlreadyLoaded: + await ctx.send(f'The extension {ext} is already loaded.') + except commands.NoEntryPointError: + await ctx.send( + f'The extension {ext} doesn\'t have an entry point (try adding the setup function)' + ) + except commands.ExtensionFailed: + await ctx.send( + f'Some unknown error happened while trying to reload extension {ext} (check logs)' + ) + self.bot.logger.exception(f'Failed to reload extension {ext}:') + + @commands.command(name='unload', hidden=True, usage='<extension>') + @commands.check(is_mod) + async def _unload(self, ctx, ext): + """Loads an extension""" + try: + self.bot.unload_extension(f'cogs.{ext}') + await ctx.send(f'The extension {ext} was unloaded!') + except commands.ExtensionNotFound: + await ctx.send(f'The extension {ext} doesn\'t exist!') + except commands.NoEntryPointError: + await ctx.send( + f'The extension {ext} doesn\'t have an entry point (try adding the setup function)' + ) + except commands.ExtensionFailed: + await ctx.send( + f'Some unknown error happened while trying to reload extension {ext} (check logs)' + ) + self.bot.logger.exception(f'Failed to unload extension {ext}:') + + @commands.command() + @commands.check(is_mod) + async def clear(self, ctx, number): + """Mass delete messages""" + await ctx.message.channel.purge(limit=int(number) + 1, + check=None, + before=None, + after=None, + around=None, + oldest_first=False, + bulk=True) + + @commands.command() + @commands.check(is_mod) + async def mute(self, + ctx, + members: commands.Greedy[discord.Member] = False, + mute_minutes: int = 0, + *, + reason: str = "absolutely no reason"): + """Mass mute members with an optional mute_minutes parameter to time it""" + + if not members: + await ctx.send("You need to name someone to mute") + return + elif type(members) == str: + members = [self.bot.get_user(int(members))] + + #muted_role = discord.utils.find(ctx.guild.roles, name="Muted") + muted_role = ctx.guild.get_role( + int(self.bot.config[str(ctx.message.guild.id)]["mute_role"])) + for member in members: + if self.bot.user == member: # what good is a muted bot? + embed = discord.Embed( + title="You can't mute me, I'm an almighty bot") + await ctx.send(embed=embed) + continue + await member.add_roles(muted_role, reason=reason) + await ctx.send( + "{0.mention} has been muted by {1.mention} for *{2}*".format( + member, ctx.author, reason)) + + if mute_minutes > 0: + await asyncio.sleep(mute_minutes * 60) + for member in members: + await member.remove_roles(muted_role, reason="time's up ") + + @commands.command() + @commands.check(is_mod) + async def unmute(self, ctx, members: commands.Greedy[discord.Member]): + """Remove the muted role""" + if not members: + await ctx.send("You need to name someone to unmute") + return + elif type(members) == str: + members = self.bot.get_user(int(user)) + + muted_role = ctx.guild.get_role( + int(self.bot.config[str(ctx.message.guild.id)]["mute_role"])) + for i in members: + await i.remove_roles(muted_role) + await ctx.send( + "{0.mention} has been unmuted by {1.mention}".format( + i, ctx.author)) + + @commands.command() + @commands.check(is_botmaster) + async def ban(self, + ctx, + members: commands.Greedy[discord.Member] = False, + ban_minutes: int = 0, + *, + reason: str = "absolutely no reason"): + """Mass ban members with an optional mute_minutes parameter to time it""" + + if not members: + await ctx.send("You need to name someone to ban") + return + elif type(members) == str: + members = [self.bot.get_user(int(members))] + for member in members: + if self.bot.user == member: # what good is a muted bot? + embed = discord.Embed( + title="You can't ban me, I'm an almighty bot") + await ctx.send(embed=embed) + continue + await member.send( + f"You have been banned from {ctx.guild.name} for {ban_minutes} minutes because: ```{reason}```" + ) + await ctx.guild.ban(member, reason=reason, delete_message_days=0) + await ctx.send( + "{0.mention} has been banned by {1.mention} for *{2}*".format( + member, ctx.author, reason)) + + if ban_minutes > 0: + await asyncio.sleep(ban_minutes * 60) + for member in members: + await ctx.guild.unban(member, reason="Time is up") + + @commands.command() + @commands.check(is_botmaster) + async def logs(self, ctx): + """Send the discord.log file""" + await ctx.message.delete() + file = discord.File("discord.log") + await ctx.send(file=file) + + @commands.command(hidden=True) + @commands.check(is_mod) + async def blacklist(self, + ctx, + members: commands.Greedy[discord.Member] = None): + """Ban someone from using the bot""" + if not members: + await ctx.send("You need to name someone to blacklist") + return + elif type(members) == "str": + members = self.bot.get_user(int(user)) + + with open('blacklist.json', 'w') as f: + for i in members: + if i.id in self.bot.blacklist: + self.bot.blacklist.remove(i.id) + json.dump(self.bot.blacklist, f, indent=4) + await ctx.send(f"{i} has been un-blacklisted.") + else: + self.bot.blacklist.append(i.id) + json.dump(self.bot.blacklist, f, indent=4) + await ctx.send(f"{i} has been blacklisted.") + + @commands.command() + @commands.check(is_mod) + async def activity(self, ctx, *, activity=None): + """Change the bot's activity""" + if activity: + game = discord.Game(activity) + else: + activity = "Mining away" + game = discord.Game(activity) + await self.bot.change_presence(activity=game) + await ctx.send(f"Activity changed to {activity}") + + @commands.command() + @commands.check(is_botmaster) + async def setvar(self, ctx, key, *, value): + """Set a config variable, ***use with caution**""" + with open('config.json', 'w') as f: + if value[0] == '[' and value[len(value) - 1] == ']': + value = list(map(int, value[1:-1].split(','))) + self.bot.config[str(ctx.message.guild.id)][key] = value + json.dump(self.bot.config, f, indent=4) + + @commands.command() + @commands.check(is_mod) + async def printvar(self, ctx, key): + """Print a config variable, use for testing""" + await ctx.send(self.bot.config[str(ctx.message.guild.id)][key]) + + @commands.command() + @commands.check(is_mod) + async def blacklistvideo(self, ctx, uri): + """Set runs from a specific url to be auto rejected""" + with open('runs_blacklist.json', 'w') as f: + self.bot.runs_blacklist["videos"].append(uri) + json.dump(self.bot.runs_blacklist, f, indent=4) + await ctx.send(f'Blacklisted runs from `{uri}`') + + @commands.command() + @commands.check(is_mod) + async def blacklistplayer(self, ctx, player): + """Set runs from a specific player to be auto rejected""" + with open('runs_blacklist.json', 'w') as f: + self.bot.runs_blacklist["players"].append(player) + json.dump(self.bot.runs_blacklist, f, indent=4) + await ctx.send(f'Blacklisted runs from `{player}`') + + @commands.command() + @commands.check(is_mod) + async def runs_blacklist(self, ctx): + """Sends a list of blacklisted videos and players""" + message = '```The following URIs are blacklisted:\n' + for uri in self.bot.runs_blacklist["videos"]: + message += f'{uri}, ' + await ctx.send(f'{message[:-2]}```') + + message = '```The following players are blacklisted:\n' + for player in self.bot.runs_blacklist["players"]: + message += f'{player}, ' + await ctx.send(f'{message[:-2]}```') + + +def setup(bot): + bot.add_cog(Admin(bot)) diff --git a/cogs/general.py b/cogs/general.py index eeaeb8c..cf05eb4 100644..100755 --- a/cogs/general.py +++ b/cogs/general.py @@ -1,42 +1,681 @@ -from discord.ext import commands -import discord -import asyncio -import datetime - -class General(commands.Cog): - def __init__(self, bot): - self.bot = bot - - @commands.command() - async def add(self, ctx): - """Show link to invite ziBot.""" - oauth_link = "https://discord.com/oauth2/authorize?client_id=740122842988937286&scope=bot" - await ctx.send(f"To add ziBot to your server, click here: \n {oauth_link}") - - @commands.command() - async def source(self, ctx): - """Show link to ziBot's source code.""" - git_link = "https://github.com/null2264/ziBot" - await ctx.send(f"ziBot's source code: \n {git_link}") - - @commands.command() - async def serverinfo(self, ctx): - """Show server information""" - embed = discord.Embed( - title=f"{ctx.guild.name} Information", - colour=discord.Colour.orange() - ) - # embed.set_author(name=f"{ctx.guild.name} Information") - embed.add_field(name="Created on",value=f"{ctx.guild.created_at.date()}") - embed.add_field(name="Created by",value=f"{ctx.guild.owner.mention}") - embed.set_thumbnail(url=ctx.guild.icon_url) - _emoji="" - embed.add_field(name="Members",value=f"{ctx.guild.member_count}") - for emoji in ctx.guild.emojis: - _emoji+= ", ".join([f"{str(emoji)}"]) - embed.add_field(name="Emojis",value=f"{_emoji}") - await ctx.send(embed=embed) - -def setup(bot): - bot.add_cog(General(bot)) - +from discord.ext import commands
+import discord
+import datetime
+import requests
+import json
+import dateutil.parser
+from random import randint
+import os, sys, inspect
+current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
+parent_dir = os.path.dirname(current_dir)
+sys.path.insert(0, parent_dir)
+import bot
+
+
+def dump(obj):
+ output = ""
+ for attr in dir(obj):
+ output += "\nobj.%s = %r" % (attr, getattr(obj, attr))
+ print("obj.%s = %r" % (attr, getattr(obj, attr)))
+ return output
+
+
+class MyHelpCommand(commands.MinimalHelpCommand):
+ messages = [
+ "As seen on TV!",
+ "Awesome!",
+ "100% pure!",
+ "May contain nuts!",
+ "Better than Prey!",
+ "More polygons!",
+ "Sexy!",
+ "Limited edition!",
+ "Flashing letters!",
+ "Made by Notch!",
+ "It's here!",
+ "Best in class!",
+ "It's finished!",
+ "Kind of dragon free!",
+ "Excitement!",
+ "More than 500 sold!",
+ "One of a kind!",
+ "Heaps of hits on YouTube!",
+ "Indev!",
+ "Spiders everywhere!",
+ "Check it out!",
+ "Holy cow",
+ " man!",
+ "It's a game!",
+ "Made in Sweden!",
+ "Uses LWJGL!",
+ "Reticulating splines!",
+ "Minecraft!",
+ "Yaaay!",
+ "Singleplayer!",
+ "Keyboard compatible!",
+ "Undocumented!",
+ "Ingots!",
+ "Exploding creepers!",
+ "That's no moon!",
+ "l33t!",
+ "Create!",
+ "Survive!",
+ "Dungeon!",
+ "Exclusive!",
+ "The bee's knees!",
+ "Down with O.P.P.!",
+ "Closed source!",
+ "Classy!",
+ "Wow!",
+ "Not on steam!",
+ "Oh man!",
+ "Awesome community!",
+ "Pixels!",
+ "Teetsuuuuoooo!",
+ "Kaaneeeedaaaa!",
+ "Now with difficulty!",
+ "Enhanced!",
+ "90% bug free!",
+ "Pretty!",
+ "12 herbs and spices!",
+ "Fat free!",
+ "Absolutely no memes!",
+ "Free dental!",
+ "Ask your doctor!",
+ "Minors welcome!",
+ "Cloud computing!",
+ "Legal in Finland!",
+ "Hard to label!",
+ "Technically good!",
+ "Bringing home the bacon!",
+ "Indie!",
+ "GOTY!",
+ "Ceci n'est pas une title screen!",
+ "Euclidian!",
+ "Now in 3D!",
+ "Inspirational!",
+ "Herregud!",
+ "Complex cellular automata!",
+ "Yes",
+ " sir!",
+ "Played by cowboys!",
+ "OpenGL 1.2!",
+ "Thousands of colors!",
+ "Try it!",
+ "Age of Wonders is better!",
+ "Try the mushroom stew!",
+ "Sensational!",
+ "Hot tamale",
+ " hot hot tamale!",
+ "Play him off",
+ " keyboard cat!",
+ "Guaranteed!",
+ "Macroscopic!",
+ "Bring it on!",
+ "Random splash!",
+ "Call your mother!",
+ "Monster infighting!",
+ "Loved by millions!",
+ "Ultimate edition!",
+ "Freaky!",
+ "You've got a brand new key!",
+ "Water proof!",
+ "Uninflammable!",
+ "Whoa",
+ " dude!",
+ "All inclusive!",
+ "Tell your friends!",
+ "NP is not in P!",
+ "Notch <3 ez!",
+ "Music by C418!",
+ "Livestreamed!",
+ "Haunted!",
+ "Polynomial!",
+ "Terrestrial!",
+ "All is full of love!",
+ "Full of stars!",
+ "Scientific!",
+ "Cooler than Spock!",
+ "Collaborate and listen!",
+ "Never dig down!",
+ "Take frequent breaks!",
+ "Not linear!",
+ "Han shot first!",
+ "Nice to meet you!",
+ "Buckets of lava!",
+ "Ride the pig!",
+ "Larger than Earth!",
+ "sqrt(-1) love you!",
+ "Phobos anomaly!",
+ "Punching wood!",
+ "Falling off cliffs!",
+ "0% sugar!",
+ "150% hyperbole!",
+ "Synecdoche!",
+ "Let's danec!",
+ "Seecret Friday update!",
+ "Reference implementation!",
+ "Lewd with two dudes with food!",
+ "Kiss the sky!",
+ "20 GOTO 10!",
+ "Verlet intregration!",
+ "Peter Griffin!",
+ "Do not distribute!",
+ "Cogito ergo sum!",
+ "4815162342 lines of code!",
+ "A skeleton popped out!",
+ "The Work of Notch!",
+ "The sum of its parts!",
+ "BTAF used to be good!",
+ "I miss ADOM!",
+ "umop-apisdn!",
+ "OICU812!",
+ "Bring me Ray Cokes!",
+ "Finger-licking!",
+ "Thematic!",
+ "Pneumatic!",
+ "Sublime!",
+ "Octagonal!",
+ "Une baguette!",
+ "Gargamel plays it!",
+ "Rita is the new top dog!",
+ "SWM forever!",
+ "Representing Edsbyn!",
+ "Matt Damon!",
+ "Supercalifragilisticexpialidocious!",
+ "Consummate V's!",
+ "Cow Tools!",
+ "Double buffered!",
+ "Fan fiction!",
+ "Flaxkikare!",
+ "Jason! Jason! Jason!",
+ "Hotter than the sun!",
+ "Internet enabled!",
+ "Autonomous!",
+ "Engage!",
+ "Fantasy!",
+ "DRR! DRR! DRR!",
+ "Kick it root down!",
+ "Regional resources!",
+ "Woo",
+ " facepunch!",
+ "Woo",
+ " somethingawful!",
+ "Woo",
+ " /v/!",
+ "Woo",
+ " tigsource!",
+ "Woo",
+ " minecraftforum!",
+ "Woo",
+ " worldofminecraft!",
+ "Woo",
+ " reddit!",
+ "Woo",
+ " 2pp!",
+ "Google anlyticsed!",
+ "Now supports åäö!",
+ "Give us Gordon!",
+ "Tip your waiter!",
+ "Very fun!",
+ "12345 is a bad password!",
+ "Vote for net neutrality!",
+ "Lives in a pineapple under the sea!",
+ "MAP11 has two names!",
+ "Omnipotent!",
+ "Gasp!",
+ "...!",
+ "Bees",
+ " bees",
+ " bees",
+ " bees!",
+ "Jag känner en bot!",
+ "This text is hard to read if you play the game at the default resolution",
+ " but at 1080p it's fine!",
+ "Haha",
+ " LOL!",
+ "Hampsterdance!",
+ "Switches and ores!",
+ "Menger sponge!",
+ "idspispopd!",
+ "Eple (original edit)!",
+ "So fresh",
+ " so clean!",
+ "Slow acting portals!",
+ "Try the Nether!",
+ "Don't look directly at the bugs!",
+ "Oh",
+ " ok",
+ " Pigmen!",
+ "Finally with ladders!",
+ "Scary!",
+ "Play Minecraft",
+ " Watch Topgear",
+ " Get Pig!",
+ "Twittered about!",
+ "Jump up",
+ " jump up",
+ " and get down!",
+ "Joel is neat!",
+ "A riddle",
+ " wrapped in a mystery!",
+ "Huge tracts of land!",
+ "Welcome to your Doom!",
+ "Stay a while",
+ " stay forever!",
+ "Stay a while and listen!",
+ "Treatment for your rash!",
+ "\"Autological\" is!",
+ "Information wants to be free!",
+ "\"Almost never\" is an interesting concept!",
+ "Lots of truthiness!",
+ "The creeper is a spy!",
+ "Turing complete!",
+ "It's groundbreaking!",
+ "Let our battle's begin!",
+ "The sky is the limit!",
+ "Jeb has amazing hair!",
+ "Ryan also has amazing hair!",
+ "Casual gaming!",
+ "Undefeated!",
+ "Kinda like Lemmings!",
+ "Follow the train",
+ " CJ!",
+ "Leveraging synergy!",
+ "This message will never appear on the splash screen",
+ " isn't that weird?",
+ "DungeonQuest is unfair!",
+ "110813!",
+ "90210!",
+ "Check out the far lands!",
+ "Tyrion would love it!",
+ "Also try VVVVVV!",
+ "Also try Super Meat Boy!",
+ "Also try Terraria!",
+ "Also try Mount And Blade!",
+ "Also try Project Zomboid!",
+ "Also try World of Goo!",
+ "Also try Limbo!",
+ "Also try Pixeljunk Shooter!",
+ "Also try Braid!",
+ "That's super!",
+ "Bread is pain!",
+ "Read more books!",
+ "Khaaaaaaaaan!",
+ "Less addictive than TV Tropes!",
+ "More addictive than lemonade!",
+ "Bigger than a bread box!",
+ "Millions of peaches!",
+ "Fnord!",
+ "This is my true form!",
+ "Totally forgot about Dre!",
+ "Don't bother with the clones!",
+ "Pumpkinhead!",
+ "Hobo humping slobo babe!",
+ "Made by Jeb!",
+ "Has an ending!",
+ "Finally complete!",
+ "Feature packed!",
+ "Boots with the fur!",
+ "Stop",
+ " hammertime!",
+ "Testificates!",
+ "Conventional!",
+ "Homeomorphic to a 3-sphere!",
+ "Doesn't avoid double negatives!",
+ "Place ALL the blocks!",
+ "Does barrel rolls!",
+ "Meeting expectations!",
+ "PC gaming since 1873!",
+ "Ghoughpteighbteau tchoghs!",
+ "Déjà vu!",
+ "Déjà vu!",
+ "Got your nose!",
+ "Haley loves Elan!",
+ "Afraid of the big",
+ " black bat!",
+ "Doesn't use the U-word!",
+ "Child's play!",
+ "See you next Friday or so!",
+ "From the streets of Södermalm!",
+ "150 bpm for 400000 minutes!",
+ "Technologic!",
+ "Funk soul brother!",
+ "Pumpa kungen!",
+ "日本ハロー!",
+ "한국 안녕하세요!",
+ "Helo Cymru!",
+ "Cześć Polsko!",
+ "你好中国!",
+ "Привет Россия!",
+ "Γεια σου Ελλάδα!",
+ "My life for Aiur!",
+ "Lennart lennart = new Lennart();",
+ "I see your vocabulary has improved!",
+ "Who put it there?",
+ "You can't explain that!",
+ "if not ok then return end",
+ "§1C§2o§3l§4o§5r§6m§7a§8t§9i§ac",
+ "§kFUNKY LOL",
+ "SOPA means LOSER in Swedish!",
+ "Big Pointy Teeth!",
+ "Bekarton guards the gate!",
+ "Mmmph",
+ " mmph!",
+ "Don't feed avocados to parrots!",
+ "Swords for everyone!",
+ "Plz reply to my tweet!",
+ ".party()!",
+ "Take her pillow!",
+ "Put that cookie down!",
+ "Pretty scary!",
+ "I have a suggestion.",
+ "Now with extra hugs!",
+ "Now java 6!",
+ "Woah.",
+ "HURNERJSGER?",
+ "What's up",
+ " Doc?",
+ "Now contains 32 random daily cats!",
+ ""]
+ def get_command_signature(self, command):
+ return f'``{self.clean_prefix}{command.qualified_name} {command.signature}``'
+ def get_ending_note(self):
+ return self.messages[randint(0, len(self.messages)-1)]
+
+class General(commands.Cog):
+ def __init__(self, bot):
+ self.bot = bot
+ self._original_help_command = bot.help_command
+ bot.help_command = MyHelpCommand()
+ bot.help_command.cog = self
+
+ def cog_unload(self):
+ self.bot.help_command = self._original_help_command
+
+ @commands.command()
+ async def prefix(self, ctx):
+ """Gets the bot prefixes"""
+ prefixes = bot.get_prefix(self.bot, ctx.message)
+ prefixes.pop(1)
+ prefixes.pop(1)
+ prefixes.pop(1)
+ output = ""
+ for i in prefixes:
+ output += i+", "
+
+ await ctx.send(f"My prefixes are {output}")
+
+ @commands.command()
+ async def userinfo(self, ctx, user: discord.Member=None):
+ """Get information about a user"""
+ #await ctx.send(f"```py\n{dump(user)}```")
+
+ if not user:
+ user = ctx.message.author
+
+ output = ""
+ for i in user.roles:
+ output += i.mention + " "
+
+ if user.color.value == 0:
+ color = 16777210
+ else:
+ color = user.color
+
+ if user.is_avatar_animated():
+ profilePic = user.avatar_url_as(format="gif")
+ else:
+ profilePic = user.avatar_url_as(format="png")
+
+ embed=discord.Embed(title=user.name, description=user.mention, color=color, timestamp=ctx.message.created_at)
+ if user.premium_since:
+ embed.add_field(name="Boosting since", value=user.premium_since.date())
+ #embed.set_thumbnail(url="attachment://temp.webp")
+ embed.set_thumbnail(url=profilePic)
+ embed.add_field(name="Nickname", value=user.display_name, inline=False)
+ embed.add_field(name="Joined on", value=user.joined_at.date(), inline=True)
+ embed.add_field(name="Status", value=user.status, inline=True)
+ embed.add_field(name="Created account on", value=user.created_at.date(), inline=True)
+ embed.add_field(name="Roles", value=output, inline=True)
+ embed.set_footer(text=f"ID: {user.id}")
+ await ctx.send(embed=embed)
+ #os.remove("temp.webp")
+ #os.remove("temp.png")
+
+ @commands.command()
+ async def coop(self, ctx, *, user: discord.Member=None):
+ """Get the coop gang role"""
+ if not user:
+ user = ctx.message.author
+ else:
+ user = self.bot.get_user(int(user))
+
+ coop_role = ctx.guild.get_role(int(self.bot.config[str(ctx.message.guild.id)]["coop_roleID"]))
+
+ if coop_role in user.roles:
+ await user.remove_roles(coop_role)
+ await ctx.send('You have left coop gang')
+ else:
+ await user.add_roles(coop_role)
+ await ctx.send("You are now in the coop gang")
+
+ @commands.command()
+ async def serverinfo(self, ctx, guild=None):
+ """Get information about the server you are in"""
+ if not guild:
+ guild = ctx.message.guild
+ else:
+ print(type(guild))
+ guild = self.bot.get_guild(int(guild))
+
+ if guild.owner.color.value == 0:
+ color = 16777210
+ else:
+ color = guild.owner.color
+
+ emojiList = " "
+ for i in guild.emojis:
+ emojiList += str(i) + " "
+
+ if guild.is_icon_animated():
+ serverIcon = guild.icon_url_as(format="gif")
+ else:
+ serverIcon = guild.icon_url_as(format="png")
+
+ inactiveMembers = await guild.estimate_pruned_members(days=7)
+
+ embed=discord.Embed(title=guild.name, description=guild.description, color=color, timestamp=ctx.message.created_at)
+ if guild.premium_subscription_count == 0:
+ pass
+ else:
+ if guild.premium_subscription_count == 1:
+ embed.add_field(name="Boosted by:", value=f"{guild.premium_subscription_count} member", inline=True)
+ else:
+ embed.add_field(name="Boosted by:", value=f"{guild.premium_subscription_count} members", inline=True)
+ if guild.premium_subscribers:
+ boosters = ""
+ for i in guild.premium_subscribers:
+ boosters += i.mention+" "
+ embed.add_field(name="Boosted by:", value=boosters, inline=True)
+ embed.set_thumbnail(url=serverIcon)
+ embed.set_image(url=guild.splash_url_as(format="png"))
+ embed.add_field(name="Created on", value=guild.created_at.date(), inline=True)
+ embed.add_field(name="Members", value=guild.member_count, inline=True)
+ embed.add_field(name="Emojis", value=emojiList, inline=True)
+ embed.add_field(name="Owner", value=guild.owner.mention, inline=True)
+ embed.add_field(name="Members who haven't spoken in 7 days:", value=inactiveMembers, inline=True)
+ embed.set_footer(text=f"ID: {guild.id}")
+ await ctx.send(embed=embed)
+
+ @commands.command()
+ async def xboxuser(self, ctx, *, gamertag=None):
+ """Get information about an xbox live user"""
+ if not gamertag:
+ await ctx.send("You need to specify a gamer, gamer")
+ return
+
+ async with self.bot.session.get(f'https://xbl-api.prouser123.me/profile/gamertag/{gamertag}') as r:
+ gamer = json.loads(await r.text())
+
+ try:
+ await ctx.send(f"{gamer['error']}: {gamer['message']}")
+ return
+ except KeyError:
+ pass
+
+ for i in gamer["profileUsers"][0]["settings"]:
+ if i["id"] == "GameDisplayName":
+ gameName = i["value"]
+ continue
+ if i["id"] == "AppDisplayPicRaw":
+ picUrl = i["value"]
+ continue
+ if i["id"] == "Gamerscore":
+ Gamerscore = i["value"]+"<:gamerscore:727131234534424586>"
+ continue
+ if i["id"] == "AccountTier":
+ accountTier = i["value"]
+ continue
+ if i["id"] == "XboxOneRep":
+ reputation = i["value"]
+ continue
+ if i["id"] == "PreferredColor":
+ color = int(json.loads(requests.get(i["value"]).text)["primaryColor"], 16)
+ continue
+ if i["id"] == "Location":
+ location = i["value"]
+ continue
+ if i["id"] == "Bio":
+ #if len(i["value"]) == 0:
+ # Bio = "Unknown"
+ #else:
+ Bio = i["value"]
+ continue
+ if i["id"] == "Watermarks":
+ Watermarks = i["value"]
+ continue
+ if i["id"] == "RealName":
+ RealName = i["value"]
+ continue
+
+
+ embed=discord.Embed(title=gameName, description=Bio, color=color, timestamp=ctx.message.created_at)
+ embed.set_thumbnail(url=picUrl)
+ embed.add_field(name="Gamerscore", value=Gamerscore, inline=True)
+ if len(location) != 0:
+ embed.add_field(name="Location", value=location, inline=True)
+ if len(Watermarks) != 0:
+ embed.add_field(name="Watermarks", value=Watermarks, inline=True)
+ embed.add_field(name="Account Tier", value=accountTier, inline=True)
+ embed.add_field(name="Reputation", value=reputation, inline=True)
+ await ctx.send(embed=embed)
+
+ @commands.command(hidden=True)
+ async def xboxpresence(self, ctx, *, gamertag=None):
+ if not gamertag:
+ await ctx.send("You need to specify a gamer, gamer")
+ return
+
+ async with self.bot.session.get(f"https://xbl-api.prouser123.me/presence/gamertag/{gamertag}") as r:
+ gamer = json.loads(await r.text())
+
+ try:
+ await ctx.send(f"{gamer['error']}: {gamer['message']}")
+ return
+ except KeyError:
+ pass
+
+ state = gamer["state"]
+
+ try:
+ game = json.loads(requests.get(f"https://xbl-api.prouser123.me/titleinfo/{gamer['lastSeen']['titleId']}").text)
+ gameName = game["titles"][0]["name"]
+ gamePic = game["titles"][0]["images"][4]["url"]
+ timestamp = dateutil.parser.isoparse(gamer["lastSeen"]["timestamp"])
+ lastSeen = True
+ except Exception as e:
+ print(e)
+ lastSeen = False
+
+ if lastSeen:
+ embed=discord.Embed(title=gamer["gamertag"], description=state, timestamp=timestamp)
+ embed.set_thumbnail(url=gamePic)
+ embed.add_field(name="Game", value=gameName, inline=True)
+ await ctx.send(embed=embed)
+ else:
+ embed=discord.Embed(title=gamer["gamertag"], description=state, timestamp=ctx.message.created_at)
+ await ctx.send(embed=embed)
+
+ @commands.command()
+ async def compile(self, ctx, language=None, *, code=None):
+ """Compile code from a variety of programming languages, powered by <https://wandbox.org/>"""
+ """
+ listRequest = requests.get("https://wandbox.org/api/list.json")
+ compilerList = json.loads(listRequest.text)
+
+ for i in compilerList:
+ if i["language"] == language:
+ compiler = i["name"]
+ print(compiler)
+ """
+ compilers = {
+ "bash": "bash",
+ "c":"gcc-head-c",
+ "c#":"dotnetcore-head",
+ "coffeescript": "coffeescript-head",
+ "cpp": "gcc-head",
+ "elixir": "elixir-head",
+ "go": "go-head",
+ "java": "openjdk-head",
+ "javascript":"nodejs-head",
+ "lua": "lua-5.3.4",
+ "perl": "perl-head",
+ "php": "php-head",
+ "python":"cpython-3.8.0",
+ "ruby": "ruby-head",
+ "rust": "rust-head",
+ "sql": "sqlite-head",
+ "swift": "swift-5.0.1",
+ "typescript":"typescript-3.5.1",
+ "vim-script": "vim-head"
+ }
+ if not language:
+ await ctx.send(f"```json\n{json.dumps(compilers, indent=4)}```")
+ if not code:
+ await ctx.send("No code found")
+ return
+ try:
+ compiler = compilers[language.lower()]
+ except KeyError:
+ await ctx.send("Language not found")
+ return
+ body = {
+ "compiler": compiler,
+ "code": code,
+ "save": True
+ }
+ head = {
+ "Content-Type":"application/json"
+ }
+ async with ctx.typing():
+ async with self.bot.session.post("https://wandbox.org/api/compile.json", headers=head, data=json.dumps(body)) as r:
+ #r = requests.post("https://wandbox.org/api/compile.json", headers=head, data=json.dumps(body))
+ try:
+ response = json.loads(await r.text())
+ #await ctx.send(f"```json\n{json.dumps(response, indent=4)}```")
+ print(f"```json\n{json.dumps(response, indent=4)}```")
+ except json.decoder.JSONDecodeError:
+ await ctx.send(f"```json\n{r.text}```")
+
+ try:
+ embed=discord.Embed(title="Compiled code")
+ embed.add_field(name="Output", value=f'```{response["program_message"]}```', inline=False)
+ embed.add_field(name="Exit code", value=response["status"], inline=True)
+ embed.add_field(name="Link", value=f"[Permalink]({response['url']})", inline=True)
+ await ctx.send(embed=embed)
+ except KeyError:
+ await ctx.send(f"```json\n{json.dumps(response, indent=4)}```")
+
+def setup(bot):
+ bot.add_cog(General(bot))
diff --git a/cogs/help.py b/cogs/help.py deleted file mode 100644 index 7fb1b9f..0000000 --- a/cogs/help.py +++ /dev/null @@ -1,49 +0,0 @@ -from discord.ext import commands -import discord -import asyncio -import json - -class Help(commands.Cog): - def __init__(self, bot): - self.bot = bot - - @commands.command(aliases=["commands"]) - async def help(self, ctx): - """Show this message.""" - embed = discord.Embed( - title = "Help", - description = f"*` Bot prefixes are '>' and '$>' `*", - colour = discord.Colour.green() - ) - - cmds = list(self.bot.commands) - for cmd in cmds: - if cmd.hidden is True: - continue - if cmd.help is None: - _desc="No description." - else: - _desc=f"{cmd.help}" - _cmd = " | ".join([str(cmd),*cmd.aliases]) - embed.add_field(name=f"{_cmd}", value=f"{_desc}", inline=False) - - await ctx.send(embed=embed) - - @commands.command(aliases=['customcommands', 'ccmds']) - async def listcommands(self, ctx): - """List all custom commands""" - embed = discord.Embed( - title = "Help", - colour = discord.Colour.gold() - ) - with open('custom_commands.json', 'r') as f: - commands = json.load(f) - ccmds = ", ".join([*commands]) - # await ctx.send(f"```List of custom commands: \n{ccmds}```") - # output += f'{ccmds}```' - embed.add_field(name="Custom Commands", value=f"{ccmds}", inline=False) - await ctx.send(embed=embed) - -def setup(bot): - bot.add_cog(Help(bot)) - diff --git a/cogs/logs.py b/cogs/logs.py new file mode 100755 index 0000000..4f6aab1 --- /dev/null +++ b/cogs/logs.py @@ -0,0 +1,61 @@ +from discord.ext import commands +import discord + + +class Logs(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.Cog.listener() + async def on_message_delete(self, message): + if (message.id in self.bot.messageBlacklist): + self.bot.messageBlacklist.remove(message.id) + return + if message.guild.id != 574267523869179904: + return + if message.author.color.value == 0: + color = 16777210 + else: + color = message.author.color + channel = self.bot.get_channel(int(self.bot.config[str(message.guild.id)]["logs_channel"])) + embed = discord.Embed( + title='**Deleted Message**', + color=color, + timestamp=message.created_at + ) + embed.add_field( + name='**User**', value=message.author.mention, inline=True) + embed.add_field( + name='**Channel**', value=message.channel.mention, inline=True) + embed.add_field(name='**Message**', value=message.content, inline=False) + await channel.send(embed=embed) + + @commands.Cog.listener() + async def on_message_edit(self, before, after): + if before.content == after.content: + return + + if after.guild.id != 574267523869179904: + return + channel = self.bot.get_channel(int(self.bot.config[str(before.guild.id)]["logs_channel"])) + if before.author.color.value == 0: + color = 16777210 + else: + color = before.author.color + embed = discord.Embed( + title='**Edited Message**', + color=color, + timestamp=after.edited_at + ) + embed.add_field( + name='**User**', value=before.author.mention, inline=True) + embed.add_field( + name='**Channel**', value=before.channel.mention, inline=True) + embed.add_field(name='**Original Message**', + value=before.content, inline=False) + embed.add_field(name='**New Message**', value=after.content, inline=False) + await channel.send(embed=embed) + + +def setup(bot): + bot.add_cog(Logs(bot)) diff --git a/cogs/moderator.py b/cogs/moderator.py deleted file mode 100644 index f9ea9c9..0000000 --- a/cogs/moderator.py +++ /dev/null @@ -1,138 +0,0 @@ -from discord.ext import commands -import discord -import asyncio - -class Admin(commands.Cog): - def __init__(self, bot): - self.bot = bot - - @commands.command(aliases=['quit'], hidden=True) - @commands.has_any_role("Zi") - async def force_close(self, ctx): - await ctx.send("Self Destructing!") - await ctx.bot.close() - - @commands.command(hidden=True) - @commands.has_any_role("Server Moderator","Zi") - async def unload(self, ctx, ext): - await ctx.send(f"Unloading {ext}...") - try: - self.bot.unload_extension(f'cogs.{ext}') - await ctx.send(f"{ext} has been unloaded.") - except commands.ExtensionNotFound: - await ctx.send(f"{ext} doesn't exist!") - except commands.ExtensionNotLoaded: - await ctx.send(f"{ext} is not loaded!") - except commands.ExtensionFailed: - await ctx.send(f"{ext} failed to unload!") - - @commands.command(hidden=True) - @commands.has_any_role("Server Moderator","Zi") - async def reload(self, ctx, ext: str=None): - await ctx.send(f"Reloading {ext}...") - try: - self.bot.reload_extension(f'cogs.{ext}') - await ctx.send(f"{ext} has been reloaded.") - except commands.ExtensionNotFound: - await ctx.send(f"{ext} doesn't exist!") - except commands.ExtensionNotLoaded: - await ctx.send(f"{ext} is not loaded!") - except commands.ExtensionFailed: - await ctx.send(f"{ext} failed to reload!") - - @commands.command(hidden=True) - @commands.has_any_role("Server Moderator","Zi") - async def load(self, ctx, ext): - await ctx.send(f"Loading {ext}...") - try: - self.bot.load_extension(f"cogs.{ext}") - await ctx.send(f"{ext} has been loaded.") - except commands.ExtensionNotFound: - await ctx.send(f"{ext} doesn't exist!") - except commands.ExtensionFailed: - await ctx.send(f"{ext} failed to load!") - - @commands.command(aliases=['cc'], hidden=True) - @commands.has_any_role("Server Moderator","Zi") - async def clearchat(self, ctx, numb: int=100): - deleted_msg = await ctx.message.channel.purge(limit=int(numb)+1, check=None, before=None, after=None, around=None, oldest_first=False, bulk=True) - - msg_num = max(len(deleted_msg) - 1, 0) - - if msg_num == 0: - resp = "Deleted `0 message` 🙄 " - # resp = "Deleted `0 message` 🙄 \n (I can't delete messages "\ - # "older than 2 weeks due to discord limitations)" - else: - resp = "Deleted `{} message{}` 👌 ".format(msg_num, - "" if msg_num <\ - 2 else "s") - - await ctx.send(resp) - - @commands.command(hidden=True) - @commands.has_any_role("Server Moderator","Zi") - async def mute(self, ctx, member: discord.Member=None, reason: str="No Reason", min_muted: int=0): - if member is None: - await ctx.send("Please specify the member you want to mute.") - return - muted_role = discord.utils.get(member.guild.roles, name="Muted") - if self.bot.user == member: # Just why would you want to mute him? - await ctx.send(f'You\'re not allowed to mute ziBot!') - else: - if muted_role in member.roles: - await ctx.send(f'{member.mention} is already muted.') - else: - await member.add_roles(muted_role) - await ctx.send(f'{member.mention} has been muted by {ctx.author.mention} for {reason}!') - - if min_muted > 0: - await asyncio.sleep(min_muted * 60) - await member.remove_roles(muted_role) - - @commands.command(hidden=True) - @commands.has_any_role("Server Moderator","Zi") - async def unmute(self, ctx, member: discord.Member=None): - if member is None: - await ctx.send("Please specify the member you want to unmute.") - return - muted_role = discord.utils.get(member.guild.roles, name="Muted") - if muted_role in member.roles: - await member.remove_roles(muted_role) - await ctx.send(f'{member.mention} has been unmuted by {ctx.author.mention}.') - else: - await ctx.send(f'{member.mention} is not muted.') - - @commands.command(hidden=True) - @commands.has_any_role("Server Moderator","Zi") - async def kick(self, ctx, member: discord.Member=None, reason: str="No Reason"): - if member is None: - await ctx.send("Please specify the member you want to kick.") - return - if self.bot.user == member: # Just why would you want to mute him? - await ctx.send(f'You\'re not allowed to kick ziBot!') - else: - await member.send(f'You have been kicked from {ctx.guild.name} for {reason}!') - await ctx.guild.kick(member, reason=reason) - await ctx.send(f'{member.mention} has been kicked by {ctx.author.mention} for {reason}!') - - @commands.command(hidden=True) - @commands.has_any_role("Server Moderator","Zi") - async def ban(self, ctx, member: discord.Member=None, reason: str="No Reason", min_ban: int=0): - if member is None: - await ctx.send("Please specify the member you want to ban.") - return - if self.bot.user == member: # Just why would you want to mute him? - await ctx.send(f'You\'re not allowed to ban ziBot!') - else: - await member.send(f'You have been banned from {ctx.guild.name} for {reason}!') - await ctx.guild.ban(member, reason=reason) - await ctx.send(f'{member.mention} has been banned by {ctx.author.mention} for {reason}!') - - if min_ban > 0: - await asyncio.sleep(min_ban * 60) - await ctx.guild.unban(member, reason="timed out") - # TODO: Make unban command? - -def setup(bot): - bot.add_cog(Admin(bot)) diff --git a/cogs/player.py b/cogs/player.py new file mode 100644 index 0000000..8a0eb97 --- /dev/null +++ b/cogs/player.py @@ -0,0 +1,444 @@ +""" +Please understand Music bots are complex, and that even this basic example can be daunting to a beginner. + +For this reason it's highly advised you familiarize yourself with discord.py, python and asyncio, BEFORE +you attempt to write a music bot. + +This example makes use of: Python 3.6 + +For a more basic voice example please read: + https://github.com/Rapptz/discord.py/blob/rewrite/examples/basic_voice.py + +This is a very basic playlist example, which allows per guild playback of unique queues. +The commands implement very basic logic for basic usage. But allow for expansion. It would be advisable to implement +your own permissions and usage logic for commands. + +e.g You might like to implement a vote before skipping the song or only allow admins to stop the player. + +Music bots require lots of work, and tuning. Goodluck. +If you find any bugs feel free to ping me on discord. @Eviee#0666 +""" +import discord +from discord.ext import commands + +import asyncio +import itertools +import sys +import traceback +from async_timeout import timeout +from functools import partial +from youtube_dl import YoutubeDL +from pathlib import Path +from discord.ext import tasks + + +ytdlopts = { + 'format': 'bestaudio/best', + 'outtmpl': 'downloads/%(extractor)s-%(id)s-%(title)s.%(ext)s', + 'restrictfilenames': True, + 'noplaylist': True, + 'nocheckcertificate': True, + 'ignoreerrors': False, + 'logtostderr': False, + 'quiet': True, + 'no_warnings': True, + 'default_search': 'auto', + 'source_address': '0.0.0.0' # ipv6 addresses cause issues sometimes +} + +ffmpegopts = { + 'before_options': '-nostdin', + 'options': '-vn -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5' +} + +ytdl = YoutubeDL(ytdlopts) + + +class VoiceConnectionError(commands.CommandError): + """Custom Exception class for connection errors.""" + + +class InvalidVoiceChannel(VoiceConnectionError): + """Exception for cases of invalid Voice Channels.""" + + +class YTDLSource(discord.PCMVolumeTransformer): + + def __init__(self, source, *, data, requester): + super().__init__(source) + self.requester = requester + + self.title = data.get('title') + self.web_url = data.get('webpage_url') + + # YTDL info dicts (data) have other useful information you might want + # https://github.com/rg3/youtube-dl/blob/master/README.md + + def __getitem__(self, item: str): + """Allows us to access attributes similar to a dict. + + This is only useful when you are NOT downloading. + """ + return self.__getattribute__(item) + + @classmethod + async def create_source(cls, ctx, search: str, *, loop, download=True): + loop = loop or asyncio.get_event_loop() + + to_run = partial(ytdl.extract_info, url=search, download=download) + data = await loop.run_in_executor(None, to_run) + + if 'entries' in data: + # take first item from a playlist + data = data['entries'][0] + + await ctx.send(f'```ini\n[Added {data["title"]} to the Queue.]\n```', delete_after=15) + + if download: + source = ytdl.prepare_filename(data) + else: + return {'webpage_url': data['webpage_url'], 'requester': ctx.author, 'title': data['title']} + + return cls(discord.FFmpegPCMAudio(source, **ffmpegopts), data=data, requester=ctx.author) + + @classmethod + async def regather_stream(cls, data, *, loop): + """Used for preparing a stream, instead of downloading. + + Since Youtube Streaming links expire.""" + loop = loop or asyncio.get_event_loop() + requester = data['requester'] + + to_run = partial(ytdl.extract_info, url=data['webpage_url']) + data = await loop.run_in_executor(None, to_run) + + return cls(discord.FFmpegPCMAudio(data['url'], **ffmpegopts), data=data, requester=requester) + + +class MusicPlayer: + """A class which is assigned to each guild using the bot for Music. + + This class implements a queue and loop, which allows for different guilds to listen to different playlists + simultaneously. + + When the bot disconnects from the Voice it's instance will be destroyed. + """ + + __slots__ = ('bot', '_guild', '_channel', '_cog', 'queue', 'next', 'current', 'np', 'volume') + + def __init__(self, ctx): + self.bot = ctx.bot + self._guild = ctx.guild + self._channel = ctx.channel + self._cog = ctx.cog + + self.queue = asyncio.Queue() + self.next = asyncio.Event() + + self.np = None # Now playing message + self.volume = .5 + self.current = None + + ctx.bot.loop.create_task(self.player_loop()) + + async def player_loop(self): + """Our main player loop.""" + await self.bot.wait_until_ready() + + while not self.bot.is_closed(): + self.next.clear() + + try: + # Wait for the next song. If we timeout cancel the player and disconnect... + async with timeout(300): # 5 minutes... + source = await self.queue.get() + except asyncio.TimeoutError: + if self in self._cog.players.values(): + return self.destroy(self._guild) + return + + if not isinstance(source, YTDLSource): + # Source was probably a stream (not downloaded) + # So we should regather to prevent stream expiration + try: + source = await YTDLSource.regather_stream(source, loop=self.bot.loop) + except Exception as e: + await self._channel.send(f'There was an error processing your song.\n' + f'```css\n[{e}]\n```') + continue + + source.volume = self.volume + self.current = source + + self._guild.voice_client.play(source, after=lambda _: self.bot.loop.call_soon_threadsafe(self.next.set)) + self.np = await self._channel.send(f'**Now Playing:** `{source.title}` requested by ' + f'`{source.requester}`') + await self.next.wait() + + # Make sure the FFmpeg process is cleaned up. + source.cleanup() + self.current = None + + try: + # We are no longer playing this song... + await self.np.delete() + except discord.HTTPException: + pass + + def destroy(self, guild): + """Disconnect and cleanup the player.""" + return self.bot.loop.create_task(self._cog.cleanup(guild)) + + +class Player(commands.Cog): + """Music related commands.""" + + __slots__ = ('bot', 'players') + + def __init__(self, bot): + self.bot = bot + #self.removedownloads.start() + self.players = {} + + async def cleanup(self, guild): + try: + await guild.voice_client.disconnect() + except AttributeError: + pass + + try: + for entry in self.players[guild.id].queue._queue: + if isinstance(entry, YTDLSource): + entry.cleanup() + self.players[guild.id].queue._queue.clear() + except KeyError: + pass + + try: + del self.players[guild.id] + except KeyError: + pass + + async def __local_check(self, ctx): + """A local check which applies to all commands in this cog.""" + if not ctx.guild: + raise commands.NoPrivateMessage + return True + + async def __error(self, ctx, error): + """A local error handler for all errors arising from commands in this cog.""" + if isinstance(error, commands.NoPrivateMessage): + try: + return await ctx.send('This command can not be used in Private Messages.') + except discord.HTTPException: + pass + elif isinstance(error, InvalidVoiceChannel): + await ctx.send('Error connecting to Voice Channel. ' + 'Please make sure you are in a valid channel or provide me with one') + + print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr) + traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr) + + def get_player(self, ctx): + """Retrieve the guild player, or generate one.""" + try: + player = self.players[ctx.guild.id] + except KeyError: + player = MusicPlayer(ctx) + self.players[ctx.guild.id] = player + + return player + + @commands.command(name='connect', aliases=['join']) + async def connect_(self, ctx, *, channel: discord.VoiceChannel=None): + """Connect to voice. + + Parameters + ------------ + channel: discord.VoiceChannel [Optional] + The channel to connect to. If a channel is not specified, an attempt to join the voice channel you are in + will be made. + + This command also handles moving the bot to different channels. + """ + if not channel: + try: + channel = ctx.author.voice.channel + except AttributeError: + raise InvalidVoiceChannel('No channel to join. Please either specify a valid channel or join one.') + + vc = ctx.voice_client + + if vc: + if vc.channel.id == channel.id: + return + try: + await vc.move_to(channel) + except asyncio.TimeoutError: + raise VoiceConnectionError(f'Moving to channel: <{channel}> timed out.') + else: + try: + await channel.connect() + except asyncio.TimeoutError: + raise VoiceConnectionError(f'Connecting to channel: <{channel}> timed out.') + + await ctx.send(f'Connected to: **{channel}**', delete_after=20) + + @commands.command(name='play', aliases=['sing']) + async def play_(self, ctx, *, search: str): + """Request a song and add it to the queue. + + This command attempts to join a valid voice channel if the bot is not already in one. + Uses YTDL to automatically search and retrieve a song. + + Parameters + ------------ + search: str [Required] + The song to search and retrieve using YTDL. This could be a simple search, an ID or URL. + """ + await ctx.trigger_typing() + + vc = ctx.voice_client + + if not vc: + await ctx.invoke(self.connect_) + + player = self.get_player(ctx) + + # If download is False, source will be a dict which will be used later to regather the stream. + # If download is True, source will be a discord.FFmpegPCMAudio with a VolumeTransformer. + source = await YTDLSource.create_source(ctx, search, loop=self.bot.loop) + + await player.queue.put(source) + + @commands.command(name='pause') + async def pause_(self, ctx): + """Pause the currently playing song.""" + vc = ctx.voice_client + + if not vc or not vc.is_playing(): + return await ctx.send('I am not currently playing anything!', delete_after=20) + elif vc.is_paused(): + return + + vc.pause() + await ctx.send(f'**`{ctx.author}`**: Paused the song!') + + @commands.command(name='resume') + async def resume_(self, ctx): + """Resume the currently paused song.""" + vc = ctx.voice_client + + if not vc or not vc.is_connected(): + return await ctx.send('I am not currently playing anything!', delete_after=20) + elif not vc.is_paused(): + return + + vc.resume() + await ctx.send(f'**`{ctx.author}`**: Resumed the song!') + + @commands.command(name='skip') + async def skip_(self, ctx): + """Skip the song.""" + vc = ctx.voice_client + + if not vc or not vc.is_connected(): + return await ctx.send('I am not currently playing anything!', delete_after=20) + + if vc.is_paused(): + pass + elif not vc.is_playing(): + return + + vc.stop() + await ctx.send(f'**`{ctx.author}`**: Skipped the song!') + + @commands.command(name='queue', aliases=['q', 'playlist']) + async def queue_info(self, ctx): + """Retrieve a basic queue of upcoming songs.""" + vc = ctx.voice_client + + if not vc or not vc.is_connected(): + return await ctx.send('I am not currently connected to voice!', delete_after=20) + + player = self.get_player(ctx) + if player.queue.empty(): + return await ctx.send('There are currently no more queued songs.') + + # Grab up to 5 entries from the queue... + upcoming = list(itertools.islice(player.queue._queue, 0, 5)) + + fmt = '\n'.join(f'**`{_["title"]}`**' for _ in upcoming) + embed = discord.Embed(title=f'Upcoming - Next {len(upcoming)}', description=fmt) + + await ctx.send(embed=embed) + + @commands.command(name='now_playing', aliases=['np', 'current', 'currentsong', 'playing']) + async def now_playing_(self, ctx): + """Display information about the currently playing song.""" + vc = ctx.voice_client + + if not vc or not vc.is_connected(): + return await ctx.send('I am not currently connected to voice!', delete_after=20) + + player = self.get_player(ctx) + if not player.current: + return await ctx.send('I am not currently playing anything!') + + try: + # Remove our previous now_playing message. + await player.np.delete() + except discord.HTTPException: + pass + + player.np = await ctx.send(f'**Now Playing:** `{vc.source.title}` ' + f'requested by `{vc.source.requester}`') + + @commands.command(name='volume', aliases=['vol']) + async def change_volume(self, ctx, *, vol: float): + """Change the player volume. + + Parameters + ------------ + volume: float or int [Required] + The volume to set the player to in percentage. This must be between 1 and 100. + """ + vc = ctx.voice_client + + if not vc or not vc.is_connected(): + return await ctx.send('I am not currently connected to voice!', delete_after=20) + + if not 0 < vol < 101: + return await ctx.send('Please enter a value between 1 and 100.') + + player = self.get_player(ctx) + + if vc.source: + vc.source.volume = vol / 100 + + player.volume = vol / 100 + await ctx.send(f'**`{ctx.author}`**: Set the volume to **{vol}%**') + + @commands.command(name='stop') + async def stop_(self, ctx): + """Stop the currently playing song and destroy the player. + + !Warning! + This will destroy the player assigned to your guild, also deleting any queued songs and settings. + """ + vc = ctx.voice_client + + if not vc or not vc.is_connected(): + return await ctx.send('I am not currently playing anything!', delete_after=20) + + await self.cleanup(ctx.guild) + + @tasks.loop(hours=10.0) + async def removedownloads(self): + for p in Path("./downloads/").glob("*"): + p.unlink() + + +def setup(bot): + bot.add_cog(Player(bot)) + diff --git a/cogs/src.py b/cogs/src.py new file mode 100755 index 0000000..e15c00d --- /dev/null +++ b/cogs/src.py @@ -0,0 +1,293 @@ +from discord.ext import commands +from discord.ext import tasks +from discord.utils import get +from datetime import timedelta +import discord +import requests +import json +import asyncio +import dateutil.parser +from pathlib import Path + + +async def rejectRun(self, apiKey, ctx, run, reason): + await ctx.message.delete() + run = run.split('/')[-1] + reject = {"status": {"status": "rejected", "reason": reason}} + r = requests.put(f"https://www.speedrun.com/api/v1/runs/{run}/status", + headers={ + "X-API-Key": apiKey, + "Accept": "application/json", + "User-Agent": "mcbeDiscordBot/1.0" + }, + data=json.dumps(reject)) + if r.status_code == 200 or r.status_code == 204: + await ctx.send(f'Run rejected succesfully for `{reason}`') + else: + await ctx.send("Something went wrong") + await ctx.message.author.send( + f"```json\n{json.dumps(json.loads(r.text),indent=4)}```") + + +async def approveRun(self, apiKey, ctx, run, reason=None): + await ctx.message.delete() + run = run.split('/')[-1] + if reason == None: + approve = {"status": {"status": "verified"}} + else: + approve = {"status": {"status": "verified", "reason": reason}} + r = requests.put(f"https://www.speedrun.com/api/v1/runs/{run}/status", + headers={ + "X-API-Key": apiKey, + "Accept": "application/json", + "User-Agent": "mcbeDiscordBot/1.0" + }, + data=json.dumps(approve)) + if r.status_code == 200 or r.status_code == 204: + await ctx.send("Run approved succesfully") + else: + await ctx.send("Something went wrong") + await ctx.message.author.send( + f"```json\n{json.dumps(json.loads(r.text),indent=4)}```") + + +async def deleteRun(self, apiKey, ctx, run): + await ctx.message.delete() + run = run.split('/')[-1] + r = requests.delete(f"https://www.speedrun.com/api/v1/runs/{run}", + headers={ + "X-API-Key": apiKey, + "Accept": "application/json", + "User-Agent": "mcbeDiscordBot/1.0" + }) + if r.status_code == 200 or r.status_code == 204: + await ctx.send("Run deleted succesfully") + else: + await ctx.send("Something went wrong") + await ctx.message.author.send( + f"```json\n{json.dumps(json.loads(r.text),indent=4)}```") + + +async def pendingRuns(self, ctx): + mcbe_runs = 0 + mcbeil_runs = 0 + mcbece_runs = 0 + runs_to_reject = [] + head = {"Accept": "application/json", "User-Agent": "mcbeDiscordBot/1.0"} + gameID = 'yd4ovvg1' # ID of Minecraft bedrock + gameID2 = 'v1po7r76' # ID of Category extension + runsRequest = requests.get( + f'https://www.speedrun.com/api/v1/runs?game={gameID}&status=new&max=200&embed=category,players,level&orderby=submitted', + headers=head) + runs = json.loads(runsRequest.text) + runsRequest2 = requests.get( + f'https://www.speedrun.com/api/v1/runs?game={gameID2}&status=new&max=200&embed=category,players,level&orderby=submitted', + headers=head) + runs2 = json.loads(runsRequest2.text) + # Use https://www.speedrun.com/api/v1/games?abbreviation=mcbe for ID + + for game in range(2): + for i in range(200): + leaderboard = '' # A little ugly but prevents name not defined error + level = False + try: + for key, value in runs['data'][i].items(): + if key == 'id': + run_id = value + if key == 'weblink': + link = value + if key == 'level': + if value["data"]: + level = True + categoryName = value["data"]["name"] + if key == 'category' and not level: + categoryName = value["data"]["name"] + if key == 'videos': + if value['links'][0]['uri'] in self.bot.runs_blacklist[ + "videos"]: + await rejectRun( + self, self.bot.config['api_key'], ctx, run_id, + 'Detected as spam by our automatic filter') + if key == 'players': + if value["data"][0]["names"][ + "international"] in self.bot.runs_blacklist[ + "players"]: + runs_to_reject.append([run_id, value['data'][0]['names']['international']]) + elif value["data"][0]['rel'] == 'guest': + player = value["data"][0]['name'] + else: + player = value["data"][0]["names"]["international"] + if key == 'times': + rta = timedelta(seconds=value['realtime_t']) + if key == 'submitted': + timestamp = dateutil.parser.isoparse(value) + except Exception as e: + print(e.args) + break + if game == 0: + if level == True: + mcbeil_runs += 1 + leaderboard = 'Individual Level Run' + else: + mcbe_runs += 1 + leaderboard = "Full Game Run" # If this doesn't work I'm starting a genocide + elif game == 1: + leaderboard = "Category Extension Run" + mcbece_runs += 1 + embed = discord.Embed( + title=leaderboard, + url=link, + description= + f"{categoryName} in `{str(rta).replace('000','')}` by **{player}**", + color=16711680 + i * 60, + timestamp=timestamp) + await self.bot.get_channel( + int(self.bot.config[str( + ctx.message.guild.id)]["pending_channel"]) + ).send(embed=embed) + runs = runs2 + gameID = gameID2 + embed_stats = discord.Embed( + title='Pending Run Stats', + description= + f"Full Game Runs: {mcbe_runs}\nIndividual Level Runs: {mcbeil_runs}\nCategory Extension Runs: {mcbece_runs}", + color=16711680 + i * 60) + await self.bot.get_channel( + int(self.bot.config[str(ctx.message.guild.id)]["pending_channel"]) + ).send(embed=embed_stats) + + for run in runs_to_reject: + await rejectRun(self, self.bot.config['api_key'], ctx, run[0], f'Detected as a banned player ({run[1]}) by our automatic filter') + + +async def verifyNew(self, apiKey=None, userID=None): + if apiKey == None: + head = { + "Accept": "application/json", + "User-Agent": "mcbeDiscordBot/1.0" + } + else: + head = { + "X-API-Key": apiKey, + "Accept": "application/json", + "User-Agent": "mcbeDiscordBot/1.0" + } + server = self.bot.get_guild(574267523869179904) + # Troll is mentally challenged I guess ¯\_(ツ)_/¯ + RunneRole = server.get_role(574268937454223361) + WrRole = server.get_role(583622436378116107) + #if userID == None: + # return + #else: + user = self.bot.get_user(int(userID)) + data = json.loads(Path('./api_keys.json').read_text()) + + if str(user.id) in data: + pbs = requests.get( + f"https://www.speedrun.com/api/v1/users/{data[str(user.id)]}/personal-bests", + headers=head) + pbs = json.loads(pbs.text) + else: + r = requests.get('https://www.speedrun.com/api/v1/profile', + headers=head) + # print(r.text) + if r.status_code >= 400: + await user.send(f"```json\n{r.text}```") + return + try: + profile = json.loads(r.text) + except json.decoder.JSONDecodeError: + return + srcUserID = profile["data"]["id"] + with open('api_keys.json', 'w') as file: + data[user.id] = srcUserID + json.dump(data, file, indent=4) + pbs = requests.get(profile["data"]["links"][3]["uri"], headers=head) + pbs = json.loads(pbs.text) + + wrCounter = False + runnerCounter = False + + for i in pbs["data"]: + if i["place"] == 1: + if i["run"]["game"] == "yd4ovvg1" or i["run"]["game"] == "v1po7r76": + if not i["run"]["level"]: + wrCounter = True + if i["run"]["game"] == "yd4ovvg1" or i["run"]["game"] == "v1po7r76": + # I have no shame + runnerCounter = True + + if wrCounter: + await server.get_member(user.id).add_roles(WrRole) + else: + await server.get_member(user.id).remove_roles(WrRole) + if runnerCounter: + await server.get_member(user.id).add_roles(RunneRole) + else: + await server.get_member(user.id).remove_roles(RunneRole) + + +class Src(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.checker.start() + + async def is_mod(ctx): + return ctx.author.guild_permissions.manage_channels + + @commands.command(description="Posts all pending runs to #pending-runs") + @commands.guild_only() + async def pending(self, ctx): + async with ctx.typing(): + await self.bot.get_channel( + int(self.bot.config[str( + ctx.message.guild.id)]["pending_channel"])).purge(limit=500 + ) + await pendingRuns(self, ctx) + + @commands.command(description="Reject runs quickly") + @commands.check(is_mod) + @commands.guild_only() + async def reject(self, ctx, apiKey, run, *, reason="Rejected using Steve. No additional reason provided"): + if apiKey == None: + apiKey = self.bot.config['api_key'] + await rejectRun(self, apiKey, ctx, run, reason) + + @commands.command(description="Approve runs quickly") + @commands.check(is_mod) + @commands.guild_only() + async def approve(self, ctx, apiKey, run, *, reason="Approved using Steve. No additional reason provided"): + if apiKey == None: + apiKey = self.bot.config['api_key'] + await approveRun(self, apiKey, ctx, run, reason) + + @commands.command(description="Delete runs quickly") + async def delete(self, ctx, apiKey, run): + await deleteRun(self, apiKey, ctx, run) + + @commands.command() + async def verify(self, ctx, apiKey=None, userID=None): + self.bot.messageBlacklist.append(ctx.message.id) + if apiKey == None: + data = json.loads(Path('./api_keys.json').read_text()) + if not str(ctx.author.id) in data: + await ctx.send( + f"Please try again this command by getting an apiKey from https://www.speedrun.com/api/auth then do `{ctx.prefix}verify <apiKey>` in my DMs or anywhere in this server. \nBe careful who you share this key with. To learn more check out https://github.com/speedruncomorg/api/blob/master/authentication.md" + ) + return + if ctx.guild != None: + await ctx.message.delete() + async with ctx.typing(): + if userID == None: + userID = ctx.message.author.id + await verifyNew(self, apiKey, userID) + + @tasks.loop(minutes=10.0) + async def checker(self): + data = json.loads(Path('./api_keys.json').read_text()) + for key, value in data.items(): + await verifyNew(self, None, key) + + +def setup(bot): + bot.add_cog(Src(bot)) diff --git a/cogs/trans.py b/cogs/trans.py new file mode 100755 index 0000000..9ca1004 --- /dev/null +++ b/cogs/trans.py @@ -0,0 +1,51 @@ +from discord.ext import commands +import discord +from google.cloud import translate_v2 as translate +import six +translate_client = translate.Client() + +async def translateMsg(text, target="en"): + # Text can also be a sequence of strings, in which case this method + # will return a sequence of results for each text. + if isinstance(text, six.binary_type): + text = text.decode('utf-8') + result = translate_client.translate( + text, target_language=target) + print(u'Text: {}'.format(result['input'])) + print(u'Translation: {}'.format(result['translatedText'])) + print(u'Detected source language: {}'.format( + result['detectedSourceLanguage'])) + result['translatedText'] = result['translatedText'].replace("<", "<") + result['translatedText'] = result['translatedText'].replace(">", ">") + result['translatedText'] = result['translatedText'].replace("'", "'") + result['translatedText'] = result['translatedText'].replace(""", '"') + result['translatedText'] = result['translatedText'].replace("<@! ", "<@!") + result['translatedText'] = result['translatedText'].replace("<@ ", "<@") + result['translatedText'] = result['translatedText'].replace("<# ", "<#") + return result; + +class Trans(commands.Cog): + + def __init__(self, bot): + self.bot = bot + + @commands.command(help="Translate text in english (using google translate)", brief="Translate to english", aliases=["翻译", "脑热", "动漫"]) + async def translate(self, ctx, *, message): + """Translate to english""" + response = await translateMsg(message) + embed=discord.Embed(title="Translation",description=f"{ctx.message.author.mention} says:", timestamp=ctx.message.created_at, color=0x4d9aff) + embed.add_field(name=f"[{response['detectedSourceLanguage']}] Source:" , value=response['input'], inline=False) + embed.add_field(name="Translation", value=response['translatedText'], inline=True) + await ctx.send(embed=embed) + + @commands.command() + async def trans(self, ctx, lan, *, message): + """Translate to a specific language""" + response = await translateMsg(message, lan) + embed=discord.Embed(title="Translation",description=f"{ctx.message.author.mention} says:", timestamp=ctx.message.created_at, color=0x4d9aff) + embed.add_field(name=f"[{response['detectedSourceLanguage']}] Source:" , value=response['input'], inline=False) + embed.add_field(name="Translation", value=response['translatedText'], inline=True) + await ctx.send(embed=embed) + +def setup(bot): + bot.add_cog(Trans(bot)) diff --git a/cogs/twitter.py b/cogs/twitter.py new file mode 100755 index 0000000..67e792a --- /dev/null +++ b/cogs/twitter.py @@ -0,0 +1,50 @@ +import json +from discord.ext import commands, tasks +import discord +import tweepy +import requests + +class StreamListener(tweepy.StreamListener): + def __init__(self): + with open('./config.json') as f: + self.config = json.load(f) + + def on_error(self, status_code): + if status_code == 420: + print("Rate limit reached. ") + #returning False in on_error disconnects the stream + return False + + def on_data(self, data): + data = json.loads(data) + try: + tweetUser = data['tweet']['user']['screen_name'] + tweetID = data['tweet']['id_str'] + except: + tweetUser = data['user']['screen_name'] + tweetID = data['id_str'] + tweetLink = f'https://twitter.com/{tweetUser}/status/{tweetID}' + body = { + "content": tweetLink + } + global config + r = requests.post(self.config['574267523869179904']['tweetWebhook'], headers={"Content-Type": "application/json"}, data=json.dumps(body))#config['574267523869179904']['tweetWebhook'], data=json.dumps(body)) + print(r.status_code) + print(r.text) + #print(json.dumps(data, indent='\t')) + + +class Twitter(commands.Cog): + def __init__(self, bot): + self.bot = bot + + auth = tweepy.OAuthHandler(self.bot.config['twitter']['consumer_key'], self.bot.config['twitter']['consumer_secret']) + auth.set_access_token(self.bot.config['twitter']['access_token'], self.bot.config['twitter']['access_token_secret']) + + api = tweepy.API(auth) + myStreamListener = StreamListener() + stream = tweepy.Stream(auth = api.auth, listener=myStreamListener) + stream.filter(follow=['1287799985040437254'], is_async=True) + +def setup(bot): + bot.add_cog(Twitter(bot))
\ No newline at end of file diff --git a/cogs/utils.py b/cogs/utils.py index 7a5ffac..1309141 100644..100755 --- a/cogs/utils.py +++ b/cogs/utils.py @@ -1,16 +1,277 @@ from discord.ext import commands +from discord.ext import tasks import discord +import json +import datetime +# forgot to import this and ended up looking mentally unstable +# troll literally pointed out atleast 4 things I did wrong in 3 lines of code +from random import choice +from random import randint +from datetime import timedelta +from selenium import webdriver +from selenium.webdriver.chrome.options import Options +#from PIL.Image import core as Image +#import image as Image +from PIL import Image +from PIL import ImageFilter +import functools import asyncio +def set_viewport_size(driver, width, height): + window_size = driver.execute_script(""" + return [window.outerWidth - window.innerWidth + arguments[0], + window.outerHeight - window.innerHeight + arguments[1]]; + """, width, height) + driver.set_window_size(*window_size) + +async def reportStuff(self, ctx, message): + channel = self.bot.get_channel(int(self.bot.config[str(ctx.message.guild.id)]["report_channel"])) + + embed = discord.Embed( + title=f"Report from {ctx.message.author}", + description=f"{message}", + color=ctx.message.author.color, timestamp=ctx.message.created_at) + + await channel.send(embed=embed) + await ctx.author.send("Report has been submitted") + +def save_leaderboard(): + DRIVER = '/usr/lib/chromium-browser/chromedriver' + chrome_options = Options() + chrome_options.add_argument("--disable-dev-shm-usage") + chrome_options.add_argument("--headless") + chrome_options.add_argument("--no-sandbox") + chrome_options.add_argument("--disable-gpu") + #chrome_options.binary_location = "" + driver = webdriver.Chrome(DRIVER, chrome_options=chrome_options) + set_viewport_size(driver, 1000, 1000) + driver.get('https://aninternettroll.github.io/mcbeVerifierLeaderboard/') + screenshot = driver.find_element_by_id('table').screenshot('leaderboard.png') + driver.quit() + #transparency time + img = Image.open('leaderboard.png') + img = img.convert("RGB") + pallette = Image.open("palette.png") + pallette = pallette.convert("P") + img = img.quantize(colors=256, method=3, kmeans=0, palette=pallette) + img = img.convert("RGBA") + datas = img.getdata() + + newData = [] + for item in datas: + if item[0] == 255 and item[1] == 255 and item[2] == 255: + newData.append((255, 255, 255, 0)) + else: + newData.append(item) + + img.putdata(newData) + """ + img = img.filter(ImageFilter.SHARPEN) + img = img.filter(ImageFilter.SHARPEN) + img = img.filter(ImageFilter.SHARPEN) + """ + #height, width = img.size + #img = img.resize((height*10,width*10), resample=Image.BOX) + img.save("leaderboard.png", "PNG") + class Utils(commands.Cog): - def __init__(self, bot): - self.bot = bot - @commands.command() - async def ping(self, ctx): - """Tell the ping of the bot to the discord servers""" - await ctx.send(f'Pong! {round(self.bot.latency*1000)}ms') + def __init__(self, bot): + self.bot = bot + self.tries = 1 -def setup(bot): - bot.add_cog(Utils(bot)) + @commands.command(description="Pong!", help="Tells the ping of the bot to the discord servers", brief="Tells the ping") + async def ping(self, ctx): + await ctx.send(f'Pong! {round(self.bot.latency*1000)}ms') + + @commands.cooldown(1, 25, commands.BucketType.guild) + @commands.command() + async def findseed(self, ctx): + """Test your luck""" + if ctx.message.channel.id != int(self.bot.config[str(ctx.message.guild.id)]["bot_channel"]): + await ctx.message.delete() + ctx.command.reset_cooldown(ctx) + return + + # Don't ask + rigged_findseed = { + 280428276810383370: 12, # Thomas's User ID + 199070670221475842: -1, # Kai's User ID + 615658069132836865: -12 # What a fraud! They didn't use their real name. + } + + if ctx.author.id in rigged_findseed: + totalEyes = rigged_findseed[ctx.author.id] + else: + totalEyes = 0 + for i in range(12): + randomness = randint(1, 10) + if randomness <= 1: + totalEyes += 1 + await ctx.send(f"{discord.utils.escape_mentions(ctx.message.author.display_name)} -> your seed is a {totalEyes} eye") + + @findseed.error + async def findseed_handler(self,ctx,error): + if isinstance(error, commands.CommandOnCooldown): + if ctx.message.channel.id != int(self.bot.config[str(ctx.message.guild.id)]["bot_channel"]): + ctx.command.reset_cooldown(ctx) + await ctx.message.delete() + return + else: + await ctx.send(error) + #await ctx.send(f"{discord.utils.escape_mentions(ctx.message.author.display_name)}, you have to wait {round(error.retry_after, 7)} seconds before using this again.") + + @commands.command() + async def findsleep(self, ctx): + """Test your sleep""" + if ctx.message.channel.id != int(self.bot.config[str(ctx.message.guild.id)]["bot_channel"]): + await ctx.message.delete() + return + + # DON'T ASK! + lessSleepMsg = [ + "gn, insomniac!", + "counting sheep didn't work? try counting chloroform vials!", + "try a glass of water", + "some decaf coffee might do the trick!" + ] + + moreSleepMsg = [ + "waaakeee uuuppp!", + "are they dead or asleep? I can't tell.", + "wake up, muffin head", + "psst... coffeeee \\:D" + ] + # Optional TODO: Create non-normal distribution + sleepHrs = randint(0, 24) + + # Add extra comment based on number of sleepHrs + if sleepHrs == 0: + await ctx.send(f"{discord.utils.escape_mentions(ctx.message.author.display_name)} -> your sleep is 0 hours long - nice try \:D") + elif sleepHrs <= 5: + if sleepHrs == 1: + s = '' + else: + s = 's' + await ctx.send(f"{discord.utils.escape_mentions(ctx.message.author.display_name)} -> your sleep is {sleepHrs} hour{s} long - {lessSleepMsg[randint(0, len(lessSleepMsg) - 1)]}") + else: + await ctx.send(f"{discord.utils.escape_mentions(ctx.message.author.display_name)} -> your sleep is {sleepHrs} hours long - {moreSleepMsg[randint(0, len(moreSleepMsg) - 1)]}") + + @commands.Cog.listener() + async def on_member_join(self, member): + if member.id == 640933433215811634: + await member.edit(nick="JoetheSheepFucker") + def check(msg): + return msg.author == member and msg.type != discord.MessageType.new_member + try: + msg = await self.bot.wait_for("message", check=check, timeout=300) + await msg.channel.send("<:PeepoPog:732172337956257872>") + except asyncio.TimeoutError: + await msg.channel.send("<:pepesadcrash:739927552260440185>") + + @commands.Cog.listener() + async def on_member_update(self, before, after): + if after.id == 640933433215811634: + await after.edit(nick="JoetheSheepFucker") + + @commands.Cog.listener() + async def on_message(self, message): + if not message.guild: + return + if message.channel.id != int(self.bot.config[str(message.guild.id)]["fair_channel"]): + return + if message.author.bot: + return + badWords = ["fair", "ⓕⓐⓘⓡ"] + count = 0 + + coolKids = [ + ['Cameron', self.bot.get_user(468262902969663488), datetime.date(2020, 10, 8)], + ['Indy', self.bot.get_user(274923326890311691), datetime.date(2020, 9, 10)], + ['Kai', self.bot.get_user(199070670221475842), datetime.date(2020, 11, 20)], + ['Luca', self.bot.get_user(99457716614885376), datetime.date(2020, 11, 5)], + ['Max', self.bot.get_user(543958509243596800), datetime.date(2020, 11, 10)], + ['Mistaken', self.bot.get_user(264121998173536256), datetime.date(2020, 7, 6)], + ['Murray', self.bot.get_user(400344183333847060), datetime.date(2020, 11, 10)], + ['RKREE', self.bot.get_user(395872198323077121), datetime.date(2020, 11, 5)], + # idk if she goes by her irl name but I'm sticking with it for the sake of uniformity + # also idk how to pronounce prakxo + ['Samantha', self.bot.get_user(226312219787264000), datetime.date(2020, 6, 24)], + ['Scott', self.bot.get_user(223937483774230528), datetime.date(2020, 6, 23)], + ['Sky', self.bot.get_user(329538915805691905), datetime.date(2020, 6, 24)], + ['Thomas', self.bot.get_user(280428276810383370), datetime.date(2020, 9, 29)], + ['Samantha', self.bot.get_user(536071288859656193), datetime.date(2020, 8, 23)], + ['Landen', self.bot.get_user(654025117025828885), datetime.date(2020, 8, 25)] + ] + + + for coolKid in coolKids: + if datetime.date.today() == coolKid[2]: + try: + for i in range(self.tries): + await coolKid[1].send(f'Happy Birthday {coolKid[0]}! You\'re a boomer now! :mango:') + self.tries = 1 + except: + self.tries +=1 + + for word in badWords: + if word in message.content.lower().replace(" ", ""): + count += 1; + fair = 'Fair '*count + await message.channel.send(fair) + + @commands.cooldown(1, 60, commands.BucketType.member) + @commands.command() + async def report(self, ctx, *, message=None): + """Send a message to the super mods about anything""" + if ctx.message.guild != None: + await ctx.message.delete() + if message == None: + await ctx.message.author.send("Please type a report to report (hehe, sounds funny)") + else: + await reportStuff(self, ctx, message) + + @commands.cooldown(1, 20, commands.BucketType.guild) + @commands.command() + async def leaderboard(self, ctx): + """Leaderboard of the people that matter""" + async with ctx.typing(): + try: + lbFunc = functools.partial(save_leaderboard) + await self.bot.loop.run_in_executor(None, lbFunc) + await ctx.send(file=discord.File("leaderboard.png")) + except: + await ctx.send("https://aninternettroll.github.io/mcbeVerifierLeaderboard/") + + @leaderboard.error + async def leaderboard_handler(self,ctx,error): + if isinstance(error, commands.CommandOnCooldown): + #return + await ctx.send(f"{discord.utils.escape_mentions(ctx.message.author.display_name)}, you have to wait {round(error.retry_after, 2)} seconds before using this again.") + + @commands.cooldown(1, 60, commands.BucketType.guild) + @commands.command() + async def someone(self, ctx): + """Discord's mistake""" + if ctx.channel.id != int(self.bot.config[str(ctx.message.guild.id)]["fair_channel"]): + await ctx.send(choice(ctx.guild.members).mention) + + @commands.command() + async def roll(self, ctx, pool): + """Roll the dice""" + await ctx.send(f"You rolled a {randint(0, int(pool))}") + + @commands.command(aliases=['commands', 'allcommands']) + async def listcommands(self, ctx): + """List all custom commands""" + with open('custom_commands.json', 'r') as f: + commands = json.load(f) + output = '```List of custom commands:\n' + for key in commands: + output += f'{key}, ' + output += '```' + await ctx.send(output) + +def setup(bot): + bot.add_cog(Utils(bot)) diff --git a/cogs/webserver.py b/cogs/webserver.py new file mode 100755 index 0000000..3061f8f --- /dev/null +++ b/cogs/webserver.py @@ -0,0 +1,57 @@ +from aiohttp import web +from discord.ext import commands, tasks +import discord +import os +import aiohttp +import json + +app = web.Application() +routes = web.RouteTableDef() + + +class Webserver(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.web_server.start() + + @routes.get('/') + async def welcome(request): + return web.Response(text="Hello, world") + + @routes.get('/keys') + async def get_keys(request): + with open('./api_keys.json') as f: + keys = json.load(f) + return web.json_response(keys) + + @routes.post('/keys') + async def post_keys(request): + data = await request.post() + try: + discord_id = data['discord_id'] + src_id = data['src_id'] + except KeyError: + return 400 + with open('./api_keys.json', 'r') as f: + keys = json.load(f) + keys[discord_id] = src_id + with open('./api_keys.json', 'w') as f: + json.dump(keys, f, indent=4) + return web.json_response(keys) + + self.webserver_port = os.environ.get('PORT', 5000) + app.add_routes(routes) + + @tasks.loop() + async def web_server(self): + runner = web.AppRunner(app) + await runner.setup() + site = web.TCPSite(runner, host='0.0.0.0', port=self.webserver_port) + await site.start() + + @web_server.before_loop + async def web_server_before_loop(self): + await self.bot.wait_until_ready() + +def setup(bot): + bot.add_cog(Webserver(bot))
\ No newline at end of file diff --git a/cogs/welcome.py b/cogs/welcome.py deleted file mode 100644 index 09bf723..0000000 --- a/cogs/welcome.py +++ /dev/null @@ -1,25 +0,0 @@ -from discord.ext import commands -import discord -import asyncio -import logging -from random import randint - -class Welcome(commands.Cog): - # Welcome message + set roles when new member joined - def __init__(self, bot): - self.bot = bot - - @commands.Cog.listener() - async def on_member_join(self, member): - server = member.guild - welcome_channel = self.bot.get_channel(740051039499059271) - # TODO: find a way to get channel id if possible - member_role = discord.utils.get(member.guild.roles, name="Member") - welcome_msg=[f"It's a Bird, It's a Plane, It's {member.mention}!", - f"Welcome {member.mention}! <:PogChamp:740102448047194152>", - f"Good to see you, {member.mention}."] - await member.add_roles(member_role) - await welcome_channel.send(f"{welcome_msg[randint(0, len(welcome_msg) - 1)]}") - -def setup(bot): - bot.add_cog(Welcome(bot)) |