diff options
author | ziro <palembani@gmail.com> | 2020-08-06 07:37:19 +0000 |
---|---|---|
committer | ziro <palembani@gmail.com> | 2020-08-06 07:37:19 +0000 |
commit | 13447ea06137e9a2d856283037692837836e9b42 (patch) | |
tree | eab42aa5e4e3615162a7581c8b44f4c962654ea3 | |
parent | f797397950a1757a978962e912edae1a7263d3cb (diff) | |
download | steve-bot-13447ea06137e9a2d856283037692837836e9b42.tar steve-bot-13447ea06137e9a2d856283037692837836e9b42.tar.gz steve-bot-13447ea06137e9a2d856283037692837836e9b42.tar.bz2 steve-bot-13447ea06137e9a2d856283037692837836e9b42.tar.lz steve-bot-13447ea06137e9a2d856283037692837836e9b42.tar.xz steve-bot-13447ea06137e9a2d856283037692837836e9b42.tar.zst steve-bot-13447ea06137e9a2d856283037692837836e9b42.zip |
+ Steve Branch
-rw-r--r-- | README.md | 21 | ||||
-rwxr-xr-x[-rw-r--r--] | bot.py | 111 | ||||
-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 | ||||
-rwxr-xr-x | custom_commands.json | 49 | ||||
-rwxr-xr-x | main.py | 67 | ||||
-rwxr-xr-x | palette.png | bin | 0 -> 591 bytes | |||
-rwxr-xr-x | readme.md | 25 | ||||
-rwxr-xr-x[-rw-r--r--] | requirements.txt | 17 | ||||
-rw-r--r-- | zibot.py | 22 |
20 files changed, 2453 insertions, 342 deletions
diff --git a/README.md b/README.md deleted file mode 100644 index 3eed704..0000000 --- a/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# ziBot - -**Python 3.5 or higher is required!** - -Just a fork of mcbeDiscordBot but rewritten a bit. Some code are translated mee6's old deprecated code that works on the new version of [discord.py](https://github.com/Rapptz/discord.py) - -### Dependencies -- [discord.py](https://github.com/Rapptz/discord.py) -- asyncio - -To install the dependencies, you can use this command: -``` -# Linux -python3 -m pip install -r requirements.txt - -# Windows -py -3 -m pip install -U discord.py -``` - -### Usage -Launch the bot with ```python3 zibot.py``` @@ -1,17 +1,28 @@ from discord.ext import commands import discord -import json -import aiohttp import logging +import aiohttp + +import datetime +import json extensions = [ - "cogs.welcome", "cogs.help", "cogs.moderator", "cogs.general", "cogs.utils" + "cogs.utils", + "cogs.admin", + "cogs.src", + "cogs.trans", + "cogs.player", + "cogs.general", + #"cogs.webserver", + #"cogs.twitter", + "cogs.logs" ] + def get_prefix(bot, message): """A callable Prefix for our bot. This could be edited to allow per server prefixes.""" - prefixes = ['>', '$>'] + prefixes = ['steve ', 'STEVE ', '/', '!', '@', 'Steve '] # Check to see if we are outside of a guild. e.g DM's etc. # if not message.guild: @@ -21,35 +32,63 @@ def get_prefix(bot, message): # If we are in a guild, we allow for the user to mention us or use any of the prefixes in our list. return commands.when_mentioned_or(*prefixes)(bot, message) -class ziBot(commands.Bot): - def __init__(self): - super().__init__(command_prefix=get_prefix,case_insensitive=True) - - self.logger = logging.getLogger('discord') - self.session = aiohttp.ClientSession() - - with open('config.json', 'r') as f: - self.config = json.load(f) - config = self.config - - async def on_ready(self): - activity=discord.Activity(name="some test",type=discord.ActivityType.watching) - await self.change_presence(activity=activity) - - for extension in extensions: - self.load_extension(extension) - - self.logger.warning(f'Online: {self.user} (ID: {self.user.id})') - print(f'Online: {self.user} (ID: {self.user.id})') - - async def on_message(self, message): - await self.process_commands(message) - try: - command = message.content.split()[0] - except IndexError: - pass - self.logger.warning('\nMessage from {message.author}: {message.content} \n on {message.channel}') - print('\nMessage from {0.author}: {0.content} \n on {0.channel}'.format(message)) - - def run(self): - super().run(self.config["token"], reconnect=True) + +class BedrockBot(commands.Bot): + def __init__(self): + super().__init__(command_prefix=get_prefix, + case_insensitive=True, + allowed_mentions=discord.AllowedMentions( + everyone=False, users=True, roles=False)) + self.logger = logging.getLogger('discord') + self.messageBlacklist = [] + self.session = aiohttp.ClientSession() + + with open('custom_commands.json', 'r') as f: + self.custom_commands = json.load(f) + + with open('config.json', 'r') as f: + self.config = json.load(f) + config = self.config + + async def on_ready(self): + self.uptime = datetime.datetime.utcnow() + + game = discord.Game("Mining away") + await self.change_presence(activity=game) + + with open('blacklist.json', 'r') as f: + try: + self.blacklist = json.load(f) + except json.decoder.JSONDecodeError: + self.blacklist = [] + + with open('runs_blacklist.json', 'r') as f: + try: + self.runs_blacklist = json.load(f) + except json.decoder.JSONDecodeError: + self.runs_blacklist = {"videos": [], "players": []} + + for extension in extensions: + self.load_extension(extension) + + self.logger.warning(f'Online: {self.user} (ID: {self.user.id})') + + async def on_message(self, message): + + if message.author.bot or message.author.id in self.blacklist: + return + await self.process_commands(message) + + try: + command = message.content.split()[0] + except IndexError: + pass + try: + if command in self.custom_commands: + await message.channel.send(self.custom_commands[command]) + return + except: + return + + def run(self): + super().run(self.config["token"], reconnect=True) 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)) diff --git a/custom_commands.json b/custom_commands.json new file mode 100755 index 0000000..ecfbe14 --- /dev/null +++ b/custom_commands.json @@ -0,0 +1,49 @@ +{ + "/Troll": "is Super at his job", + "/src": "https://www.speedrun.com/mcbe", + "!Walk-On-through-the-Wind": "Walk On through the rain", + "!When-you-walk": "through a storm hold your head up high and don't be afraid of the dark\nAt the end of a Storm there's a golden sky and the sweet silver song of the lark\nWalk on through the wind\nWalk on through the rain, for your dreams be tossed and blown\nWalk On, Walk On with hope in your hearts and You'll Never Walk Alone, **YOU'LL NEVER WALK ALONE**\nWalk On, Walk On with hope in your hearts and You'll Never Walk Alone, **YOU'LL NEVER WALK ALONE**", + "/troll": "The greatest mod this game has", + "/hoes": "stop asking for this shit", + "!At-the-end-of-a-Storm": "there's a golden sky and the sweet silver song of the lark", + "!Scotland'sNo1": "Celtic", + "/launcher": "https://github.com/MCMrARM/mc-w10-version-launcher/releases/tag/0.1.0", + "/blacklist": "Done! That felt good", + "!Walk-On": "Walk On with hope in your hearts and You'll Never Walk Alone", + "/welcome": "Welcome! <:Cake:619552132298964993>", + "/leaderboards": "https://www.speedrun.com/mcbe", + "@testy": "test", + "!uwu": "uwu", + "/Don't": "Buy The Sun Newspaper", + "!For-your-dreams": "be tossed and blown", + "/swipe": "not an alt", + "!HereWeGo": "10 in a Row!", + "/Make": "Troll Supermod", + "/boards": "https://www.speedrun.com/mcbe", + "/ban": "shut up", + "/pending": "this annoys me", + "/locate": "head north", + "/ssg": "https://www.speedrun.com/mcbe#Any_Glitchless", + "!When-you-walk-through-a-storm": "hold your head up high", + "!lenny": "( \u0361\u00b0 \u035c\u0296 \u0361\u00b0)", + "!hwg": "10 iar", + "!GlasgowRangers": "You Let Your Club Die!", + "!e\ud83c\udd71\ufe0fic": "HEY GUYS ITS SHIGAME45", + "!testy": "test", + "!toxicitypass": "```TOXICITY PASS\n\nTHE HOLDER OF THIS PASS HAS PERMISSION FROM THE HEAVENS ABOVE TO DO AND SAY WHATEVER THE HELL THEY WANT\nTHEY ARE ALLOWED TO BE AS TOXIC AS THEY WILL WITHOUT REPERCUSSIONS\n\nPASS OWNER: Duck W aka Weexy aka Indy Kambeitz\nPASS OWNER: Ex-Duck K aka Kai McCann aka Madelines long lost sibling```", + "!welcome": "Welcome! <:mango:715738087627685908>", + "!learntoread": "When two players are tied on the leaderboards **ONLY ONE FUCKER WILL BE VISIBLE**", + "!mobiledupe": "1) place item to dupe in the crafting grid\n2) keep pressed until the green bar thing appears\nclick any \"Impossible\" craft (red ones)\n3)press the item to dupe (that is in your hotbar now -> it should have moved there since you pressed the impossible craft)\n4)your item should be duped", + "!source": "https://github.com/AnInternetTroll/mcbeDiscordBot", + "!sr.c": "https://www.speedrun.com/mcbe", + "!lbsite": "https://aninternettroll.github.io/mcbeVerifierLeaderboard/index.html", + "!leaderboardsite": "https://aninternettroll.github.io/mcbeVerifierLeaderboard/index.html", + "!stop_crying": "Mods are human, we have lives. If your run isn't verified within 0.1 seconds of you submitting it then please don't Dm us asking us to verify your run. Speedrun.com gives you up to 3 weeks as an estimated time so if after 3 weeks have passed and your run hasn't been verified, then shoot one of us a Dm. If you constantly Dm us and hassle mods, you will get 3 warnings before you become muted and if you still continue, punishments will escalate like other breachments of the rules.", + "!staff": "https://media.discordapp.net/attachments/709672550707363931/721226547817873519/tenor.gif", + "!reeeee": "Oh my fucking god my run has been submitted for 3+ hours!!!! And you still didn't verify it?!?!!!!!?!!??? You guys are terrible mods I can NOT BELIEVE THIS!!!!!! are you actually serious?!?!?!? 3 WHOLE HOURS!!!!!! I genuinely do not know what you guys are DOING!!!!! what is taking so DAMN LONG?!?!?!!!! i am UPSET and would like to you verify my run!!!!!!!! It's your JOB!!!!!!!!! You can't even get your job right?!?!?!?!???!?! I will be contacting the OWNER and tell them how ABSOLUTELY TERRIBLE you are at your ONLY JOB!!!!!!!!!!!!!!!!!!!", + "!findimg": "me when I findseed:\nhttps://i.imgur.com/K8D5p.jpg", + "!ez": "I am practicing speed running right now and I am planning to have my friend and I speed run in bedrock - both mobile. Since there are no runs in bedrock co-op mobile any% I think we have what it takes. (We are both beginners.) So do I need to glitch in order to get into any% or do I get sent to any% glitchless? If so, then I can just look up some glitches or use some that I know but I would like to know.", + "!clap": "\ud83d\udc4f", + "!faq": "https://www.speedrun.com/mcbe/thread/vdv9t", + "!hell": "@everyone" +}
\ No newline at end of file @@ -0,0 +1,67 @@ +import asyncio +import logging +import json + +from colorama import init as init_colorama + +from bot import BedrockBot + + +def check_jsons(): + try: + f = open('config.json', 'r') + except FileNotFoundError: + token = input('BOT SETUP - Enter bot token: ') + with open('config.json', 'w+') as f: + json.dump({"token": token}, f, indent=4) + + try: + f = open('blacklist.json', 'r') + except FileNotFoundError: + with open('blacklist.json', 'w+') as f: + json.dump([], f, indent=4) + + try: + f = open('runs_blacklist.json', 'r') + except FileNotFoundError: + with open('runs_blacklist.json', 'w+') as f: + json.dump({"videos": [], "players": []}, f, indent=4) + + +def setup_logging(): + FORMAT = '%(asctime)s - [%(levelname)s]: %(message)s' + DATE_FORMAT = '%d/%m/%Y (%H:%M:%S)' + + logger = logging.getLogger('discord') + logger.setLevel(logging.INFO) + + file_handler = logging.FileHandler(filename='discord.log', + mode='a', + encoding='utf-8') + file_handler.setFormatter( + logging.Formatter(fmt=FORMAT, datefmt=DATE_FORMAT)) + file_handler.setLevel(logging.INFO) + logger.addHandler(file_handler) + + console_handler = logging.StreamHandler() + console_handler.setFormatter( + logging.Formatter(fmt=FORMAT, datefmt=DATE_FORMAT)) + console_handler.setLevel(logging.WARNING) + logger.addHandler(console_handler) + + +def run_bot(): + + bot = BedrockBot() + bot.run() + + +if __name__ == "__main__": + + init_colorama(autoreset=True) + + setup_logging() + + check_jsons() + + run_bot() diff --git a/palette.png b/palette.png Binary files differnew file mode 100755 index 0000000..973a599 --- /dev/null +++ b/palette.png diff --git a/readme.md b/readme.md new file mode 100755 index 0000000..245abe1 --- /dev/null +++ b/readme.md @@ -0,0 +1,25 @@ +# Minecraft Bedrock Discord Bot + +## How to +Launch the bot with `python3 main.py` and you're ready to go. Unless dependencies. Dependencies are google cloud and discord. + +Install the dependencies with `python -m pip install -r requirements.txt` + +A few "dangerous" commands such as `!purge` are restriced to `bot_masters`, to add a bot master add it to `config.json`. Example: +```json +{ + "token": "your_bot_token", + "<guild_id>": { + "bot_masters": <users_discord_id> + } +} +``` +`guild_id` is the ID of the discord server in the form of a string while user IDs are integers + +You can also use lists, for example: `"bot_masters": [280428276810383370, 99457716614885376]` + +A user added as a botmaster will be able to edit the config via discord with the command `!setvar <var_name> <var_value>` +`!setvar` also supports lists which can be added like so: `!setvar <var_name> [<index 0>, <index 1>]` + +This bot was built as a fork of [celesteBot](https://github.com/CelesteClassic/celestebot), so a lot of code is recycled. +Feel free to make a pull request or use the code here. diff --git a/requirements.txt b/requirements.txt index a920b4b..57d4bd7 100644..100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,18 @@ asyncio==3.4.3 -discord.py>=1.3.4 +discord.py>=1.4.0a2409+gb77af19 colorama==0.4.3 +GitPython==2.1.15 +google-api-core==1.17.0 +google-auth==1.14.1 +google-cloud-core==1.3.0 +google-cloud-translate==2.0.1 +googleapis-common-protos==1.51.0 +pathlib2==2.3.5 +Pillow>=7 +PyNaCl==1.3.0 +python-dateutil==2.8.1 +requests==2.22.0 +selenium==3.141.0 +six==1.12.0 +subprocess32==3.5.4 +youtube_dl>=2020.6.6 diff --git a/zibot.py b/zibot.py deleted file mode 100644 index 2e0c3e0..0000000 --- a/zibot.py +++ /dev/null @@ -1,22 +0,0 @@ -import asyncio -import json -from bot import ziBot - -def check_jsons(): - try: - f = open('config.json', 'r') - except FileNotFoundError: - token = input('Enter your bot\'s token: ') - with open('config.json', 'w+') as f: - json.dump({"token": token}, f, indent=4) - -def init_bot(): - bot = ziBot() - with open('config.json', 'r') as f: - data=json.load(f) - bot.remove_command('help') - bot.run() - -if __name__ == "__main__": - check_jsons() - init_bot() |