async def unmuteTask(modcog: Moderation): GearbotLogging.info("Started unmute background task") skips = [] updated = False while modcog.running: userid = 0 guildid = 0 try: guildstoremove = [] for guildid, list in modcog.mutes.items(): guild: discord.Guild = modcog.bot.get_guild(int(guildid)) toremove = [] if Configuration.getConfigVar(int(guildid), "MUTE_ROLE") is 0: guildstoremove.append(guildid) for userid, until in list.items(): if time.time() > until and userid not in skips: member = guild.get_member(int(userid)) role = discord.utils.get(guild.roles, id=Configuration.getConfigVar(int(guildid), "MUTE_ROLE")) if guild.me.guild_permissions.manage_roles: await member.remove_roles(role, reason="Mute expired") await GearbotLogging.logToModLog(guild, f"<:gearInnocent:465177981287923712> {member.name}#{member.discriminator} (`{member.id}`) has automaticaly been unmuted") else: await GearbotLogging.logToModLog(guild, f":no_entry: ERROR: {member.name}#{member.discriminator} (`{member.id}`) was muted earlier but I no longer have the permissions needed to unmute this person, please remove the role manually!") updated = True toremove.append(userid) for todo in toremove: del list[todo] await asyncio.sleep(0) if updated: Utils.saveToDisk("mutes", modcog.mutes) updated = False for id in guildstoremove: del modcog.mutes[id] await asyncio.sleep(10) except CancelledError: pass # bot shutdown except Exception as ex: GearbotLogging.error("Something went wrong in the unmute task") GearbotLogging.error(traceback.format_exc()) skips.append(userid) embed = discord.Embed(colour=discord.Colour(0xff0000), timestamp=datetime.datetime.utcfromtimestamp(time.time())) embed.set_author(name="Something went wrong in the unmute task:") embed.add_field(name="Current guildid", value=guildid) embed.add_field(name="Current userid", value=userid) embed.add_field(name="Exception", value=ex) v = "" for line in traceback.format_exc().splitlines(): if len(v) + len(line) > 1024: embed.add_field(name="Stacktrace", value=v) v = "" v = f"{v}\n{line}" if len(v) > 0: embed.add_field(name="Stacktrace", value=v) await GearbotLogging.logToBotlog(embed=embed) await asyncio.sleep(10) GearbotLogging.info("Unmute background task terminated")
def load_config(guild): from Bot.TheRealGearBot import handle_exception global SERVER_CONFIGS try: config = Utils.fetch_from_disk(f'config/{guild}') except JSONDecodeError as e: GearbotLogging.error(f"Failed to deserialize config! {e}") asyncio.create_task(handle_exception("loading config", BOT, e)) config = Utils.fetch_from_disk("template") if len(config.keys()) != 0 and "VERSION" not in config and len( config) < 15: GearbotLogging.info( f"The config for {guild} is to old to migrate, resetting") config = dict() elif len(config.keys()) != 0: if "VERSION" not in config: config["VERSION"] = 0 SERVER_CONFIGS[guild] = update_config(guild, config) if len(config.keys()) == 0: GearbotLogging.info( f"No config available for {guild}, creating a blank one.") SERVER_CONFIGS[guild] = Utils.fetch_from_disk("template") save(guild) validate_config(guild) Features.check_server(guild)
async def handle_database_error(bot): GearbotLogging.error(traceback.format_exc()) # database trouble, notify bot owner message = f"{Emoji.get_chat_emoji('WARNING')} Peewee exception caught! attempting to reconnect to the database!" await GearbotLogging.message_owner(bot, message) await GearbotLogging.logToBotlog(message) try: DatabaseConnector.init() bot.database_connection = DatabaseConnector.connection except: # fail, trying again in 10 just in case the database is rebooting time.sleep(15) try: DatabaseConnector.init() bot.database_connection = DatabaseConnector.connection except: if os.path.isfile('stage_2.txt'): message = f"{Emoji.get_chat_emoji('NO')} VM reboot did not fix the problem, shutting down completely for fixes" await bot.get_user(bot.owner_id).dm_channel.send(message) await GearbotLogging.logToBotlog(message) with open("stage_3.txt", "w") as file: file.write("stage_3") os.kill(os.getpid(), 9) elif os.path.isfile('stage_1.txt'): with open("stage_2.txt", "w") as file: file.write("stage_2") message = f"{Emoji.get_chat_emoji('NO')} Reconnecting and bot rebooting failed, escalating to VM reboot" await GearbotLogging.message_owner(bot, message) await GearbotLogging.logToBotlog(message) data = {'type': 'reboot'} async with aiohttp.ClientSession( headers= { 'Content-Type': 'application/json', 'Authorization': f'Bearer {Configuration.getMasterConfigVar("DO_TOKEN")}' }) as session: await session.post( f'https://api.digitalocean.com/v2/droplets/{Configuration.getMasterConfigVar("DO_ID")}/actions', data=json.dumps(data), timeout=30) time.sleep(60) else: message = f"{Emoji.get_chat_emoji('NO')} Reconnecting failed, escalating to reboot" await GearbotLogging.message_owner(bot, message) await GearbotLogging.logToBotlog(message) with open("stage_1.txt", "w") as file: file.write("stage_1") os.kill(os.getpid(), 9) else: message = f"{Emoji.get_chat_emoji('YES')} 2nd reconnection attempt successfully connected!" await GearbotLogging.message_owner(bot, message) await GearbotLogging.logToBotlog(message) else: message = f"{Emoji.get_chat_emoji('YES')} 1st reconnection attempt successfully connected!" await GearbotLogging.message_owner(bot, message) await GearbotLogging.logToBotlog(message)
async def on_command_error(bot, ctx: commands.Context, error): if isinstance(error, NotCachedException): if bot.loading_task is not None: if bot.initial_fill_complete: await ctx.send( f"{Emoji.get_chat_emoji('CLOCK')} Due to a earlier connection failure the cached data for this guild is no longer up to date and is being rebuild. Please try again in a few minutes." ) else: await ctx.send( f"{Emoji.get_chat_emoji('CLOCK')} GearBot is in the process of starting up and has not received the member info for this guild. Please try again in a few minutes." ) else: await ctx.send( f"{Emoji.get_chat_emoji('CLOCK')} GearBot only just joined this guild and is still receiving the initial member info for this guild, please try again in a few seconds" ) if isinstance(error, commands.BotMissingPermissions): GearbotLogging.error( f"Encountered a permission error while executing {ctx.command}: {error}" ) await ctx.send(error) elif isinstance(error, commands.CheckFailure): if ctx.command.qualified_name is not "latest" and ctx.guild is not None and Configuration.get_var( ctx.guild.id, "GENERAL", "PERM_DENIED_MESSAGE"): await MessageUtils.send_to(ctx, 'LOCK', 'permission_denied') elif isinstance(error, commands.CommandOnCooldown): await ctx.send(error) elif isinstance(error, commands.MissingRequiredArgument): param = list(ctx.command.params.values())[min( len(ctx.args) + len(ctx.kwargs), len(ctx.command.params))] bot.help_command.context = ctx await ctx.send( f"{Emoji.get_chat_emoji('NO')} {Translator.translate('missing_arg', ctx, arg=param._name, error=Utils.replace_lookalikes(str(error)))}\n{Emoji.get_chat_emoji('WRENCH')} {Translator.translate('command_usage', ctx, usage=bot.help_command.get_command_signature(ctx.command))}" ) elif isinstance(error, PostParseError): bot.help_command.context = ctx await ctx.send( f"{Emoji.get_chat_emoji('NO')} {Translator.translate('bad_argument', ctx, type=error.type, error=Utils.replace_lookalikes(str(error.error)))}\n{Emoji.get_chat_emoji('WRENCH')} {Translator.translate('command_usage', ctx, usage=bot.help_command.get_command_signature(ctx.command))}" ) elif isinstance(error, commands.BadArgument): param = list(ctx.command.params.values())[min( len(ctx.args) + len(ctx.kwargs), len(ctx.command.params))] bot.help_command.context = ctx await ctx.send( f"{Emoji.get_chat_emoji('NO')} {Translator.translate('bad_argument', ctx, type=param._name, error=Utils.replace_lookalikes(str(error)))}\n{Emoji.get_chat_emoji('WRENCH')} {Translator.translate('command_usage', ctx, usage=bot.help_command.get_command_signature(ctx.command))}" ) elif isinstance(error, commands.CommandNotFound): return else: await handle_exception( "Command execution failed", bot, error.original if hasattr(error, "original") else error, ctx=ctx) # notify caller e = Emoji.get_chat_emoji('BUG') if ctx.channel.permissions_for(ctx.me).send_messages: await ctx.send( f"{e} Something went wrong while executing that command. If this keeps happening please report it on support server (DM me ``!about`` or check the website for an invite) {e}" )
def translate(key, location, **kwargs): lid = None if location is not None: if hasattr(location, "guild"): location = location.guild if location is not None and hasattr(location, "id"): lid = location.id else: lid = location if lid is None: lang_key = "en_US" else: lang_key = Configuration.get_var(lid, "LANG") translated = key if key not in LANGS[lang_key]: if key not in untranlatable: BOT.loop.create_task(tranlator_log('WARNING', f'Untranslatable string detected: {key}\n')) untranlatable.add(key) return key if key not in LANGS["en_US"] else format(LANGS['en_US'][key], kwargs, 'en_US') try: translated = format(LANGS[lang_key][key], kwargs, lang_key) except (KeyError, ValueError, ParseError, VisitationError) as ex: BOT.loop.create_task(tranlator_log('NO', f'Corrupt translation detected!\n**Lang code:** {lang_key}\n**Translation key:** {key}\n```\n{LANGS[lang_key][key]}```')) GearbotLogging.exception("Corrupt translation", ex) GearbotLogging.error(ex) if key in LANGS["en_US"].keys(): try: translated = format(LANGS['en_US'][key], kwargs, 'en_US') except (KeyError, ValueError, ParseError, VisitationError) as ex: BOT.loop.create_task(tranlator_log('NO', f'Corrupt English source string detected!\n**Translation key:** {key}\n```\n{LANGS["en_US"][key]}```')) GearbotLogging.error(ex) return translated
async def on_command_error(bot, ctx: commands.Context, error): if isinstance(error, commands.BotMissingPermissions): GearbotLogging.error(f"Encountered a permission error while executing {ctx.command}: {error}") await ctx.send(error) elif isinstance(error, commands.CheckFailure): if ctx.command.qualified_name is not "latest" and ctx.guild is not None and Configuration.get_var(ctx.guild.id, "GENERAL", "PERM_DENIED_MESSAGE"): await MessageUtils.send_to(ctx, 'LOCK', 'permission_denied') elif isinstance(error, commands.CommandOnCooldown): await ctx.send(error) elif isinstance(error, commands.MissingRequiredArgument): param = list(ctx.command.params.values())[min(len(ctx.args) + len(ctx.kwargs), len(ctx.command.params))] bot.help_command.context = ctx await ctx.send( f"{Emoji.get_chat_emoji('NO')} {Translator.translate('missing_arg', ctx, arg=param._name, error=Utils.replace_lookalikes(str(error)))}\n{Emoji.get_chat_emoji('WRENCH')} {Translator.translate('command_usage', ctx, usage=bot.help_command.get_command_signature(ctx.command))}") elif isinstance(error, PostParseError): bot.help_command.context = ctx await ctx.send(f"{Emoji.get_chat_emoji('NO')} {Translator.translate('bad_argument', ctx, type=error.type, error=Utils.replace_lookalikes(str(error.error)))}\n{Emoji.get_chat_emoji('WRENCH')} {Translator.translate('command_usage', ctx, usage=bot.help_command.get_command_signature(ctx.command))}") elif isinstance(error, commands.BadArgument): param = list(ctx.command.params.values())[min(len(ctx.args) + len(ctx.kwargs), len(ctx.command.params))] bot.help_command.context = ctx await ctx.send(f"{Emoji.get_chat_emoji('NO')} {Translator.translate('bad_argument', ctx, type=param._name, error=Utils.replace_lookalikes(str(error)))}\n{Emoji.get_chat_emoji('WRENCH')} {Translator.translate('command_usage', ctx, usage=bot.help_command.get_command_signature(ctx.command))}") elif isinstance(error, commands.CommandNotFound): return elif isinstance(error, PeeweeException): await handle_database_error(bot) else: await handle_exception("Command execution failed", bot, error.original if hasattr(error, "original") else error, ctx=ctx) # notify caller e = Emoji.get_chat_emoji('BUG') if ctx.channel.permissions_for(ctx.me).send_messages: await ctx.send(f"{e} Something went wrong while executing that command {e}")
async def translation_task(bot): while not bot.is_closed(): try: await Translator.update() except Exception as ex: GearbotLogging.error( "Something went wrong during translation updates") GearbotLogging.error(traceback.format_exc()) embed = Embed(colour=Colour(0xff0000), timestamp=datetime.utcfromtimestamp(time.time())) embed.set_author( name="Something went wrong during translation updates") embed.add_field(name="Exception", value=str(ex)) v = "" for line in traceback.format_exc().splitlines(): if len(v) + len(line) >= 1024: embed.add_field(name="Stacktrace", value=v) v = "" v = f"{v}\n{line}" if len(v) > 0: embed.add_field(name="Stacktrace", value=v) await GearbotLogging.bot_log(embed=embed) try: await asyncio.sleep(6 * 60 * 60) except asyncio.CancelledError: pass # bot shutting down
async def initialize(bot): #lock event handling while we get ready bot.locked = True try: #database GearbotLogging.info("Connecting to the database.") DatabaseConnector.init() bot.database_connection = DatabaseConnector.connection GearbotLogging.info("Database connection established.") GearbotLogging.initialize_pump(bot) Emoji.initialize(bot) Pages.initialize(bot) Utils.initialize(bot) Translator.initialize(bot) InfractionUtils.initialize(bot) bot.data = { "forced_exits": set(), "unbans": set(), "message_deletes": set() } await GearbotLogging.initialize( bot, Configuration.get_master_var("BOT_LOG_CHANNEL")) if bot.redis_pool is None or not hasattr( bot, 'redis_raid_pool') or bot.redis_raid_pool is None: try: bot.redis_pool = await aioredis.create_redis_pool( (Configuration.get_master_var('REDIS_HOST', "localhost"), Configuration.get_master_var('REDIS_PORT', 6379)), encoding="utf-8", db=0) bot.redis_raid_pool = await aioredis.create_redis_pool( (Configuration.get_master_var('REDIS_HOST', "localhost"), Configuration.get_master_var('REDIS_PORT', 6379)), encoding="utf-8", db=1) except OSError: GearbotLogging.error( "==============Failed to connect to redis==============") await GearbotLogging.bot_log( f"{Emoji.get_chat_emoji('NO')} Failed to connect to redis, caching and anti-raid connections unavailable" ) else: GearbotLogging.info("Redis connection established") await GearbotLogging.bot_log( f"{Emoji.get_chat_emoji('YES')} Redis connection established, caching and anti-raid connections established" ) if bot.aiosession is None: bot.aiosession = aiohttp.ClientSession() bot.being_cleaned.clear() await Configuration.initialize(bot) except Exception as ex: #make sure we always unlock, even when something went wrong! bot.locked = False raise ex bot.locked = False
async def fetch_info(self, project_name): session: aiohttp.ClientSession = self.bot.aiosession async with session.get(f"https://api.cfwidget.com/mc-mods/minecraft/{project_name}") as reply: if reply.status is 200: # all good, we can parse it parsed = json.loads(await reply.text()) p_type = parsed["type"] info = { "title": parsed["title"], "type": f'{parsed["game"]} {p_type[:-1] if p_type.endswith("s") else p_type}', "updated": parsed["last_fetch"], "categories": parsed["categories"], "links": dict(), "thumbnail": parsed["thumbnail"], "downloads": parsed["downloads"]["total"] } for link in parsed["links"]: info["links"][link["title"]] = link["href"] mc_versions = [] for k, v in parsed["versions"].items(): if "Java" not in k: mc_versions.append(k) sorted = VersionInfo.getSortedVersions(mc_versions) map = OrderedDict() for version in sorted: mod_versions_unsorted = dict() mod_versions = OrderedDict() version_list = [] for v2 in parsed["versions"][version]: mod_versions_unsorted[v2["id"]] = v2 version_list.append(v2["id"]) version_list.sort() version_list.reverse() for v3 in version_list: mod_versions[v3] = mod_versions_unsorted[v3] map[version] = mod_versions info["versions"] = map return info elif reply.status is 202: # New project, wait for the api to fetch it GearbotLogging.info(f"Info for {project_name} not available yet, trying again in 10 seconds.") await asyncio.sleep(10) return await self.fetch_info(project_name) elif reply.status in (400, 404): return None elif reply.status is 500: GearbotLogging.error(f"Fetching info for {project_name} failed.") return False else: GearbotLogging.error( f"Got unexpected response code ({reply.status}) when fetching info for {project_name}.") return None # TODO: handle failure
def loadGlobalConfig(): global MASTER_CONFIG, master_loaded try: with open('config/master.json', 'r') as jsonfile: MASTER_CONFIG = json.load(jsonfile) master_loaded = True except FileNotFoundError: GearbotLogging.error("Unable to load config, running with defaults.") except Exception as e: GearbotLogging.error("Failed to parse configuration.") print(e) raise e
def gen_command_listing(command): try: listing = f"|{command.qualified_name}|{Permissioncheckers.get_perm_dict(command.qualified_name.split(' '), command.instance.permissions)['required']}|{Translator.translate(command.short_doc, None)}|\n" except Exception as ex: GearbotLogging.error(command.qualified_name) raise ex else: if isinstance(command, GroupMixin) and hasattr(command, "all_commands"): for c in command.all_commands.values(): listing += gen_command_listing(c) return listing
async def initialize(bot, startup=False): #lock event handling while we get ready bot.locked = True try: #database GearbotLogging.info("Connecting to the database.") DatabaseConnector.init() bot.database_connection = DatabaseConnector.connection GearbotLogging.info("Database connection established.") Emoji.initialize(bot) Utils.initialize(bot) InfractionUtils.initialize(bot) bot.data = { "forced_exits": set(), "unbans": set(), "message_deletes": set(), "nickname_changes": set() } await GearbotLogging.initialize(bot, Configuration.get_master_var("BOT_LOG_CHANNEL")) if startup: c = await Utils.get_commit() bot.version = c GearbotLogging.info(f"GearBot spinning up version {c}") await GearbotLogging.bot_log(f"{Emoji.get_chat_emoji('ALTER')} GearBot spinning up version {c}") if bot.redis_pool is None: try: socket = Configuration.get_master_var("REDIS_SOCKET", "") if socket == "": bot.redis_pool = await aioredis.create_redis_pool((Configuration.get_master_var('REDIS_HOST', "localhost"), Configuration.get_master_var('REDIS_PORT', 6379)), encoding="utf-8", db=0) else: bot.redis_pool = await aioredis.create_redis_pool(socket, encoding="utf-8", db=0) except OSError: GearbotLogging.error("==============Failed to connect to redis==============") await GearbotLogging.bot_log(f"{Emoji.get_chat_emoji('NO')} Failed to connect to redis, caching unavailable") else: GearbotLogging.info("Redis connection established") await GearbotLogging.bot_log(f"{Emoji.get_chat_emoji('YES')} Redis connection established, let's go full speed!") if bot.aiosession is None: bot.aiosession = aiohttp.ClientSession() await Translator.initialize(bot) bot.being_cleaned.clear() await Configuration.initialize(bot) DashConfig.initialize(bot) except Exception as ex: #make sure we always unlock, even when something went wrong! bot.locked = False raise ex bot.locked = False
async def dash_monitor(self): DASH_OUTAGE_INFO: dict = Configuration.get_master_var("DASH_OUTAGE") DASH_OUTAGE_CHANNEl = DASH_OUTAGE_INFO["dash_outage_channel"] MAX_WARNINGS = DASH_OUTAGE_INFO["max_bot_outage_warnings"] BOT_OUTAGE_PINGED_ROLES = DASH_OUTAGE_INFO["dash_outage_pinged_roles"] while True: if (time.time() - self.last_dash_heartbeat[0]) > 5: self.last_dash_heartbeat[1] += 1 if self.last_dash_heartbeat[ 1] >= 3 and self.last_dash_heartbeat[2] < MAX_WARNINGS: print( "The dashboard API keepalive hasn't responded in over 3 minutes!" ) self.last_dash_heartbeat[2] += 1 self.last_dash_heartbeat[1] = 0 if DASH_OUTAGE_CHANNEl: outage_message = DASH_OUTAGE_INFO["dash_outage_embed"] # Apply the timestamp outage_message["timestamp"] = datetime.now().isoformat( ) # Set the color to the format Discord understands outage_message["color"] = outage_message["color"] # Generate the custom message and role pings notify_message = DASH_OUTAGE_INFO[ "dash_outage_message"] if BOT_OUTAGE_PINGED_ROLES: pinged_roles = [] for role_id in BOT_OUTAGE_PINGED_ROLES: pinged_roles.append(f"<@&{role_id}>") notify_message += f" Pinging: {', '.join(pinged_roles)}" try: outage_channel = self.bot.get_channel( DASH_OUTAGE_CHANNEl) await outage_channel.send( notify_message, embed=Embed.from_dict(outage_message)) except Forbidden: GearbotLogging.error( "We couldn't access the specified channel, the notification will not be sent!" ) # Wait a little bit longer so the dashboard has a chance to update before we check await asyncio.sleep(65)
async def versionChecker(checkcog:BCVersionChecker): GearbotLogging.info("Started BC version checking background task") session:aiohttp.ClientSession = checkcog.bot.aiosession reply:aiohttp.ClientResponse lastUpdate = 0 while checkcog.running: try: async with session.get('https://www.mod-buildcraft.com/build_info_full/last_change.txt') as reply: stamp = await reply.text() stamp = int(stamp[:-1]) if stamp > lastUpdate: GearbotLogging.info("New BC version somewhere!") lastUpdate = stamp checkcog.BC_VERSION_LIST = await getList(session, "BuildCraft") checkcog.BCC_VERSION_LIST = await getList(session, "BuildCraftCompat") highestMC = VersionInfo.getLatest(checkcog.BC_VERSION_LIST.keys()) latestBC = VersionInfo.getLatest(checkcog.BC_VERSION_LIST[highestMC]) generalID = 309218657798455298 channel:discord.TextChannel = checkcog.bot.get_channel(generalID) if channel is not None and latestBC not in channel.topic: info = await checkcog.getVersionDetails("BuildCraft", latestBC) newTopic = f"General discussions about BuildCraft.\n" \ f"Latest version: {latestBC}\n" \ f"Full changelog and download: {info['blog_entry']}" await channel.edit(topic=newTopic) pass pass except CancelledError: pass # bot shutdown except Exception as ex: checkcog.bot.errors = checkcog.bot.errors + 1 GearbotLogging.error("Something went wrong in the BC version checker task") GearbotLogging.error(traceback.format_exc()) embed = discord.Embed(colour=discord.Colour(0xff0000), timestamp=datetime.datetime.utcfromtimestamp(time.time())) embed.set_author(name="Something went wrong in the BC version checker task:") embed.add_field(name="Exception", value=str(ex)) v = "" for line in traceback.format_exc().splitlines(): if len(v) + len(line) >= 1024: embed.add_field(name="Stacktrace", value=v) v = "" v = f"{v}\n{line}" if len(v) > 0: embed.add_field(name="Stacktrace", value=v) await GearbotLogging.bot_log(embed=embed) for i in range(1,60): if checkcog.force or not checkcog.running: break await asyncio.sleep(10) GearbotLogging.info("BC version checking background task terminated")
def gen_command_listing(bot, cog, command, code): try: perm_lvl = Permissioncheckers.get_perm_dict( command.qualified_name.split(' '), cog.permissions)['required'] listing = f"| | | {Translator.translate_by_code(command.short_doc, code)} |\n" listing += f"|{command.qualified_name}|{Translator.translate_by_code(f'perm_lvl_{perm_lvl}', code)} ({perm_lvl})| |\n" signature = bot.help_command.get_command_signature(command).replace( "|", "ǀ") listing += f"| | |{Translator.translate_by_code('example', code)}: ``{signature}``|\n" except Exception as ex: GearbotLogging.error(command.qualified_name) raise ex return listing
async def on_command_error(bot, ctx: commands.Context, error): if isinstance(error, commands.NoPrivateMessage): await ctx.send("This command cannot be used in private messages.") elif isinstance(error, commands.BotMissingPermissions): GearbotLogging.error( f"Encountered a permission error while executing {ctx.command}: {error}" ) await ctx.send(error) elif isinstance(error, commands.DisabledCommand): await ctx.send("Sorry. This command is disabled and cannot be used.") elif isinstance(error, commands.CheckFailure): if ctx.command.qualified_name is not "latest" and ctx.guild is not None and Configuration.get_var( ctx.guild.id, "PERM_DENIED_MESSAGE"): await ctx.send( ":lock: You do not have the required permissions to run this command" ) elif isinstance(error, commands.CommandOnCooldown): await ctx.send(error) elif isinstance(error, commands.MissingRequiredArgument): param = list(ctx.command.params.values())[min( len(ctx.args) + len(ctx.kwargs), len(ctx.command.params))] await ctx.send( f"{Emoji.get_chat_emoji('NO')} {Translator.translate('missing_arg', ctx, arg=param._name, error=error)}\n{Emoji.get_chat_emoji('WRENCH')} {Translator.translate('command_usage', ctx, usage=ctx.prefix.replace(ctx.me.mention, f'@{ctx.me.name}') + ctx.command.signature)}" ) elif isinstance(error, commands.BadArgument): param = list(ctx.command.params.values())[min( len(ctx.args) + len(ctx.kwargs), len(ctx.command.params))] await ctx.send( f"{Emoji.get_chat_emoji('NO')} {Translator.translate('bad_argument', ctx, type=param._name, error=error)}\n{Emoji.get_chat_emoji('WRENCH')} {Translator.translate('command_usage', ctx, usage=ctx.prefix.replace(ctx.me.mention, f'@{ctx.me.name}') + ctx.command.signature)}" ) elif isinstance(error, commands.CommandNotFound): return elif isinstance(error, PeeweeException): await handle_database_error(bot) else: await handle_exception("Command execution failed", bot, error.original, ctx=ctx) # notify caller await ctx.send( ":rotating_light: Something went wrong while executing that command :rotating_light:" )
def gen_command_listing2(bot, cog, command): command_listing = dict() try: perm_lvl = Permissioncheckers.get_perm_dict( command.qualified_name.split(' '), cog.permissions)['required'] command_listing["commandlevel"] = perm_lvl command_listing["description"] = command.short_doc command_listing["aliases"] = command.aliases example = bot.help_command.get_command_signature(command).strip() parts = str(example).split(' ') parts[0] = ''.join(parts[0][1:]) for i in range(0, len(parts)): if "[" == parts[i][0] and "|" in parts[i]: parts[i] = ''.join(parts[i].split('|')[0][1:]) command_listing["example"] = '!' + ' '.join(parts) command_listing["subcommands"] = {} return command_listing except Exception as ex: GearbotLogging.error(command.qualified_name) raise ex
async def on_error(bot, event, *args, **kwargs): type, exception, info = sys.exc_info() if isinstance(exception, PeeweeException): await handle_database_error(bot) try: # something went wrong and it might have been in on_command_error, make sure we log to the log file first bot.errors = bot.errors + 1 GearbotLogging.error(f"error in {event}\n{args}\n{kwargs}") embed = discord.Embed(colour=discord.Colour(0xff0000), timestamp=datetime.datetime.utcfromtimestamp( time.time())) embed.set_author(name=f"Caught an error in {event}:") embed.add_field(name="args", value=str(args)) embed.add_field(name="kwargs", value=str(kwargs)) embed.add_field(name="cause message", value=traceback._cause_message) v = "" for line in traceback.format_exc(): if len(v) + len(line) > 1024: embed.add_field(name="Stacktrace", value=v) v = "" v = f"{v}{line}" if len(v) > 0: embed.add_field(name="Stacktrace", value=v) # try logging to botlog, wrapped in an try catch as there is no higher lvl catching to prevent taking donwn the bot (and if we ended here it might have even been due to trying to log to botlog await GearbotLogging.logToBotlog(embed=embed) except Exception as ex: GearbotLogging.error( f"Failed to log to botlog, either Discord broke or something is seriously wrong!\n{ex}" ) GearbotLogging.error(traceback.format_exc())
def translate(key, location, **kwargs): lid = None if location is not None: if hasattr(location, "guild"): location = location.guild if location is not None and hasattr(location, "id"): lid = location.id else: lid = location if lid is None: lang_key = "en_US" else: lang_key = Configuration.get_var(lid, "LANG") if key in LANGS[lang_key].keys(): try: return LANGS[lang_key][key].format(**kwargs) except (KeyError, ValueError): GearbotLogging.error( f"Corrupt translation detected in {lang_key}: {key}\n```\n{LANGS[lang_key][key]}```" ) if key in LANGS["en_US"].keys(): return LANGS["en_US"][key].format(**kwargs) return key
async def handle_exception(exception_type, bot, exception, event=None, message=None, ctx=None, *args, **kwargs): if bot is not None: bot.errors = bot.errors + 1 with sentry_sdk.push_scope() as scope: embed = Embed(colour=Colour(0xff0000), timestamp=datetime.utcfromtimestamp(time.time())) # something went wrong and it might have been in on_command_error, make sure we log to the log file first lines = [ "\n===========================================EXCEPTION CAUGHT, DUMPING ALL AVAILABLE INFO===========================================", f"Type: {exception_type}" ] arg_info = "" for arg in list(args): arg_info += extract_info(arg) + "\n" if arg_info == "": arg_info = "No arguments" kwarg_info = "" for name, arg in kwargs.items(): kwarg_info += "{}: {}\n".format(name, extract_info(arg)) if kwarg_info == "": kwarg_info = "No keyword arguments" lines.append("======================Exception======================") lines.append(f"{str(exception)} ({type(exception)})") lines.append("======================ARG INFO======================") lines.append(arg_info) sentry_sdk.add_breadcrumb(category='arg info', message=arg_info, level='info') lines.append("======================KWARG INFO======================") lines.append(kwarg_info) sentry_sdk.add_breadcrumb(category='kwarg info', message=kwarg_info, level='info') lines.append("======================STACKTRACE======================") tb = "".join(traceback.format_tb(exception.__traceback__)) lines.append(tb) if message is None and event is not None and hasattr(event, "message"): message = event.message if message is None and ctx is not None: message = ctx.message if message is not None and hasattr(message, "content"): lines.append( "======================ORIGINAL MESSAGE======================") lines.append(message.content) if message.content is None or message.content == "": content = "<no content>" else: content = message.content scope.set_tag('message content', content) embed.add_field(name="Original message", value=Utils.trim_message(content, 1000), inline=False) lines.append( "======================ORIGINAL MESSAGE (DETAILED)======================" ) lines.append(extract_info(message)) if event is not None: lines.append( "======================EVENT NAME======================") lines.append(event) scope.set_tag('event name', event) embed.add_field(name="Event", value=event) if ctx is not None: lines.append( "======================COMMAND INFO======================") lines.append(f"Command: {ctx.command.name}") embed.add_field(name="Command", value=ctx.command.name) scope.set_tag('command', ctx.command.name) channel_name = 'Private Message' if isinstance( ctx.channel, PrivateChannel) else f"{ctx.channel.name} (`{ctx.channel.id}`)" lines.append(f"Channel: {channel_name}") embed.add_field(name="Channel", value=channel_name, inline=False) scope.set_tag('channel', channel_name) sender = f"{str(ctx.author)} (`{ctx.author.id}`)" scope.user = dict(id=ctx.author.id, username=str(ctx.author)) lines.append(f"Sender: {sender}") embed.add_field(name="Sender", value=sender, inline=False) lines.append( "===========================================DATA DUMP COMPLETE===========================================" ) GearbotLogging.error("\n".join(lines)) for t in [ConnectionClosed, ClientOSError, ServerDisconnectedError]: if isinstance(exception, t): return #nice embed for info on discord embed.set_author(name=exception_type) embed.add_field(name="Exception", value=f"{str(exception)} (`{type(exception)}`)", inline=False) parts = Pages.paginate(tb, max_chars=1024) num = 1 for part in parts: embed.add_field(name=f"Traceback {num}/{len(parts)}", value=part) num += 1 sentry_sdk.capture_exception(exception) # try logging to botlog, wrapped in an try catch as there is no higher lvl catching to prevent taking down the bot (and if we ended here it might have even been due to trying to log to botlog try: await GearbotLogging.bot_log(embed=embed) except Exception as ex: GearbotLogging.error( f"Failed to log to botlog, either Discord broke or something is seriously wrong!\n{ex}" ) GearbotLogging.error(traceback.format_exc())
async def updater(cog: BCVersionChecker): GearbotLogging.info("Started BC version checking background task") session: aiohttp.ClientSession = cog.bot.aiosession lastUpdate = 0 while cog.running: try: # check for a newer bc version async with session.get( 'https://www.mod-buildcraft.com/build_info_full/last_change.txt' ) as reply: stamp = await reply.text() stamp = int(stamp[:-1]) if stamp > lastUpdate: GearbotLogging.info("New BC version somewhere!") lastUpdate = stamp cog.BC_VERSION_LIST = await getList(session, "BuildCraft") cog.BCC_VERSION_LIST = await getList( session, "BuildCraftCompat") highestMC = VersionInfo.getLatest( cog.BC_VERSION_LIST.keys()) latestBC = VersionInfo.getLatest( cog.BC_VERSION_LIST[highestMC]) generalID = 309218657798455298 channel: discord.TextChannel = cog.bot.get_channel( generalID) old_latest = Configuration.get_persistent_var( "latest_bc", "0.0.0") Configuration.set_persistent_var( "latest_bc", latestBC ) # save already so we don't get stuck and keep trying over and over if something goes wrong if channel is not None and latestBC != old_latest: GearbotLogging.info( f"New BuildCraft version found: {latestBC}") notify_channel = cog.bot.get_channel( 349517224320565258) await notify_channel.send( f"{Emoji.get_chat_emoji('WRENCH')} New BuildCraft version detected ({latestBC})" ) GearbotLogging.info( f"Fetching metadata for BuildCraft {latestBC}") message = await notify_channel.send( f"{Emoji.get_chat_emoji('REFRESH')} Fetching metadata..." ) info = await cog.getVersionDetails( "BuildCraft", latestBC) GearbotLogging.info(f"Metadata acquired: {info}") await message.edit( content= f"{Emoji.get_chat_emoji('YES')} Metadata acquired") if 'blog_entry' in info: message = await notify_channel.send( f"{Emoji.get_chat_emoji('REFRESH')} Updating general topic..." ) newTopic = f"General discussions about BuildCraft.\n" \ f"Latest version: {latestBC}\n" \ f"Full changelog and download: {info['blog_entry']}" await channel.edit(topic=newTopic) await message.edit( content= f"{Emoji.get_chat_emoji('YES')} Topic updated") else: notify_channel.send( f"{Emoji.get_chat_emoji('WARNING')} No blog post data found, notifying <@180057061353193472>" ) # message = await notify_channel.send(f"{Emoji.get_chat_emoji('REFRESH')} Uploading files to CurseForge...") # code, output, errors = await Utils.execute(f'cd BuildCraft/uploader && gradle curseforge -Pnew_version="{latestBC}"') # GearbotLogging.info(f"Upload to CF complete\n)------stdout------\n{output}\n------stderr------\n{errors}") # if code is 0: # content = f"{Emoji.get_chat_emoji('YES')} All archives successfully uploaded" # await message.edit(content=content) # else: # content = f"{Emoji.get_chat_emoji('NO')} Upload failed with code {code}, notifying <@106354106196570112>" # await notify_channel.send(content) # update FAQs if needed async with session.get( 'https://mod-buildcraft.com/website_src/faq.md') as reply: data = await reply.text() h = hashlib.md5(data.encode('utf-8')).hexdigest() old = Configuration.get_persistent_var("BCFAQ", "") channel = cog.bot.get_channel(361557801492938762) # FAQs if channel is not None and h != old: Configuration.set_persistent_var("BCFAQ", h) #clean the old stuff await channel.purge() #send banner with open("BuildCraft/FAQs.png", "rb") as file: await channel.send(file=File(file, filename="FAQs.png") ) #send content out = "" parts = [ d.strip("#").strip() for d in data.split("##")[1:] ] for part in parts: lines = part.splitlines() content = '\n'.join(lines[1:]) out += f"**```{lines[0].strip()}```**{content}\n" for page in Pages.paginate(out, max_chars=2048, max_lines=50): embed = Embed(description=page) await channel.send(embed=embed) pass except CancelledError: pass # bot shutdown except Exception as ex: cog.bot.errors = cog.bot.errors + 1 GearbotLogging.error( "Something went wrong in the BC version checker task") GearbotLogging.error(traceback.format_exc()) embed = discord.Embed(colour=discord.Colour(0xff0000), timestamp=datetime.datetime.utcfromtimestamp( time.time())) embed.set_author( name="Something went wrong in the BC version checker task:") embed.add_field(name="Exception", value=str(ex)) v = "" for line in traceback.format_exc().splitlines(): if len(v) + len(line) >= 1024: embed.add_field(name="Stacktrace", value=v) v = "" v = f"{v}\n{line}" if len(v) > 0: embed.add_field(name="Stacktrace", value=v) await GearbotLogging.bot_log(embed=embed) for i in range(1, 60): if cog.force or not cog.running: break await asyncio.sleep(10) GearbotLogging.info("BC version checking background task terminated")
async def on_command_error(bot, ctx: commands.Context, error): if isinstance(error, commands.NoPrivateMessage): await ctx.send("This command cannot be used in private messages.") elif isinstance(error, commands.BotMissingPermissions): GearbotLogging.error( f"Encountered a permission error while executing {ctx.command}.") await ctx.send(error) elif isinstance(error, commands.DisabledCommand): await ctx.send("Sorry. This command is disabled and cannot be used.") elif isinstance(error, commands.CheckFailure): if ctx.command.qualified_name is not "latest" and ctx.guild is not None and Configuration.getConfigVar( ctx.guild.id, "PERM_DENIED_MESSAGE"): await ctx.send( ":lock: You do not have the required permissions to run this command" ) elif isinstance(error, commands.CommandOnCooldown): await ctx.send(error) elif isinstance(error, commands.MissingRequiredArgument): await ctx.send( f"You are missing a required argument! (See {ctx.prefix}help {ctx.command.qualified_name} for info on how to use this command)." ) elif isinstance(error, commands.BadArgument): await ctx.send( f"Invalid argument given! (See {ctx.prefix}help {ctx.command.qualified_name} for info on how to use this commmand)." ) elif isinstance(error, commands.CommandNotFound): return elif isinstance(error, PeeweeException): await handle_database_error(bot) else: bot.errors = bot.errors + 1 # log to logger first just in case botlog logging fails as well GearbotLogging.exception( f"Command execution failed:\n" f" Command: {ctx.command}\n" f" Message: {ctx.message.content}\n" f" Channel: {'Private Message' if isinstance(ctx.channel, abc.PrivateChannel) else ctx.channel.name}\n" f" Sender: {ctx.author.name}#{ctx.author.discriminator}\n" f" Exception: {error}", error.original) # notify caller await ctx.send( ":rotating_light: Something went wrong while executing that command :rotating_light:" ) embed = discord.Embed(colour=discord.Colour(0xff0000), timestamp=datetime.datetime.utcfromtimestamp( time.time())) embed.set_author(name="Command execution failed:") embed.add_field(name="Command", value=ctx.command) embed.add_field(name="Original message", value=Utils.trim_message(ctx.message.content, 1024)) embed.add_field(name="Channel", value='Private Message' if isinstance( ctx.channel, abc.PrivateChannel) else f"{ctx.channel.name} ({ctx.channel.id})") embed.add_field(name="Sender", value=f"{ctx.author.name}#{ctx.author.discriminator}") embed.add_field(name="Exception", value=error.original) v = "" for line in traceback.format_tb(error.original.__traceback__): if len(v) + len(line) > 1024: embed.add_field(name="Stacktrace", value=v) v = "" v = f"{v}\n{line}" if len(v) > 0: embed.add_field(name="Stacktrace", value=v) await GearbotLogging.logToBotlog(embed=embed)