async def hotreload(self, ctx:commands.Context): self.bot.hot_reloading = True GearbotLogging.SHOULD_TERMINATE = True message = await GearbotLogging.bot_log(f"{Emoji.get_chat_emoji('REFRESH')} Hot reload in progress...") ctx_message = await ctx.send(f"{Emoji.get_chat_emoji('REFRESH')} Hot reload in progress...") GearbotLogging.info("Initiating hot reload") await asyncio.sleep(2) utils = importlib.reload(Util) await utils.reload(self.bot) GearbotLogging.info("Reloading all cogs...") temp = [] for cog in ctx.bot.cogs: temp.append(cog) for cog in temp: self.bot.unload_extension(f"Cogs.{cog}") GearbotLogging.info(f'{cog} has been unloaded.') self.bot.load_extension(f"Cogs.{cog}") GearbotLogging.info(f'{cog} has been loaded.') GearbotLogging.info("Hot reload complete.") m = f"{Emoji.get_chat_emoji('YES')} Hot reload complete" await message.edit(content=m) await ctx_message.edit(content=m) await Translator.upload() await DocUtils.update_docs(ctx.bot) self.bot.hot_reloading = False
async def on_ready(bot): if not bot.STARTUP_COMPLETE: await initialize(bot, True) #shutdown handler for clean exit on linux try: for signame in ('SIGINT', 'SIGTERM'): asyncio.get_event_loop().add_signal_handler(getattr(signal, signame), lambda: asyncio.ensure_future(Utils.cleanExit(bot, signame))) except Exception: pass #doesn't work on windows bot.start_time = datetime.utcnow() GearbotLogging.info("Loading cogs...") for extension in Configuration.get_master_var("COGS"): try: bot.load_extension("Cogs." + extension) except Exception as e: await handle_exception(f"Failed to load cog {extension}", bot, e) GearbotLogging.info("Cogs loaded") to_unload = Configuration.get_master_var("DISABLED_COMMANDS", []) for c in to_unload: bot.remove_command(c) bot.STARTUP_COMPLETE = True info = await bot.application_info() bot.loop.create_task(keepDBalive(bot)) # ping DB every hour so it doesn't run off gears = [Emoji.get_chat_emoji(e) for e in ["WOOD", "STONE", "IRON", "GOLD", "DIAMOND"]] a = " ".join(gears) b = " ".join(reversed(gears)) await GearbotLogging.bot_log(message=f"{a} All gears turning at full speed, {info.name} ready to go! {b}") await bot.change_presence(activity=Activity(type=3, name='the gears turn')) else: await bot.change_presence(activity=Activity(type=3, name='the gears turn'))
async def taco_eater(self): """A person can eat a taco every 5 mins, we run every 5s""" GearbotLogging.info("Time to start munching on some 🌮") while self.running: self.bot.eaten += len(self.bot.users) / 60 await asyncio.sleep(5) GearbotLogging.info("Cog terminated, guess no more 🌮 for people")
async def on_ready(): if not bot.STARTUP_COMPLETE: await Util.readyBot(bot) Emoji.on_ready(bot) Utils.on_ready(bot) Translator.on_ready(bot) bot.loop.create_task( keepDBalive()) # ping DB every hour so it doesn't run off #shutdown handler for clean exit on linux try: for signame in ('SIGINT', 'SIGTERM'): asyncio.get_event_loop().add_signal_handler( getattr(signal, signame), lambda: asyncio.ensure_future( Utils.cleanExit(bot, signame))) except Exception: pass #doesn't work on windows bot.aiosession = aiohttp.ClientSession() bot.start_time = datetime.datetime.utcnow() GearbotLogging.info("Loading cogs...") for extension in extensions: try: bot.load_extension("Cogs." + extension) except Exception as e: GearbotLogging.exception( f"Failed to load extention {extension}", e) GearbotLogging.info("Cogs loaded") if Configuration.getMasterConfigVar("CROWDIN_KEY") is not None: bot.loop.create_task(translation_task()) bot.STARTUP_COMPLETE = True await bot.change_presence( activity=discord.Activity(type=3, name='the gears turn'))
async def hotreload(self, ctx: commands.Context): message = await GearbotLogging.bot_log( f"{Emoji.get_chat_emoji('REFRESH')} Hot reload in progress...") ctx_message = await ctx.send( f"{Emoji.get_chat_emoji('REFRESH')} Hot reload in progress...") GearbotLogging.info("Initiating hot reload") GearbotLogging.LOG_PUMP.running = False importlib.reload(Reloader) for c in Reloader.components: importlib.reload(c) GearbotLogging.info("Reloading all cogs...") temp = [] for cog in self.bot.cogs: temp.append(cog) for cog in temp: self.bot.unload_extension(f"Cogs.{cog}") GearbotLogging.info(f'{cog} has been unloaded.') self.bot.load_extension(f"Cogs.{cog}") GearbotLogging.info(f'{cog} has been loaded.') to_unload = Configuration.get_master_var("DISABLED_COMMANDS", []) for c in to_unload: self.bot.remove_command(c) await TheRealGearBot.initialize(self.bot) GearbotLogging.info("Hot reload complete.") m = f"{Emoji.get_chat_emoji('YES')} Hot reload complete" await message.edit(content=m) await ctx_message.edit(content=m) await Translator.upload() self.bot.hot_reloading = False
async def taco_eater(self): """A person can eat a taco every 5 mins, we run every 15s""" GearbotLogging.info("Time to start munching on some ЁЯМо") while self.running: self.bot.eaten += len(self.bot.users) / 20 # update stats in redis await self.bot.redis_pool.hmset_dict( "botstats", { "start_time": str(self.bot.start_time), "user_mesages": str(self.bot.user_messages), "bot_messages": str(self.bot.bot_messages), "own_messages": str(self.bot.self_messages), "total_members": str(sum(len(guild.members) for guild in self.bot.guilds)), "unique_members": str(len(self.bot.users)), "taco_count": str(round(self.bot.eaten)), "random_number": random.randint(0, 5000), "commands_executed": str(self.bot.commandCount), "custom_commands_executed": str(self.bot.custom_command_count), "guilds": len(self.bot.guilds) }) await asyncio.sleep(15) GearbotLogging.info("Cog terminated, guess no more ЁЯМо for people")
async def node_init(generation, resource_version): from database import DatabaseConnector from database.DatabaseConnector import Node await DatabaseConnector.init() hostname = os.uname()[1] GearbotLogging.info( f"GearBot clusternode {hostname} (generation {generation}). Trying to figure out where i fit in" ) existing = await Node.filter(hostname=hostname, generation=generation).get_or_none() if existing is None: count = 0 while count < 100: try: await Node.create(hostname=hostname, generation=generation, resource_version=resource_version, shard=count) return count except Exception as ex: GearbotLogging.exception("did something go wrong?", ex) count += 1 else: return existing.shard
async def update_site(self, ctx): GearbotLogging.info("Site update initiated") await DocUtils.update_docs(ctx) message = await ctx.send( f"{Emoji.get_chat_emoji('REFRESH')} Purging cloudflare cache") cloudflare_info = Configuration.get_master_var("CLOUDFLARE", {}) if 'ZONE' in cloudflare_info: headers = { "X-Auth-Email": cloudflare_info["EMAIL"], "X-Auth-Key": cloudflare_info["KEY"], "Content-Type": "application/json" } async with self.bot.aiosession.post( f"https://api.cloudflare.com/client/v4/zones/{cloudflare_info['ZONE']}/purge_cache", json=dict(purge_everything=True), headers=headers) as reply: content = await reply.json() GearbotLogging.info(f"Cloudflare purge response: {content}") if content["success"]: await message.edit( content= f"{Emoji.get_chat_emoji('YES')} Cloudflare cache has been purged" ) else: await message.edit( content= f"{Emoji.get_chat_emoji('NO')} Cloudflare cache purge failed" )
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")
async def db_cleaner(self): if Configuration.get_master_var("purge_db", True): # purge all messages older then 6 weeks snowflake = time_snowflake(datetime.datetime.utcfromtimestamp(time() - 60*60*24*7*6).replace(tzinfo=datetime.timezone.utc)) purged_attachments = await LoggedAttachment.filter(id__lt=snowflake).delete() purged = await LoggedMessage.filter(messageid__lt=snowflake).delete() GearbotLogging.info(f"Purged {purged} old messages and {purged_attachments} attachments")
async def get_info(self, ctx, project_name, log): while project_name in self.fetching: # already fetching, wait for data to arrive await asyncio.sleep(1) if not project_name in self.cf_cache.keys(): self.fetching.append(project_name) if log: message = await ctx.send( f"<a:gearLoading:468054357724889089> {Translator.translate('fetching_info', ctx)} <a:gearLoading:468054357724889089>" ) info = await self.fetch_info(project_name) if info is False: if log: await ctx.send(Translator.translate( 'cf_fetch_failed', ctx)) else: GearbotLogging.info( f"Retrieved project data for {project_name}, adding to cache." ) self.cf_cache[project_name] = { "info": info, "time": datetime.datetime.utcnow() } self.fetching.remove(project_name) if log: await message.delete() return info else: return self.cf_cache[project_name]["info"]
async def update_lang(lang, retry=True): t_info = Configuration.get_master_var("TRANSLATIONS") if t_info["SOURCE"] == "DISABLED": return if t_info["SOURCE"] == "CROWDIN": download_link = f"https://api.crowdin.com/api/project/gearbot/export-file?login={t_info['LOGIN']}&account-key={t_info['KEY']}&json&file={urllib.parse.quote('/bot/commands.json', safe='')}&language={lang}" else: download_link = f"https://gearbot.rocks/lang/{lang}.json" GearbotLogging.info(f"Updating {lang} ({LANG_NAMES[lang]}) file...") async with BOT.aiosession.get(download_link) as response: content = await response.text() content = json.loads(content) if "success" in content: if retry: GearbotLogging.warn( f"Failed to update {lang} ({LANG_NAMES[lang]}), trying again in 3 seconds" ) await asyncio.sleep(3) await update_lang(lang, False) else: await tranlator_log( 'NO', f"Failed to update {lang} ({LANG_NAMES[lang]}) from {t_info['SOURCE']}" ) Utils.save_to_disk(f'lang/{lang}', content) LANGS[lang] = content GearbotLogging.info(f"Updated {lang} ({LANG_NAMES[lang]})!")
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)
def getConfigVar(id, key): if not id in SERVER_CONFIGS.keys(): GearbotLogging.info( f"Config entry requested before config was loaded for guild {id}, loading config for it" ) loadConfig(id) return SERVER_CONFIGS[id][key]
async def actually_fill_cache(bot): # don't try to start a new one when one is already pending if bot.chunker_pending: return await GearbotLogging.bot_log( f"{Emoji.get_chat_emoji('REFRESH')} Cluster {bot.cluster} cache reset initiated" ) # claim the pending spot bot.chunker_pending = True # terminate running queue if needed count = 0 while bot.chunker_active: bot.chunker_should_terminate = True await asyncio.sleep(0.5) count += 1 if count > 120: await GearbotLogging.bot_log( "Failure to reset the chunker after a reconnect, assuming it is stuck and proceeding with the chunking to attempt to recover." ) break # we are now the active chunker bot.chunker_pending = False bot.chunker_active = True bot.chunker_should_terminate = False # grab a copy of the current guild list as this can mutate while we work guild_ids = [guild.id for guild in bot.guilds] await GearbotLogging.bot_log( f"{Emoji.get_chat_emoji('LOADING')} Cache population in progress on cluster {bot.cluster}, fetching users from {len(bot.guilds)} guilds" ) # chunk them all # TODO: split per shard if this turns out to be too slow but the distribution between shards should be fairly even so the performance impact should be limited done = 0 for gid in guild_ids: if bot.chunker_should_terminate is True: return guild = bot.get_guild(gid) if guild is None: GearbotLogging.info( f"Tried to fetch {gid} for chunking but it no longer exists, assuming we where removed or it went unavailable" ) else: await guild.chunk(cache=True) await asyncio.sleep(0.1) done += 1 bot.chunker_active = False if bot.chunker_should_terminate: await GearbotLogging.bot_log( f"{Emoji.get_chat_emoji('WARNING')} Cache population aborted for cluster {bot.cluster} with {len(guild_ids) - done} left to go!" ) else: await GearbotLogging.bot_log( f"{Emoji.get_chat_emoji('YES')} Cache population completed for cluster {bot.cluster}!" )
async def upgrade(name, bot): await GearbotLogging.bot_log( f"{Emoji.get_chat_emoji('REFRESH')} Upgrade initiated by {name}") GearbotLogging.info(f"Upgrade initiated by {name}") file = open("upgradeRequest", "w") file.write("upgrade requested") file.close() await bot.logout()
async def on_guild_remove(guild): blocked = Configuration.get_persistent_var("blacklist", []) if guild.id not in blocked: GearbotLogging.info( f"I was removed from a guild: {guild.name} ({guild.id}).") await GearbotLogging.bot_log( f"{Emoji.get_chat_emoji('LEAVE')} I was removed from a guild: {guild.name} ({guild.id}).", embed=server_info.server_info(guild))
async def unload(self, ctx, cog: str): if cog in ctx.bot.cogs: self.bot.unload_extension(f"Cogs.{cog}") await ctx.send(f'**{cog}** has been unloaded.') await GearbotLogging.bot_log(f'**{cog}** has been unloaded by {ctx.author.name}') GearbotLogging.info(f"{cog} has been unloaded") else: await ctx.send(f"{Emoji.get_chat_emoji('NO')} I can't find that cog.")
async def load(self, ctx, cog: str): if os.path.isfile(f"Cogs/{cog}.py") or os.path.isfile(f"GearBot/Cogs/{cog}.py"): self.bot.load_extension(f"Cogs.{cog}") await ctx.send(f"**{cog}** has been loaded!") await GearbotLogging.bot_log(f"**{cog}** has been loaded by {ctx.author.name}.") GearbotLogging.info(f"{cog} has been loaded") else: await ctx.send(f"{Emoji.get_chat_emoji('NO')} I can't find that cog.")
async def cache_task(modlog: ModLog): GearbotLogging.info("Started modlog background task.") while modlog.running: if len(modlog.bot.to_cache) > 0: ctx = modlog.bot.to_cache.pop(0) await modlog.buildCache(ctx.guild) await ctx.send("Caching complete.") await asyncio.sleep(1) GearbotLogging.info("modlog background task terminated.")
def get_var(id, key): if id is None: raise ValueError("Where is this coming from?") if not id in SERVER_CONFIGS.keys(): GearbotLogging.info( f"Config entry requested before config was loaded for guild {id}, loading config for it" ) load_config(id) return SERVER_CONFIGS[id][key]
def upload_file(): data = {'files[/bot/commands.json]': open('lang/en_US.json', 'r')} crowdin_data = Configuration.get_master_var( "TRANSLATIONS", dict(SOURCE="SITE", CHANNEL=0, KEY="", LOGIN="", WEBROOT="")) reply = requests.post( f"https://api.crowdin.com/api/project/gearbot/update-file?login={crowdin_data['LOGIN']}&account-key={crowdin_data['KEY']}&json", files=data) GearbotLogging.info(reply)
async def on_guild_update(before, after): if after.owner is not None and after.owner.id in Configuration.get_persistent_var("user_blacklist", []): GearbotLogging.info( f"Someone transferred {after.name} ({after.id}) to ({after.owner} ({after.owner.id})) but they are blacklisted") try: await after.owner.send(f"Someone transferred {after.name} (``{after.id}``) to you, but you have been blacklisted due to bot abuse, so i left") except Exception: pass await after.leave()
def get_var(id, section, key=None, default=None): if id is None: raise ValueError("Where is this coming from?") if not id in SERVER_CONFIGS.keys(): GearbotLogging.info(f"Config entry requested before config was loaded for guild {id}, loading config for it") load_config(id) s = SERVER_CONFIGS[id].get(section, {}) if key is not None: s = s.get(key, default) return s
async def on_guild_remove(bot, guild): blocked = Configuration.get_persistent_var("server_blocklist", []) blocked_users = Configuration.get_persistent_var("user_blocklist", []) if guild.id not in blocked and guild.owner_id not in blocked_users: GearbotLogging.info( f"I was removed from a guild: {guild.name} ({guild.id}).") bot.metrics.bot_guilds.labels(cluster=bot.cluster).dec() await GearbotLogging.bot_log( f"{Emoji.get_chat_emoji('LEAVE')} I was removed from a guild: {guild.name} ({guild.id}).", embed=server_info.server_info_embed(guild))
async def selfrole_updater(self): GearbotLogging.info("Selfrole view updater enabled") while self.running: guild_id = await self.bot.wait_for("self_roles_update") # make sure we shouldn't have terminated yet if not self.running: return todo = await Selfroles.self_cleaner(self.bot, guild_id) for t in sorted(todo, key=lambda l: l[0], reverse=True): await ReactionManager.on_reaction(self.bot, t[0], t[1], 0, "ЁЯФБ")
def fetch_from_disk(filename, alternative=None): try: with open(f"{filename}.json", encoding="UTF-8") as file: return json.load(file) except FileNotFoundError: GearbotLogging.info( f"Tried to load {filename}.json but couldn't find it on disk") if alternative is not None: return fetch_from_disk(alternative) return dict()
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
async def upgrade(self, ctx): await ctx.send( "<:BCWrench:344163417981976578> I'll be right back with new gears! <:woodGear:344163118089240596> <:stoneGear:344163146325295105> <:ironGear:344163170664841216> <:goldGear:344163202684289024> <:diamondGear:344163228101640192>" ) await GearbotLogging.bot_log(f"Upgrade initiated by {ctx.author.name}") GearbotLogging.info(f"Upgrade initiated by {ctx.author.name}") file = open("upgradeRequest", "w") file.write("upgrade requested") file.close() await self.bot.logout() await self.bot.close()
async def upgrade(self, ctx): await ctx.send( f"{Emoji.get_chat_emoji('WRENCH')} I'll be right back with new gears! {Emoji.get_chat_emoji('WOOD')} {Emoji.get_chat_emoji('STONE')} {Emoji.get_chat_emoji('IRON')} {Emoji.get_chat_emoji('GOLD')} {Emoji.get_chat_emoji('DIAMOND')}" ) await GearbotLogging.bot_log( f"{Emoji.get_chat_emoji('REFRESH')} Upgrade initiated by {ctx.author.name}" ) GearbotLogging.info(f"Upgrade initiated by {ctx.author.name}") file = open("upgradeRequest", "w") file.write("upgrade requested") file.close() await self.bot.logout()