def __init__(self, parameters): super().__init__(command_prefix=_determine_prefix, pm_help=True, shard_count=parameters.get('shards total'), shard_ids=parameters.get('shards mine'), max_messages=2000, fetch_offline_members=False) self.parameters = parameters self.release = parameters.get('release') self.keystore = _create_keystore(parameters) self.settings = core.settings.Settings(self.keystore) self.command_output_map = QueueDict(timeout=60 * 10) # 10 minute timeout assert self.release in ['development', 'beta', 'release'] self.remove_command('help') for i in _get_extensions(parameters): self.load_extension(i)
def __init__(self, parameters): shard_count = parameters.get('shards total') shard_ids = parameters.get('shards mine') print(f'Starting bot shards {shard_ids} ({shard_count} total)') super().__init__( command_prefix=_determine_prefix, pm_help=True, shard_count=shard_count, shard_ids=shard_ids, max_messages=2000, fetch_offline_members=False ) self.parameters = parameters self.release = parameters.get('release') self.keystore = _create_keystore(parameters) self.settings = core.settings.Settings(self.keystore) self.command_output_map = QueueDict(timeout = 60 * 10) # 10 minute timeout self.blocked_users = parameters.get('blocked-users') self.closing_due_to_indeterminite_prefix = False assert self.release in ['development', 'beta', 'release'] self.remove_command('help') for i in _get_extensions(parameters): self.load_extension(i)
class MathBot(AdvertisingMixin, PatronageMixin, discord.ext.commands.AutoShardedBot): def __init__(self, parameters): super().__init__( command_prefix=_determine_prefix, pm_help=True, shard_count=parameters.get('shards total'), shard_ids=parameters.get('shards mine'), max_messages=2000, fetch_offline_members=False ) self.parameters = parameters self.release = parameters.get('release') self.keystore = _create_keystore(parameters) self.settings = core.settings.Settings(self.keystore) self.command_output_map = QueueDict(timeout = 60 * 10) # 10 minute timeout assert self.release in ['development', 'beta', 'release'] self.remove_command('help') for i in _get_extensions(parameters): self.load_extension(i) def run(self): super().run(self.parameters.get('token')) async def on_shard_ready(self, shard_id): print('on_shard_ready', shard_id) async def on_ready(self): print('on_ready') self._connection.emoji = [] gc.collect() objgraph.show_most_common_types() # await self.leave_inactive_servers() async def on_disconnect(self): print('on_disconnect') async def on_resumed(self): print('on_resumed') async def leave_inactive_servers(self): ''' There's definitely something wrong with this Or at least, at one point it was suprting "leaving guild None", so it's definitely not fault tolerent enough ''' threshhold = time.time() - (60 * 60 * 24 * 30 * 7) # Approx. 7 months for guild in self.guilds: try: should_leave = True for channel in guild.text_channels: try: async for message in channel.history(limit=4): if message.created_at.timestamp() > threshhold: should_leave = False break except (discord.errors.NotFound, discord.errors.Forbidden): pass if not should_leave: break if should_leave: print(f'Leaving guild {guild.name}') await guild.leave() await report(self, f'Leaving guild: {guild.name}') # else: # print(f'Staying in {guild.name}') except discord.errors.HTTPException: print(f'HTTPException while getting activity for guild: {guild.name}') async def on_message(self, message): if self.release != 'production' or not message.author.bot: if utils.is_private(message.channel) or self._can_post_in_guild(message): context = await self.get_context(message) perms = context.message.channel.permissions_for(context.me) required = [ perms.add_reactions, perms.attach_files, perms.embed_links, perms.read_message_history, ] if not context.valid: # dispatch a custom event self.dispatch('message_discarded', message) elif not all(required): await message.channel.send(REQUIRED_PERMISSIONS_MESSAGE) else: # Use d.py to invoke the actual command handler context.send = self.send_patch(message, context.send) await self.invoke(context) def send_patch(self, invoker, original): async def send(*args, **kwargs): sent = await original(*args, **kwargs) await core.blame.set_blame(self.keystore, sent, invoker.author) self.message_link(invoker, sent) return sent return send def dump_members(self, d): print({k: v.name for k, v in d.items()}) def _can_post_in_guild(self, message): # return True # TODO: Fix this if message.channel is None: return False # print('==== stuff ====') # print(message.guild) # print(message.guild.me) # self.dump_members(message.guild._members_cache) # self.dump_members(message.guild._important_members) perms = message.channel.permissions_for(message.guild.me) return perms.read_messages and perms.send_messages # Enabling this will cause output to be deleted with the coresponding # commands are deleted. # async def on_message_delete(self, message): # to_delete = self.command_output_map.pop(message.id, []) # await self._delete_messages(to_delete) # Using this, it's possible to edit a tex message # into some other command, so I might add some additional # restrictions to this later. async def on_message_edit(self, before, after): if before.content != after.content: to_delete = self.command_output_map.pop(before.id, []) await asyncio.gather( self._delete_messages(to_delete), self.on_message(after) ) async def _delete_messages(self, messages): for i in messages: try: await i.delete() except (discord.errors.Forbidden, discord.errors.NotFound): pass await asyncio.sleep(2) def message_link(self, invoker, sent): lst = self.command_output_map.get(invoker.id, default=[]) self.command_output_map[invoker.id] = lst + [sent] async def on_error(self, event, *args, **kwargs): _, error, _ = sys.exc_info() if event in ['message', 'on_message', 'message_discarded', 'on_message_discarded', 'on_command_error']: msg = f'**Error while handling a message**' await self.handle_contextual_error(args[0].channel, error, msg) else: termcolor.cprint(traceback.format_exc(), 'blue') await self.report_error(None, error, f'An error occurred during and event and was not reported: {event}') async def on_command_error(self, context, error): details = f'**Error while running command**\n```\n{context.message.clean_content}\n```' await self.handle_contextual_error(context, error, details) async def handle_contextual_error(self, destination, error, human_details=''): if isinstance(error, CommandNotFound): pass # Ignore unfound commands elif isinstance(error, MissingRequiredArgument): await destination.send(f'Argument {error.param} required.') elif isinstance(error, TooManyArguments): await destination.send(f'Too many arguments given.') elif isinstance(error, BadArgument): await destination.send(f'Bad argument: {error}') elif isinstance(error, NoPrivateMessage): await destination.send(f'That command cannot be used in DMs.') elif isinstance(error, MissingPermissions): await destination.send(f'You are missing the following permissions required to run the command: {", ".join(error.missing_perms)}.') elif isinstance(error, core.settings.DisabledCommandByServerOwner): await destination.send(embed=discord.Embed( title='Command disabled', description=f'The sever owner has disabled that command in this location.', colour=discord.Colour.orange() )) elif isinstance(error, DisabledCommand): await destination.send(embed=discord.Embed( title='Command globally disabled', description=f'That command is currently disabled. Either it relates to an unreleased feature or is undergoing maintaiance.', colour=discord.Colour.orange() )) elif isinstance(error, CommandInvokeError): await self.report_error(destination, error.original, human_details) else: await self.report_error(destination, error, human_details) async def report_error(self, destination, error, human_details): tb = ''.join(traceback.format_exception(etype=type(error), value=error, tb=error.__traceback__)) termcolor.cprint(human_details, 'red') termcolor.cprint(tb, 'blue') try: if destination is not None: embed = discord.Embed( title='An internal error occurred.', colour=discord.Colour.red(), description='A report has been automatically sent to the developer. If you wish to follow up, or seek additional assistance, you may do so at the mathbot server: https://discord.gg/JbJbRZS' ) await destination.send(embed=embed) finally: await report(self, f'{self.shard_ids} {human_details}\n```\n{tb}\n```')
class MathBot(AdvertisingMixin, PatronageMixin, discord.ext.commands.AutoShardedBot): def __init__(self, parameters): super().__init__(command_prefix=_determine_prefix, pm_help=True, shard_count=parameters.get('shards total'), shard_ids=parameters.get('shards mine'), max_messages=2000, fetch_offline_members=False) self.parameters = parameters self.release = parameters.get('release') self.keystore = _create_keystore(parameters) self.settings = core.settings.Settings(self.keystore) self.command_output_map = QueueDict(timeout=60 * 10) # 10 minute timeout assert self.release in ['development', 'beta', 'release'] self.remove_command('help') for i in _get_extensions(parameters): self.load_extension(i) def run(self): super().run(self.parameters.get('token')) async def on_message(self, message): if self.release != 'production' or not message.author.bot: if utils.is_private( message.channel) or self._can_post_in_guild(message): context = await self.get_context(message) perms = context.message.channel.permissions_for(context.me) required = [ perms.add_reactions, perms.attach_files, perms.embed_links, perms.read_message_history, ] if not context.valid: self.dispatch('message_discarded', message) elif not all(required): await message.channel.send(REQUIRED_PERMISSIONS_MESSAGE) else: await self.invoke(context) def _can_post_in_guild(self, message): perms = message.channel.permissions_for(message.guild.me) return perms.read_messages and perms.send_messages # Enabling this will cause output to be deleted with the coresponding # commands are deleted. # async def on_message_delete(self, message): # to_delete = self.command_output_map.pop(message.id, []) # await self._delete_messages(to_delete) # Using this, it's possible to edit a tex message # into some other command, so I might add some additional # restrictions to this later. async def on_message_edit(self, before, after): to_delete = self.command_output_map.pop(before.id, []) await asyncio.gather(self._delete_messages(to_delete), self.on_message(after)) async def _delete_messages(self, messages): for i in messages: try: await i.delete() except (discord.errors.Forbidden, discord.errors.NotFound): pass await asyncio.sleep(2) def message_link(self, invoker, sent): lst = self.command_output_map.get(invoker.id, default=[]) self.command_output_map[invoker.id] = lst + [sent] async def on_error(self, event, *args, **kwargs): _, error, _ = sys.exc_info() if event in [ 'message', 'on_message', 'message_discarded', 'on_message_discarded' ]: msg = f'**Error while handling a message**' await self.handle_contextual_error(args[0].channel, error, msg) else: termcolor.cprint(traceback.format_exc(), 'blue') await self.report_error( None, error, f'An error occurred during and event and was not reported: {event}' ) async def on_command_error(self, context, error): details = f'**Error while running command**\n```\n{context.message.clean_content}\n```' await self.handle_contextual_error(context, error, details) async def handle_contextual_error(self, destination, error, human_details=''): if isinstance(error, CommandNotFound): pass # Ignore unfound commands elif isinstance(error, MissingRequiredArgument): await destination.send(f'Argument {error.param} required.') elif isinstance(error, TooManyArguments): await destination.send(f'Too many arguments given.') elif isinstance(error, BadArgument): await destination.send(f'Bad argument: {error}') elif isinstance(error, NoPrivateMessage): await destination.send(f'That command cannot be used in DMs.') elif isinstance(error, MissingPermissions): await destination.send( f'You are missing the following permissions required to run the command: {", ".join(error.missing_perms)}.' ) elif isinstance(error, core.settings.DisabledCommandByServerOwner): await destination.send(embed=discord.Embed( title='Command disabled', description= f'The sever owner has disabled that command in this location.', colour=discord.Colour.orange())) elif isinstance(error, DisabledCommand): await destination.send(embed=discord.Embed( title='Command globally disabled', description= f'That command is currently disabled. Either it relates to an unreleased feature or is undergoing maintaiance.', colour=discord.Colour.orange())) elif isinstance(error, CommandInvokeError): await self.report_error(destination, error.original, human_details) else: await self.report_error(destination, error, human_details) async def report_error(self, destination, error, human_details): tb = ''.join( traceback.format_exception(etype=type(error), value=error, tb=error.__traceback__)) termcolor.cprint(human_details, 'red') termcolor.cprint(tb, 'blue') try: if destination is not None: embed = discord.Embed( title='An internal error occurred.', colour=discord.Colour.red(), description= 'A report has been automatically sent to the developer. If you wish to follow up, or seek additional assistance, you may do so at the mathbot server: https://discord.gg/JbJbRZS' ) await destination.send(embed=embed) finally: await report(self, f'{self.shard_ids} {human_details}\n```\n{tb}\n```')