From 0df744c946de44526bd97aef8905a909c65e9dc1 Mon Sep 17 00:00:00 2001 From: Infinidoge Date: Mon, 23 Dec 2024 00:49:14 -0500 Subject: [PATCH 1/7] implement plaintext notifications --- nomen/notifications.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/nomen/notifications.py b/nomen/notifications.py index da9c406..f5818a0 100644 --- a/nomen/notifications.py +++ b/nomen/notifications.py @@ -1,5 +1,6 @@ import logging from asyncio import TaskGroup +from textwrap import indent from disnake import Embed from disnake.ext.commands import Cog, group, guild_only @@ -10,7 +11,7 @@ log = logging.getLogger("nomen.notifications") log.setLevel(logging.INFO) -async def handle_notification(db_updates, ctx, message, keyword, user_id): +async def handle_notification(db_updates, ctx, message, keyword, user_id, use_embed): """ Async task to dispatch a notification """ @@ -26,17 +27,19 @@ async def handle_notification(db_updates, ctx, message, keyword, user_id): log.debug(f"- - Notifying {user_id}") db_updates.append((ctx.guild.id, keyword, user_id)) - footer = f"\n\n | [Show]({message.jump_url}) | <#{ctx.channel.id}>" + header = f"🔔 `{message.author.display_name}` mentioned `{keyword}` on `{ctx.guild}`:" + footer = f"\n | [Show]({message.jump_url}) | <#{ctx.channel.id}>" - embed = Embed( - description=message.content + footer, - ) - embed.set_author(name=message.author, icon_url=message.author.display_avatar) + if use_embed: + embed = Embed(description=message.content + "\n" + footer) + embed.set_author( + name=f"{message.author.display_name} ({message.author})", + icon_url=message.author.display_avatar, + ) - await member.send( - f":bell: `{message.author}` mentioned `{keyword}` on `{ctx.guild}`:", - embed=embed, - ) + await member.send(header, embed=embed) + else: + await member.send("\n".join((header, indent(message.content, "> ", lambda line: True).strip(), footer))) async def handle_triggers(ctx, message): @@ -88,7 +91,7 @@ async def handle_triggers(ctx, message): async for keyword, user_id, use_embed in cur: log.debug(f"- Creating notification task: '{keyword}' for {user_id}") - tg.create_task(handle_notification(db_updates, ctx, message, keyword, user_id)) + tg.create_task(handle_notification(db_updates, ctx, message, keyword, user_id, use_embed)) await ctx.bot.db.executemany( "UPDATE keywords SET count = count + 1 WHERE guild_id=? AND keyword=? AND user_id=?", From 9885dce54417776e45ddb299dcaa78588d5f71a9 Mon Sep 17 00:00:00 2001 From: Infinidoge Date: Mon, 23 Dec 2024 00:50:14 -0500 Subject: [PATCH 2/7] improve logging in notifications, add logging to utils --- nomen/main.py | 2 +- nomen/notifications.py | 28 +++++++++++++++++++++++----- nomen/utils.py | 3 +++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/nomen/main.py b/nomen/main.py index e9d7928..ab00792 100644 --- a/nomen/main.py +++ b/nomen/main.py @@ -23,7 +23,7 @@ logger_disnake.setLevel(logging.WARNING) log = logging.getLogger("nomen") log.setLevel(logging.DEBUG) -formatter = logging.Formatter("%(asctime)s:%(levelname)s:%(name)s: %(message)s") +formatter = logging.Formatter("{asctime} {levelname:8} {name}: {message}", "%Y-%m-%d %H:%M:%S", "{") handler = logging.StreamHandler(sys.stdout) handler.setFormatter(formatter) logging.getLogger(None).addHandler(handler) diff --git a/nomen/notifications.py b/nomen/notifications.py index f5818a0..74b7fd8 100644 --- a/nomen/notifications.py +++ b/nomen/notifications.py @@ -8,7 +8,16 @@ from disnake.ext.commands import Cog, group, guild_only from .utils import can_view, confirm, fetch_unpacked, test_keyword log = logging.getLogger("nomen.notifications") -log.setLevel(logging.INFO) +log.setLevel(logging.DEBUG) +log_ = log + + +class NotifierLogAdapter(logging.LoggerAdapter): + def process(self, msg, kwargs): + return ( + f"[guild: {self.extra["guild"]}, user: {self.extra["user"]}, keyword: {self.extra["keyword"]}] {msg}", + kwargs, + ) async def handle_notification(db_updates, ctx, message, keyword, user_id, use_embed): @@ -16,21 +25,24 @@ async def handle_notification(db_updates, ctx, message, keyword, user_id, use_em Async task to dispatch a notification """ - log.debug(f"- Handling `{keyword}` for {user_id}") - member = await ctx.guild.getch_member(user_id) + log = NotifierLogAdapter(log_.getChild("notifier"), {"guild": ctx.guild, "user": member, "keyword": keyword}) + + log.debug("Handling notification") + if not await can_view(ctx, member): - log.debug(f"- - Missing permission {user_id}") + log.debug("Missing permission") return - log.debug(f"- - Notifying {user_id}") + log.debug("Notifying") db_updates.append((ctx.guild.id, keyword, user_id)) header = f"🔔 `{message.author.display_name}` mentioned `{keyword}` on `{ctx.guild}`:" footer = f"\n | [Show]({message.jump_url}) | <#{ctx.channel.id}>" if use_embed: + log.debug("Sending embed") embed = Embed(description=message.content + "\n" + footer) embed.set_author( name=f"{message.author.display_name} ({message.author})", @@ -39,14 +51,19 @@ async def handle_notification(db_updates, ctx, message, keyword, user_id, use_em await member.send(header, embed=embed) else: + log.debug("Sending plain message") await member.send("\n".join((header, indent(message.content, "> ", lambda line: True).strip(), footer))) + log.debug("Sent") + async def handle_triggers(ctx, message): """ Main function that handles message triggers """ + log.debug(f"Handling triggers for '{message.content}' from {ctx.author}") + params = { "author": ctx.author.id, "channel": ctx.channel.id, @@ -93,6 +110,7 @@ async def handle_triggers(ctx, message): tg.create_task(handle_notification(db_updates, ctx, message, keyword, user_id, use_embed)) + log.debug("Incrementing notification counts") await ctx.bot.db.executemany( "UPDATE keywords SET count = count + 1 WHERE guild_id=? AND keyword=? AND user_id=?", db_updates, diff --git a/nomen/utils.py b/nomen/utils.py index 61bde64..c042c1c 100644 --- a/nomen/utils.py +++ b/nomen/utils.py @@ -1,9 +1,12 @@ +import logging from asyncio import TimeoutError from itertools import chain import re2 as re from disnake import ChannelType +log = logging.getLogger("nomen.utils") + ALPHABET = list("abcdefghijklmnopqrstuvwxyz ") COMMON_WORDS = [ From ecf96b427b24d76e0accc8015e8aa262c5e844d2 Mon Sep 17 00:00:00 2001 From: Infinidoge Date: Mon, 23 Dec 2024 00:50:50 -0500 Subject: [PATCH 3/7] explicitly specify replace into fields --- nomen/main.py | 5 +---- nomen/settings.py | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/nomen/main.py b/nomen/main.py index ab00792..241f258 100644 --- a/nomen/main.py +++ b/nomen/main.py @@ -78,10 +78,7 @@ class Nomen(Bot): return prefix async def set_guild_prefix(self, guild: Guild, prefix): - await self.db.execute( - "REPLACE INTO guilds VALUES(?, ?)", - (guild.id, prefix), - ) + await self.db.execute("REPLACE INTO guilds (guild_id, prefix) VALUES(?, ?)", (guild.id, prefix)) self.prefixes[guild.id] = prefix async def start(self, *args, **kwargs): diff --git a/nomen/settings.py b/nomen/settings.py index c88274a..ce6c5fa 100644 --- a/nomen/settings.py +++ b/nomen/settings.py @@ -91,7 +91,7 @@ You may want to `{ctx.clean_prefix}nomen export` first""" f"You have now opted-out and will no longer trigger or receive notifications. To opt back in, run `{ctx.clean_prefix}nomen opt-in`" ) - await self.bot.db.execute("REPLACE INTO users VALUES(?, 1)", (ctx.author.id,)) + await self.bot.db.execute("REPLACE INTO users (user_id, disabled) VALUES(?, 1)", (ctx.author.id,)) await self.bot.db.commit() @nomen.command(name="opt-in") @@ -106,5 +106,5 @@ You may want to `{ctx.clean_prefix}nomen export` first""" f"You have opted back in and will now trigger and receive notifications. To opt out, run `{ctx.clean_prefix}nomen opt-out`" ) - await self.bot.db.execute("REPLACE INTO users VALUES(?, 0)", (ctx.author.id,)) + await self.bot.db.execute("REPLACE INTO users (user_id, disabled) VALUES(?, 0)", (ctx.author.id,)) await self.bot.db.commit() From 1cb36d41aebb7bb081578f4ae6cbb921a1296de8 Mon Sep 17 00:00:00 2001 From: Infinidoge Date: Mon, 23 Dec 2024 00:51:06 -0500 Subject: [PATCH 4/7] handle disabled check with fetch_exists --- nomen/notifications.py | 8 +++----- nomen/utils.py | 5 +++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/nomen/notifications.py b/nomen/notifications.py index 74b7fd8..e046c76 100644 --- a/nomen/notifications.py +++ b/nomen/notifications.py @@ -5,7 +5,7 @@ from textwrap import indent from disnake import Embed from disnake.ext.commands import Cog, group, guild_only -from .utils import can_view, confirm, fetch_unpacked, test_keyword +from .utils import can_view, confirm, fetch_exists, fetch_unpacked, test_keyword log = logging.getLogger("nomen.notifications") log.setLevel(logging.DEBUG) @@ -72,11 +72,9 @@ async def handle_triggers(ctx, message): "is_bot": ctx.author.bot, } - disabled = await ctx.bot.db.execute_fetchall( - "SELECT EXISTS(SELECT * FROM users WHERE user_id=:author AND disabled IS 1)", params - ) + disabled = await fetch_exists(ctx.bot.db, "SELECT * FROM users WHERE user_id=:author AND disabled IS 1", params) - if disabled[0][0]: + if disabled: log.debug(f"User {ctx.author} ({ctx.author.id}) opted out") return diff --git a/nomen/utils.py b/nomen/utils.py index c042c1c..1dd643a 100644 --- a/nomen/utils.py +++ b/nomen/utils.py @@ -118,6 +118,11 @@ async def fetch_unpacked(db, sql, params=None): return await cur.fetchall() +async def fetch_exists(db, sql, params=None): + result = await db.execute_fetchall(f"SELECT EXISTS({sql})", params) + return result[0][0] + + async def in_thread(member, thread): # FIXME: Currently overlooks the situation where a moderator isn't in a thread but has manage threads return any(member.id == thread_member.id for thread_member in await thread.fetch_members()) From 5adf365d3e9f95c2930e76e8d2c4a6c0e8f94fe3 Mon Sep 17 00:00:00 2001 From: Infinidoge Date: Mon, 23 Dec 2024 00:51:27 -0500 Subject: [PATCH 5/7] misc cleanup --- nomen/notifications.py | 4 ++-- nomen/utils.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nomen/notifications.py b/nomen/notifications.py index e046c76..6fb54d7 100644 --- a/nomen/notifications.py +++ b/nomen/notifications.py @@ -90,7 +90,7 @@ async def handle_triggers(ctx, message): AND (notify_self IS 1 OR user_id IS NOT :author) -- Don't notify author unless wanted AND (bots_notify IS 1 OR :is_bot IS NOT 1) -- Don't notify from bots unless wanted AND :author NOT IN ( -- Don't notify if... - SELECT target FROM user_ignores -- Author is ignored in this guilde + SELECT target FROM user_ignores -- Author is ignored in this guild WHERE user_id=user_id AND guild_id=guild_id UNION SELECT target FROM user_blocks -- Author is blocked @@ -304,7 +304,7 @@ class Notifications(Cog): @guild_only() async def ignore(self, ctx, channel): pass - # TODO: Ignore channels + # TODO: Ignore channels and users @ignore.command() @guild_only() diff --git a/nomen/utils.py b/nomen/utils.py index 1dd643a..7186c19 100644 --- a/nomen/utils.py +++ b/nomen/utils.py @@ -190,6 +190,7 @@ async def confirm(ctx, message, delete_msgs=False, response_check=get_positivity pass return reply_bool + def cleanup_code(content): """Automatically removes code blocks from the code.""" # remove ```py\n``` @@ -199,4 +200,5 @@ def cleanup_code(content): # remove `foo` return content.strip("` \n") + # ===== End code borrowed from Avrae ===== From 11e0b05be1e1ccd107580c8446ccdf37783224a3 Mon Sep 17 00:00:00 2001 From: Infinidoge Date: Mon, 23 Dec 2024 00:51:37 -0500 Subject: [PATCH 6/7] add delete and del as aliases for remove --- nomen/notifications.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nomen/notifications.py b/nomen/notifications.py index 6fb54d7..3afdb4d 100644 --- a/nomen/notifications.py +++ b/nomen/notifications.py @@ -226,7 +226,7 @@ class Notifications(Cog): await self._add_keyword(ctx, keyword, True) - @keyword.command() + @keyword.command(aliases=["delete", "del"]) @guild_only() async def remove(self, ctx, keyword): """ From 38e41dc1b5d6612f4b6f403355cc04aff62abacb Mon Sep 17 00:00:00 2001 From: Infinidoge Date: Mon, 23 Dec 2024 00:51:48 -0500 Subject: [PATCH 7/7] fix fetch_unpacked row factory --- nomen/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nomen/utils.py b/nomen/utils.py index 7186c19..3b9861e 100644 --- a/nomen/utils.py +++ b/nomen/utils.py @@ -112,8 +112,8 @@ def unpack(lst_of_tpl): async def fetch_unpacked(db, sql, params=None): - cur = db.cursor() - cur.row_factory = first + cur = await db.cursor() + cur.row_factory = lambda cursor, row: first(row) cur = await cur.execute(sql, params) return await cur.fetchall()