chore: update gitignore, add translations
This commit is contained in:
+29
-2
@@ -29,8 +29,35 @@ traefik/certs/*
|
|||||||
|
|
||||||
# Python
|
# Python
|
||||||
.python-version
|
.python-version
|
||||||
.venv/
|
|
||||||
venv/
|
venv/
|
||||||
|
pyc
|
||||||
|
unknown_errors.txt
|
||||||
|
moonlogs.txt
|
||||||
|
thumb.jpg
|
||||||
|
antipm_pic.jpg
|
||||||
|
musicbot/
|
||||||
|
.trunk/
|
||||||
|
previous_profiles/
|
||||||
|
.python-version
|
||||||
|
/modules/__pycache__/
|
||||||
|
__pycache__/
|
||||||
|
*.session
|
||||||
|
*.session-old
|
||||||
|
*.db
|
||||||
|
*.sqlite3
|
||||||
|
*-journal
|
||||||
|
/venv/
|
||||||
|
.venv/
|
||||||
|
|
||||||
#DataSecurity
|
# DataSecurity
|
||||||
replacements.txt
|
replacements.txt
|
||||||
|
|
||||||
|
# Vscode
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.gitattributes
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
.DS_Store
|
||||||
|
.idea
|
||||||
@@ -40,6 +40,156 @@ except Exception as e:
|
|||||||
logger.error(f"Failed to connect to Redis: {e}")
|
logger.error(f"Failed to connect to Redis: {e}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
# --- Translations ---
|
||||||
|
|
||||||
|
TRANSLATIONS = {
|
||||||
|
'ru': {
|
||||||
|
'welcome': "👋 Привет, {name}!\n\nЯ бот-уведомитель о вебинарах. Я буду сообщать вам, когда появится новый вебинар.\nВы подписаны на уведомления.",
|
||||||
|
'welcome_admin': "\n\n👑 <b>Режим администратора активен</b>",
|
||||||
|
'access_denied': "⛔ Доступ запрещен. Вас нет в белом списке.",
|
||||||
|
'help_title': "🤖 <b>Помощь по боту</b>\n\n",
|
||||||
|
'help_commands': "/start - Подписаться на уведомления\n/help - Показать это сообщение\n/language - Сменить язык",
|
||||||
|
'help_admin': "\n<b>Команды администратора:</b>\n/adduser [user_id] - Добавить пользователя в белый список\n/removeuser [user_id] - Удалить пользователя из белого списка\nИли используйте панель ниже для управления настройками.",
|
||||||
|
'admin_only': "⛔ Только для администратора!",
|
||||||
|
'user_added': "✅ Пользователь {user_id} добавлен в белый список",
|
||||||
|
'user_removed': "✅ Пользователь {user_id} удален из белого списка",
|
||||||
|
'user_not_in_whitelist': "⚠️ Пользователь {user_id} не был в белом списке",
|
||||||
|
'cannot_remove_admin': "❌ Невозможно удалить администратора из белого списка",
|
||||||
|
'invalid_user_id': "❌ Неверный ID пользователя. Должно быть число.",
|
||||||
|
'usage_adduser': "Использование: /adduser [user_id]",
|
||||||
|
'usage_removeuser': "Использование: /removeuser [user_id]",
|
||||||
|
'whitelist_enabled': "✅ Белый список включен",
|
||||||
|
'whitelist_disabled': "✅ Белый список отключен",
|
||||||
|
'whitelist_title': "📋 <b>Белый список:</b>\n",
|
||||||
|
'subscribers_title': "👥 <b>Подписчики:</b>\n",
|
||||||
|
'empty': "Пусто",
|
||||||
|
'force_check_running': "🔄 Запускаю проверку...",
|
||||||
|
'check_failed': "❌ Проверка не удалась. Смотрите логи.",
|
||||||
|
'check_completed_none': "✅ Проверка завершена. Вебинаров не найдено.",
|
||||||
|
'check_completed': "✅ Проверка завершена. Найдено {count} вебинар(ов)!",
|
||||||
|
'toggle_whitelist_disable': "🔒 Отключить белый список",
|
||||||
|
'toggle_whitelist_enable': "🔓 Включить белый список",
|
||||||
|
'view_whitelist': "📋 Посмотреть белый список",
|
||||||
|
'view_subscribers': "👥 Посмотреть подписчиков",
|
||||||
|
'force_check': "🔄 Принудительная проверка",
|
||||||
|
'webinar_found': "🎓 <b>Новый вебинар!</b>\n\n",
|
||||||
|
'webinar_item': "📌 <b>{name}</b>\n🔗 https://edu.edu.vn.ua{url}",
|
||||||
|
'select_language': "🌐 <b>Выберите язык / Оберіть мову / Select language:</b>",
|
||||||
|
'language_changed': "✅ Язык изменен на русский",
|
||||||
|
'flag_ru': "🇷🇺 Русский",
|
||||||
|
'flag_uk': "🇺🇦 Українська",
|
||||||
|
'flag_en': "🇬🇧 English",
|
||||||
|
},
|
||||||
|
'uk': {
|
||||||
|
'welcome': "👋 Привіт, {name}!\n\nЯ бот-сповіщувач про вебінари. Я повідомлятиму вас, коли з'явиться новий вебінар.\nВи підписані на сповіщення.",
|
||||||
|
'welcome_admin': "\n\n👑 <b>Режим адміністратора активний</b>",
|
||||||
|
'access_denied': "⛔ Доступ заборонено. Вас немає в білому списку.",
|
||||||
|
'help_title': "🤖 <b>Довідка по боту</b>\n\n",
|
||||||
|
'help_commands': "/start - Підписатися на сповіщення\n/help - Показати це повідомлення\n/language - Змінити мову",
|
||||||
|
'help_admin': "\n<b>Команди адміністратора:</b>\n/adduser [user_id] - Додати користувача до білого списку\n/removeuser [user_id] - Видалити користувача з білого списку\nАбо використовуйте панель нижче для керування налаштуваннями.",
|
||||||
|
'admin_only': "⛔ Тільки для адміністратора!",
|
||||||
|
'user_added': "✅ Користувач {user_id} доданий до білого списку",
|
||||||
|
'user_removed': "✅ Користувач {user_id} видалений з білого списку",
|
||||||
|
'user_not_in_whitelist': "⚠️ Користувач {user_id} не був у білому списку",
|
||||||
|
'cannot_remove_admin': "❌ Неможливо видалити адміністратора з білого списку",
|
||||||
|
'invalid_user_id': "❌ Невірний ID користувача. Має бути число.",
|
||||||
|
'usage_adduser': "Використання: /adduser [user_id]",
|
||||||
|
'usage_removeuser': "Використання: /removeuser [user_id]",
|
||||||
|
'whitelist_enabled': "✅ Білий список увімкнено",
|
||||||
|
'whitelist_disabled': "✅ Білий список вимкнено",
|
||||||
|
'whitelist_title': "📋 <b>Білий список:</b>\n",
|
||||||
|
'subscribers_title': "👥 <b>Підписники:</b>\n",
|
||||||
|
'empty': "Порожньо",
|
||||||
|
'force_check_running': "🔄 Запускаю перевірку...",
|
||||||
|
'check_failed': "❌ Перевірка не вдалася. Дивіться логи.",
|
||||||
|
'check_completed_none': "✅ Перевірка завершена. Вебінарів не знайдено.",
|
||||||
|
'check_completed': "✅ Перевірка завершена. Знайдено {count} вебінар(ів)!",
|
||||||
|
'toggle_whitelist_disable': "🔒 Вимкнути білий список",
|
||||||
|
'toggle_whitelist_enable': "🔓 Увімкнути білий список",
|
||||||
|
'view_whitelist': "📋 Переглянути білий список",
|
||||||
|
'view_subscribers': "👥 Переглянути підписників",
|
||||||
|
'force_check': "🔄 Примусова перевірка",
|
||||||
|
'webinar_found': "🎓 <b>Новий вебінар!</b>\n\n",
|
||||||
|
'webinar_item': "📌 <b>{name}</b>\n🔗 https://edu.edu.vn.ua{url}",
|
||||||
|
'select_language': "🌐 <b>Виберіть мову / Выберите язык / Select language:</b>",
|
||||||
|
'language_changed': "✅ Мову змінено на українську",
|
||||||
|
'flag_ru': "🇷🇺 Русский",
|
||||||
|
'flag_uk': "🇺🇦 Українська",
|
||||||
|
'flag_en': "🇬🇧 English",
|
||||||
|
},
|
||||||
|
'en': {
|
||||||
|
'welcome': "👋 Hello, {name}!\n\nI am the Webinar Checker Bot. I will notify you when a new webinar appears.\nYou have been subscribed to notifications.",
|
||||||
|
'welcome_admin': "\n\n👑 <b>Admin Mode Active</b>",
|
||||||
|
'access_denied': "⛔ Access denied. You are not on the whitelist.",
|
||||||
|
'help_title': "🤖 <b>Bot Help</b>\n\n",
|
||||||
|
'help_commands': "/start - Subscribe to notifications\n/help - Show this message\n/language - Change language",
|
||||||
|
'help_admin': "\n<b>Admin Commands:</b>\n/adduser [user_id] - Add user to whitelist\n/removeuser [user_id] - Remove user from whitelist\nOr use the panel below to manage settings.",
|
||||||
|
'admin_only': "⛔ Admin only!",
|
||||||
|
'user_added': "✅ User {user_id} added to whitelist",
|
||||||
|
'user_removed': "✅ User {user_id} removed from whitelist",
|
||||||
|
'user_not_in_whitelist': "⚠️ User {user_id} was not in whitelist",
|
||||||
|
'cannot_remove_admin': "❌ Cannot remove admin from whitelist",
|
||||||
|
'invalid_user_id': "❌ Invalid user ID. Must be a number.",
|
||||||
|
'usage_adduser': "Usage: /adduser [user_id]",
|
||||||
|
'usage_removeuser': "Usage: /removeuser [user_id]",
|
||||||
|
'whitelist_enabled': "✅ Whitelist Enabled",
|
||||||
|
'whitelist_disabled': "✅ Whitelist Disabled",
|
||||||
|
'whitelist_title': "📋 <b>Whitelist:</b>\n",
|
||||||
|
'subscribers_title': "👥 <b>Subscribers:</b>\n",
|
||||||
|
'empty': "Empty",
|
||||||
|
'force_check_running': "🔄 Running immediate check...",
|
||||||
|
'check_failed': "❌ Check failed. See logs for details.",
|
||||||
|
'check_completed_none': "✅ Check completed. No webinars found.",
|
||||||
|
'check_completed': "✅ Check completed. Found {count} webinar(s)!",
|
||||||
|
'toggle_whitelist_disable': "🔒 Disable Whitelist",
|
||||||
|
'toggle_whitelist_enable': "🔓 Enable Whitelist",
|
||||||
|
'view_whitelist': "📋 View Whitelist",
|
||||||
|
'view_subscribers': "👥 View Subscribers",
|
||||||
|
'force_check': "🔄 Force Check",
|
||||||
|
'webinar_found': "🎓 <b>New webinar found!</b>\n\n",
|
||||||
|
'webinar_item': "📌 <b>{name}</b>\n🔗 https://edu.edu.vn.ua{url}",
|
||||||
|
'select_language': "🌐 <b>Select language / Виберіть мову / Выберите язык:</b>",
|
||||||
|
'language_changed': "✅ Language changed to English",
|
||||||
|
'flag_ru': "🇷🇺 Русский",
|
||||||
|
'flag_uk': "🇺🇦 Українська",
|
||||||
|
'flag_en': "🇬🇧 English",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Language Helper Functions ---
|
||||||
|
|
||||||
|
def get_user_language(user_id: int) -> str:
|
||||||
|
"""Get user's preferred language from Redis. Default: Ukrainian."""
|
||||||
|
lang = redis_client.get(f"user:{user_id}:language")
|
||||||
|
return lang if lang in ['ru', 'uk', 'en'] else 'uk'
|
||||||
|
|
||||||
|
def set_user_language(user_id: int, lang: str):
|
||||||
|
"""Save user's language preference to Redis."""
|
||||||
|
if lang in ['ru', 'uk', 'en']:
|
||||||
|
redis_client.set(f"user:{user_id}:language", lang)
|
||||||
|
logger.info(f"User {user_id} language set to {lang}")
|
||||||
|
|
||||||
|
def t(user_id: int, key: str, **kwargs) -> str:
|
||||||
|
"""Translate message for user with optional formatting."""
|
||||||
|
lang = get_user_language(user_id)
|
||||||
|
message = TRANSLATIONS.get(lang, TRANSLATIONS['uk']).get(key, key)
|
||||||
|
if kwargs:
|
||||||
|
return message.format(**kwargs)
|
||||||
|
return message
|
||||||
|
|
||||||
|
def get_language_keyboard():
|
||||||
|
"""Generate language selection keyboard."""
|
||||||
|
keyboard = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("🇷🇺 Русский", callback_data="lang_ru"),
|
||||||
|
InlineKeyboardButton("🇺🇦 Українська", callback_data="lang_uk"),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("🇬🇧 English", callback_data="lang_en"),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
return InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
# --- Helper Functions ---
|
# --- Helper Functions ---
|
||||||
|
|
||||||
def is_whitelisted(user_id: int) -> bool:
|
def is_whitelisted(user_id: int) -> bool:
|
||||||
@@ -53,16 +203,16 @@ def is_whitelisted(user_id: int) -> bool:
|
|||||||
|
|
||||||
return redis_client.sismember(KEY_WHITELIST, str(user_id))
|
return redis_client.sismember(KEY_WHITELIST, str(user_id))
|
||||||
|
|
||||||
def get_admin_keyboard():
|
def get_admin_keyboard(user_id: int):
|
||||||
"""Generate admin panel keyboard."""
|
"""Generate admin panel keyboard."""
|
||||||
whitelist_enabled = redis_client.get(KEY_WHITELIST_ENABLED) != "0"
|
whitelist_enabled = redis_client.get(KEY_WHITELIST_ENABLED) != "0"
|
||||||
toggle_text = "🔒 Disable Whitelist" if whitelist_enabled else "🔓 Enable Whitelist"
|
toggle_text = t(user_id, 'toggle_whitelist_disable') if whitelist_enabled else t(user_id, 'toggle_whitelist_enable')
|
||||||
|
|
||||||
keyboard = [
|
keyboard = [
|
||||||
[InlineKeyboardButton(toggle_text, callback_data="toggle_whitelist")],
|
[InlineKeyboardButton(toggle_text, callback_data="toggle_whitelist")],
|
||||||
[InlineKeyboardButton("📋 View Whitelist", callback_data="view_whitelist")],
|
[InlineKeyboardButton(t(user_id, 'view_whitelist'), callback_data="view_whitelist")],
|
||||||
[InlineKeyboardButton("👥 View Subscribers", callback_data="view_subscribers")],
|
[InlineKeyboardButton(t(user_id, 'view_subscribers'), callback_data="view_subscribers")],
|
||||||
[InlineKeyboardButton("🔄 Force Check", callback_data="force_check")]
|
[InlineKeyboardButton(t(user_id, 'force_check'), callback_data="force_check")]
|
||||||
]
|
]
|
||||||
return InlineKeyboardMarkup(keyboard)
|
return InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
@@ -74,84 +224,84 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
logger.info(f"User {user.id} ({user.username}) started the bot.")
|
logger.info(f"User {user.id} ({user.username}) started the bot.")
|
||||||
|
|
||||||
if not is_whitelisted(user.id):
|
if not is_whitelisted(user.id):
|
||||||
await update.message.reply_text("⛔ Access denied. You are not on the whitelist.")
|
await update.message.reply_text(t(user.id, 'access_denied'))
|
||||||
return
|
return
|
||||||
|
|
||||||
# Add to subscribers
|
# Add to subscribers
|
||||||
redis_client.sadd(KEY_SUBSCRIBERS, user.id)
|
redis_client.sadd(KEY_SUBSCRIBERS, user.id)
|
||||||
|
|
||||||
msg = (
|
msg = t(user.id, 'welcome', name=user.first_name)
|
||||||
f"👋 Hello {user.first_name}!\n\n"
|
|
||||||
"I am the Webinar Checker Bot. I will notify you when a new webinar appears.\n"
|
|
||||||
"You have been subscribed to notifications."
|
|
||||||
)
|
|
||||||
|
|
||||||
if user.id == ADMIN_ID:
|
if user.id == ADMIN_ID:
|
||||||
msg += "\n\n👑 <b>Admin Mode Active</b>"
|
msg += t(user.id, 'welcome_admin')
|
||||||
await update.message.reply_text(msg, parse_mode='HTML', reply_markup=get_admin_keyboard())
|
await update.message.reply_text(msg, parse_mode='HTML', reply_markup=get_admin_keyboard(user.id))
|
||||||
else:
|
else:
|
||||||
await update.message.reply_text(msg, parse_mode='HTML')
|
await update.message.reply_text(msg, parse_mode='HTML')
|
||||||
|
|
||||||
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""Handle /help command."""
|
"""Handle /help command."""
|
||||||
msg = (
|
user_id = update.effective_user.id
|
||||||
"🤖 <b>Bot Help</b>\n\n"
|
msg = t(user_id, 'help_title') + t(user_id, 'help_commands')
|
||||||
"/start - Subscribe to notifications\n"
|
|
||||||
"/help - Show this message\n"
|
if user_id == ADMIN_ID:
|
||||||
)
|
msg += t(user_id, 'help_admin')
|
||||||
if update.effective_user.id == ADMIN_ID:
|
await update.message.reply_text(msg, parse_mode='HTML', reply_markup=get_admin_keyboard(user_id))
|
||||||
msg += (
|
|
||||||
"\n<b>Admin Commands:</b>\n"
|
|
||||||
"/adduser [user_id] - Add user to whitelist\n"
|
|
||||||
"/removeuser [user_id] - Remove user from whitelist\n"
|
|
||||||
"Or use the panel below to manage settings."
|
|
||||||
)
|
|
||||||
await update.message.reply_text(msg, parse_mode='HTML', reply_markup=get_admin_keyboard())
|
|
||||||
else:
|
else:
|
||||||
await update.message.reply_text(msg, parse_mode='HTML')
|
await update.message.reply_text(msg, parse_mode='HTML')
|
||||||
|
|
||||||
|
async def language_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
"""Handle /language command."""
|
||||||
|
user_id = update.effective_user.id
|
||||||
|
await update.message.reply_text(
|
||||||
|
t(user_id, 'select_language'),
|
||||||
|
parse_mode='HTML',
|
||||||
|
reply_markup=get_language_keyboard()
|
||||||
|
)
|
||||||
|
|
||||||
async def add_user(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def add_user(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""Add user to whitelist (admin only)."""
|
"""Add user to whitelist (admin only)."""
|
||||||
if update.effective_user.id != ADMIN_ID:
|
admin_id = update.effective_user.id
|
||||||
await update.message.reply_text("⛔ Admin only!")
|
if admin_id != ADMIN_ID:
|
||||||
|
await update.message.reply_text(t(admin_id, 'admin_only'))
|
||||||
return
|
return
|
||||||
|
|
||||||
if not context.args:
|
if not context.args:
|
||||||
await update.message.reply_text("Usage: /adduser [user_id]")
|
await update.message.reply_text(t(admin_id, 'usage_adduser'))
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_id = int(context.args[0])
|
user_id = int(context.args[0])
|
||||||
redis_client.sadd(KEY_WHITELIST, str(user_id))
|
redis_client.sadd(KEY_WHITELIST, str(user_id))
|
||||||
await update.message.reply_text(f"✅ User {user_id} added to whitelist")
|
await update.message.reply_text(t(admin_id, 'user_added', user_id=user_id))
|
||||||
logger.info(f"Admin added user {user_id} to whitelist")
|
logger.info(f"Admin added user {user_id} to whitelist")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
await update.message.reply_text("❌ Invalid user ID. Must be a number.")
|
await update.message.reply_text(t(admin_id, 'invalid_user_id'))
|
||||||
|
|
||||||
async def remove_user(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def remove_user(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""Remove user from whitelist (admin only)."""
|
"""Remove user from whitelist (admin only)."""
|
||||||
if update.effective_user.id != ADMIN_ID:
|
admin_id = update.effective_user.id
|
||||||
await update.message.reply_text("⛔ Admin only!")
|
if admin_id != ADMIN_ID:
|
||||||
|
await update.message.reply_text(t(admin_id, 'admin_only'))
|
||||||
return
|
return
|
||||||
|
|
||||||
if not context.args:
|
if not context.args:
|
||||||
await update.message.reply_text("Usage: /removeuser [user_id]")
|
await update.message.reply_text(t(admin_id, 'usage_removeuser'))
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_id = int(context.args[0])
|
user_id = int(context.args[0])
|
||||||
if str(user_id) == str(ADMIN_ID):
|
if str(user_id) == str(ADMIN_ID):
|
||||||
await update.message.reply_text("❌ Cannot remove admin from whitelist")
|
await update.message.reply_text(t(admin_id, 'cannot_remove_admin'))
|
||||||
return
|
return
|
||||||
|
|
||||||
removed = redis_client.srem(KEY_WHITELIST, str(user_id))
|
removed = redis_client.srem(KEY_WHITELIST, str(user_id))
|
||||||
if removed:
|
if removed:
|
||||||
await update.message.reply_text(f"✅ User {user_id} removed from whitelist")
|
await update.message.reply_text(t(admin_id, 'user_removed', user_id=user_id))
|
||||||
logger.info(f"Admin removed user {user_id} from whitelist")
|
logger.info(f"Admin removed user {user_id} from whitelist")
|
||||||
else:
|
else:
|
||||||
await update.message.reply_text(f"⚠️ User {user_id} was not in whitelist")
|
await update.message.reply_text(t(admin_id, 'user_not_in_whitelist', user_id=user_id))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
await update.message.reply_text("❌ Invalid user ID. Must be a number.")
|
await update.message.reply_text(t(admin_id, 'invalid_user_id'))
|
||||||
|
|
||||||
# --- Admin Callbacks ---
|
# --- Admin Callbacks ---
|
||||||
|
|
||||||
@@ -161,7 +311,7 @@ async def admin_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
user_id = query.from_user.id
|
user_id = query.from_user.id
|
||||||
|
|
||||||
if user_id != ADMIN_ID:
|
if user_id != ADMIN_ID:
|
||||||
await query.answer("⛔ Admin only!", show_alert=True)
|
await query.answer(t(user_id, 'admin_only'), show_alert=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
await query.answer()
|
await query.answer()
|
||||||
@@ -171,30 +321,45 @@ async def admin_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
current = redis_client.get(KEY_WHITELIST_ENABLED)
|
current = redis_client.get(KEY_WHITELIST_ENABLED)
|
||||||
new_state = "0" if current != "0" else "1"
|
new_state = "0" if current != "0" else "1"
|
||||||
redis_client.set(KEY_WHITELIST_ENABLED, new_state)
|
redis_client.set(KEY_WHITELIST_ENABLED, new_state)
|
||||||
state_text = "Disabled" if new_state == "0" else "Enabled"
|
state_text = t(user_id, 'whitelist_disabled') if new_state == "0" else t(user_id, 'whitelist_enabled')
|
||||||
await query.edit_message_reply_markup(reply_markup=get_admin_keyboard())
|
await query.edit_message_reply_markup(reply_markup=get_admin_keyboard(user_id))
|
||||||
await query.message.reply_text(f"✅ Whitelist {state_text}")
|
await query.message.reply_text(state_text)
|
||||||
|
|
||||||
elif data == "view_whitelist":
|
elif data == "view_whitelist":
|
||||||
members = redis_client.smembers(KEY_WHITELIST)
|
members = redis_client.smembers(KEY_WHITELIST)
|
||||||
msg = "📋 <b>Whitelist:</b>\n" + ("\n".join(members) if members else "Empty")
|
msg = t(user_id, 'whitelist_title') + ("\n".join(members) if members else t(user_id, 'empty'))
|
||||||
await query.message.reply_text(msg, parse_mode='HTML')
|
await query.message.reply_text(msg, parse_mode='HTML')
|
||||||
|
|
||||||
elif data == "view_subscribers":
|
elif data == "view_subscribers":
|
||||||
subs = redis_client.smembers(KEY_SUBSCRIBERS)
|
subs = redis_client.smembers(KEY_SUBSCRIBERS)
|
||||||
msg = "👥 <b>Subscribers:</b>\n" + ("\n".join(subs) if subs else "Empty")
|
msg = t(user_id, 'subscribers_title') + ("\n".join(subs) if subs else t(user_id, 'empty'))
|
||||||
await query.message.reply_text(msg, parse_mode='HTML')
|
await query.message.reply_text(msg, parse_mode='HTML')
|
||||||
|
|
||||||
elif data == "force_check":
|
elif data == "force_check":
|
||||||
await query.message.reply_text("🔄 Running immediate check...")
|
await query.message.reply_text(t(user_id, 'force_check_running'))
|
||||||
result = await check_webinars_job(context)
|
result = await check_webinars_job(context)
|
||||||
|
|
||||||
if result is None:
|
if result is None:
|
||||||
await query.message.reply_text("❌ Check failed. See logs for details.")
|
await query.message.reply_text(t(user_id, 'check_failed'))
|
||||||
elif result == 0:
|
elif result == 0:
|
||||||
await query.message.reply_text("✅ Check completed. No webinars found.")
|
await query.message.reply_text(t(user_id, 'check_completed_none'))
|
||||||
else:
|
else:
|
||||||
await query.message.reply_text(f"✅ Check completed. Found {result} webinar(s)!")
|
await query.message.reply_text(t(user_id, 'check_completed', count=result))
|
||||||
|
|
||||||
|
async def language_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
"""Handle language selection button clicks."""
|
||||||
|
query = update.callback_query
|
||||||
|
user_id = query.from_user.id
|
||||||
|
data = query.data
|
||||||
|
|
||||||
|
if data.startswith("lang_"):
|
||||||
|
lang = data.split("_")[1]
|
||||||
|
set_user_language(user_id, lang)
|
||||||
|
await query.answer()
|
||||||
|
await query.edit_message_text(
|
||||||
|
t(user_id, 'language_changed'),
|
||||||
|
parse_mode='HTML'
|
||||||
|
)
|
||||||
|
|
||||||
# --- Webinar Checking Job ---
|
# --- Webinar Checking Job ---
|
||||||
|
|
||||||
@@ -364,16 +529,18 @@ async def check_webinars_job(context: ContextTypes.DEFAULT_TYPE):
|
|||||||
|
|
||||||
# Notify subscribers ONLY about NEW webinars
|
# Notify subscribers ONLY about NEW webinars
|
||||||
if new_webinars:
|
if new_webinars:
|
||||||
message = "🎓 <b>Новий вебінар!</b>\n\n" + "\n\n".join([
|
|
||||||
f"📌 <b>{w['name']}</b>\n🔗 https://edu.edu.vn.ua{w['url']}"
|
|
||||||
for w in new_webinars
|
|
||||||
])
|
|
||||||
|
|
||||||
subscribers = redis_client.smembers(KEY_SUBSCRIBERS)
|
subscribers = redis_client.smembers(KEY_SUBSCRIBERS)
|
||||||
logger.info(f"Sending notification about {len(new_webinars)} new webinar(s) to {len(subscribers)} subscriber(s)")
|
logger.info(f"Sending notification about {len(new_webinars)} new webinar(s) to {len(subscribers)} subscriber(s)")
|
||||||
|
|
||||||
for sub_id in subscribers:
|
for sub_id in subscribers:
|
||||||
try:
|
try:
|
||||||
|
# Build message in user's language
|
||||||
|
webinar_items = "\n\n".join([
|
||||||
|
t(int(sub_id), 'webinar_item', name=w['name'], url=w['url'])
|
||||||
|
for w in new_webinars
|
||||||
|
])
|
||||||
|
message = t(int(sub_id), 'webinar_found') + webinar_items
|
||||||
|
|
||||||
await context.bot.send_message(chat_id=sub_id, text=message, parse_mode='HTML')
|
await context.bot.send_message(chat_id=sub_id, text=message, parse_mode='HTML')
|
||||||
logger.info(f"Notification sent to {sub_id}")
|
logger.info(f"Notification sent to {sub_id}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -403,8 +570,12 @@ def main():
|
|||||||
# Handlers
|
# Handlers
|
||||||
app.add_handler(CommandHandler("start", start))
|
app.add_handler(CommandHandler("start", start))
|
||||||
app.add_handler(CommandHandler("help", help_command))
|
app.add_handler(CommandHandler("help", help_command))
|
||||||
|
app.add_handler(CommandHandler("language", language_command))
|
||||||
app.add_handler(CommandHandler("adduser", add_user))
|
app.add_handler(CommandHandler("adduser", add_user))
|
||||||
app.add_handler(CommandHandler("removeuser", remove_user))
|
app.add_handler(CommandHandler("removeuser", remove_user))
|
||||||
|
|
||||||
|
# Callback handlers - language selection first, then admin panel
|
||||||
|
app.add_handler(CallbackQueryHandler(language_callback, pattern="^lang_"))
|
||||||
app.add_handler(CallbackQueryHandler(admin_callback))
|
app.add_handler(CallbackQueryHandler(admin_callback))
|
||||||
|
|
||||||
# Job Queue
|
# Job Queue
|
||||||
|
|||||||
+1
-10
@@ -1,5 +1,6 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.gitattributes
|
.gitattributes
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
/modules/__pycache__/
|
/modules/__pycache__/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
@@ -12,14 +13,4 @@ __pycache__/
|
|||||||
.venv/
|
.venv/
|
||||||
/downloads/
|
/downloads/
|
||||||
/Downloads/
|
/Downloads/
|
||||||
/modules/custom_modules
|
|
||||||
.idea
|
|
||||||
config.ini
|
config.ini
|
||||||
unknown_errors.txt
|
|
||||||
moonlogs.txt
|
|
||||||
thumb.jpg
|
|
||||||
antipm_pic.jpg
|
|
||||||
musicbot/
|
|
||||||
.trunk/
|
|
||||||
previous_profiles/
|
|
||||||
.python-version
|
|
||||||
Reference in New Issue
Block a user