async def on_raw_reaction_remove(payload: discord.RawReactionActionEvent, **kargs: Any) -> None: if not payload.guild_id: return inc_statistics_better(payload.guild_id, "on-raw-reaction-remove", kargs["kernel_ramfs"]) client = kargs["client"] rrconf: Optional[Dict[str, Dict[str, int]]] = load_message_config( payload.guild_id, kargs["ramfs"], datatypes=reactionrole_types)["reaction-role-data"] if rrconf: emojiname = emojifrompayload(payload) if str(payload.message_id) in rrconf and emojiname in rrconf[str( payload.message_id)]: role_id = rrconf[str(payload.message_id)][emojiname] if (guild := (await client.fetch_guild( payload.guild_id ))) and (member := (await guild.fetch_member( payload.user_id))) and (role := guild.get_role(role_id)): try: await member.remove_roles(role) except discord.errors.Forbidden: pass
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_ready(**kargs: Any) -> None: inc_statistics_better(0, "on-ready", kargs["kernel_ramfs"]) Client: discord.Client = kargs["client"] print(f'{Client.user} has connected to Discord!') # Warn if user is not bot if not Client.user.bot: print( "WARNING: The connected account is not a bot, as it is against ToS we do not condone user botting" ) # bot start time check to not reparse timers on network disconnect if kargs["bot_start"] > (time.time() - 10): with db_hlapi(None) as db: mutes: List[Tuple[str, str, str, int]] = db.fetch_all_mutes() lost_mutes = sorted(mutes, key=lambda a: a[3]) ts = datetime_now().timestamp() lost_mute_timers = [i for i in lost_mutes if 0 != i[3]] if lost_mute_timers: print(f"Lost mutes: {len(lost_mute_timers)}") for i in lost_mute_timers: if i[3] < ts: await attempt_unmute(Client, i) lost_mute_timers = [i for i in lost_mute_timers if i[3] >= ts] if lost_mute_timers: print( f"Mute timers to recover: {len(lost_mute_timers)}\nThis process will end in {round(lost_mutes[-1][3]-time.time())} seconds" ) for i in lost_mute_timers: await asyncio.sleep(i[3] - datetime_now().timestamp()) with db_hlapi(int(i[0])) as db: if db.is_muted(infractionid=i[1]): await attempt_unmute(Client, i) print("Mutes recovered")
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 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 on_reaction_add(reaction: discord.Reaction, user: discord.User, **kargs: Any) -> None: client: discord.Client = kargs["client"] kernel_ramfs: lexdpyk.ram_filesystem = kargs["kernel_ramfs"] ramfs: lexdpyk.ram_filesystem = kargs["ramfs"] message = reaction.message # Skip if not a guild if not message.guild: return inc_statistics_better(message.guild.id, "on-reaction-add", kernel_ramfs) mconf = load_message_config(message.guild.id, ramfs, datatypes=starboard_cache) if bool( int(mconf["starboard-enabled"]) ) and reaction.emoji == mconf["starboard-emoji"] and reaction.count >= int( mconf["starboard-count"]): if (channel_id := mconf["starboard-channel"]) and (channel := client.get_channel( int(channel_id))) and isinstance(channel, discord.TextChannel): with db_hlapi(message.guild.id) as db: with db.inject_enum_context("starboard", [("messageID", str)]) as starboard: if not (starboard.grab(str(message.id))) and not ( int(channel_id) == message.channel.id): # Add to starboard starboard.set([str(message.id)]) try: await channel.send( embed=(await build_starboard_embed(message))) except discord.errors.Forbidden: pass
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))
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(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"] main_version_info: str = kargs["kernel_version"] bot_start_time: float = kargs["bot_start"] command_modules: List[lexdpyk.cmd_module] command_modules_dict: lexdpyk.cmd_modules_dict command_modules, command_modules_dict = kargs["command_modules"] # Statistics. stats: Dict[str, int] = {"start": round(time.time() * 100000)} if parse_skip_message(client, message): return elif not message.guild: return inc_statistics_better(message.guild.id, "on-message", kernel_ramfs) # Load message conf stats["start-load-blacklist"] = round(time.time() * 100000) mconf = load_message_config(message.guild.id, ramfs) stats["end-load-blacklist"] = round(time.time() * 100000) # Check message against automod stats["start-automod"] = round(time.time() * 100000) spammer, spamstr = antispam_check(message, ramfs, mconf["antispam"], mconf["char-antispam"]) message_deleted: bool = False command_ctx = CommandCtx( stats=stats, cmds=command_modules, ramfs=ramfs, bot_start=bot_start_time, dlibs=kargs["dynamiclib_modules"][0], main_version=main_version_info, kernel_ramfs=kargs["kernel_ramfs"], conf_cache=mconf, verbose=True, cmds_dict=command_modules_dict, automod=False ) automod_ctx = pycopy.copy(command_ctx) automod_ctx.verbose = False automod_ctx.automod = True # If blacklist broken generate infraction broke_blacklist, notify, infraction_type = parse_blacklist((message, mconf, ramfs), ) if broke_blacklist: message_deleted = True asyncio.create_task(attempt_message_delete(message)) execargs = [str(message.author.id), "[AUTOMOD]", ", ".join(infraction_type), "Blacklist"] asyncio.create_task(CallCtx(command_modules_dict[mconf["blacklist-action"]]['execute'])(message, execargs, client, automod_ctx)) if spammer: message_deleted = True asyncio.create_task(attempt_message_delete(message)) with db_hlapi(message.guild.id) as db: if not db.is_muted(userid=message.author.id): execargs = [str(message.author.id), mconf["antispam-time"], "[AUTOMOD]", spamstr] asyncio.create_task(CallCtx(command_modules_dict["mute"]["execute"])(message, execargs, client, automod_ctx)) if notify: asyncio.create_task(grab_an_adult(message, message.guild, client, mconf, ramfs)) stats["end-automod"] = round(time.time() * 100000) # Log files if not deleted if not message_deleted: asyncio.create_task(log_message_files(message, kernel_ramfs)) # Check if this is meant for us. if not (message.content.startswith(mconf["prefix"])) or message_deleted: if client.user.mentioned_in(message) and str(client.user.id) == message.content.strip("<@!>"): try: await message.channel.send(f"My prefix for this guild is {mconf['prefix']}") except discord.errors.Forbidden: pass # Nothing we can do if we lack perms to speak return # Split into cmds and arguments. arguments = message.content.split() command = arguments[0][len(mconf["prefix"]):] # Remove command from the arguments. del arguments[0] # Process commands if command in command_modules_dict: if "alias" in command_modules_dict[command]: # Do alias mapping command = command_modules_dict[command]["alias"] cmd = SonnetCommand(command_modules_dict[command]) if not await parse_permissions(message, mconf, cmd.permission): return # Return on no perms try: stats["end"] = round(time.time() * 100000) try: await cmd.execute_ctx(message, arguments, client, command_ctx) except lib_sonnetcommands.CommandError as ce: try: await message.channel.send(ce) except discord.errors.Forbidden: pass # Regenerate cache if cmd.cache in ["purge", "regenerate"]: for i in ["caches", "regex"]: try: ramfs.rmdir(f"{message.guild.id}/{i}") except FileNotFoundError: pass elif cmd.cache.startswith("direct:"): for i in cmd.cache[len('direct:'):].split(";"): try: if i.startswith("(d)"): ramfs.rmdir(f"{message.guild.id}/{i[3:]}") elif i.startswith("(f)"): ramfs.remove_f(f"{message.guild.id}/{i[3:]}") else: raise RuntimeError("Cache directive is invalid") except FileNotFoundError: pass except discord.errors.Forbidden as e: try: await message.channel.send(f"ERROR: Encountered a uncaught permission error while processing {command}") terr = True except discord.errors.Forbidden: terr = False # Nothing we can do if we lack perms to speak if terr: # If the error was not caused by message send perms then raise raise e except Exception as e: try: await message.channel.send(f"FATAL ERROR: uncaught exception while processing {command}") except discord.errors.Forbidden: pass raise e
async def on_guild_join(guild: discord.Guild, **kargs: Any) -> None: inc_statistics_better(guild.id, "on-guild-join", kargs["kernel_ramfs"]) with db_hlapi(guild.id) as db: db.create_guild_db()