async def feature_disable(self, ctx, types:str): types = types.upper() disabled= [] ignored = [] known = [] unknown = [] for t2 in types.split(","): for t in t2.split(): t = t.strip(",").strip() if t != "": if t in Features.requires_logging: known.append(t) else: unknown.append(t) message = "" for t in known: if not Configuration.get_var(ctx.guild.id, t): ignored.append(t) else: disabled.append(t) Configuration.set_var(ctx.guild.id, t, False) if len(disabled) > 0: message += MessageUtils.assemble(ctx.guild.id, 'YES', 'features_disabled', count=len(disabled)) + ', '.join(disabled) if len(ignored) > 0: message += MessageUtils.assemble(ctx.guild.id, 'WARNING', 'feature_already_disabled', count=len(ignored)) + ', '.join(ignored) if len(unknown) > 0: message += MessageUtils.assemble(ctx.guild.id, 'NO', 'features_unknown', count=len(unknown)) + ', '.join(unknown) await ctx.send(message, embed=self.get_features_status(ctx))
async def enable_feature(self, ctx, types): types = types.upper() enabled = [] ignored = [] known = [] unknown = [] for t2 in types.split(","): for t in t2.split(): t = t.strip(",").strip() if t != "": if t in Features.requires_logging: known.append(t) else: unknown.append(t) message = "" for t in known: if Configuration.get_var(ctx.guild.id, t): ignored.append(t) else: enabled.append(t) Configuration.set_var(ctx.guild.id, t, True) if t == "EDIT_LOGS": await ctx.send(Translator.translate('minor_log_caching_start', ctx)) self.bot.to_cache.append(ctx) if len(enabled) > 0: message += MessageUtils.assemble(ctx.guild.id, 'YES', 'features_enabled', count=len(enabled)) + ', '.join(enabled) if len(ignored) > 0: message += MessageUtils.assemble(ctx.guild.id, 'WARNING', 'feature_already_enabled', count=len(ignored)) + ', '.join(ignored) if len(unknown) > 0: message += MessageUtils.assemble(ctx.guild.id, 'NO', 'logs_unknown', count=len(unknown)) + ', '.join(unknown) await ctx.send(message, embed=self.get_features_status(ctx))
async def emoji_roles_remove(self, ctx, emote: discord.Emoji, roles: Greedy[discord.Role]): if roles is None: return MessageUtils.send_to(ctx, 'NO', 'roles_no_roles') todo = set() refused = set() for role in roles: (refused if role not in emote.roles else todo).add(role) new_roles = list(emote.roles) for role in todo: new_roles.remove(role) await emote.edit(name=emote.name, roles=new_roles) await asyncio.sleep(1) # sleep so the cache can update embed = Embed(color=0x2db1f3) self.add_emoji_info(ctx, embed, emote) message = MessageUtils.assemble(ctx, "YES", "emoji_roles_remove_success", roles=self.pretty_role_list(todo, ctx)) if len(refused) > 0: message += "\n" + MessageUtils.assemble( ctx, "NO", "emoji_roles_remove_role_not_in_list", roles=self.pretty_role_list(refused, ctx)) await ctx.send(message)
async def yes(interaction: Interaction): await interaction.response.edit_message( content=MessageUtils.assemble(ctx, "REFRESH", "processing"), view=None) failures = await Actions.mass_action(ctx, "warning", targets, self._warn, max_targets=10, allow_bots=False, message=False, reason=reason, dm_action=True) await interaction.edit_original_message( content=MessageUtils.assemble(ctx, "YES", "mwarn_confirmation", count=len(targets) - len(failures))) if len(failures) > 0: f = "\n".join(failures) pipe = self.bot.redis_pool.pipeline() k = f'mass_failures:{ctx.message.id}' pipe.set(k, f) pipe.expire(k, 7 * 24 * 60 * 60) await pipe.execute() pages = Pages.paginate(f, prefix='```\n', suffix='```') content, view, _ = SimplePager.get_parts( pages, 0, ctx.guild.id, f'mass_failures:{ctx.message.id}:warn') await ctx.send( f"**{Translator.translate('mass_failures_warn', ctx, page_num=1, pages=len(pages))}**{content}", view=view)
async def about(self, ctx): """about_help""" uptime = datetime.utcnow() - self.bot.start_time hours, remainder = divmod(int(uptime.total_seconds()), 3600) days, hours = divmod(hours, 24) minutes, seconds = divmod(remainder, 60) tacos = "{:,}".format(round(self.bot.eaten)) user_messages = "{:,}".format(self.bot.user_messages) bot_messages = "{:,}".format(self.bot.bot_messages) self_messages = "{:,}".format(self.bot.self_messages) total = "{:,}".format(sum(len(guild.members) for guild in self.bot.guilds)) unique = "{:,}".format(len(self.bot.users)) embed = discord.Embed(colour=discord.Colour(0x00cea2), timestamp=datetime.utcfromtimestamp(time.time()), description= MessageUtils.assemble(ctx, 'DIAMOND', 'about_spinning_gears', duration=Translator.translate('dhms', ctx, days=days, hours=hours, minutes=minutes, seconds=seconds)) + "\n"+ MessageUtils.assemble(ctx, 'GOLD', 'about_messages', user_messages=user_messages, bot_messages=bot_messages, self_messages=self_messages) + "\n"+ MessageUtils.assemble(ctx, 'IRON', 'about_grinders', errors=self.bot.errors) + "\n" + MessageUtils.assemble(ctx, 'STONE', 'about_commands', commandCount=self.bot.commandCount, custom_command_count=self.bot.custom_command_count) + "\n" + MessageUtils.assemble(ctx, 'WOOD', 'about_guilds', guilds=len(self.bot.guilds)) + "\n" + MessageUtils.assemble(ctx, 'INNOCENT', 'about_users', total=total, unique=unique) + "\n" + MessageUtils.assemble(ctx, 'TACO', 'about_tacos', tacos=tacos) + "\n" + MessageUtils.assemble(ctx, 'ALTER', 'commit_hash', hash=self.bot.version) + MessageUtils.assemble(ctx, 'TODO', 'about_stats')) click_here = Translator.translate('click_here', ctx) embed.add_field(name=Translator.translate('support_server', ctx), value=f"[{click_here}](https://discord.gg/vddW3D9)") embed.add_field(name=Translator.translate('website', ctx), value=f"[{click_here}](https://gearbot.rocks)") embed.add_field(name=f"Github", value=f"[{click_here}](https://github.com/gearbot/GearBot)") embed.set_footer(text=self.bot.user.name, icon_url=self.bot.user.avatar_url) await ctx.send(embed=embed)
async def on_member_ban(self, guild, user): if user.id == self.bot.user.id or not Features.is_logged( guild.id, "MOD_ACTIONS"): return fid = f"{guild.id}-{user.id}" if fid in self.bot.data["forced_exits"]: return self.bot.data["forced_exits"].add(fid) Infraction.update( active=False).where((Infraction.user_id == user.id) & (Infraction.type == "Unban") & (Infraction.guild_id == guild.id)).execute() await asyncio.sleep( 1 ) # sometimes we get the event before things are in the log for some reason limit = datetime.datetime.utcfromtimestamp(time.time() - 60) log = await self.find_log( guild, AuditLogAction.ban, lambda e: e.target == user and e.created_at > limit) if log is None: await asyncio.sleep(1) #is the api having a fit or so? #this fails way to often for my liking, alternative is adding a delay but this seems to do the trick for now log = await self.find_log( guild, AuditLogAction.ban, lambda e: e.target == user and e.created_at > limit) if log is not None: if log.reason is None: reason = Translator.translate("no_reason", guild.id) else: reason = log.reason InfractionUtils.add_infraction(guild.id, log.target.id, log.user.id, "Ban", reason) GearbotLogging.log_to( guild.id, "MOD_ACTIONS", MessageUtils.assemble(guild.id, "BAN", 'ban_log', user=Utils.clean_user(user), user_id=user.id, moderator=Utils.clean_user(log.user), moderator_id=log.user.id, reason=reason)) else: InfractionUtils.add_infraction(guild.id, user.id, 0, "Ban", "Manual ban") GearbotLogging.log_to( guild.id, "MOD_ACTIONS", MessageUtils.assemble(guild.id, "BAN", 'manual_ban_log', user=Utils.clean_user(user), user_id=user.id))
async def enable_trusted_bypass(self, ctx: commands.Context, enabled_status: bool): config_status = Configuration.get_var(ctx.guild.id, "CENSORING", "ALLOW_TRUSTED_BYPASS") enabled_string = "enabled" if enabled_status else "disabled" enabled_string = Translator.translate(enabled_string, ctx.guild.id) message = MessageUtils.assemble(ctx, "YES", "censor_trusted_bypass", status=enabled_string) if enabled_status == config_status: message = MessageUtils.assemble(ctx, "NO", f"censor_trusted_bypass_unchanged", status=enabled_string) else: Configuration.set_var(ctx.guild.id, "CENSORING", "ALLOW_TRUSTED_BYPASS", enabled_status) await ctx.send(message)
def log_key(guild_id, key, embed=None, file=None, can_stamp=True, tag_on=None, timestamp=datetime.now(), **kwargs): # logging category, emoji and info = LOG_TYPES[key] # determine where it should be logged so we don't need to bother assembling everything when it's just gona be voided anyways targets = [] channels = Configuration.get_var(guild_id, "LOG_CHANNELS") for cid, settings in channels.items(): if info.category in settings["CATEGORIES"] and info.config_key not in settings["DISABLED_KEYS"]: targets.append(cid) # no targets? don't bother with assembly if len(targets) is 0: return message = MessageUtils.assemble(guild_id, info.emoji, key, **kwargs).replace('@', '@\u200b') if can_stamp and Configuration.get_var(guild_id, 'GENERAL', "TIMESTAMPS"): s = datetime.strftime( timestamp.now().astimezone(pytz.timezone(Configuration.get_var(guild_id, 'GENERAL', 'TIMEZONE'))), '%H:%M:%S') stamp = f"[``{s}``] " message = Utils.trim_message(f'{stamp} {message}', 2000) if tag_on is not None: tag_on = tag_on.replace('@', '@\u200b') if tag_on is not None and len(message) + len(tag_on) <= 1998: message = f"{message} {tag_on}" tag_on = None message = Utils.trim_message(message, 2000) # queuing up log_to(guild_id, targets, message, embed, file, tag_on)
async def attempt_delivery(self, location, package): try: if location is None: return False if package.guild_id is None: jumplink_available = "Unavailable" else: jumplink_available = MessageUtils.construct_jumplink(package.guild_id, package.channel_id, package.message_id) mode = "dm" if isinstance(location, User) else "channel" now = datetime.utcfromtimestamp(time.time()) send_time = datetime.utcfromtimestamp(package.send.timestamp()) parts = { "date": send_time.strftime('%c'), "timediff": server_info.time_difference(now, send_time, None if isinstance(location, User) else location.guild.id), "now_date": now.strftime('%c'), "jump_link": jumplink_available, "recipient": None if isinstance(location, User) else (await Utils.get_user(package.user_id)).mention } parcel = Translator.translate(f"reminder_delivery_{mode}", None if isinstance(location, User) else location, **parts) content = f"```\n{package.to_remind}\n```" try: if len(parcel) + len(content) < 2000: await location.send(parcel + content) else: await location.send(parcel) await location.send(content) except (Forbidden, NotFound): return False else: return True except Exception as ex: await TheRealGearBot.handle_exception("Reminder delivery", self.bot, ex, None, None, None, location, package) return False
async def get_cached_page(interaction: Interaction, diff): meta = await get_meta(interaction.message.id) if meta is None: await interaction.response.send_message(MessageUtils.assemble(interaction.guild_id, 'NO', 'no_longer_valid'), ephemeral=True) return m = json.loads(meta) key = get_key(interaction.guild_id, m['query'], m['fields'].split(' '), m['amount']) count = await Utils.BOT.redis_pool.llen(key) page_num = m['current_page'] + diff if count == 0: count = await fetch_infraction_pages(interaction.guild_id, m['query'], m['amount'], m['fields'].split(' '), meta['current_page']) if page_num >= count: page_num = 0 elif page_num < 0: page_num = count - 1 page = (await Utils.BOT.wait_for("page_assembled", check=lambda l: l["key"] == key and l["page_num"] == page_num))["page"] else: if page_num >= count: page_num = 0 elif page_num < 0: page_num = count - 1 page = await Utils.BOT.redis_pool.lindex(key, page_num) pipe = Utils.BOT.redis_pool.pipeline() m['current_page'] = page_num pipe.set(f"inf_meta:{interaction.message.id}", json.dumps(m)) pipe.expire(f"inf_meta:{interaction.message.id}", 60 * 60 * 12) await pipe.execute() return page, page_num, count, m['query'], m['fields'].split(' ')
async def info(self, ctx, infraction:ServerInfraction): """inf_info_help""" embed = discord.Embed(color=0x00cea2, description=f"**{Translator.translate('reason', ctx)}**\n{infraction.reason}", timestamp=infraction.start) user = await Utils.get_user(infraction.user_id) mod = await Utils.get_user(infraction.mod_id) key = f"inf_{infraction.type.lower().replace(' ', '_')}" if infraction.end is None: duration = Translator.translate("unknown_duration", ctx) else: time = (infraction.end - infraction.start).total_seconds() if time % (60 * 60 * 24 * 7) == 0: duration = Translator.translate('weeks', ctx, weeks=int(time / (60 * 60 * 24 * 7))) elif time % (60 * 60 * 24) == 0: duration = Translator.translate('days', ctx, days=int(time / (60 * 60 * 24))) elif time % (60 * 60) == 0: duration = Translator.translate('hours_solo', ctx, hours=int(time / (60 * 60))) elif time % 60 == 0: duration = Translator.translate('minutes', ctx, minutes=int(time / 60)) else: # if you wana mute for someone for an arbitrary amount of seconds that isn't round minute, hour, day or week then it's not my problem it shows arbitrary amount of seconds duration = Translator.translate('seconds', ctx, seconds=int(time)) embed.set_author(name=Translator.translate(key, ctx, mod=Utils.username_from_user(mod), user=Utils.username_from_user(user), duration=duration), icon_url=mod.avatar_url) embed.set_thumbnail(url=user.avatar_url) embed.add_field(name=Translator.translate('moderator', ctx), value=Utils.clean_user(mod)) embed.add_field(name=Translator.translate('user', ctx), value=Utils.clean_user(user)) embed.add_field(name=Translator.translate('mod_id', ctx), value=infraction.mod_id) embed.add_field(name=Translator.translate('user_id', ctx), value=infraction.user_id) embed.add_field(name=Translator.translate('inf_added', ctx), value=infraction.start) if infraction.end is not None: embed.add_field(name=Translator.translate('inf_end', ctx), value=infraction.end) embed.add_field(name=Translator.translate('inf_active', ctx), value=MessageUtils.assemble(ctx, 'YES' if infraction.active else 'NO', str(infraction.active).lower())) images = self.IMAGE_MATCHER.findall(infraction.reason) if len(images) > 0: embed.set_image(url=images[0]) await ctx.send(embed=embed)
async def warn(self, ctx: commands.Context, member: DiscordUser, *, reason: Reason): """warn_help""" # don't allow warning GearBot, get some feedback about issues instead reason = Utils.enrich_reason(ctx, reason) if len(reason) > 1800: raise TranslatedBadArgument('reason_too_long', ctx) if member.id == self.bot.user.id: async def yes(): channel = self.bot.get_channel(Configuration.get_master_var("inbox", 0)) if channel is not None: await channel.send( f"[`{ctx.message.created_at.strftime('%c')}`] {ctx.message.author} (`{ctx.message.author.id}`) submitted feedback: {reason}") await MessageUtils.send_to(ctx, 'YES', 'feedback_submitted') message = MessageUtils.assemble(ctx, "THINK", "warn_to_feedback") await Confirmation.confirm(ctx, message, on_yes=yes) return if member.discriminator == '0000': await MessageUtils.send_to(ctx, 'NO', 'cant_warn_system_user') return if member.bot: await MessageUtils.send_to(ctx, "THINK", "cant_warn_bot") return await Actions.act(ctx, "warning", member.id, self._warn, allow_bots=False, reason=reason, check_bot_ability=False, require_on_server=False)
async def warn(self, ctx: commands.Context, member: discord.Member, *, reason: Reason): """warn_help""" # don't allow warning GearBot, get some feedback about issues instead if member.id == self.bot.user.id: async def yes(): channel = self.bot.get_channel( Configuration.get_master_var("inbox", 0)) if channel is not None: await channel.send( f"[`{ctx.message.created_at.strftime('%c')}`] {ctx.message.author} (`{ctx.message.author.id}`) submitted feedback: {reason}" ) await MessageUtils.send_to(ctx, 'YES', 'feedback_submitted') message = MessageUtils.assemble(ctx, "THINK", "warn_to_feedback") await Confirmation.confirm(ctx, message, on_yes=yes) if member.bot: await MessageUtils.send_to(ctx, "THINK", "cant_warn_bot") return await Actions.act(ctx, "warning", member.id, self._warn, allow_bots=False, reason=reason, check_bot_ability=False)
async def warn_punishment(self, v: Violation): reason = v.bucket["PUNISHMENT"].get("REASON", self.assemble_reason(v)) i = await InfractionUtils.add_infraction(v.guild.id, v.member.id, self.bot.user.id, 'Warn', reason) GearbotLogging.log_key(v.guild.id, 'warning_added_modlog', user=Utils.clean_user(v.member), moderator=Utils.clean_user(v.guild.me), reason=reason, user_id=v.member.id, moderator_id=v.guild.me.id, inf=i.id) try: dm_channel = await v.member.create_dm() await dm_channel.send( MessageUtils.assemble(dm_channel, 'WARNING', 'warning_dm', server=v.member.guild.name) + f"```{reason}```") except Forbidden: GearbotLogging.log_key(v.member.guild.id, 'warning_could_not_dm', user=Utils.escape_markdown(v.member.name), userid=v.member.id)
async def warn(self, ctx: commands.Context, member: discord.Member, *, reason: Reason): """warn_help""" if ctx.author != member and (ctx.author.top_role > member.top_role or ctx.guild.owner == ctx.author): if member.id == self.bot.user.id: async def yes(): channel = self.bot.get_channel(Configuration.get_master_var("inbox", 0)) if channel is not None: await channel.send(f"[`{ctx.message.created_at.strftime('%c')}`] {ctx.message.author} (`{ctx.message.author.id}`) submitted feedback: {reason}") await MessageUtils.send_to(ctx, 'YES', 'feedback_submitted') message = MessageUtils.assemble(ctx, "THINK", "warn_to_feedback") await Confirmation.confirm(ctx, message, on_yes=yes) else: if member.bot: await MessageUtils.send_to(ctx, "THINK", "cant_warn_bot") return i = InfractionUtils.add_infraction(ctx.guild.id, member.id, ctx.author.id, "Warn", reason) name = Utils.clean_user(member) await MessageUtils.send_to(ctx, 'YES', 'warning_added', user=name, inf=i.id) aname = Utils.clean_user(ctx.author) GearbotLogging.log_key(ctx.guild.id, 'warning_added_modlog', user=name, moderator=aname, reason=reason, user_id=member.id, moderator_id=ctx.author.id, inf=i.id) if Configuration.get_var(ctx.guild.id, "INFRACTIONS", "DM_ON_WARN"): try: dm_channel = await member.create_dm() await dm_channel.send( f"{Emoji.get_chat_emoji('WARNING')} {Translator.translate('warning_dm', ctx.guild.id, server=ctx.guild.name)}```{reason}```") except discord.Forbidden: GearbotLogging.log_key(ctx.guild.id, 'warning_could_not_dm', user=name, userid=member.id) else: await ctx.send(f"{Emoji.get_chat_emoji('NO')} {Translator.translate('warning_not_allowed', ctx.guild.id, user=member)}")
async def info(self, ctx, infraction: ServerInfraction): """inf_info_help""" embed = discord.Embed(color=0x00cea2, description=f"**{Translator.translate('reason', ctx)}**\n{infraction.reason}", timestamp=datetime.fromtimestamp(infraction.start)) user = await Utils.get_user(infraction.user_id) mod = await Utils.get_user(infraction.mod_id) key = f"inf_{infraction.type.lower().replace(' ', '_')}" if infraction.end is None: duration = Translator.translate("unknown_duration", ctx) else: time = (datetime.fromtimestamp(infraction.end) - datetime.fromtimestamp(infraction.start)).total_seconds() duration = Utils.to_pretty_time(time, ctx.guild.id) embed.set_author( name=Translator.translate(key, ctx, mod=Utils.username_from_user(mod), user=Utils.username_from_user(user), duration=duration), icon_url=mod.avatar_url) embed.set_thumbnail(url=user.avatar_url) embed.add_field(name=Translator.translate('moderator', ctx), value=Utils.clean_user(mod)) embed.add_field(name=Translator.translate('user', ctx), value=Utils.clean_user(user)) embed.add_field(name=Translator.translate('mod_id', ctx), value=infraction.mod_id) embed.add_field(name=Translator.translate('user_id', ctx), value=infraction.user_id) embed.add_field(name=Translator.translate('inf_added', ctx), value=datetime.fromtimestamp(infraction.start)) if infraction.end is not None: embed.add_field(name=Translator.translate('inf_end', ctx), value=datetime.fromtimestamp(infraction.end)) embed.add_field(name=Translator.translate('inf_active', ctx), value=MessageUtils.assemble(ctx, 'YES' if infraction.active else 'NO', str(infraction.active).lower())) images = self.IMAGE_MATCHER.findall(infraction.reason) if len(images) > 0: embed.set_image(url=images[0]) await ctx.send(embed=embed)
async def cancel(self, interaction: Interaction): if await self.execution_check(interaction): await interaction.response.edit_message( content=MessageUtils.assemble(self.guild_id, 'NO', 'command_canceled'), view=None) self.stop()
async def yes(interaction): nonlocal ok ok = True await interaction.response.edit_message( content=MessageUtils.assemble( ctx, 'YES', 'substituting_message_author'), view=None) ctx.bot.dispatch('message_author_confirmation', m.id)
async def on_member_unban(self, guild, user): fid = f"{guild.id}-{user.id}" if fid in self.bot.data["unbans"]: self.bot.data["unbans"].remove(fid) return elif not Features.is_logged(guild.id, "MOD_ACTIONS"): return Infraction.update( active=False).where((Infraction.user_id == user.id) & (Infraction.type == "Ban") & (Infraction.guild_id == guild.id)).execute() limit = datetime.datetime.utcfromtimestamp(time.time() - 60) log = await self.find_log( guild, AuditLogAction.unban, lambda e: e.target == user and e.created_at > limit) if log is None: # this fails way to often for my liking, alternative is adding a delay but this seems to do the trick for now log = await self.find_log( guild, AuditLogAction.unban, lambda e: e.target == user and e.created_at > limit) if log is not None: GearbotLogging.log_to( guild.id, "MOD_ACTIONS", MessageUtils.assemble(guild.id, 'INNOCENT', 'unban_log', user=Utils.clean_user(user), user_id=user.id, moderator=log.user, moderator_id=log.user.id, reason='Manual unban')) InfractionUtils.add_infraction(guild.id, user.id, log.user.id, "Unban", "Manual ban") else: GearbotLogging.log_to( guild.id, "MOD_ACTIONS", MessageUtils.assemble(guild.id, 'INNOCENT', 'manual_unban_log', user=Utils.clean_user(user), user_id=user.id)) InfractionUtils.add_infraction(guild.id, user.id, 0, "Unban", "Manual ban")
async def callback(self, interaction: Interaction): if self.user_id == interaction.user.id: await self.to_callback(interaction) self.view.stop() else: interaction.response.send_message(MessageUtils.assemble( self.guild_id, "NO", "wrong_interactor"), ephemeral=True) await self.to_callback(interaction)
def add_emoji_info(location, embed, emoji): embed.set_image(url=emoji.url) embed.add_field(name=Translator.translate('id', location), value=emoji.id) embed.add_field(name=Translator.translate('name', location), value=emoji.name) for t in ["require_colons", "animated", "managed"]: v = str(getattr(emoji, t)).lower() embed.add_field(name=Translator.translate(f'emoji_{t}', location), value=MessageUtils.assemble(location, 'YES' if v == 'true' else 'NO', v)) if len(emoji.roles) > 0: roles = ", ".join(r.mention for r in emoji.roles) else: roles = Translator.translate("emoji_role_no_restrictions", location) embed.add_field(name=Translator.translate("emoji_role_restrictions", location), value=roles)
async def info(self, ctx, infraction: ServerInfraction): """inf_info_help""" embed = discord.Embed(color=0x00cea2, description=f"**{Translator.translate('reason', ctx)}**\n{infraction.reason}", timestamp=infraction.start) user = await Utils.get_user(infraction.user_id) mod = await Utils.get_user(infraction.mod_id) key = f"inf_{infraction.type.lower().replace(' ', '_')}" if infraction.end is None: duration = Translator.translate("unknown_duration", ctx) else: partcount = 0 parts = { 'weeks': 60 * 60 * 24 * 7, 'days': 60 * 60 * 24, 'hours_solo': 60 * 60, 'minutes': 60, 'seconds': 1 } duration = "" time = (infraction.end - infraction.start).total_seconds() for k, v in parts.items(): if time / v >= 1: amount = math.floor(time/v) time -= amount if partcount == 1: duration += ", " duration += Translator.translate(k, ctx, amount=amount) embed.set_author( name=Translator.translate(key, ctx, mod=Utils.username_from_user(mod), user=Utils.username_from_user(user), duration=duration), icon_url=mod.avatar_url) embed.set_thumbnail(url=user.avatar_url) embed.add_field(name=Translator.translate('moderator', ctx), value=Utils.clean_user(mod)) embed.add_field(name=Translator.translate('user', ctx), value=Utils.clean_user(user)) embed.add_field(name=Translator.translate('mod_id', ctx), value=infraction.mod_id) embed.add_field(name=Translator.translate('user_id', ctx), value=infraction.user_id) embed.add_field(name=Translator.translate('inf_added', ctx), value=infraction.start) if infraction.end is not None: embed.add_field(name=Translator.translate('inf_end', ctx), value=infraction.end) embed.add_field(name=Translator.translate('inf_active', ctx), value=MessageUtils.assemble(ctx, 'YES' if infraction.active else 'NO', str(infraction.active).lower())) images = self.IMAGE_MATCHER.findall(infraction.reason) if len(images) > 0: embed.set_image(url=images[0]) await ctx.send(embed=embed)
async def add_logging(self, ctx, channel:discord.TextChannel, *, types): cid = str(channel.id) channels = Configuration.get_var(ctx.guild.id, "LOG_CHANNELS") if cid not in channels: channels[cid] = { "CATEGORIES": [], "DISABLED_KEYS": [] } info = channels[cid]["CATEGORIES"] added = [] ignored = [] message = "" known, unknown = self.extract_types(types) for t in known: if t in info: ignored.append(t) else: info.append(t) added.append(t) if len(added) > 0: message += f"{Emoji.get_chat_emoji('YES')} {Translator.translate('logs_added', ctx)}{', '.join(added)}" if len(ignored) > 0: message += f"\n{Emoji.get_chat_emoji('WARNING')}{Translator.translate('logs_ignored', ctx)}{', '.join(ignored)}" if len(unknown) > 0: message += f"\n {Emoji.get_chat_emoji('NO')}{Translator.translate('logs_unknown', ctx)}{', '.join(unknown)}" embed = discord.Embed(color=6008770) embed.add_field(name=channel.id, value=self.get_channel_properties(ctx, channel.id, channels[cid]["CATEGORIES"])) await ctx.send(message, embed=embed) Configuration.save(ctx.guild.id) features = [] for a in added: feature = Utils.find_key(Features.requires_logging, a) if feature is not None and not Configuration.get_var(ctx.guild.id, feature): features.append(feature) if len(features) > 0: async def yes(): await ctx.invoke(self.enable_feature, ", ".join(features)) await Confirmation.confirm(ctx, MessageUtils.assemble(ctx.guild.id, 'WHAT', 'confirmation_enable_features', count=len(features)) + ', '.join(features), on_yes=yes)
async def yes(interaction: disnake.Interaction): await infraction.delete() await interaction.response.edit_message( content=MessageUtils.assemble(ctx, "YES", "inf_delete_deleted", id=infraction.id), view=None) GearbotLogging.log_key(ctx.guild.id, 'inf_delete_log', id=infraction.id, target=Utils.clean_user(target), target_id=target.id, mod=Utils.clean_user(mod), mod_id=mod.id if mod is not None else 0, reason=reason, user=Utils.clean_user(ctx.author), user_id=ctx.author.id) InfractionUtils.clear_cache(ctx.guild.id)
async def about(self, ctx): """about_help""" uptime = datetime.utcnow() - self.bot.start_time hours, remainder = divmod(int(uptime.total_seconds()), 3600) days, hours = divmod(hours, 24) minutes, seconds = divmod(remainder, 60) user_messages = str(self.bot.user_messages) bot_messages = str(self.bot.bot_messages) self_messages = str(self.bot.self_messages) total = str(sum(len(guild.members) for guild in self.bot.guilds)) unique = str(len(self.bot.users)) embed = discord.Embed( colour=discord.Colour(0x00cea2), timestamp=datetime.utcfromtimestamp(time.time()), description=MessageUtils.assemble(ctx, 'DIAMOND', 'about_spinning_gears', duration=Translator.translate( 'dhms', ctx, days=days, hours=hours, minutes=minutes, seconds=seconds)) + "\n" + MessageUtils.assemble(ctx, 'GOLD', 'about_messages', user_messages=user_messages, bot_messages=bot_messages, self_messages=self_messages) + "\n" + MessageUtils.assemble( ctx, 'IRON', 'about_grinders', errors=self.bot.errors) + "\n" + MessageUtils.assemble( ctx, 'STONE', 'about_commands', commandCount=self.bot.commandCount, custom_command_count=self.bot.custom_command_count) + "\n" + MessageUtils.assemble( ctx, 'WOOD', 'about_guilds', guilds=len(self.bot.guilds)) + "\n" + MessageUtils.assemble( ctx, 'INNOCENT', 'about_users', total=total, unique=unique) + "\n" + "\n[bargebot](https://github.com/ginkoid/bargebot), a fork of [GearBot](https://github.com/gearbot/GearBot)" ) embed.set_footer(text=self.bot.user.name, icon_url=self.bot.user.avatar_url) await ctx.send(embed=embed)
async def channel(self, interaction: Interaction): if await self.execution_check(interaction): await Reminder.create(user_id=self.user_id, channel_id=self.channel_id, dm=False, to_remind=self.reminder, time=time.time() + self.duration, send=datetime.now().timestamp(), status=1, guild_id=self.guild_id, message_id=self.mid) await interaction.response.edit_message( content=MessageUtils.assemble(self.guild_id, "YES", f"reminder_confirmation_here", duration=Utils.to_pretty_time( self.duration, self.guild_id)), view=None) self.stop()
def log_to(guild_id, key, embed=None, file=None, can_stamp=True, tag_on=None, **kwargs): if isinstance(embed, int): raise ValueError("WTH IS SPAMMING MY LOGS?") info = LOG_TYPES[key] remaining = None if key is None and embed is None and file is None: raise ValueError("What the heck is trying to log nothing?") stamp = f"[``{datetime.strftime(datetime.now().astimezone(pytz.timezone(Configuration.get_var(guild_id, 'TIMEZONE'))), '%H:%M:%S')}``] " if can_stamp and Configuration.get_var( guild_id, "TIMESTAMPS") else "" m = MessageUtils.assemble(guild_id, info.emoji, key, **kwargs).replace('@', '@\u200b') message = f"{stamp}{Utils.trim_message(m, 1984)}" if tag_on is not None: tag_on = tag_on.replace('@', '@\u200b') if message is None: message = tag_on else: if len(message) + len(tag_on) <= 1998: message = f"{message} {tag_on}" else: remaining = tag_on if message is not None: message = Utils.trim_message(message, 1998) channels = Configuration.get_var(guild_id, "LOG_CHANNELS") for cid, logging_keys in channels.items(): if info.category in logging_keys: if remaining is None: LOG_PUMP.receive(cid, (message, embed, file)) else: LOG_PUMP.receive(cid, (message, None, None)) LOG_PUMP.receive(cid, (tag_on, embed, file))
async def on_interaction(self, interaction): guild = self.bot.get_guild(interaction.guild_id) if interaction.type == InteractionType.component: cid = interaction.data.custom_id if cid.startswith('self_role'): parts = cid.split(':') if parts[1] == 'role': rid = parts[2] if rid.isnumeric(): rid = int(rid) roles = Configuration.get_var(interaction.guild_id, "ROLES", "SELF_ROLES") if rid in roles: role = guild.get_role(rid) if role is None: roles.remove(rid) Configuration.set_var(interaction.guild_id, "ROLES", "SELF_ROLES", roles) v = SelfRoleView(guild=guild, page=0) interaction.response.edit_message( content=Translator.translate( "assignable_roles", interaction.guild_id, server_name=guild.name, page_num=1, page_count=v.pages), view=v) interaction.followup.send_message( MessageUtils.assemble( interaction.guild_id, 'WARNING', 'self_role_missing'), ephemeral=True) else: try: if role in interaction.user.roles: await interaction.user.remove_roles( role) await interaction.response.send_message( Translator.translate( "role_left", interaction.guild_id, role_name=role.name, user=interaction.user), ephemeral=True) else: await interaction.user.add_roles(role) await interaction.response.send_message( Translator.translate( "role_joined", interaction.guild_id, role_name=role.name, user=interaction.user), ephemeral=True) except disnake.Forbidden: await interaction.response.send_message( f"{Emoji.get_chat_emoji('NO')} {Translator.translate('role_too_high_add', interaction.guild_id, role=role.name)}", ephemeral=True) elif parts[1] == "page": v = SelfRoleView(guild=interaction.guild, page=int(parts[2])) await interaction.response.edit_message( content=Translator.translate( "assignable_roles", interaction.guild_id, server_name=interaction.guild.name, page_num=int(parts[2]) + 1, page_count=v.pages), view=v) elif cid.startswith('help:'): parts = cid.split(':') if parts[1] == 'page': q = parts[3] if parts[3] != 'None' else None content, view = await Help.message_parts( self.bot, q, guild, interaction.user, int(parts[2])) await interaction.response.edit_message(content=content, view=view) elif parts[1] == 'selector': q = interaction.values[0] q = q if q != 'None' else None content, view = await Help.message_parts( self.bot, q, guild, interaction.user, 0) await interaction.response.edit_message(content=content, view=view) elif cid.startswith('pager:'): parts = cid.split(':') t = parts[2] if t == 'eval': if interaction.user.id not in Configuration.get_master_var( 'BOT_ADMINS'): return output = await self.bot.redis_pool.get(f'eval:{parts[3]}') if output is None: await interaction.response.send_message( "Eval output no longer available", ephemeral=True) else: pages = Pages.paginate(output, prefix='```py\n', suffix='```') content, view, page_num = SimplePager.get_parts( pages, int(parts[1]), interaction.guild_id, f'eval:{parts[3]}') await interaction.response.edit_message( content= f'Eval output {page_num + 1}/{len(pages)}{content}', view=view) elif t == 'commands': cog = self.bot.get_command("CustCommands") if cog is not None: pages = cog.get_command_pages(interaction.guild_id) content, view, page_num = SimplePager.get_parts( pages, int(parts[1]), guild.id, 'commands') page = cog.gen_command_page(pages, page_num, interaction.guild) await interaction.response.edit_message(embed=page, view=view) elif t == 'emoji': cog = self.bot.get_cog('Emoji') if cog is not None: amount = len(guild.emojis) + 1 content, view, page_num = SimplePager.get_parts( range(amount), int(parts[1]), interaction.guild.id, 'emoji') await interaction.response.edit_message( embed=cog.gen_emoji_page(guild, page_num), view=view) elif t == 'role_list': cog = self.bot.get_cog('Moderation') if cog is not None: pages = cog.gen_roles_pages(interaction.guild, parts[3]) content, view, page_num = SimplePager.get_parts( pages, int(parts[1]), guild.id, f'role_list:{parts[3]}') await interaction.response.edit_message( content= f"**{Translator.translate('roles', interaction.guild_id, server_name=guild.name, page_num=page_num + 1, pages=len(pages))}**```\n{pages[page_num]}```", view=view) elif t == 'censor_list': cog = self.bot.get_cog('Moderation') if cog is not None: censor_list = Configuration.get_var( interaction.guild.id, "CENSORING", "TOKEN_CENSORLIST") pages = Pages.paginate("\n".join(censor_list)) page, view, page_num = SimplePager.get_parts( pages, int(parts[1]), interaction.guild.id, 'censor_list') await interaction.response.edit_message( content= f"**{Translator.translate(f'censor_list', guild, server=guild.name, page_num=page_num + 1, pages=len(pages))}**```\n{page}```", view=view) elif t == 'word_censor_list': cog = self.bot.get_cog('Moderation') if cog is not None: censor_list = Configuration.get_var( guild.id, "CENSORING", "WORD_CENSORLIST") pages = Pages.paginate("\n".join(censor_list)) page, view, page_num = SimplePager.get_parts( pages, int(parts[1]), guild.id, 'word_censor_list') await interaction.response.edit_message( content= f"**{Translator.translate(f'word_censor_list', guild, server=guild.name, page_num=page_num + 1, pages=len(pages))}**```\n{page}```", view=view) elif t == 'full_censor_list': cog = self.bot.get_cog('Moderation') if cog is not None: censor_list = Configuration.get_var( guild.id, "CENSORING", "FULL_MESSAGE_LIST") pages = Pages.paginate("\n".join(censor_list)) page, view, page_num = SimplePager.get_parts( pages, int(parts[1]), guild.id, 'full_censor_list') await interaction.response.edit_message( content= f"**{Translator.translate(f'full_censor_list', guild, server=guild.name, page_num=page_num + 1, pages=len(pages))}**```\n{page}```", view=view) elif t == 'flag_list': cog = self.bot.get_cog('Moderation') if cog is not None: censor_list = Configuration.get_var( guild.id, "FLAGGING", "TOKEN_LIST") pages = Pages.paginate("\n".join(censor_list)) page, view, page_num = SimplePager.get_parts( pages, int(parts[1]), guild.id, 'flag_list') await interaction.response.edit_message( content= f"**{Translator.translate(f'flagged_list', guild, server=guild.name, page_num=page_num + 1, pages=len(pages))}**```\n{page}```", view=view) elif t == 'word_flag_list': cog = self.bot.get_cog('Moderation') if cog is not None: censor_list = Configuration.get_var( guild.id, "FLAGGING", "WORD_LIST") pages = Pages.paginate("\n".join(censor_list)) page, view, page_num = SimplePager.get_parts( pages, int(parts[1]), guild.id, 'word_flag_list') await interaction.response.edit_message( content= f"**{Translator.translate(f'flagged_word_list', guild, server=guild.name, page_num=page_num + 1, pages=len(pages))}**```\n{page}```", view=view) elif t == 'mass_failures': output = await self.bot.redis_pool.get( f'mass_failures:{parts[3]}') if output is None: await interaction.response.send_message( MessageUtils.assemble(interaction.guild_id, 'NO', 'view_expired'), ephemeral=True) else: pages = Pages.paginate(output, prefix='```\n', suffix='```') content, view, page_num = SimplePager.get_parts( pages, int(parts[1]), interaction.guild_id, f'mass_failures:{parts[3]}:{parts[4]}') await interaction.response.edit_message( content= f"**{Translator.translate(f'mass_failures_{parts[4]}', interaction.guild_id, page_num=page_num+1, pages=len(pages))}**{content}", view=view) elif cid.startswith('einf_search:'): parts = cid.split(':') uid = int(parts[1]) old_page = int(parts[2]) t = parts[3] if t == 'first_page': page, current, pages, query, fields = await get_ephemeral_cached_page( interaction, uid, 0) await interaction.response.edit_message( content=await InfractionUtils.assemble_message( interaction.guild_id, page, query, current, pages), view=EphemeralInfSearch(filters=fields, pages=pages, current_page=current, guild_id=interaction.guild_id, userid=uid)) elif t == 'prev_page': page, current, pages, query, fields = await get_ephemeral_cached_page( interaction, uid, old_page - 1) await interaction.response.edit_message( content=await InfractionUtils.assemble_message( interaction.guild_id, page, query, current, pages), view=EphemeralInfSearch(filters=fields, pages=pages, current_page=current, guild_id=interaction.guild_id, userid=uid)) elif t == 'blank': await interaction.response.send_message( Emoji.get_chat_emoji('AE'), ephemeral=True) elif t == 'next_page': page, current, pages, query, fields = await get_ephemeral_cached_page( interaction, uid, old_page + 1) await interaction.response.edit_message( content=await InfractionUtils.assemble_message( interaction.guild_id, page, query, current, pages), view=EphemeralInfSearch(filters=fields, pages=pages, current_page=current, guild_id=interaction.guild_id, userid=uid)) elif t == 'last_page': page, current, pages, query, fields = await get_ephemeral_cached_page( interaction, uid, 1000) await interaction.response.edit_message( content=await InfractionUtils.assemble_message( interaction.guild_id, page, query, current, pages), view=EphemeralInfSearch(filters=fields, pages=pages, current_page=current, guild_id=interaction.guild_id, userid=uid)) elif interaction.type == InteractionType.application_command: if interaction.data.name == "Extract user IDs": self.bot.metrics.uid_usage.labels( type="channel", cluster=self.bot.cluster).inc() await interaction.response.defer(ephemeral=True) parts = await Utils.get_user_ids(interaction.target.content) if len(parts) > 0: for chunk in Pages.paginate("\n".join(parts), 200): await interaction.followup.send(chunk) else: await interaction.followup.send( MessageUtils.assemble(interaction.guild, "NO", "no_uids_found")) elif interaction.data.name == "Send user IDs to DM": self.bot.metrics.uid_usage.labels( type="DM", cluster=self.bot.cluster).inc() await interaction.response.defer(ephemeral=True) parts = await Utils.get_user_ids(interaction.target.content) if len(parts) > 0: try: for chunk in Pages.paginate("\n".join(parts), 200): await interaction.user.send(chunk) except Forbidden: await interaction.followup.send("Unable to send DM") else: await interaction.followup.send("IDs sent in DM") else: try: await interaction.user.send( MessageUtils.assemble(interaction.guild, "NO", "no_uids_found")) except Forbidden: await interaction.followup.send("Unable to send DM") else: await interaction.followup.send("IDs sent in DM") elif interaction.data.name == "Userinfo": if await Permissioncheckers.check_permission( self.bot.get_command("userinfo"), interaction.guild, interaction.user, self.bot): t = "allowed" target = interaction.data.target embed = await Utils.generate_userinfo_embed( target, target if isinstance(target, Member) else None, interaction.guild, interaction.user) await interaction.response.send_message(embed=embed, ephemeral=True) else: t = "denied" await interaction.response.send_message( MessageUtils.assemble(interaction.guild, 'LOCK', 'permission_denied'), ephemeral=True) self.bot.metrics.userinfo_usage.labels( type=t, cluster=self.bot.cluster).inc() elif interaction.data.name == "Search Infractions": if await Permissioncheckers.check_permission( self.bot.get_command("inf search"), interaction.guild, interaction.user, self.bot): uid = interaction.data.target.id t = "allowed" await interaction.response.send_message( MessageUtils.assemble(interaction.guild, 'SEARCH', 'inf_search_compiling'), ephemeral=True) pages = await InfractionUtils.fetch_infraction_pages( interaction.guild.id, uid, 100, ["[user]", "[mod]", "[reason]"], 0) page = await self.bot.wait_for( 'page_assembled', check=lambda l: l['key'] == get_key( interaction.guild.id, uid, [ "[user]", "[mod]", "[reason]" ], 100) and l['page_num'] == 0) await interaction.edit_original_message( content=await InfractionUtils.assemble_message( interaction.guild.id, page['page'], uid, 0, pages), view=EphemeralInfSearch( filters=["[user]", "[mod]", "[reason]"], pages=pages, guild_id=interaction.guild.id, userid=uid)) else: t = "denied" await interaction.response.send_message( MessageUtils.assemble(interaction.guild, 'LOCK', 'permission_denied'), ephemeral=True) self.bot.metrics.inf_search_usage.labels( type=t, cluster=self.bot.cluster).inc()
async def generate_userinfo_embed(user, member, guild, requested_by): now = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc) embed = disnake.Embed( color=member.top_role.color if member is not None else 0x00cea2, timestamp=now) embed.set_thumbnail(url=user.display_avatar.url) embed.set_footer(text=Translator.translate('requested_by', guild, user=requested_by.name), icon_url=requested_by.display_avatar.url) embed.add_field(name=Translator.translate('name', guild), value=escape_markdown(f"{user.name}#{user.discriminator}"), inline=True) embed.add_field(name=Translator.translate('id', guild), value=user.id, inline=True) embed.add_field(name=Translator.translate('bot_account', guild), value=user.bot, inline=True) embed.add_field(name=Translator.translate('animated_avatar', guild), value=user.display_avatar.is_animated if isinstance( user.display_avatar.is_animated, bool) else user.display_avatar.is_animated(), inline=True) embed.add_field( name=Translator.translate('avatar_url', guild), value= f"[{Translator.translate('avatar_url', guild)}]({user.display_avatar.url})" ) embed.add_field(name=Translator.translate("profile", guild), value=user.mention) if member is not None: embed.add_field(name=Translator.translate('nickname', guild), value=escape_markdown(member.nick), inline=True) role_list = [ role.mention for role in reversed(member.roles) if role is not guild.default_role ] if len(role_list) > 60: embed.add_field(name=Translator.translate('all_roles', guild), value=Translator.translate('too_many_many_roles', guild), inline=False) elif len(role_list) > 40: embed.add_field(name=Translator.translate('all_roles', guild), value=Translator.translate('too_many_roles', guild), inline=False) elif len(role_list) > 0: embed.add_field(name=Translator.translate('all_roles', guild), value=" ".join(role_list), inline=False) else: embed.add_field(name=Translator.translate('all_roles', guild), value=Translator.translate("no_roles", guild), inline=False) embed.add_field( name=Translator.translate('joined_at', guild), value= f"{(now - member.joined_at).days} days ago (``{member.joined_at}``)", inline=True) embed.add_field( name=Translator.translate('account_created_at', guild), value= f"{(now - user.created_at).days} days ago (``{user.created_at}``)", inline=True) infs = "" if Configuration.get_master_var("global_inf_counter", True): infractions = await Infraction.filter(user_id=user.id, type__not="Note") il = len(infractions) seen = [] ild = 0 for i in infractions: if i.guild_id not in seen: seen.append(i.guild_id) ild += 1 emoji = "SINISTER" if il >= 2 else "INNOCENT" infs += MessageUtils.assemble( guild, emoji, "total_infractions", total=il, servers=ild) + "\n" infractions = await Infraction.filter(user_id=user.id, guild_id=guild.id, type__not="Note") emoji = "SINISTER" if len(infractions) >= 2 else "INNOCENT" embed.add_field( name=Translator.translate("infractions", guild), value=infs + MessageUtils.assemble( guild, emoji, "guild_infractions", count=len(infractions))) return embed
async def timeout(): if m is not None: await m.edit(content=MessageUtils.assemble( ctx, 'NO', 'command_canceled'), view=None)