async def try_mute_on_rejoin(member: discord.Member, db: db_hlapi, client: discord.Client, log: str, ramfs: lexdpyk.ram_filesystem) -> None: mute_role_id = db.grab_config("mute-role") if mute_role_id and (mute_role := member.guild.get_role(int(mute_role_id))): success: bool try: await member.add_roles(mute_role) success = True except discord.errors.Forbidden: success = False stringcases = { False: "was not able to be remuted (permissions error)", True: "was remuted", } if log and (channel := client.get_channel(int(log))): if isinstance(channel, discord.TextChannel): muted_embed = discord.Embed(title=f"Notify on muted member join: {member}", description=f"This user has an entry in the mute database and {stringcases[success]}.") muted_embed.color = load_embed_color(member.guild, embed_colors.primary, ramfs) muted_embed.set_footer(text=f"uid: {member.id}") await catch_logging_error(channel, muted_embed)
async def ping_function(message: discord.Message, args: List[str], client: discord.Client, **kwargs: Any) -> Any: if not message.guild: return 1 stats = kwargs["stats"] ping_embed = discord.Embed(title="Pong!", color=load_embed_color(message.guild, embed_colors.primary, kwargs["ramfs"])) add_timestamp(ping_embed, "Total Process Time", stats["start"], stats["end"]) add_timestamp(ping_embed, "Config Load Time", stats["start-load-blacklist"], stats["end-load-blacklist"]) add_timestamp(ping_embed, "Automod Process Time", stats["start-automod"], stats["end-automod"]) add_timestamp(ping_embed, "WS Latency", 0, ctime(client.latency)) send_start = ctime(time.time()) sent_message = await message.channel.send(embed=ping_embed) send_end = ctime(time.time()) add_timestamp(ping_embed, "Send Message", send_start, send_end) await sent_message.edit(embed=ping_embed)
async def grab_guild_info(message: discord.Message, args: List[str], client: discord.Client, **kwargs: Any) -> Any: if not message.guild: return 1 guild = message.guild embed_col = load_embed_color(guild, embed_colors.primary, kwargs["ramfs"]) guild_embed = discord.Embed(title=f"Information on {guild}", color=embed_col) if guild.owner: guild_embed.add_field(name="Server Owner:", value=guild.owner.mention) guild_embed.add_field(name="# of Roles:", value=f"{len(guild.roles)} Roles") guild_embed.add_field(name="Top Role:", value=guild.roles[-1].mention) guild_embed.add_field(name="Member Count:", value=str(guild.member_count)) guild_embed.add_field(name="Creation Date:", value=parsedate(guild.created_at)) guild_embed.set_footer(text=f"gid: {guild.id}") guild_embed.set_thumbnail(url=cast(str, guild.icon_url)) try: await message.channel.send(embed=guild_embed) except discord.errors.Forbidden: raise lib_sonnetcommands.CommandError(constants.sonnet.error_embed)
async def single_command(self, cmd_name: str) -> discord.Embed: cmds_dict = self.ctx.cmds_dict if "alias" in cmds_dict[cmd_name]: cmd_name = cmds_dict[cmd_name]["alias"] command = SonnetCommand(cmds_dict[cmd_name]) cmd_embed = discord.Embed(title=f'Command "{cmd_name}"', description=command.description, color=load_embed_color( self.guild, embed_colors.primary, self.ctx.ramfs)) cmd_embed.set_author(name=self.helpname) cmd_embed.add_field(name="Usage:", value=self.prefix + command.pretty_name, inline=False) if "rich_description" in command: cmd_embed.add_field(name="Detailed information:", value=command.rich_description, inline=False) if isinstance(command.permission, str): perms = command.permission elif isinstance(command["permission"], (tuple, list)): perms = command.permission[0] else: perms = "NULL" hasperm = await parse_permissions(self.message, self.ctx.conf_cache, command.permission, verbose=False) permstr = f" (You {'do not '*(not hasperm)}have this perm)" cmd_embed.add_field(name="Permission level:", value=perms + permstr) aliases = ", ".join( filter( lambda c: "alias" in cmds_dict[c] and cmds_dict[c]["alias"] == cmd_name, cmds_dict)) if aliases: cmd_embed.add_field(name="Aliases:", value=aliases, inline=False) return cmd_embed
async def full_help(self, page: int, per_page: int) -> discord.Embed: cmds = self.ctx.cmds cmds_dict = self.ctx.cmds_dict if page < 0 or page >= (len(self.ctx.cmds) + (per_page - 1)) // per_page: raise lib_sonnetcommands.CommandError( f"ERROR: No such page {page+1}") cmd_embed = discord.Embed( title= f"Category Listing (Page {page+1} / {(len(cmds) + (per_page-1))//per_page})", color=load_embed_color(self.guild, embed_colors.primary, self.ctx.ramfs)) cmd_embed.set_author(name=self.helpname) total = 0 # Total counting is seperate due to pagination not counting all modules for cmd in cmds_dict: if 'alias' not in cmds_dict[cmd]: total += 1 for module in sorted( cmds, key=lambda m: m.category_info['pretty_name'])[( page * per_page):(page * per_page) + per_page]: mnames = [ f"`{i}`" for i in module.commands if 'alias' not in module.commands[i] ] helptext = ', '.join( mnames) if mnames else module.category_info['description'] cmd_embed.add_field( name= f"{module.category_info['pretty_name']} ({module.category_info['name']})", value=helptext, inline=False) cmd_embed.set_footer( text=f"Total Commands: {total} | Total Endpoints: {len(cmds_dict)}" ) return cmd_embed
async def delete_infraction(message: discord.Message, args: List[str], client: discord.Client, ctx: CommandCtx) -> Any: if not message.guild: return 1 if args: with db_hlapi(message.guild.id) as db: infraction = db.grab_infraction(args[0]) if not infraction: await message.channel.send( "ERROR: Infraction ID does not exist") return 1 # pylint: disable=E1136 db.delete_infraction(infraction[0]) else: await message.channel.send("ERROR: No argument supplied") return 1 if not ctx.verbose: return # pylint: disable=E0633 infraction_id, user_id, moderator_id, infraction_type, reason, timestamp = infraction infraction_embed = discord.Embed( title="Infraction Deleted", description=f"Infraction for <@{user_id}>:", color=load_embed_color(message.guild, embed_colors.deletion, ctx.ramfs)) infraction_embed.add_field(name="Infraction ID", value=infraction_id) infraction_embed.add_field(name="Moderator", value=f"<@{moderator_id}>") infraction_embed.add_field(name="Type", value=infraction_type) infraction_embed.add_field(name="Reason", value=reason) infraction_embed.set_footer(text=f"uid: {user_id}, unix: {timestamp}") infraction_embed.timestamp = datetime_unix(int(timestamp)) try: await message.channel.send(embed=infraction_embed) except discord.errors.Forbidden: await message.channel.send(constants.sonnet.error_embed) return 1
async def on_member_update(before: discord.Member, after: discord.Member, **kargs: Any) -> None: inc_statistics_better(before.guild.id, "on-member-update", kargs["kernel_ramfs"]) username_log = load_message_config(before.guild.id, kargs["ramfs"], datatypes=join_leave_user_logs)["username-log"] if username_log and (channel := kargs["client"].get_channel(int(username_log))): if before.nick == after.nick: return message_embed = discord.Embed(title="Nickname updated", color=load_embed_color(before.guild, embed_colors.edit, kargs["ramfs"])) message_embed.set_author(name=f"{before} ({before.id})", icon_url=user_avatar_url(before)) message_embed.add_field(name=("Before" + " | False" * (not before.nick)), value=str(before.nick)) message_embed.add_field(name=("After" + " | False" * (not after.nick)), value=str(after.nick)) message_embed.timestamp = ts = datetime_now() message_embed.set_footer(text=f"unix: {int(ts.timestamp())}") await catch_logging_error(channel, message_embed)
async def avatar_function(message: discord.Message, args: List[str], client: discord.Client, **kwargs: Any) -> Any: if not message.guild: return 1 user, _ = await parse_user_member_noexcept(message, args, client, default_self=True) embed = discord.Embed(description=f"{user.mention}'s Avatar", color=load_embed_color(message.guild, embed_colors.primary, kwargs["ramfs"])) embed.set_image(url=user_avatar_url(user)) embed.timestamp = datetime_now() try: await message.channel.send(embed=embed) except discord.errors.Forbidden: raise lib_sonnetcommands.CommandError(constants.sonnet.error_embed)
async def gdpr_database(message: discord.Message, args: List[str], client: discord.Client, **kwargs: Any) -> Any: if not message.guild: return 1 ramfs = kwargs["ramfs"] if len(args) >= 2: command = args[0] confirmation = args[1] elif len(args) >= 1: command = args[0] confirmation = "" else: command = "" PREFIX = kwargs["conf_cache"]["prefix"] gdprfunctions = gdpr_functions() if command and command in gdprfunctions.commands: if confirmation and confirmation == str(message.guild.id): await gdprfunctions.commands[command](message, message.guild.id, ramfs, kwargs["kernel_ramfs"]) else: await message.channel.send( f"Please provide the guild id to confirm\nEx: `{PREFIX}gdpr {command} {message.guild.id}`" ) else: message_embed = discord.Embed(title="GDPR COMMANDS", color=load_embed_color( message.guild, embed_colors.primary, kwargs["ramfs"])) message_embed.add_field(name=f"{PREFIX}gdpr download <guild id>", value="Download the databases of this guild", inline=False) message_embed.add_field( name=f"{PREFIX}gdpr delete <guild id>", value="Delete the databases of this guild and clear cache", inline=False) await message.channel.send(embed=message_embed)
async def list_reactionroles(message: discord.Message, args: List[str], client: discord.Client, **kwargs: Any) -> Any: if not message.guild: return 1 with db_hlapi(message.guild.id) as db: data = json.loads(db.grab_config("reaction-role-data") or "{}") reactionrole_embed = discord.Embed( title=f"ReactionRoles in {message.guild}", color=load_embed_color(message.guild, embed_colors.primary, kwargs["ramfs"])) if data: if len(data) <= 25: for i in data: reactionrole_embed.add_field( name=i, value="\n".join([ f"{emoji}: <@&{data[i][emoji]}>" for emoji in data[i] ])) await message.channel.send(embed=reactionrole_embed) else: fileobj = io.BytesIO() fileobj.write(json.dumps(data).encode("utf8")) fileobj.seek(0) dfile = discord.File(fileobj, filename="RR.json") await message.channel.send("Too many rr messages to send in embed", file=dfile) else: await message.channel.send("This guild has no reactionroles")
async def on_message_edit(old_message: discord.Message, message: discord.Message, **kargs: Any) -> None: client: discord.Client = kargs["client"] ramfs: lexdpyk.ram_filesystem = kargs["ramfs"] kernel_ramfs: lexdpyk.ram_filesystem = kargs["kernel_ramfs"] # Ignore bots if parse_skip_message(client, message): return elif not message.guild: return inc_statistics_better(message.guild.id, "on-message-edit", kernel_ramfs) # Add to log with db_hlapi(message.guild.id) as db: message_log_str = db.grab_config("message-edit-log") or db.grab_config("message-log") # Skip logging if message is the same or mlog doesnt exist if message_log_str and not (old_message.content == message.content): if message_log := client.get_channel(int(message_log_str)): if not isinstance(message_log, discord.TextChannel): return lim: int = constants.embed.field.value message_embed = discord.Embed(title=f"Message edited in #{message.channel}", color=load_embed_color(message.guild, embed_colors.edit, ramfs)) message_embed.set_author(name=f"{message.author} ({message.author.id})", icon_url=user_avatar_url(message.author)) old_msg = (old_message.content or "NULL") message_embed.add_field(name="Old Message", value=(old_msg)[:lim], inline=False) if len(old_msg) > lim: message_embed.add_field(name="(Continued)", value=(old_msg)[lim:lim * 2], inline=False) msg = (message.content or "NULL") message_embed.add_field(name="New Message", value=(msg)[:lim], inline=False) if len(msg) > lim: message_embed.add_field(name="(Continued)", value=(msg)[lim:lim * 2], inline=False) message_embed.set_footer(text=f"Message ID: {message.id}") message_embed.timestamp = datetime_now() asyncio.create_task(catch_logging_error(message_log, message_embed, None))
async def notify_problem(member: discord.Member, ptype: List[str], log: str, client: discord.Client, ramfs: lexdpyk.ram_filesystem) -> None: if log and (channel := client.get_channel(int(log))): if not isinstance(channel, discord.TextChannel): return notify_embed = discord.Embed(title=f"Notify on member join: {member}", description=f"Notifying for: {', '.join(ptype)}", color=load_embed_color(member.guild, embed_colors.primary, ramfs)) notify_embed.set_footer(text=f"uid: {member.id}") await catch_logging_error(channel, notify_embed)
async def help_function(message: discord.Message, args: List[str], client: discord.Client, ctx: CommandCtx) -> Any: if not message.guild: return 1 helpname: str = f"{BOT_NAME} Help" per_page: int = 10 cmds = ctx.cmds cmds_dict = ctx.cmds_dict parser = Parser("help") pageP = parser.add_arg(["-p", "--page"], lambda s: int(s) - 1) commandonlyP = parser.add_arg("-c", lib_tparse.store_true, flag=True) try: parser.parse(args, stderr=io.StringIO(), exit_on_fail=False, lazy=True, consume=True) except lib_tparse.ParseFailureError: raise lib_sonnetcommands.CommandError("Could not parse pagecount") page = pageP.get(0) commandonly = commandonlyP.get() is True prefix = ctx.conf_cache["prefix"] help_helper = HelpHelper(message, message.guild, args, client, ctx, prefix, helpname) if args: modules = {mod.category_info["name"] for mod in cmds} # Per module help if (a := args[0].lower()) in modules and not commandonly: description, commands, curmod = await help_helper.category_help(a) pagecount = (len(commands) + (per_page - 1)) // per_page cmd_embed = discord.Embed( title= f"{curmod.category_info['pretty_name']} (Page {page+1} / {pagecount})", description=description, color=load_embed_color(message.guild, embed_colors.primary, ctx.ramfs)) cmd_embed.set_author(name=helpname) if not (0 <= page < pagecount): # pytype: disable=unsupported-operands raise lib_sonnetcommands.CommandError( f"ERROR: No such page {page+1}") for name, desc in commands[page * per_page:(page * per_page) + per_page]: cmd_embed.add_field(name=name, value=desc, inline=False) try: await message.channel.send(embed=cmd_embed) except discord.errors.Forbidden: raise lib_sonnetcommands.CommandError( constants.sonnet.error_embed) # Per command help elif a in cmds_dict: try: await message.channel.send( embed=await help_helper.single_command(a)) except discord.errors.Forbidden: raise lib_sonnetcommands.CommandError( constants.sonnet.error_embed) # Do not echo user input else: # lets check if they cant read documentation probably_tried_paging: bool try: probably_tried_paging = int( args[0]) <= ((len(cmds) + (per_page - 1)) // per_page) except ValueError: probably_tried_paging = False no_command_text: str = f"No command {'or command module '*(not commandonly)}with that name" if probably_tried_paging: raise lib_sonnetcommands.CommandError( f"{no_command_text} (did you mean `{prefix}help -p {int(args[0])}`?)" ) raise lib_sonnetcommands.CommandError(no_command_text)
except ValueError: chan = 0 c = client.get_channel(chan) log_channel = c if isinstance(c, discord.TextChannel) else None # Send infraction to database db.add_infraction(generated_id, str(user.id), str(moderator.id), i_type, i_reason, int(timestamp.timestamp())) if log_channel: log_embed = discord.Embed(title=BOT_NAME, description=f"New infraction for {user}:", color=load_embed_color( message.guild, embed_colors.creation, ramfs)) log_embed.set_thumbnail(url=user_avatar_url(user)) log_embed.add_field(name="Infraction ID", value=generated_id) log_embed.add_field(name="Moderator", value=moderator.mention) log_embed.add_field(name="User", value=user.mention) log_embed.add_field(name="Type", value=i_type) log_embed.add_field(name="Reason", value=i_reason) log_embed.set_footer( text=f"uid: {user.id}, unix: {int(timestamp.timestamp())}") asyncio.create_task(catch_logging_error(log_embed, log_channel)) if not to_dm: return generated_id, None
async def grab_guild_message(message: discord.Message, args: List[str], client: discord.Client, ctx: CommandCtx) -> Any: if not message.guild: return 1 discord_message, nargs = await parse_channel_message_noexcept( message, args, client) if not discord_message.guild: await message.channel.send("ERROR: Message not in any guild") return 1 sendraw = False for arg in args[nargs:]: if arg in ["-r", "--raw"]: sendraw = True break # Generate replies message_content = generate_reply_field(discord_message) # Message has been grabbed, start generating embed message_embed = discord.Embed( title=f"Message in #{discord_message.channel}", description=message_content, color=load_embed_color(message.guild, embed_colors.primary, ctx.ramfs)) message_embed.set_author(name=str(discord_message.author), icon_url=user_avatar_url(discord_message.author)) message_embed.timestamp = discord_message.created_at # Grab files from cache fileobjs = grab_files(discord_message.guild.id, discord_message.id, ctx.kernel_ramfs) # Grab files async if not in cache if not fileobjs: awaitobjs = [ asyncio.create_task(i.to_file()) for i in discord_message.attachments ] fileobjs = [await i for i in awaitobjs] if sendraw: file_content = io.BytesIO(discord_message.content.encode("utf8")) fileobjs.append( discord.File( file_content, filename= f"{discord_message.id}.at.{int(datetime_now().timestamp())}.txt" )) try: await message.channel.send(embed=message_embed, files=fileobjs) except discord.errors.HTTPException: try: await message.channel.send( "There were files attached but they exceeded the guild filesize limit", embed=message_embed) except discord.errors.Forbidden: await message.channel.send(constants.sonnet.error_embed) return 1
async def profile_function(message: discord.Message, args: List[str], client: discord.Client, **kwargs: Any) -> Any: if not message.guild: return 1 user, member = await parse_user_member_noexcept(message, args, client, default_self=True) # Status hashmap status_map = { "online": "🟢 (online)", "offline": "⚫ (offline)", "idle": "🟡 (idle)", "dnd": "🔴 (dnd)", "do_not_disturb": "🔴 (dnd)", "invisible": "⚫ (offline)" } embed = discord.Embed(title="User Information", description=f"User information for {user.mention}:", color=load_embed_color(message.guild, embed_colors.primary, kwargs["ramfs"])) embed.set_thumbnail(url=user_avatar_url(user)) embed.add_field(name="Username", value=str(user), inline=True) embed.add_field(name="User ID", value=str(user.id), inline=True) if member: embed.add_field(name="Status", value=status_map[member.raw_status], inline=True) embed.add_field(name="Highest Rank", value=f"{member.top_role.mention}", inline=True) embed.add_field(name="Created", value=parsedate(user.created_at), inline=True) if member: embed.add_field(name="Joined", value=parsedate(member.joined_at), inline=True) # Parse adding infraction count with db_hlapi(message.guild.id) as db: viewinfs = parse_boolean( db.grab_config("member-view-infractions") or "0") moderator = await parse_permissions(message, kwargs["conf_cache"], "moderator", verbose=False) if moderator or (viewinfs and user.id == message.author.id): embed.add_field( name="Infractions", value=f"{db.grab_filter_infractions(user=user.id, count=True)}" ) embed.timestamp = datetime_now() try: await message.channel.send(embed=embed) except discord.errors.Forbidden: raise lib_sonnetcommands.CommandError(constants.sonnet.error_embed)
async def grab_an_adult(discord_message: discord.Message, guild: discord.Guild, client: discord.Client, mconf: Dict[str, Any], ramfs: lexdpyk.ram_filesystem) -> None: if mconf["regex-notifier-log"] and (notify_log := client.get_channel(int(mconf["regex-notifier-log"]))): if not isinstance(notify_log, discord.TextChannel): return message_content = generate_reply_field(discord_message) # Message has been grabbed, start generating embed message_embed = discord.Embed(title=f"Auto Flagged Message in #{discord_message.channel}", description=message_content, color=load_embed_color(guild, embed_colors.primary, ramfs)) message_embed.set_author(name=str(discord_message.author), icon_url=user_avatar_url(discord_message.author)) message_embed.timestamp = discord_message.created_at # Grab files async awaitobjs = [asyncio.create_task(i.to_file()) for i in discord_message.attachments] fileobjs = [await i for i in awaitobjs] await catch_logging_error(notify_log, message_embed, fileobjs)
async def on_member_remove(member: discord.Member, **kargs: Any) -> None: inc_statistics_better(member.guild.id, "on-member-remove", kargs["kernel_ramfs"]) log_channels = load_message_config(member.guild.id, kargs["ramfs"], datatypes=join_leave_user_logs) # Try for leave-log, default to join-log if (joinlog := (log_channels["leave-log"] or log_channels["join-log"])): if logging_channel := kargs["client"].get_channel(int(joinlog)): # Only run if in a TextChannel if not isinstance(logging_channel, discord.TextChannel): return embed = discord.Embed(title=f"{member} left.", description=f"*{member.mention} left the server.*", color=load_embed_color(member.guild, embed_colors.deletion, kargs["ramfs"])) embed.set_thumbnail(url=user_avatar_url(member)) embed.timestamp = ts = datetime_now() embed.set_footer(text=f"uid: {member.id}, unix: {int(ts.timestamp())}") embed.add_field(name="Created", value=parsedate(member.created_at), inline=True) embed.add_field(name="Joined", value=parsedate(member.joined_at), inline=True) await catch_logging_error(logging_channel, embed)
async def on_message_delete(message: discord.Message, **kargs: Any) -> None: client: discord.Client = kargs["client"] kernel_ramfs: lexdpyk.ram_filesystem = kargs["kernel_ramfs"] ramfs: lexdpyk.ram_filesystem = kargs["ramfs"] # Ignore bots if parse_skip_message(client, message): return elif not message.guild: return files: Optional[List[discord.File]] = grab_files(message.guild.id, message.id, kernel_ramfs, delete=True) inc_statistics_better(message.guild.id, "on-message-delete", kernel_ramfs) # Add to log with db_hlapi(message.guild.id) as db: message_log = db.grab_config("message-log") try: if not (message_log and (log_channel := client.get_channel(int(message_log)))): return except ValueError: try: await message.channel.send("ERROR: message-log config is corrupt in database, please reset") except discord.errors.Forbidden: pass return if not isinstance(log_channel, discord.TextChannel): return message_embed = discord.Embed( title=f"Message deleted in #{message.channel}", description=message.content[:constants.embed.description], color=load_embed_color(message.guild, embed_colors.deletion, ramfs) ) # Parse for message lengths >2048 (discord now does 4000 hhhhhh) if len(message.content) > constants.embed.description: limend = constants.embed.description + constants.embed.field.value message_embed.add_field(name="(Continued)", value=message.content[constants.embed.description:limend]) if len(message.content) > limend: flimend = limend + constants.embed.field.value message_embed.add_field(name="(Continued further)", value=message.content[limend:flimend]) message_embed.set_author(name=f"{message.author} ({message.author.id})", icon_url=user_avatar_url(message.author)) if (r := message.reference) and (rr := r.resolved) and isinstance(rr, discord.Message): message_embed.add_field(name="Replying to:", value=f"{rr.author.mention} [(Link)]({rr.jump_url})")
async def on_member_join(member: discord.Member, **kargs: Any) -> None: client: discord.Client = kargs["client"] ramfs: lexdpyk.ram_filesystem = kargs["ramfs"] inc_statistics_better(member.guild.id, "on-member-join", kargs["kernel_ramfs"]) notifier_cache = load_message_config(member.guild.id, ramfs, datatypes=join_notifier) issues: List[str] = [] # Handle notifer logging if member.id in notifier_cache["notifier-log-users"]: issues.append("User") if abs(discord_datetime_now().timestamp() - member.created_at.timestamp()) < int(notifier_cache["notifier-log-timestamp"]): issues.append("Timestamp") if int(notifier_cache["notifier-log-defaultpfp"]) and has_default_avatar(member): issues.append("Default pfp") if issues: asyncio.create_task(notify_problem(member, issues, notifier_cache["regex-notifier-log"], client, ramfs)) joinlog = load_message_config(member.guild.id, ramfs, datatypes=join_leave_user_logs)["join-log"] # Handle join logs if joinlog and (logging_channel := client.get_channel(int(joinlog))): embed = discord.Embed(title=f"{member} joined.", description=f"*{member.mention} joined the server.*", color=load_embed_color(member.guild, embed_colors.creation, ramfs)) embed.set_thumbnail(url=user_avatar_url(member)) embed.timestamp = ts = datetime_now() embed.set_footer(text=f"uid: {member.id}, unix: {int(ts.timestamp())}") embed.add_field(name="Created", value=parsedate(member.created_at), inline=True) if isinstance(logging_channel, discord.TextChannel): asyncio.create_task(catch_logging_error(logging_channel, embed))