Compare commits

...

9 commits

3 changed files with 61 additions and 20 deletions

View file

@ -13,11 +13,12 @@ schema = """
PRAGMA user_version = 1;
PRAGMA main.synchronous = NORMAL;
PRAGMA foreign_keys = ON;
CREATE TABLE keywords (
guild_id INTEGER NOT NULL,
keyword TEXT NOT NULL,
user_id INTEGER NOT NULL,
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
regex INTEGER NOT NULL DEFAULT 0 CHECK(regex IN (0, 1)),
count INTEGER NOT NULL DEFAULT 0
);
@ -33,22 +34,34 @@ CREATE TABLE users (
disabled INTEGER NOT NULL DEFAULT 0 CHECK(disabled IN (0, 1)),
use_embed INTEGER NOT NULL DEFAULT 1 CHECK(use_embed IN (0, 1)),
notify_self INTEGER NOT NULL DEFAULT 0 CHECK(notify_self IN (0, 1)),
bots_notify INTEGER NOT NULL DEFAULT 0 CHECK(bots_notify IN (0, 1))
bots_notify INTEGER NOT NULL DEFAULT 0 CHECK(bots_notify IN (0, 1)),
ignore_active INTEGER NOT NULL DEFAULT 0 CHECK(bots_notify IN (0, 1))
)
WITHOUT ROWID;
CREATE TABLE user_ignores (
user_id INTEGER NOT NULL,
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
guild_id INTEGER NOT NULL,
target INTEGER NOT NULL,
target INTEGER NOT NULL, -- channel or user id
PRIMARY KEY (user_id, guild_id, target)
);
CREATE TABLE user_blocks (
user_id INTEGER NOT NULL,
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
target INTEGER NOT NULL,
PRIMARY KEY (user_id, target)
);
CREATE TABLE user_pauses (
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
guild_id INTEGER NOT NULL,
PRIMARY KEY (user_id, guild_id)
);
CREATE INDEX keywords_index ON keywords(user_id);
CREATE INDEX user_ignores_index ON user_ignores(user_id);
CREATE INDEX user_blocks_index ON user_blocks(user_id);
CREATE INDEX user_pauses_index ON user_pauses(user_id);
"""

View file

@ -89,6 +89,13 @@ class Nomen(Bot):
await super().close()
await self.db.close()
async def user_toggle(self, user_id, item):
await self.db.execute(
"REPLACE INTO users (user_id, {item}) VALUES(:user_id, iff((SELECT {item} FROM users WHERE user_id=:user_id)), 0, 1)",
{"user_id": user_id},
)
await self.db.commit()
bot = Nomen(
description="Keeper of Names",

View file

@ -1,8 +1,9 @@
import logging
from asyncio import TaskGroup
from textwrap import indent
from typing import Union
from disnake import Embed
from disnake import Embed, Member, TextChannel
from disnake.ext.commands import Cog, group, guild_only
from .utils import can_view, confirm, fetch_exists, fetch_unpacked, test_keyword
@ -72,7 +73,9 @@ async def handle_triggers(ctx, message):
"is_bot": ctx.author.bot,
}
disabled = await fetch_exists(ctx.bot.db, "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 unlikely(disabled IS 1)", params
)
if disabled:
log.debug(f"User {ctx.author} ({ctx.author.id}) opted out")
@ -86,17 +89,17 @@ async def handle_triggers(ctx, message):
SELECT keyword, user_id, use_embed, row_number() over (partition by user_id) n
FROM keywords
LEFT JOIN users USING (user_id)
WHERE disabled IS NOT 1 -- Don't notify users who opted out
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 guild
WHERE likely(disabled IS NOT 1) -- Don't notify users who opted out
AND (unlikely(notify_self IS 1) OR user_id IS NOT :author) -- Don't notify author unless wanted
AND (unlikely(bots_notify IS 1) OR :is_bot IS NOT 1) -- Don't notify from bots unless wanted
AND likely(NOT EXISTS(SELECT * FROM ( -- Don't notify if...
SELECT target FROM user_ignores -- Author or channel is ignored in this guild
WHERE user_id=user_id AND guild_id=guild_id
UNION
SELECT target FROM user_blocks -- Author is blocked
WHERE user_id=user_id
)
AND guild_id=:guild AND contains(:content, keyword, regex)
) WHERE target IN (:author, :channel)))
AND guild_id=:guild AND unlikely(contains(:content, keyword, regex))
)
WHERE n=1
"""
@ -135,6 +138,9 @@ class Notifications(Cog):
await handle_triggers(ctx, message)
async def cog_before_invoke(ctx):
await ctx.bot.db.execute("INSERT OR IGNORE INTO users (userid) VALUE(?)", (ctx.author.id,))
@group(
aliases=["kw", "notification", "notifications", "notif", "noti"],
invoke_without_command=True,
@ -297,17 +303,32 @@ class Notifications(Cog):
@keyword.command()
@guild_only()
async def pause(self, ctx):
pass
# TODO: Pause guild notifications
params = (ctx.author.id, ctx.guild_id)
if await fetch_exists("SELECT * FROM user_pauses WHERE user_id=? AND guild_id=?"):
await ctx.bot.db.execute("DELETE FROM user_pauses WHERE user_id=? AND guild_id=?", params)
await ctx.bot.send(f"Resumed notifications in {ctx.guild}")
else:
await ctx.bot.db.execute("INSERT INTO user_pauses VALUES(?, ?)", params)
await ctx.bot.send(f"Paused notifications in {ctx.guild}")
# TODO: Pause guild notifications in handler
@keyword.group(invoke_without_command=True)
@guild_only()
async def ignore(self, ctx, channel):
async def ignore(self, ctx, target: Union[TextChannel, Member]):
await ctx.bot.db.execute("INSERT INTO user_ignores VALUES(?, ?, ?)", (ctx.author.id, ctx.guild.id, target.id))
await ctx.bot.db.commit()
await ctx.send(f"Now ignoring {target}")
@keyword.command()
@guild_only()
async def uningnore(self, ctx, target: Union[TextChannel, Member]):
pass
# TODO: Ignore channels and users
# TODO: Unignore
@ignore.command()
@guild_only()
async def active(self, ctx):
pass
# TODO: Ignore active channel
await ctx.bot.user_toggle(ctx.author.id, "ignore_active")
await ctx.bot.send("Toggled ignore active channel") # TODO: Send current state
# TODO: Ignore active channel in handler