diff --git a/edu_master/webinar-checker/checker.py b/edu_master/webinar-checker/checker.py
index e0217f2..6eb5f50 100644
--- a/edu_master/webinar-checker/checker.py
+++ b/edu_master/webinar-checker/checker.py
@@ -1,6 +1,7 @@
import os
import logging
import redis
+import json
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes
@@ -28,6 +29,7 @@ KEY_WHITELIST = "bot:whitelist"
KEY_WHITELIST_ENABLED = "bot:whitelist_enabled"
KEY_SUBSCRIBERS = "bot:subscribers"
KEY_PHPSESSID = "EDU_PHPSESSID"
+KEY_WEBINAR_HISTORY = "bot:webinar_history" # Stores last 5 webinars
# Initialize Redis
try:
@@ -196,6 +198,30 @@ async def admin_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
# --- Webinar Checking Job ---
+def get_webinar_key(name: str, url: str) -> str:
+ """Generate unique key for a webinar based on name and URL."""
+ return f"{name}|{url}"
+
+def get_stored_webinars() -> list:
+ """Get list of stored webinar keys from Redis."""
+ data = redis_client.get(KEY_WEBINAR_HISTORY)
+ if data:
+ try:
+ return json.loads(data)
+ except Exception as e:
+ logger.error(f"Failed to parse webinar history: {e}")
+ return []
+
+def store_webinars(webinar_keys: list):
+ """Store up to 5 most recent webinar keys in Redis."""
+ # Keep only last 5
+ webinar_keys = webinar_keys[-5:]
+ try:
+ redis_client.set(KEY_WEBINAR_HISTORY, json.dumps(webinar_keys))
+ logger.info(f"Stored {len(webinar_keys)} webinar(s) in history")
+ except Exception as e:
+ logger.error(f"Failed to store webinar history: {e}")
+
async def check_webinars_job(context: ContextTypes.DEFAULT_TYPE):
"""Background job to check for webinars using Async Playwright.
@@ -216,7 +242,7 @@ async def check_webinars_job(context: ContextTypes.DEFAULT_TYPE):
# ---------------------
return None
- webinar_details = []
+ current_webinars = [] # List of dicts with name, url, and formatted text
content = ""
try:
@@ -259,9 +285,26 @@ async def check_webinars_job(context: ContextTypes.DEFAULT_TYPE):
count = await rows.count()
for i in range(count):
- text = await rows.nth(i).inner_text()
+ row = rows.nth(i)
+ text = await row.inner_text()
+
if "Жодного онлайн уроку зараз" not in text:
- webinar_details.append(text.strip())
+ # Extract name (topic) from first column
+ name_elem = row.locator('td').nth(0)
+ name = await name_elem.inner_text()
+ name = name.strip()
+
+ # Extract join URL from fourth column
+ url_elem = row.locator('td').nth(3).locator('a[href*="/webinar/join/"]').first
+ url = await url_elem.get_attribute('href')
+
+ if name and url:
+ current_webinars.append({
+ 'name': name,
+ 'url': url,
+ 'text': text.strip()
+ })
+ logger.info(f"Found webinar: {name} -> {url}")
else:
logger.info("No webinars found (expected message present)")
@@ -287,7 +330,7 @@ async def check_webinars_job(context: ContextTypes.DEFAULT_TYPE):
return None
# --- DEBUG LOGGING (Saving last response content) ---
- if not webinar_details and content: #if no webinars found, save the page content
+ if not current_webinars and content: #if no webinars found, save the page content
try:
with open('response.html', 'w', encoding='utf-8') as f:
f.write(content)
@@ -296,19 +339,49 @@ async def check_webinars_job(context: ContextTypes.DEFAULT_TYPE):
logger.error(f"Failed to write debug HTML: {e}")
# -----------------------------------------------------
- # Notify subscribers if webinars found
- if webinar_details:
- message = "🎓 Найден вебинар!\n\n" + "\n\n".join([f"📌 {d}" for d in webinar_details])
- subscribers = redis_client.smembers(KEY_SUBSCRIBERS)
+ # Check for NEW webinars and notify
+ if current_webinars:
+ # Get stored webinar history
+ stored_keys = get_stored_webinars()
+ logger.info(f"Stored webinar keys: {stored_keys}")
- for sub_id in subscribers:
- try:
- await context.bot.send_message(chat_id=sub_id, text=message, parse_mode='HTML')
- logger.info(f"Notification sent to {sub_id}")
- except Exception as e:
- logger.error(f"Failed to send to {sub_id}: {e}")
+ # Find new webinars (not in history)
+ new_webinars = []
+ current_keys = []
+
+ for webinar in current_webinars:
+ key = get_webinar_key(webinar['name'], webinar['url'])
+ current_keys.append(key)
+
+ if key not in stored_keys:
+ new_webinars.append(webinar)
+ logger.info(f"NEW webinar detected: {webinar['name']}")
+
+ # Update stored history with current webinars
+ # Merge old and new, keeping only last 5
+ updated_keys = stored_keys + [k for k in current_keys if k not in stored_keys]
+ store_webinars(updated_keys)
+
+ # Notify subscribers ONLY about NEW webinars
+ if new_webinars:
+ message = "🎓 Новий вебінар!\n\n" + "\n\n".join([
+ f"📌 {w['name']}\n🔗 https://edu.edu.vn.ua{w['url']}"
+ for w in new_webinars
+ ])
+
+ subscribers = redis_client.smembers(KEY_SUBSCRIBERS)
+ logger.info(f"Sending notification about {len(new_webinars)} new webinar(s) to {len(subscribers)} subscriber(s)")
+
+ for sub_id in subscribers:
+ try:
+ await context.bot.send_message(chat_id=sub_id, text=message, parse_mode='HTML')
+ logger.info(f"Notification sent to {sub_id}")
+ except Exception as e:
+ logger.error(f"Failed to send to {sub_id}: {e}")
+ else:
+ logger.info(f"Found {len(current_webinars)} webinar(s), but all are already known")
- return len(webinar_details)
+ return len(current_webinars)
# --- Main ---