From 671e08ca5aa8fa63793af67b8d6e30d0c6d1b82f Mon Sep 17 00:00:00 2001 From: nya~ <40436815+neko404notfound@users.noreply.github.com> Date: Thu, 21 Jun 2018 19:09:49 +0100 Subject: [PATCH 01/10] Update. (#2) * Update sebi_machine_launcher.sh * Update sebi_machine_launcher.sh * Update requirements.txt * removed numpy import * Fixed a stupid typo. * Fixed broken import someone didn't check. * Updated bot_management.py * Updated bot_management.py * Update bot_management.py * Update bot_management.py * Update bot_management.py * Added ban command And changed the response because if you were talking without commas you would need to breathe heavily and and and and *huff* * Update bot_management.py * Aaa wrong word lmao * Update bot_management.py * Update bot_management.py * Update bot_management.py * Update bot_management.py * Update bot_management.py * added new responses to agree And added async2rw to the tutorials * readded d.js-start Because i am retarded and removed it * Changed d.js-start back to d.js Because im too dumb * Update bot_management.py * Update bot_management.py * Fixed typos * Update sebi_machine_launcher.sh * Updated some files --- sebi_machine_launcher.sh | 4 +- sebimachine/cogs/basic_commands.py | 98 +++++-------- sebimachine/cogs/bot_management.py | 227 ++++++++++++----------------- sebimachine/shared_libs/utils.py | 115 +++++++++++++++ 4 files changed, 248 insertions(+), 196 deletions(-) diff --git a/sebi_machine_launcher.sh b/sebi_machine_launcher.sh index 9ce90f1..e470234 100644 --- a/sebi_machine_launcher.sh +++ b/sebi_machine_launcher.sh @@ -4,6 +4,7 @@ # Esp: added a trap here, as it otherwise attempts to restart when given # the interrupt signal. This is really annoying over SSH when I have # a 1-second lag anyway. + trap "echo 'Received interrupt. Exiting.'; exit 0" SIGINT SIGTERM # Also loads the venv if it is present. @@ -23,7 +24,7 @@ while true; do fi # Just respawn repeatedly until sigint. - python3.6 -m src + python3.6 -m sebimachine EXIT_STATUS=${?} if [ ${EXIT_STATUS} -ne 0 ]; then let FAIL_COUNTER=${FAIL_COUNTER}+1 @@ -31,6 +32,7 @@ while true; do let FAIL_COUNTER=0 fi + # Added colouring to ensure the date of shutdown and the exit code stands # out from the other clutter in the traceback that might have been output. echo -e "\e[0;31m[$(date --utc)]\e[0m Sebi-Machine shutdown with error \e[0;31m${EXIT_STATUS}\e[0m. Restarting..." >&2 diff --git a/sebimachine/cogs/basic_commands.py b/sebimachine/cogs/basic_commands.py index 74eec70..80fc5d5 100644 --- a/sebimachine/cogs/basic_commands.py +++ b/sebimachine/cogs/basic_commands.py @@ -1,10 +1,9 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import asyncio from discord.ext import commands import discord - +import asyncio class BasicCommands: def __init__(self, bot): @@ -12,83 +11,58 @@ class BasicCommands: @commands.command() async def tutorial(self, ctx): - await ctx.send( - f"Hello, {ctx.author.display_name}. Welcome to Sebi's Bot Tutorials. \nFirst off, would you like a quick walkthrough on the server channels?" - ) + await ctx.send(f"Hello, {ctx.author.display_name}. Welcome to Sebi's Bot Tutorials. \nFirst off, would you like a quick walkthrough on the server channels?") + + channel_list = {'channel-1' : self.bot.get_channel(333149949883842561).mention, + 'd.py-rewrite-start' : self.bot.get_channel(386419285439938560).mention, + 'js-klasa-start' : self.bot.get_channel(341816240186064897).mention, + 'async2rewrite-start' : self.bot.get_channel(392223495389577217).mention, + 'd.js' : self.bot.get_channel(436771798303113217).mention} - channel_list = { - "channel-1": self.bot.get_channel(333149949883842561).mention, - "d.py-rewrite-start": self.bot.get_channel(386419285439938560).mention, - "js-klasa-start": self.bot.get_channel(341816240186064897).mention, - "d.js": self.bot.get_channel(436771798303113217).mention, - } + bots_channels = (self.bot.get_channel(339112602867204097).mention, + self.bot.get_channel(411586546551095296).mention) - bots_channels = ( - self.bot.get_channel(339112602867204097).mention, - self.bot.get_channel(411586546551095296).mention, - ) - - help_channels = ( - self.bot.get_channel(425315253153300488).mention, - self.bot.get_channel(392215236612194305).mention, - self.bot.get_channel(351034776985141250).mention, - ) + help_channels = (self.bot.get_channel(425315253153300488).mention, + self.bot.get_channel(392215236612194305).mention, + self.bot.get_channel(351034776985141250).mention) def check(m): - return ( - True - if m.author.id == ctx.author.id and m.channel.id == ctx.channel.id - else False - ) + return True if m.author.id == ctx.author.id and m.channel.id == ctx.channel.id else False + + msg = await self.bot.wait_for('message', check = check, timeout = 15) - msg = await self.bot.wait_for("message", check=check, timeout=15) - - agree = ("yes", "yep", "yesn't", "ya", "ye") + agree = ("yes", "yep", "non't", "ya", "ye", "yup", "ok", "why not") if msg is None: - await ctx.send( - "Sorry, {ctx.author.mention}, you didn't reply on time. You can run the command again when you're free :)" - ) + await ctx.send("Sorry, {ctx.author.mention}, you didn't reply on time. You can run the command again when you're free :)") else: if msg.content.lower() in agree: async with ctx.typing(): await ctx.send("Alrighty-Roo... Check your DMs!") await ctx.author.send("Alrighty-Roo...") - await ctx.author.send( - f"To start making your bot from scratch, you first need to head over to {channel_list['channel-1']}" - " (Regardless of the language you're gonna use)." - ) - + await ctx.author.send(f"To start making your bot from scratch, you first need to head over to {channel_list['channel-1']}" + " (Regardless of the language you're gonna use).") + await asyncio.sleep(0.5) - await ctx.author.send( - f"After you have a bot account, you can either continue with {channel_list['d.py-rewrite-start']}" - f"if you want to make a bot in discord.py rewrite __or__ go to {channel_list['js-klasa-start']} or " - f"{channel_list['d.js']} for making a bot in JavaScript." - ) - - await ctx.author.send( - "...Read all the tutorials and still need help? You have two ways to get help." - ) + await ctx.author.send(f"After you have a bot account, you can either continue with {channel_list['d.py-rewrite-start']} " + f"if you want to make a bot in discord.py rewrite __or__ go to {channel_list['js-klasa-start']} or " + f"{channel_list['d.js']} for making a bot in JavaScript." + f"If you already have old Discord.py async code and you want to use it with the new Rewrite versions, head to {channel_list['async2rewrite-start']}") + + await ctx.author.send("...Read all the tutorials and still need help? You have two ways to get help.") await asyncio.sleep(1.5) - await ctx.author.send( - "**Method-1**\nThis is the best method of getting help. You help yourself.\n" - f"To do so, head over to a bots dedicated channel (either {bots_channels[0]} or {bots_channels[1]})" - " and type `?rtfm rewrite thing_you_want_help_with`.\nThis will trigger the bot R.Danny Bot and will" - "give you links on your query on the official discord.py rewrite docs. *PS: Let the page completely load*" - ) - + await ctx.author.send("**Method-1**\nThis is the best method of getting help. You help yourself.\n" + f"To do so, head over to a bots dedicated channel (either {bots_channels[0]} or {bots_channels[1]})" + " and type `?rtfm rewrite thing_you_want_help_with`.\nThis will trigger the bot R.Danny Bot and will " + "give you links on your query on the official discord.py rewrite docs. *PS: Let the page completely load*") + await asyncio.sleep(5) - await ctx.author.send( - "**Method-2**\nIf you haven't found anything useful with Method-1, feel free to ask your question " - f"in any of the related help channels. ({', '.join(help_channels)})\nMay the force be with you!!" - ) - + await ctx.author.send("**Method-2**\nIf you haven't found anything useful with Method-1, feel free to ask your question " + f"in any of the related help channels. ({', '.join(help_channels)})\nMay the force be with you!!") + else: - return await ctx.send( - "Session terminated. You can run this command again whenever you want." - ) - + return await ctx.send("Session terminated. You can run this command again whenever you want.") def setup(bot): bot.add_cog(BasicCommands(bot)) diff --git a/sebimachine/cogs/bot_management.py b/sebimachine/cogs/bot_management.py index a28526f..2e8d523 100644 --- a/sebimachine/cogs/bot_management.py +++ b/sebimachine/cogs/bot_management.py @@ -1,7 +1,6 @@ import discord from discord.ext import commands - - + class BotManager: def __init__(self, bot): self.bot = bot @@ -12,88 +11,76 @@ class BotManager: return else: # The member is a bot - await member.add_roles(discord.utils.get(member.guild.roles, name="Bots")) + bot_owner = member.guild.get_member((await self.bot.db_con.fetchval('select owner from bots where id = $1', member.id)) + await bot_owner.add_roles(discord.utils.get(member.guild.roles, name='Bot Developers')) + + await member.add_roles(discord.utils.get(member.guild.roles, name='Bots')) try: - await member.edit( - nick="[" - + await self.bot.db_con.fetch( - "select prefix from bots where id = $1", member.id - ) - + "] " - + member.name - ) + await member.edit(nick='[' + await self.bot.db_con.fetchval('select prefix from bots where id = $1', member.id) + + '] ' + member.name) except: pass - + async def on_member_remove(self, member): # If the member is not a bot if member.bot is False: return else: # The member is a bot - await self.bot.db_con.execute("DELETE FROM bots WHERE id = $1", member.id) + await self.bot.db_con.execute('DELETE FROM bots WHERE id = $1', member.id) + + async def on_member_ban(self, guild, user): + if member.bot is True: + return + else: + # I need to finish this + pass @commands.command() async def invite(self, ctx, bot=None, prefix=None): bot = await ctx.bot.get_user_info(bot) if not bot: - raise Warning( - "You must include the id of the bot you are trying to invite... Be exact." - ) + raise Warning('You must include the id of the bot you are trying to invite... Be exact.') if not bot.bot: - raise Warning("You can only invite bots.") + raise Warning('You can only invite bots.') if not prefix: - raise Warning("Please provide a prefix") + raise Warning('Please provide a prefix') # Make sure that the bot has not been invited already and it is not being tested - if ( - await self.bot.db_con.fetch( - "select count(*) from bots where id = $1", bot.id - ) - == 1 - ): - raise Warning("The bot has already been invited or is being tested") + if await self.bot.db_con.fetch('select count(*) from bots where id = $1', bot.id) == 1: + raise Warning('The bot has already been invited or is being tested') - await self.bot.db_con.execute( - "insert into bots (id, owner, prefix) values ($1, $2, $3)", - bot.id, - ctx.author.id, - prefix, - ) + await self.bot.db_con.execute('insert into bots (id, owner, prefix) values ($1, $2, $3)', + bot.id, ctx.author.id, prefix) em = discord.Embed(colour=self.bot.embed_color) em.title = "Hello {},".format(ctx.author.name) - em.description = "Thanks for inviting your bot! It will be tested and invited shortly. " "Please open your DMs if they are not already so the bot can contact " "you to inform you about the progress of the bot!" + em.description = "Thanks for inviting your bot! It will be tested and invited shortly. " \ + "Please open your DMs if they are not already so the bot can contact " \ + "you to inform you about the progress of the bot!" await ctx.send(embed=em) - + em = discord.Embed(title="Bot invite", colour=discord.Color(0x363941)) + em.description = discord.utils.oauth_url(client_id, permissions=None, guild=ctx.guild)) em.set_thumbnail(url=bot.avatar_url) em.add_field(name="Bot name", value=bot.name) em.add_field(name="Bot id", value="`" + str(bot.id) + "`") em.add_field(name="Bot owner", value=ctx.author.mention) em.add_field(name="Bot prefix", value="`" + prefix + "`") - await ctx.bot.get_channel(448803675574370304).send(embed=em) + await ctx.bot.get_channel(459280759945953300).send(embed=em) - @commands.command(name="claim", aliases=["makemine", "gimme"]) + @commands.command(name='claim', aliases=['makemine', 'gimme']) @commands.cooldown(1, 5, commands.BucketType.user) - async def _claim_bot( - self, - ctx, - bot: discord.Member = None, - prefix: str = None, - owner: discord.Member = None, - ): + async def _claim_bot(self, ctx, bot: discord.Member = None, prefix: str = None, owner: discord.Member = None): if not bot: - raise Warning( - "You must include the name of the bot you are trying to claim... Be exact." - ) + raise Warning('You must include the name of the bot you are trying to claim... Be exact.') if not bot.bot: - raise Warning("You can only claim bots.") + raise Warning('You can only claim bots.') if not prefix: - if bot.display_name.startswith("["): - prefix = bot.display_name.split("]")[0].strip("[") + if bot.display_name.startswith('['): + prefix = bot.display_name.split(']')[0].strip('[') else: - raise Warning("Prefix not provided and can't be found in bot nick.") + raise Warning('Prefix not provided and can\'t be found in bot nick.') if owner is not None and ctx.author.guild_permissions.manage_roles: author_id = owner.id @@ -102,126 +89,100 @@ class BotManager: em = discord.Embed() - if ( - await self.bot.db_con.fetchval( - "select count(*) from bots where owner = $1", author_id - ) - >= 10 - ): + if await self.bot.db_con.fetchval('select count(*) from bots where owner = $1', author_id) >= 10: em.colour = self.bot.error_color - em.title = "Too Many Bots Claimed" - em.description = "Each person is limited to claiming 10 bots as that is how " "many bots are allowed by the Discord API per user." + em.title = 'Too Many Bots Claimed' + em.description = 'Each person is limited to claiming 10 bots as that is how ' \ + 'many bots are allowed by the Discord API per user.' return await ctx.send(embed=em) - existing = await self.bot.db_con.fetchrow( - "select * from bots where id = $1", bot.id - ) + existing = await self.bot.db_con.fetchrow('select * from bots where id = $1', bot.id) if not existing: - await self.bot.db_con.execute( - "insert into bots (id, owner, prefix) values ($1, $2, $3)", - bot.id, - author_id, - prefix, - ) + await self.bot.db_con.execute('insert into bots (id, owner, prefix) values ($1, $2, $3)', + bot.id, author_id, prefix) em.colour = self.bot.embed_color - em.title = "Bot Claimed" - em.description = f"You have claimed {bot.display_name} with a prefix of {prefix}\n" f"If there is an error please run command again to correct the prefix,\n" f"or {ctx.prefix}unclaim {bot.mention} to unclaim the bot." - elif existing["owner"] and existing["owner"] != author_id: + em.title = 'Bot Claimed' + em.description = f'You have claimed {bot.display_name} with a prefix of {prefix}\n' \ + f'If there is an error please run command again to correct the prefix,\n' \ + f'or {ctx.prefix}unclaim {bot.mention} to unclaim the bot.' + elif existing['owner'] and existing['owner'] != author_id: em.colour = self.bot.error_color - em.title = "Bot Already Claimed" - em.description = "This bot has already been claimed by someone else.\n" "If this is actually your bot please let the guild Administrators know." - elif existing["owner"] and existing["owner"] == author_id: + em.title = 'Bot Already Claimed' + em.description = 'This bot has already been claimed by someone else.\n' \ + 'If this is actually your bot please let the guild Administrators know.' + elif existing['owner'] and existing['owner'] == author_id: em.colour = self.bot.embed_color - em.title = "Bot Already Claimed" - em.description = "You have already claimed this bot.\n" "If the prefix you provided is different from what is already in the database" " it will be updated for you." - if existing["prefix"] != prefix: - await self.bot.db_con.execute( - "update bots set prefix = $1 where id = $2", prefix, bot.id - ) - elif not existing["owner"]: - await self.bot.db_con.execute( - "update bots set owner = $1, prefix = $2 where id = $3", - author_id, - prefix, - bot.id, - ) + em.title = 'Bot Already Claimed' + em.description = 'You have already claimed this bot.\n' \ + 'If the prefix you provided is different from what is already in the database' \ + ' it will be updated for you.' + if existing['prefix'] != prefix: + await self.bot.db_con.execute("update bots set prefix = $1 where id = $2", prefix, bot.id) + elif not existing['owner']: + await self.bot.db_con.execute('update bots set owner = $1, prefix = $2 where id = $3', + author_id, prefix, bot.id) em.colour = self.bot.embed_color - em.title = "Bot Claimed" - em.description = f"You have claimed {bot.display_name} with a prefix of {prefix}\n" f"If there is an error please run command again to correct the prefix,\n" f"or {ctx.prefix}unclaim {bot.mention} to unclaim the bot." + em.title = 'Bot Claimed' + em.description = f'You have claimed {bot.display_name} with a prefix of {prefix}\n' \ + f'If there is an error please run command again to correct the prefix,\n' \ + f'or {ctx.prefix}unclaim {bot.mention} to unclaim the bot.' else: em.colour = self.bot.error_color - em.title = "Something Went Wrong..." + em.title = 'Something Went Wrong...' await ctx.send(embed=em) - @commands.command(name="unclaim") + @commands.command(name='unclaim') @commands.cooldown(1, 5, commands.BucketType.user) async def _unclaim_bot(self, ctx, bot: discord.Member = None): if not bot: - raise Warning( - "You must include the name of the bot you are trying to claim... Be exact." - ) + raise Warning('You must include the name of the bot you are trying to claim... Be exact.') if not bot.bot: - raise Warning("You can only unclaim bots.") + raise Warning('You can only unclaim bots.') em = discord.Embed() - existing = await self.bot.db_con.fetchrow( - "select * from bots where id = $1", bot.id - ) - if not existing or not existing["owner"]: + existing = await self.bot.db_con.fetchrow('select * from bots where id = $1', bot.id) + if not existing or not existing['owner']: em.colour = self.bot.error_color - em.title = "Bot Not Found" - em.description = "That bot is not claimed" - elif ( - existing["owner"] != ctx.author.id - and not ctx.author.guild_permissions.manage_roles - ): + em.title = 'Bot Not Found' + em.description = 'That bot is not claimed' + elif existing['owner'] != ctx.author.id and not ctx.author.guild_permissions.manage_roles: em.colour = self.bot.error_color - em.title = "Not Claimed By You" - em.description = "That bot is claimed by someone else.\n" "You can't unclaim someone else's bot" + em.title = 'Not Claimed By You' + em.description = 'That bot is claimed by someone else.\n' \ + 'You can\'t unclaim someone else\'s bot' else: - await self.bot.db_con.execute( - "update bots set owner = null where id = $1", bot.id - ) + await self.bot.db_con.execute('update bots set owner = null where id = $1', bot.id) em.colour = self.bot.embed_color - em.title = "Bot Unclaimed" - em.description = f"You have unclaimed {bot.display_name}\n" f"If this is an error please reclaim using\n" f'{ctx.prefix}claim {bot.mention} {existing["prefix"]}' + em.title = 'Bot Unclaimed' + em.description = f'You have unclaimed {bot.display_name}\n' \ + f'If this is an error please reclaim using\n' \ + f'{ctx.prefix}claim {bot.mention} {existing["prefix"]}' await ctx.send(embed=em) - @commands.command(name="listclaims", aliases=["claimed", "mybots"]) + @commands.command(name='listclaims', aliases=['claimed', 'mybots']) @commands.cooldown(1, 5, commands.BucketType.user) async def _claimed_bots(self, ctx, usr: discord.Member = None): if usr is None: usr = ctx.author - bots = await self.bot.db_con.fetch( - "select * from bots where owner = $1", usr.id - ) + bots = await self.bot.db_con.fetch('select * from bots where owner = $1', usr.id) if bots: - em = discord.Embed( - title=f"{usr.display_name} has claimed the following bots:", - colour=self.bot.embed_color, - ) + em = discord.Embed(title=f'{usr.display_name} has claimed the following bots:', + colour=self.bot.embed_color) for bot in bots: - member = ctx.guild.get_member(int(bot["id"])) - em.add_field( - name=member.display_name, - value=f'Stored Prefix: {bot["prefix"]}', - inline=False, - ) + member = ctx.guild.get_member(int(bot['id'])) + em.add_field(name=member.display_name, value=f'Stored Prefix: {bot["prefix"]}', inline=False) else: - em = discord.Embed( - title="You have not claimed any bots.", colour=self.bot.embed_color - ) + em = discord.Embed(title='You have not claimed any bots.', + colour=self.bot.embed_color) await ctx.send(embed=em) - @commands.command(name="whowns") + @commands.command(name='whowns') async def _whowns(self, ctx, bot: discord.Member): if not bot.bot: - await ctx.send("this commands only for bots") + await ctx.send('this commands only for bots') else: - owner = await self.bot.db_con.fetchrow( - "select * from bots where id = $1", bot.id - ) - await ctx.send(ctx.guild.get_member(owner["owner"]).display_name) + owner = await self.bot.db_con.fetchrow('select * from bots where id = $1', bot.id) + await ctx.send(ctx.guild.get_member(owner['owner']).display_name) def setup(bot): diff --git a/sebimachine/shared_libs/utils.py b/sebimachine/shared_libs/utils.py index 48bea65..6281f43 100644 --- a/sebimachine/shared_libs/utils.py +++ b/sebimachine/shared_libs/utils.py @@ -1,3 +1,117 @@ +<<<<<<< HEAD:src/shared_libs/utils.py +""" +=== + +MIT License + +Copyright (c) 2018 Dusty.P https://github.com/dustinpianalto + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +""" + + +from io import StringIO +import sys +import asyncio +import discord +from discord.ext.commands.formatter import Paginator + + +class Capturing(list): + def __enter__(self): + self._stdout = sys.stdout + sys.stdout = self._stringio = StringIO() + return self + + def __exit__(self, *args): + self.extend(self._stringio.getvalue().splitlines()) + del self._stringio # free up some memory + sys.stdout = self._stdout + + +def to_list_of_str(items, out: list=list(), level=1, recurse=0): + def rec_loop(item, key, out, level): + quote = '"' + if type(item) == list: + out.append(f'{" "*level}{quote+key+quote+": " if key else ""}[') + new_level = level + 1 + out = to_list_of_str(item, out, new_level, 1) + out.append(f'{" "*level}]') + elif type(item) == dict: + out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{{') + new_level = level + 1 + out = to_list_of_str(item, out, new_level, 1) + out.append(f'{" "*level}}}') + else: + out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{repr(item)},') + + if type(items) == list: + if not recurse: + out = list() + out.append('[') + for item in items: + rec_loop(item, None, out, level) + if not recurse: + out.append(']') + elif type(items) == dict: + if not recurse: + out = list() + out.append('{') + for key in items: + rec_loop(items[key], key, out, level) + if not recurse: + out.append('}') + + return out + + +def paginate(text, maxlen=1990): + paginator = Paginator(prefix='```py', max_size=maxlen+10) + if type(text) == list: + data = to_list_of_str(text) + elif type(text) == dict: + data = to_list_of_str(text) + else: + data = str(text).split('\n') + for line in data: + if len(line) > maxlen: + n = maxlen + for l in [line[i:i+n] for i in range(0, len(line), n)]: + paginator.add_line(l) + else: + paginator.add_line(line) + return paginator.pages + + +async def run_command(args): + # Create subprocess + process = await asyncio.create_subprocess_shell( + args, + # stdout must a pipe to be accessible as process.stdout + stdout=asyncio.subprocess.PIPE) + # Wait for the subprocess to finish + stdout, stderr = await process.communicate() + # Return stdout + return stdout.decode().strip() +======= """ === @@ -113,3 +227,4 @@ async def run_command(args): stdout, stderr = await process.communicate() # Return stdout return stdout.decode().strip() +>>>>>>> e62845ade82bc5e3ade059021693f99b8efcf6a9:sebimachine/shared_libs/utils.py From f4b0f03a23579e97ac6fc713c7dcc0d401a5da0e Mon Sep 17 00:00:00 2001 From: nya~ <40436815+neko404notfound@users.noreply.github.com> Date: Thu, 21 Jun 2018 19:10:34 +0100 Subject: [PATCH 02/10] Fixed path issue. --- sebimachine/config/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sebimachine/config/config.py b/sebimachine/config/config.py index df4be54..7370fbc 100644 --- a/sebimachine/config/config.py +++ b/sebimachine/config/config.py @@ -13,7 +13,7 @@ class LoadConfig: def __init__(self): # Read our config file - with open("src/config/Config.json") as fp: + with open("sebimachine/config/Config.json") as fp: self.config = json.load(fp) # Initialize config From 1ddf76707456e92c3693a7a577b167bd9d266563 Mon Sep 17 00:00:00 2001 From: nya~ <40436815+neko404notfound@users.noreply.github.com> Date: Thu, 21 Jun 2018 19:17:05 +0100 Subject: [PATCH 03/10] Logs and reports broken cogs on restart. --- sebimachine/__main__.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/sebimachine/__main__.py b/sebimachine/__main__.py index 4300b3b..1ff1195 100644 --- a/sebimachine/__main__.py +++ b/sebimachine/__main__.py @@ -61,6 +61,7 @@ class SebiMachine(commands.Bot, LoadConfig, Loggable): with open(in_here("config", "PrivateConfig.json")) as fp: self.bot_secrets = json.load(fp) self.db_con = database.DatabaseConnection(**self.bot_secrets["db-con"]) + self.failed_cogs_on_startup = {} self.book_emojis: Dict[str, str] = { "unlock": "🔓", "start": "⏮", @@ -78,17 +79,27 @@ class SebiMachine(commands.Bot, LoadConfig, Loggable): for cog in cogs: # Could this just be replaced with `strip()`? - cog = cog.replace("\n", "") - self.load_extension(f"src.cogs.{cog}") - self.logger.info(f"Loaded: {cog}") - - async def on_ready(self): + try: + cog = cog.replace("\n", "") + self.load_extension(f"src.cogs.{cog}") + self.logger.info(f"Loaded: {cog}") + except (ModuleNotFoundError, ImportError) as ex: + logging.exception(f'Could not load {cog}', exc_info=(type(ex), ex, ex.__traceback__)) + self.failed_cogs_on_startup[cog] = ex + + async def on_ready(self): """On ready function""" self.maintenance and self.logger.warning("MAINTENANCE ACTIVE") with open(f"src/config/reboot", "r") as f: reboot = f.readlines() if int(reboot[0]) == 1: await self.get_channel(int(reboot[1])).send("Restart Finished.") + for cog, ex in self.failed_cogs_on_startup.items(): + tb = ''.join(traceback.format_exception(type(ex), ex, ex.__traceback__))[-1500:] + await ctx.send( + f'FAILED TO LOAD {cog} BECAUSE OF {type(ex).__name__}: {ex}\n' + f'{tb}' + ) with open(f"src/config/reboot", "w") as f: f.write(f"0") From b189f2ca5eb7dbd7429e5b295d5ebe7cdf564391 Mon Sep 17 00:00:00 2001 From: nya~ <40436815+neko404notfound@users.noreply.github.com> Date: Thu, 21 Jun 2018 19:17:29 +0100 Subject: [PATCH 04/10] Oops --- sebimachine/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sebimachine/__main__.py b/sebimachine/__main__.py index 1ff1195..e1b41b6 100644 --- a/sebimachine/__main__.py +++ b/sebimachine/__main__.py @@ -87,7 +87,7 @@ class SebiMachine(commands.Bot, LoadConfig, Loggable): logging.exception(f'Could not load {cog}', exc_info=(type(ex), ex, ex.__traceback__)) self.failed_cogs_on_startup[cog] = ex - async def on_ready(self): + async def on_ready(self): """On ready function""" self.maintenance and self.logger.warning("MAINTENANCE ACTIVE") with open(f"src/config/reboot", "r") as f: From 63d852f37b3784230ca0b7a2c9e03182702f083c Mon Sep 17 00:00:00 2001 From: nya~ <40436815+neko404notfound@users.noreply.github.com> Date: Thu, 21 Jun 2018 19:22:04 +0100 Subject: [PATCH 05/10] Fixed syntax error. --- sebimachine/cogs/bot_management.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sebimachine/cogs/bot_management.py b/sebimachine/cogs/bot_management.py index 2e8d523..910fabe 100644 --- a/sebimachine/cogs/bot_management.py +++ b/sebimachine/cogs/bot_management.py @@ -11,7 +11,7 @@ class BotManager: return else: # The member is a bot - bot_owner = member.guild.get_member((await self.bot.db_con.fetchval('select owner from bots where id = $1', member.id)) + bot_owner = member.guild.get_member(await self.bot.db_con.fetchval('select owner from bots where id = $1', member.id)) await bot_owner.add_roles(discord.utils.get(member.guild.roles, name='Bot Developers')) await member.add_roles(discord.utils.get(member.guild.roles, name='Bots')) From 08f19038716dc2dcdeb39110cad0abecdf2fa597 Mon Sep 17 00:00:00 2001 From: nya~ <40436815+neko404notfound@users.noreply.github.com> Date: Thu, 21 Jun 2018 19:25:12 +0100 Subject: [PATCH 06/10] Unborked a thing. --- sebimachine/__main__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sebimachine/__main__.py b/sebimachine/__main__.py index e1b41b6..d9a117e 100644 --- a/sebimachine/__main__.py +++ b/sebimachine/__main__.py @@ -75,13 +75,11 @@ class SebiMachine(commands.Bot, LoadConfig, Loggable): # Load plugins # Add your cog file name in this list with open(in_here("extensions.txt")) as cog_file: - cogs = cog_file.readlines() + cogs = {f'sebimachine.cogs.{c.strip()}' for c in cog_file.readlines()} for cog in cogs: - # Could this just be replaced with `strip()`? try: - cog = cog.replace("\n", "") - self.load_extension(f"src.cogs.{cog}") + self.load_extension(cog) self.logger.info(f"Loaded: {cog}") except (ModuleNotFoundError, ImportError) as ex: logging.exception(f'Could not load {cog}', exc_info=(type(ex), ex, ex.__traceback__)) From 66d994023e747f53d26ec0dce0a5a0d543c57dda Mon Sep 17 00:00:00 2001 From: nya~ <40436815+neko404notfound@users.noreply.github.com> Date: Thu, 21 Jun 2018 19:26:18 +0100 Subject: [PATCH 07/10] Removed junk from merge conflict issue. --- sebimachine/shared_libs/utils.py | 118 ------------------------------- 1 file changed, 118 deletions(-) diff --git a/sebimachine/shared_libs/utils.py b/sebimachine/shared_libs/utils.py index 6281f43..eef669d 100644 --- a/sebimachine/shared_libs/utils.py +++ b/sebimachine/shared_libs/utils.py @@ -1,4 +1,3 @@ -<<<<<<< HEAD:src/shared_libs/utils.py """ === @@ -111,120 +110,3 @@ async def run_command(args): stdout, stderr = await process.communicate() # Return stdout return stdout.decode().strip() -======= -""" -=== - -MIT License - -Copyright (c) 2018 Dusty.P https://github.com/dustinpianalto - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -""" - - -from io import StringIO -import sys -import asyncio -import discord -from discord.ext.commands.formatter import Paginator - - -class Capturing(list): - def __enter__(self): - self._stdout = sys.stdout - sys.stdout = self._stringio = StringIO() - return self - - def __exit__(self, *args): - self.extend(self._stringio.getvalue().splitlines()) - del self._stringio # free up some memory - sys.stdout = self._stdout - - -def to_list_of_str(items, out: list = list(), level=1, recurse=0): - def rec_loop(item, key, out, level): - quote = '"' - if type(item) == list: - out.append(f'{" "*level}{quote+key+quote+": " if key else ""}[') - new_level = level + 1 - out = to_list_of_str(item, out, new_level, 1) - out.append(f'{" "*level}]') - elif type(item) == dict: - out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{{') - new_level = level + 1 - out = to_list_of_str(item, out, new_level, 1) - out.append(f'{" "*level}}}') - else: - out.append( - f'{" "*level}{quote+key+quote+": " if key else ""}{repr(item)},' - ) - - if type(items) == list: - if not recurse: - out = list() - out.append("[") - for item in items: - rec_loop(item, None, out, level) - if not recurse: - out.append("]") - elif type(items) == dict: - if not recurse: - out = list() - out.append("{") - for key in items: - rec_loop(items[key], key, out, level) - if not recurse: - out.append("}") - - return out - - -def paginate(text, maxlen=1990): - paginator = Paginator(prefix="```py", max_size=maxlen + 10) - if type(text) == list: - data = to_list_of_str(text) - elif type(text) == dict: - data = to_list_of_str(text) - else: - data = str(text).split("\n") - for line in data: - if len(line) > maxlen: - n = maxlen - for l in [line[i : i + n] for i in range(0, len(line), n)]: - paginator.add_line(l) - else: - paginator.add_line(line) - return paginator.pages - - -async def run_command(args): - # Create subprocess - process = await asyncio.create_subprocess_shell( - args, - # stdout must a pipe to be accessible as process.stdout - stdout=asyncio.subprocess.PIPE, - ) - # Wait for the subprocess to finish - stdout, stderr = await process.communicate() - # Return stdout - return stdout.decode().strip() ->>>>>>> e62845ade82bc5e3ade059021693f99b8efcf6a9:sebimachine/shared_libs/utils.py From 0a9ab007e5394885b059f4269fb626656474a911 Mon Sep 17 00:00:00 2001 From: nya~ <40436815+neko404notfound@users.noreply.github.com> Date: Thu, 21 Jun 2018 19:26:58 +0100 Subject: [PATCH 08/10] Fixed borked path for tags. --- sebimachine/cogs/tag.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sebimachine/cogs/tag.py b/sebimachine/cogs/tag.py index f94ae06..08ee6fd 100644 --- a/sebimachine/cogs/tag.py +++ b/sebimachine/cogs/tag.py @@ -8,7 +8,7 @@ import asyncio class Tag: def __init__(self, bot): self.bot = bot - with open("src/shared_libs/tags.json", "r") as fp: + with open("sebimachine/shared_libs/tags.json", "r") as fp: json_data = fp.read() global tags tags = json.loads(json_data) From ba27d7c390b387503f54a272cbc7bb4e99f61fb4 Mon Sep 17 00:00:00 2001 From: nya~ <40436815+neko404notfound@users.noreply.github.com> Date: Thu, 21 Jun 2018 19:27:32 +0100 Subject: [PATCH 09/10] Removed a `)` --- sebimachine/cogs/bot_management.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sebimachine/cogs/bot_management.py b/sebimachine/cogs/bot_management.py index 910fabe..0e25965 100644 --- a/sebimachine/cogs/bot_management.py +++ b/sebimachine/cogs/bot_management.py @@ -61,7 +61,7 @@ class BotManager: await ctx.send(embed=em) em = discord.Embed(title="Bot invite", colour=discord.Color(0x363941)) - em.description = discord.utils.oauth_url(client_id, permissions=None, guild=ctx.guild)) + em.description = discord.utils.oauth_url(client_id, permissions=None, guild=ctx.guild) em.set_thumbnail(url=bot.avatar_url) em.add_field(name="Bot name", value=bot.name) em.add_field(name="Bot id", value="`" + str(bot.id) + "`") From 6f7e3b4bbc319d852dc1ca18c5eace306d2aa91e Mon Sep 17 00:00:00 2001 From: nya~ <40436815+neko404notfound@users.noreply.github.com> Date: Thu, 21 Jun 2018 19:35:02 +0100 Subject: [PATCH 10/10] Fixed another thingy regarding borked paths. --- sebimachine/__main__.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/sebimachine/__main__.py b/sebimachine/__main__.py index d9a117e..c866a27 100644 --- a/sebimachine/__main__.py +++ b/sebimachine/__main__.py @@ -26,6 +26,8 @@ from .shared_libs.loggable import Loggable # Init logging to output on INFO level to stderr. logging.basicConfig(level="INFO") +REBOOT_FILE = "sebimachine/config/reboot" + # If uvloop is installed, change to that eventloop policy as it # is more efficient @@ -88,17 +90,18 @@ class SebiMachine(commands.Bot, LoadConfig, Loggable): async def on_ready(self): """On ready function""" self.maintenance and self.logger.warning("MAINTENANCE ACTIVE") - with open(f"src/config/reboot", "r") as f: - reboot = f.readlines() - if int(reboot[0]) == 1: - await self.get_channel(int(reboot[1])).send("Restart Finished.") - for cog, ex in self.failed_cogs_on_startup.items(): - tb = ''.join(traceback.format_exception(type(ex), ex, ex.__traceback__))[-1500:] - await ctx.send( - f'FAILED TO LOAD {cog} BECAUSE OF {type(ex).__name__}: {ex}\n' - f'{tb}' - ) - with open(f"src/config/reboot", "w") as f: + if os.path.exists(REBOOT_FILE): + with open(REBOOT_FILE, "r") as f: + reboot = f.readlines() + if int(reboot[0]) == 1: + await self.get_channel(int(reboot[1])).send("Restart Finished.") + for cog, ex in self.failed_cogs_on_startup.items(): + tb = ''.join(traceback.format_exception(type(ex), ex, ex.__traceback__))[-1500:] + await ctx.send( + f'FAILED TO LOAD {cog} BECAUSE OF {type(ex).__name__}: {ex}\n' + f'{tb}' + ) + with open(REBOOT_FILE, "w") as f: f.write(f"0") async def on_command_error(self, ctx, error):