fix: skip notifications during baseline load, add /interval command with /list display

This commit is contained in:
2026-06-17 08:44:35 +02:00
parent c373e09567
commit 88059fa0ee
3 changed files with 81 additions and 4 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
CREATE TABLE IF NOT EXISTS keywords (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
keyword text NOT NULL,
interval_minutes int NOT NULL DEFAULT 60,
interval_minutes int NOT NULL DEFAULT 5,
is_active boolean NOT NULL DEFAULT true,
last_scraped_at timestamptz,
initial_loaded boolean NOT NULL DEFAULT false,
+79 -2
View File
@@ -79,6 +79,7 @@ def register_handlers(app: Application) -> None:
app.add_handler(CommandHandler("add", add_handler))
app.add_handler(CommandHandler("list", list_handler))
app.add_handler(CommandHandler("delete", delete_handler))
app.add_handler(CommandHandler("interval", interval_handler))
app.add_handler(CommandHandler("stats", stats_handler))
app.add_handler(CommandHandler("adduser", adduser_handler))
app.add_handler(CommandHandler("removeuser", removeuser_handler))
@@ -99,6 +100,7 @@ async def start_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> N
"/add <keyword> — Subscribe to a keyword (shared across users)\n"
"/list — List your subscriptions\n"
"/delete <keyword_id> — Unsubscribe from a keyword\n"
"/interval <keyword_id> <minutes> — Change scrape interval\n"
"/stats — View your tracking statistics",
)
@@ -127,7 +129,7 @@ async def add_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> Non
if not existing:
kw_id = str(uuid.uuid4())
await pool.execute(
"INSERT INTO keywords (id, keyword, interval_minutes, is_active) VALUES ($1, $2, 60, true)",
"INSERT INTO keywords (id, keyword, interval_minutes, is_active) VALUES ($1, $2, 5, true)",
kw_id,
keyword,
)
@@ -171,7 +173,7 @@ async def list_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
pool = await get_pool()
subscriptions = await pool.fetch(
"SELECT kw.id, kw.keyword, kw.is_active, kw.last_scraped_at, COUNT(ks2.user_id) AS subs "
"SELECT kw.id, kw.keyword, kw.is_active, kw.interval_minutes, kw.last_scraped_at, COUNT(ks2.user_id) AS subs "
"FROM keyword_subscriptions ks "
"JOIN keywords kw ON kw.id = ks.keyword_id "
"LEFT JOIN keyword_subscriptions ks2 ON ks2.keyword_id = ks.keyword_id "
@@ -194,12 +196,87 @@ async def list_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
f"{i}. \"{s['keyword']}\"\n"
f" ID: {s['id']}\n"
f" Status: {status}{subs}\n"
f" Interval: {s['interval_minutes']} min\n"
f" Last scraped: {last}"
)
await update.message.reply_text("\n\n".join(lines)) # type: ignore[union-attr]
# -- /interval ------------------------------------------------------------
async def interval_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
row = await _require_user(update)
if not row:
return
text = (update.message or update.callback_query).text or "" # type: ignore[union-attr]
parts = text.split()
if len(parts) < 3:
await update.message.reply_text("Usage: /interval <keyword_id> <minutes>") # type: ignore[union-attr]
return
keyword_id = parts[1].strip()
try:
uuid.UUID(keyword_id)
except ValueError:
await update.message.reply_text("Invalid keyword ID format.") # type: ignore[union-attr]
return
try:
minutes = int(parts[2])
except ValueError:
await update.message.reply_text("Interval must be a number (minutes).") # type: ignore[union-attr]
return
if minutes < 1 or minutes > 1440:
await update.message.reply_text("Interval must be between 1 and 1440 minutes.") # type: ignore[union-attr]
return
pool = await get_pool()
sub_check = await pool.fetchrow(
"SELECT 1 FROM keyword_subscriptions WHERE keyword_id = $1 AND user_id = $2",
keyword_id,
row["id"],
)
if not sub_check:
await update.message.reply_text("You are not subscribed to this keyword.") # type: ignore[union-attr]
return
kw_row = await pool.fetchrow(
"SELECT id, interval_minutes FROM keywords WHERE id = $1",
keyword_id,
)
if not kw_row:
await update.message.reply_text("Keyword not found.") # type: ignore[union-attr]
return
old_interval = kw_row["interval_minutes"]
await pool.execute(
"UPDATE keywords SET interval_minutes = $1 WHERE id = $2",
minutes,
keyword_id,
)
sub_count = await pool.fetchval(
"SELECT COUNT(*) FROM keyword_subscriptions WHERE keyword_id = $1",
keyword_id,
)
if sub_count > 1:
await update.message.reply_text( # type: ignore[union-attr]
f"Interval updated: {old_interval} min → {minutes} min\n"
f"(affects all {sub_count} subscriber(s))",
)
else:
await update.message.reply_text( # type: ignore[union-attr]
f"Interval updated: {old_interval} min → {minutes} min",
)
logger.info("User %s changed interval for keyword '%s' to %d min", update.effective_user.id, keyword_id, minutes)
# -- /delete --------------------------------------------------------------
async def delete_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
+1 -1
View File
@@ -96,7 +96,7 @@ async def scheduler_task(pool: object, bot: ExtBot) -> None:
fields.get("main_image_url"), fields.get("postcode"), ad_uuid,
)
if not initial_loaded:
if initial_loaded:
notify_fields = {**fields, "keyword": keyword}
for tg_id in telegram_ids:
await notify_new_ad(bot, tg_id, notify_fields)