Compare commits

..

7 commits

4 changed files with 59 additions and 33 deletions

View file

@ -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)
@ -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):

View file

@ -1,42 +1,60 @@
import logging
from asyncio import TaskGroup
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.INFO)
log.setLevel(logging.DEBUG)
log_ = log
async def handle_notification(db_updates, ctx, message, keyword, user_id):
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):
"""
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))
footer = f"\n\n<t:{int(message.created_at.timestamp())}:R> | [Show]({message.jump_url}) | <#{ctx.channel.id}>"
header = f"🔔 `{message.author.display_name}` mentioned `{keyword}` on `{ctx.guild}`:"
footer = f"\n<t:{int(message.created_at.timestamp())}:R> | [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:
log.debug("Sending 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:
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):
@ -44,6 +62,8 @@ 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,
@ -52,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
@ -72,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
@ -88,8 +106,9 @@ 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))
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,
@ -207,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):
"""
@ -285,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()

View file

@ -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()

View file

@ -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 = [
@ -109,12 +112,17 @@ 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()
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())
@ -182,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```
@ -191,4 +200,5 @@ def cleanup_code(content):
# remove `foo`
return content.strip("` \n")
# ===== End code borrowed from Avrae =====