Initial commit: Telegram bot with scheduler and database functionality

This commit is contained in:
2025-10-15 13:02:57 +03:00
parent 16e3432345
commit accd5d0f16
6 changed files with 567 additions and 4054 deletions
+154 -314
View File
@@ -6,6 +6,14 @@ from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, InlineQ
from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, filters, InlineQueryHandler, ChatMemberHandler from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, filters, InlineQueryHandler, ChatMemberHandler
from database_sqlite import Database from database_sqlite import Database
from config import BOT_TOKEN, OPENWEATHER_API_KEY, NEWS_API_KEY from config import BOT_TOKEN, OPENWEATHER_API_KEY, NEWS_API_KEY
from messages import (
PREDEFINED_RESPONSES, HELP_TEXT, BANK_DETAILS_TEXT,
SCHEDULER_MESSAGES, RANK_MESSAGES, MODERATION_MESSAGES,
GAME_MESSAGES, WEATHER_MESSAGES, NEWS_MESSAGES, TRANSLATE_MESSAGES,
IMPORT_MESSAGES, USER_MESSAGES, INLINE_MESSAGES, TECH_MESSAGES,
GAME_CHOICES, GAME_KEYBOARDS, QUIZ_QUESTIONS, RANK_THRESHOLDS,
SCORE_VALUES, SCHEDULER_CONFIG, POST_STATUSES, USER_ROLES, TELEGRAM_STATUSES
)
# Настройка логирования # Настройка логирования
logging.basicConfig( logging.basicConfig(
@@ -16,53 +24,7 @@ logging.basicConfig(
# Инициализация базы данных # Инициализация базы данных
db = Database() db = Database()
# Предопределенные ответы # Все текстовые сообщения теперь хранятся в отдельном файле messages.py
PREDEFINED_RESPONSES = {
'привет': 'Привет! Как дела?',
'как дела': 'Отлично! А у тебя?',
'что делаешь': 'Отвечаю на сообщения в чате 😊',
'спасибо': 'Пожалуйста! Рад помочь!',
'пока': 'До свидания! Заходи еще!',
}
# Текст справки
HELP_TEXT = """
🤖 <b>Команды бота:</b>
<b>Основные:</b>
/start - Начать работу с ботом
/help - Показать это сообщение
/info - Информация о вас
<b>Рейтинг:</b>
/rank - Ваш текущий рейтинг
/ranks_info - Информация о системе рангов
/leaderboard - Топ-10 участников
<b>Информация:</b>
/weather [город] - Погода в городе
/news - Последние новости
/translate [текст] [язык] - Перевод текста
<b>Прочее:</b>
Введите "реквизиты" - Получить реквизиты для оплаты
<b>Игры:</b>
/play_game - Запустить мини-игру
<b>Модерация (только для админов):</b>
/warn [пользователь] [причина] - Выдать предупреждение
/mute [пользователь] [время] - Заглушить пользователя
/unmute [пользователь] - Снять заглушку
/ban [пользователь] [причина] - Забанить пользователя
/unban [пользователь] - Разбанить пользователя
/kick [пользователь] [причина] - Кикнуть пользователя
/promote [пользователь] - Повысить до модератора
/demote [пользователь] - Понизить с модератора
"""
# Текст реквизитов
BANK_DETAILS_TEXT = "Денежные средства можно перечислить по номерам: \r\n💳 89066935474 Елена, \r\n💳89207144698 Людмила\r\nБольшая просьба не писать сообщения при переводе, карту могут заблокировать!!!"
class TelegramBot: class TelegramBot:
def __init__(self): def __init__(self):
@@ -113,84 +75,19 @@ class TelegramBot:
user = update.effective_user user = update.effective_user
db.add_user(user.id, user.username, user.first_name, user.last_name) db.add_user(user.id, user.username, user.first_name, user.last_name)
keyboard = [ keyboard = GAME_KEYBOARDS['main_menu']
[InlineKeyboardButton("📋 Помощь", callback_data='cmd_help')],
[InlineKeyboardButton("🎮 Мини игры", callback_data='cmd_play_game')]
]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
welcome_text = f""" welcome_text = USER_MESSAGES['welcome'].format(name=user.first_name)
Привет, {user.first_name}! 👋
Я ваш помощник в чате. Вот что я умею:
• Отвечать на сообщения
• Вести рейтинг участников
• Предоставлять погоду и новости
• Играть в мини-игры
• Помогать с модерацией
Используйте /help для подробной информации.
"""
await update.message.reply_text(welcome_text, reply_markup=reply_markup) await update.message.reply_text(welcome_text, reply_markup=reply_markup)
async def help(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def help(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик команды /help""" """Обработчик команды /help"""
keyboard = [ keyboard = GAME_KEYBOARDS['help_menu']
[InlineKeyboardButton("🚀 Старт", callback_data='cmd_start')],
[InlineKeyboardButton("🔄 Начать заново", callback_data='cmd_restart')]
]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
help_text = """ await update.message.reply_text(messages.HELP_TEXT, parse_mode='HTML', reply_markup=reply_markup)
🤖 <b>Команды бота:</b>
<b>Основные:</b>
/start - Начать работу с ботом
/help - Показать это сообщение
/info - Информация о вас
<b>Рейтинг:</b>
/rank - Ваш текущий рейтинг
/ranks_info - Информация о системе рангов
/leaderboard - Топ-10 участников
<b>Информация:</b>
/weather [город] - Погода в городе
/news - Последние новости
/translate [текст] [язык] - Перевод текста
<b>Прочее:</b>
Введите "реквизиты" - Получить реквизиты для оплаты
<b>Игры:</b>
/play_game - Запустить мини-игру
<b>Посты по расписанию (только для админов):</b>
/schedule_post [время] [текст] - Запланировать пост
/list_posts - Показать запланированные посты
/delete_post [ID] - Удалить пост по расписанию
/publish_now [ID] - Опубликовать пост немедленно
<b>Посты по расписанию (только для админов):</b>
/schedule_post [время] [текст] - Запланировать пост
/list_posts - Показать запланированные посты
/delete_post [ID] - Удалить пост по расписанию
/publish_now [ID] - Опубликовать пост немедленно
<b>Модерация (только для админов):</b>
/warn [пользователь] [причина] - Выдать предупреждение
/mute [пользователь] [время] - Заглушить пользователя
/unmute [пользователь] - Снять заглушку
/ban [пользователь] [причина] - Забанить пользователя
/unban [пользователь] - Разбанить пользователя
/kick [пользователь] [причина] - Кикнуть пользователя
/promote [пользователь] - Повысить до модератора
/demote [пользователь] - Понизить с модератора
/import_csv [файл] - Импорт пользователей из CSV файла
"""
await update.message.reply_text(help_text, parse_mode='HTML', reply_markup=reply_markup)
async def rank(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def rank(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Показать рейтинг пользователя""" """Показать рейтинг пользователя"""
@@ -199,29 +96,29 @@ class TelegramBot:
user_info = db.get_user_info(user.id) user_info = db.get_user_info(user.id)
if user_info: if user_info:
rank_text = f""" rank_text = RANK_MESSAGES['rank_title'].format(
🏆 <b>Ваш рейтинг:</b> score=user_info['Очки'],
warnings=user_info['Предупреждений'],
Очки: {user_info['Очки']} role=user_info['Роль']
Предупреждений: {user_info['Предупреждений']} )
Роль: {user_info['Роль']}
"""
else: else:
rank_text = "Информация о вас не найдена." rank_text = RANK_MESSAGES['rank_not_found']
await update.message.reply_text(rank_text, parse_mode='HTML') await update.message.reply_text(rank_text, parse_mode='HTML')
async def leaderboard(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def leaderboard(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Показать топ пользователей""" """Показать топ пользователей"""
top_users = db.get_top_users(10) top_users = db.get_top_users(100)
if top_users: if top_users:
leaderboard_text = "🏆 <b>Топ-10 участников:</b>\n\n" leaderboard_text = RANK_MESSAGES['leaderboard_title']
for i, (user_id, username, first_name, score) in enumerate(top_users, 1): for i, (user_id, username, first_name, score) in enumerate(top_users, 1):
name = username if username else first_name name = username if username else first_name
leaderboard_text += f"{i}. {name} - {score} очков\n" leaderboard_text += RANK_MESSAGES['leaderboard_entry'].format(
position=i, name=name, score=score
)
else: else:
leaderboard_text = "Рейтинг пока пуст." leaderboard_text = RANK_MESSAGES['leaderboard_empty']
await update.message.reply_text(leaderboard_text, parse_mode='HTML') await update.message.reply_text(leaderboard_text, parse_mode='HTML')
@@ -297,7 +194,7 @@ ID: {user_info['ID']}
async def weather(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def weather(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Получить погоду""" """Получить погоду"""
if not OPENWEATHER_API_KEY: if not OPENWEATHER_API_KEY:
await update.message.reply_text("API ключ для погоды не настроен.") await update.message.reply_text(WEATHER_MESSAGES['no_api_key'])
return return
city = ' '.join(context.args) if context.args else 'Moscow' city = ' '.join(context.args) if context.args else 'Moscow'
@@ -308,25 +205,24 @@ ID: {user_info['ID']}
data = response.json() data = response.json()
if data['cod'] == 200: if data['cod'] == 200:
weather_text = f""" weather_text = WEATHER_MESSAGES['weather_info'].format(
🌤️ <b>Погода в {data['name']}:</b> city=data['name'],
temp=data['main']['temp'],
Температура: {data['main']['temp']}°C feels_like=data['main']['feels_like'],
Ощущается как: {data['main']['feels_like']}°C humidity=data['main']['humidity'],
Влажность: {data['main']['humidity']}% description=data['weather'][0]['description']
Описание: {data['weather'][0]['description']} )
"""
else: else:
weather_text = "Город не найден." weather_text = WEATHER_MESSAGES['city_not_found']
await update.message.reply_text(weather_text, parse_mode='HTML') await update.message.reply_text(weather_text, parse_mode='HTML')
except Exception as e: except Exception as e:
await update.message.reply_text(f"Ошибка при получении погоды: {e}") await update.message.reply_text(WEATHER_MESSAGES['weather_error'].format(error=e))
async def news(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def news(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Получить новости""" """Получить новости"""
if not NEWS_API_KEY: if not NEWS_API_KEY:
await update.message.reply_text("API ключ для новостей не настроен.") await update.message.reply_text(NEWS_MESSAGES['no_api_key'])
return return
try: try:
@@ -335,20 +231,20 @@ ID: {user_info['ID']}
data = response.json() data = response.json()
if data['status'] == 'ok' and data['articles']: if data['status'] == 'ok' and data['articles']:
news_text = "📰 <b>Последние новости:</b>\n\n" news_text = NEWS_MESSAGES['news_title']
for i, article in enumerate(data['articles'][:5], 1): for i, article in enumerate(data['articles'][:5], 1):
news_text += f"{i}. {article['title']}\n{article['url']}\n\n" news_text += f"{i}. {article['title']}\n{article['url']}\n\n"
else: else:
news_text = "Новости не найдены." news_text = NEWS_MESSAGES['news_not_found']
await update.message.reply_text(news_text, parse_mode='HTML') await update.message.reply_text(news_text, parse_mode='HTML')
except Exception as e: except Exception as e:
await update.message.reply_text(f"Ошибка при получении новостей: {e}") await update.message.reply_text(NEWS_MESSAGES['news_error'].format(error=e))
async def translate(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def translate(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Перевод текста""" """Перевод текста"""
if len(context.args) < 2: if len(context.args) < 2:
await update.message.reply_text("Использование: /translate [текст] [язык] (например: /translate hello en)") await update.message.reply_text(TRANSLATE_MESSAGES['usage'])
return return
text = ' '.join(context.args[:-1]) text = ' '.join(context.args[:-1])
@@ -357,9 +253,9 @@ ID: {user_info['ID']}
# Простой перевод с помощью Google Translate API (нужен API ключ) # Простой перевод с помощью Google Translate API (нужен API ключ)
try: try:
# В реальном проекте используйте Google Translate API или другой сервис # В реальном проекте используйте Google Translate API или другой сервис
await update.message.reply_text(f"Перевод '{text}' на {target_lang}: [здесь будет перевод]") await update.message.reply_text(TRANSLATE_MESSAGES['result'].format(text=text, lang=target_lang))
except Exception as e: except Exception as e:
await update.message.reply_text(f"Ошибка при переводе: {e}") await update.message.reply_text(TRANSLATE_MESSAGES['error'].format(error=e))
async def play_game(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def play_game(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Запустить мини-игру""" """Запустить мини-игру"""
@@ -407,7 +303,7 @@ ID: {user_info['ID']}
] ]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("🤖 Камень, ножницы, бумага!\n\nВыберите ваш ход:", reply_markup=reply_markup) await query.edit_message_text(GAME_MESSAGES['rps_game'], reply_markup=reply_markup)
async def start_tic_tac_toe_game(self, query, context): async def start_tic_tac_toe_game(self, query, context):
"""Запуск игры 'Крестики-нолики'""" """Запуск игры 'Крестики-нолики'"""
@@ -444,13 +340,16 @@ ID: {user_info['ID']}
board[row][col] = 'X' board[row][col] = 'X'
if self.check_winner(board, 'X'): if self.check_winner(board, 'X'):
await query.edit_message_text("🎉 Вы выиграли! 🏆") await query.edit_message_text(GAME_MESSAGES['tic_tac_toe_win'])
db.update_score(query.from_user.id, 15) db.update_score(query.from_user.id, SCORE_VALUES['tic_tac_toe_win'])
db.update_reputation(query.from_user.id, 15) db.update_reputation(query.from_user.id, SCORE_VALUES['reputation_per_message'])
rank_update = db.update_rank(query.from_user.id, query.message.chat.id, query.from_user.first_name) rank_update = db.update_rank(query.from_user.id, query.message.chat.id, query.from_user.first_name)
if rank_update and rank_update.get("promoted"): if rank_update and rank_update.get("promoted"):
await query.message.chat.send_message( await query.message.chat.send_message(
f"🌟 За активное участие в группе {rank_update['name']} получил(-а) новое звание {rank_update['new_rank']}!" RANK_MESSAGES['promotion_message'].format(
name=rank_update['name'],
new_rank=rank_update['new_rank']
)
) )
# Показать финальное поле с кнопками # Показать финальное поле с кнопками
keyboard = self.create_tic_tac_toe_keyboard(board, game_over=True) keyboard = self.create_tic_tac_toe_keyboard(board, game_over=True)
@@ -473,16 +372,16 @@ ID: {user_info['ID']}
if self.check_winner(board, 'O'): if self.check_winner(board, 'O'):
keyboard = self.create_tic_tac_toe_keyboard(board, game_over=True) keyboard = self.create_tic_tac_toe_keyboard(board, game_over=True)
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("😞 Вы проиграли. Бот победил!", reply_markup=reply_markup) await query.edit_message_text(GAME_MESSAGES['tic_tac_toe_lose'], reply_markup=reply_markup)
return return
if self.is_board_full(board): if self.is_board_full(board):
await query.edit_message_text("🤝 Ничья!") await query.edit_message_text(GAME_MESSAGES['tic_tac_toe_draw'])
return return
keyboard = self.create_tic_tac_toe_keyboard(board) keyboard = self.create_tic_tac_toe_keyboard(board)
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("❌⭕ Крестики-нолики!\n\nВаш ход. Выберите клетку:", reply_markup=reply_markup) await query.edit_message_text(GAME_MESSAGES['tic_tac_toe_game'], reply_markup=reply_markup)
def create_tic_tac_toe_keyboard(self, board, game_over=False): def create_tic_tac_toe_keyboard(self, board, game_over=False):
"""Создание клавиатуры для игры""" """Создание клавиатуры для игры"""
@@ -566,14 +465,22 @@ ID: {user_info['ID']}
result = self.determine_rps_winner(user_choice, bot_choice) result = self.determine_rps_winner(user_choice, bot_choice)
if result == 'win': if result == 'win':
message = f"🎉 Вы выиграли!\n\nВаш выбор: {choices[user_choice]}\nМой выбор: {choices[bot_choice]}" message = GAME_MESSAGES['rps_win'].format(
db.update_score(query.from_user.id, 5) user_choice=choices[user_choice],
db.update_reputation(query.from_user.id, 5) bot_choice=choices[bot_choice]
db.update_reputation(query.from_user.id, 5) )
db.update_score(query.from_user.id, SCORE_VALUES['game_win'])
db.update_reputation(query.from_user.id, SCORE_VALUES['reputation_per_message'])
elif result == 'lose': elif result == 'lose':
message = f"😞 Вы проиграли!\n\nВаш выбор: {choices[user_choice]}\nМой выбор: {choices[bot_choice]}" message = GAME_MESSAGES['rps_lose'].format(
user_choice=choices[user_choice],
bot_choice=choices[bot_choice]
)
else: else:
message = f"🤝 Ничья!\n\nВаш выбор: {choices[user_choice]}\nМой выбор: {choices[bot_choice]}" message = GAME_MESSAGES['rps_draw'].format(
user_choice=choices[user_choice],
bot_choice=choices[bot_choice]
)
keyboard = [ keyboard = [
[InlineKeyboardButton("🔄 Новая игра", callback_data='game_rps')], [InlineKeyboardButton("🔄 Новая игра", callback_data='game_rps')],
@@ -612,7 +519,7 @@ ID: {user_info['ID']}
keyboard.append([InlineKeyboardButton(answer, callback_data=f'quiz_{i}')]) keyboard.append([InlineKeyboardButton(answer, callback_data=f'quiz_{i}')])
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text(f"🧠 Викторина:\n\n{question['question']}", reply_markup=reply_markup) await query.edit_message_text(GAME_MESSAGES['quiz_game'].format(question=question['question']), reply_markup=reply_markup)
async def handle_quiz_answer(self, query, context): async def handle_quiz_answer(self, query, context):
"""Обработка ответа на вопрос викторины""" """Обработка ответа на вопрос викторины"""
@@ -620,27 +527,27 @@ ID: {user_info['ID']}
correct_index = context.user_data.get('quiz_correct') correct_index = context.user_data.get('quiz_correct')
if correct_index is None: if correct_index is None:
await query.edit_message_text("Вопрос викторины не найден.") await query.edit_message_text(GAME_MESSAGES['quiz_not_found'])
return return
question = context.user_data.get('quiz_question', {}) question = context.user_data.get('quiz_question', {})
if answer_index == correct_index: if answer_index == correct_index:
keyboard = [ keyboard = GAME_KEYBOARDS['help_menu']
[InlineKeyboardButton("🔄 Новая игра", callback_data='game_quiz')],
[InlineKeyboardButton("⬅️ Назад к играм", callback_data='cmd_play_game')]
]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text(f"🎉 Правильно! Вы заработали 5 очков!\n\nВопрос: {question.get('question', '')}\nОтвет: {question.get('answers', [])[correct_index]}", reply_markup=reply_markup) await query.edit_message_text(GAME_MESSAGES['quiz_correct'].format(
db.update_score(query.from_user.id, 5) question=question.get('question', ''),
answer=question.get('answers', [])[correct_index]
), reply_markup=reply_markup)
db.update_score(query.from_user.id, SCORE_VALUES['game_win'])
else: else:
keyboard = [ keyboard = GAME_KEYBOARDS['help_menu']
[InlineKeyboardButton("🔄 Новая игра", callback_data='game_quiz')],
[InlineKeyboardButton("⬅️ Назад к играм", callback_data='cmd_play_game')]
]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
correct_answer = question.get('answers', [])[correct_index] if correct_index < len(question.get('answers', [])) else "неизвестен" correct_answer = question.get('answers', [])[correct_index] if correct_index < len(question.get('answers', [])) else "неизвестен"
await query.edit_message_text(f"❌ Неправильно!\n\nВопрос: {question.get('question', '')}\nПравильный ответ: {correct_answer}", reply_markup=reply_markup) await query.edit_message_text(GAME_MESSAGES['quiz_wrong'].format(
question=question.get('question', ''),
correct_answer=correct_answer
), reply_markup=reply_markup)
# Очистка данных викторины # Очистка данных викторины
context.user_data.pop('quiz_correct', None) context.user_data.pop('quiz_correct', None)
@@ -649,11 +556,11 @@ ID: {user_info['ID']}
async def ban_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def ban_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Забанить пользователя (только админы)""" """Забанить пользователя (только админы)"""
if not await self.is_admin(update.effective_chat, update.effective_user.id): if not await self.is_admin(update.effective_chat, update.effective_user.id):
await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") await update.message.reply_text(MODERATION_MESSAGES['no_permission'])
return return
if len(context.args) < 2: if len(context.args) < 2:
await update.message.reply_text("Использование: /ban [пользователь] [причина]") await update.message.reply_text(MODERATION_MESSAGES['ban_usage'])
return return
user_id = context.args[0] user_id = context.args[0]
@@ -661,9 +568,9 @@ ID: {user_info['ID']}
try: try:
await update.effective_chat.ban_member(int(user_id)) await update.effective_chat.ban_member(int(user_id))
await update.message.reply_text(f"🚫 Пользователь {user_id} забанен.\nПричина: {reason}") await update.message.reply_text(MODERATION_MESSAGES['user_banned'].format(user_id=user_id, reason=reason))
except Exception as e: except Exception as e:
await update.message.reply_text(f"❌ Ошибка при бане пользователя: {e}") await update.message.reply_text(MODERATION_MESSAGES['ban_error'].format(error=e))
async def unban_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def unban_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Разбанить пользователя (только админы)""" """Разбанить пользователя (только админы)"""
@@ -679,9 +586,9 @@ ID: {user_info['ID']}
try: try:
await update.effective_chat.unban_member(int(user_id)) await update.effective_chat.unban_member(int(user_id))
await update.message.reply_text(f"✅ Пользователь {user_id} разбанен.") await update.message.reply_text(MODERATION_MESSAGES['user_unbanned'].format(user_id=user_id))
except Exception as e: except Exception as e:
await update.message.reply_text(f"❌ Ошибка при разбане пользователя: {e}") await update.message.reply_text(MODERATION_MESSAGES['unban_error'].format(error=e))
async def mute_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def mute_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Заглушить пользователя (только админы)""" """Заглушить пользователя (только админы)"""
@@ -697,7 +604,7 @@ ID: {user_info['ID']}
try: try:
mute_time = int(context.args[1]) mute_time = int(context.args[1])
except ValueError: except ValueError:
await update.message.reply_text("❌ Время должно быть числом в секундах.") await update.message.reply_text(MODERATION_MESSAGES['mute_invalid_time'])
return return
from datetime import datetime, timedelta from datetime import datetime, timedelta
@@ -709,18 +616,18 @@ ID: {user_info['ID']}
until_date=until_date, until_date=until_date,
can_send_messages=False can_send_messages=False
) )
await update.message.reply_text(f"🔇 Пользователь {user_id} заглушен на {mute_time} секунд.") await update.message.reply_text(MODERATION_MESSAGES['user_muted'].format(user_id=user_id, time=mute_time))
except Exception as e: except Exception as e:
await update.message.reply_text(f"❌ Ошибка при mute пользователя: {e}") await update.message.reply_text(MODERATION_MESSAGES['mute_error'].format(error=e))
async def unmute_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def unmute_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Снять заглушку с пользователя (только админы)""" """Снять заглушку с пользователя (только админы)"""
if not await self.is_admin(update.effective_chat, update.effective_user.id): if not await self.is_admin(update.effective_chat, update.effective_user.id):
await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") await update.message.reply_text(MODERATION_MESSAGES['no_permission'])
return return
if len(context.args) < 1: if len(context.args) < 1:
await update.message.reply_text("Использование: /unmute [пользователь]") await update.message.reply_text(MODERATION_MESSAGES['unmute_usage'])
return return
user_id = context.args[0] user_id = context.args[0]
@@ -733,18 +640,18 @@ ID: {user_info['ID']}
can_send_other_messages=True, can_send_other_messages=True,
can_add_web_page_previews=True can_add_web_page_previews=True
) )
await update.message.reply_text(f"🔊 Заглушка с пользователя {user_id} снята.") await update.message.reply_text(MODERATION_MESSAGES['user_unmuted'].format(user_id=user_id))
except Exception as e: except Exception as e:
await update.message.reply_text(f"❌ Ошибка при unmute пользователя: {e}") await update.message.reply_text(MODERATION_MESSAGES['unmute_error'].format(error=e))
async def kick_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def kick_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Кикнуть пользователя (только админы)""" """Кикнуть пользователя (только админы)"""
if not await self.is_admin(update.effective_chat, update.effective_user.id): if not await self.is_admin(update.effective_chat, update.effective_user.id):
await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") await update.message.reply_text(MODERATION_MESSAGES['no_permission'])
return return
if len(context.args) < 2: if len(context.args) < 2:
await update.message.reply_text("Использование: /kick [пользователь] [причина]") await update.message.reply_text(MODERATION_MESSAGES['kick_usage'])
return return
user_id = context.args[0] user_id = context.args[0]
@@ -753,18 +660,18 @@ ID: {user_info['ID']}
try: try:
await update.effective_chat.ban_member(int(user_id)) await update.effective_chat.ban_member(int(user_id))
await update.effective_chat.unban_member(int(user_id)) # Разбан сразу после бана = кик await update.effective_chat.unban_member(int(user_id)) # Разбан сразу после бана = кик
await update.message.reply_text(f"👢 Пользователь {user_id} кикнут.\nПричина: {reason}") await update.message.reply_text(MODERATION_MESSAGES['user_kicked'].format(user_id=user_id, reason=reason))
except Exception as e: except Exception as e:
await update.message.reply_text(f"❌ Ошибка при кике пользователя: {e}") await update.message.reply_text(MODERATION_MESSAGES['kick_error'].format(error=e))
async def promote_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def promote_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Повысить пользователя до модератора (только админы)""" """Повысить пользователя до модератора (только админы)"""
if not await self.is_admin(update.effective_chat, update.effective_user.id): if not await self.is_admin(update.effective_chat, update.effective_user.id):
await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") await update.message.reply_text(MODERATION_MESSAGES['no_permission'])
#return return
if len(context.args) < 1: if len(context.args) < 1:
await update.message.reply_text("Использование: /promote [пользователь]") await update.message.reply_text(MODERATION_MESSAGES['promote_usage'])
return return
user_id = context.args[0] user_id = context.args[0]
@@ -776,18 +683,18 @@ ID: {user_info['ID']}
can_restrict_members=True, can_restrict_members=True,
can_invite_users=True can_invite_users=True
) )
await update.message.reply_text(f"⬆️ Пользователь {user_id} повышен до модератора.") await update.message.reply_text(MODERATION_MESSAGES['user_promoted'].format(user_id=user_id))
except Exception as e: except Exception as e:
await update.message.reply_text(f"❌ Ошибка при повышении пользователя: {e}") await update.message.reply_text(MODERATION_MESSAGES['promote_error'].format(error=e))
async def demote_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def demote_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Понизить пользователя с модератора (только админы)""" """Понизить пользователя с модератора (только админы)"""
if not await self.is_admin(update.effective_chat, update.effective_user.id): if not await self.is_admin(update.effective_chat, update.effective_user.id):
await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") await update.message.reply_text(MODERATION_MESSAGES['no_permission'])
return return
if len(context.args) < 1: if len(context.args) < 1:
await update.message.reply_text("Использование: /demote [пользователь]") await update.message.reply_text(MODERATION_MESSAGES['demote_usage'])
return return
user_id = context.args[0] user_id = context.args[0]
@@ -800,82 +707,38 @@ ID: {user_info['ID']}
can_invite_users=False, can_invite_users=False,
can_pin_messages=False can_pin_messages=False
) )
await update.message.reply_text(f"⬇️ Пользователь {user_id} понижен с модератора.") await update.message.reply_text(MODERATION_MESSAGES['user_demoted'].format(user_id=user_id))
except Exception as e: except Exception as e:
await update.message.reply_text(f"❌ Ошибка при понижении пользователя: {e}") await update.message.reply_text(MODERATION_MESSAGES['demote_error'].format(error=e))
async def warn_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def warn_user(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Выдать предупреждение пользователю (только админы)""" """Выдать предупреждение пользователю (только админы)"""
if not await self.is_admin(update.effective_chat, update.effective_user.id): if not await self.is_admin(update.effective_chat, update.effective_user.id):
await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") await update.message.reply_text(MODERATION_MESSAGES['no_permission'])
return return
if len(context.args) < 2: if len(context.args) < 2:
await update.message.reply_text("Использование: /warn [пользователь] [причина]") await update.message.reply_text(MODERATION_MESSAGES['warn_usage'])
return return
user_id = context.args[0] user_id = context.args[0]
reason = ' '.join(context.args[1:]) reason = ' '.join(context.args[1:])
db.add_warning(user_id, reason, update.effective_user.id) db.add_warning(user_id, reason, update.effective_user.id)
await update.message.reply_text(f"⚠️ Предупреждение выдано пользователю {user_id} по причине: {reason}") await update.message.reply_text(MODERATION_MESSAGES['warning_issued'].format(user_id=user_id, reason=reason))
async def ranks_info(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def ranks_info(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Показать информацию о системе рангов""" """Показать информацию о системе рангов"""
ranks_text = """ await update.message.reply_text(RANK_MESSAGES['rank_info'], parse_mode='HTML')
🏆 <b>Военная система рангов</b>
<b>Как работает система:</b>
• Каждый участник начинает с звания "Рядовой"
• Звание повышается автоматически при достижении определенного количества очков
• Очки начисляются за активность в чате и победы в играх
<b>Иерархия званий:</b>
🪖 Рядовой (0 очков)
🪖 Ефрейтор (100 очков)
🪖 Младший сержант (235 очков)
🪖 Сержант (505 очков)
🪖 Старший сержант (810 очков)
🪖 Старшина (1,250 очков)
🪖 Прапорщик (1,725 очков)
🪖 Старший прапорщик (2,335 очков)
🪖 Младший лейтенант (2,980 очков)
🪖 Лейтенант (3,760 очков)
🪖 Старший лейтенант (4,575 очков)
🪖 Капитан (5,525 очков)
🪖 Майор (6,510 очков)
🪖 Подполковник (7,630 очков)
🪖 Полковник (8,785 очков)
🪖 Генерал майор (16,075 очков)
🪖 Генерал лейтенант (32,150 очков)
🪖 Генерал полковник (64,300 очков)
🪖 Генерал армии (128,600 очков)
🪖 Маршал (256,000 очков)
<b>Как получить очки:</b>
• +1 очко за каждое сообщение в чате
• +5 очков за победу в мини-игре
• +15 очков за победу в крестиках-ноликах
• Бонусные очки за активность (случайно 1-5 очков)
<b>Команды для работы с рангом:</b>
/rank - Показать ваш текущий ранг и очки
/leaderboard - Топ-10 участников по очкам
/info - Детальная информация о вашем профиле
Служите чату верой и правдой, чтобы достичь высших воинских званий! 🎖️
"""
await update.message.reply_text(ranks_text, parse_mode='HTML')
async def import_csv(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def import_csv(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Импорт пользователей из CSV файла (только админы)""" """Импорт пользователей из CSV файла (только админы)"""
if not await self.is_admin(update.effective_chat, update.effective_user.id): if not await self.is_admin(update.effective_chat, update.effective_user.id):
await update.message.reply_text("❌ У вас нет прав для выполнения этой команды.") await update.message.reply_text(IMPORT_MESSAGES['no_permission'])
return return
if len(context.args) < 1: if len(context.args) < 1:
await update.message.reply_text("Использование: /import_csv [путь_к_файлу]\n\nПо умолчанию: /import_csv chat_-1001519866478_users_full_20251014.csv") await update.message.reply_text(IMPORT_MESSAGES['usage'])
return return
csv_file = context.args[0] if context.args else 'chat_-1001519866478_users_full_20251014.csv' csv_file = context.args[0] if context.args else 'chat_-1001519866478_users_full_20251014.csv'
@@ -884,18 +747,18 @@ ID: {user_info['ID']}
if not os.path.isabs(csv_file): if not os.path.isabs(csv_file):
csv_file = os.path.join('telegram_bot', csv_file) csv_file = os.path.join('telegram_bot', csv_file)
await update.message.reply_text(f"🔄 Начинаю импорт пользователей из файла: {csv_file}") await update.message.reply_text(IMPORT_MESSAGES['start_import'].format(file=csv_file))
try: try:
success = db.import_users_from_csv(csv_file) success = db.import_users_from_csv(csv_file)
if success: if success:
await update.message.reply_text("✅ Импорт пользователей успешно завершен!") await update.message.reply_text(IMPORT_MESSAGES['success'])
else: else:
await update.message.reply_text("❌ Произошла ошибка при импорте пользователей.") await update.message.reply_text(IMPORT_MESSAGES['error'])
except Exception as e: except Exception as e:
await update.message.reply_text(f"❌ Ошибка при импорте: {str(e)}") await update.message.reply_text(IMPORT_MESSAGES['file_error'].format(error=str(e)))
async def schedule_post(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def schedule_post(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Запланировать пост для публикации""" """Запланировать пост для публикации"""
@@ -903,17 +766,11 @@ ID: {user_info['ID']}
# Проверка прав администратора # Проверка прав администратора
if not await self.is_admin(update.effective_chat, user.id): if not await self.is_admin(update.effective_chat, user.id):
await update.message.reply_text("❌ У вас нет прав для планирования постов.") await update.message.reply_text(SCHEDULER_MESSAGES['no_permission'])
return return
if len(context.args) < 2: if len(context.args) < 2:
await update.message.reply_text( await update.message.reply_text(SCHEDULER_MESSAGES['usage_schedule'])
"Использование: /schedule_post [время] [текст поста]\n\n"
"Формат времени:\n"
"• Абсолютное: 2024-01-15 14:30:00\n"
"• Относительное: +30m (через 30 минут), +2h (через 2 часа), +1d (через день)\n\n"
"Пример: /schedule_post +2h Привет всем!"
)
return return
# Парсинг времени и текста # Парсинг времени и текста
@@ -923,11 +780,11 @@ ID: {user_info['ID']}
try: try:
schedule_time = self.parse_schedule_time(time_str) schedule_time = self.parse_schedule_time(time_str)
except ValueError as e: except ValueError as e:
await update.message.reply_text(f"❌ Ошибка формата времени: {str(e)}") await update.message.reply_text(SCHEDULER_MESSAGES['invalid_format'].format(error=str(e)))
return return
if schedule_time <= datetime.now(): if schedule_time <= datetime.now():
await update.message.reply_text("❌ Время публикации должно быть в будущем!") await update.message.reply_text(SCHEDULER_MESSAGES['time_in_past'])
return return
# Добавление поста в базу данных # Добавление поста в базу данных
@@ -940,13 +797,14 @@ ID: {user_info['ID']}
if post_id: if post_id:
await update.message.reply_text( await update.message.reply_text(
f"✅ Пост запланирован!\n\n" SCHEDULER_MESSAGES['post_scheduled'].format(
f"📅 Время публикации: {schedule_time.strftime('%Y-%m-%d %H:%M:%S')}\n" time=schedule_time.strftime('%Y-%m-%d %H:%M:%S'),
f"📝 Текст: {text[:50]}{'...' if len(text) > 50 else ''}\n" text=text[:50] + ('...' if len(text) > 50 else ''),
f"🆔 ID поста: {post_id}" post_id=post_id
)
) )
else: else:
await update.message.reply_text("❌ Ошибка при сохранении поста в базу данных.") await update.message.reply_text(SCHEDULER_MESSAGES['save_error'])
async def list_posts(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def list_posts(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Показать список запланированных постов""" """Показать список запланированных постов"""
@@ -954,13 +812,13 @@ ID: {user_info['ID']}
# Проверка прав администратора # Проверка прав администратора
if not await self.is_admin(update.effective_chat, user.id): if not await self.is_admin(update.effective_chat, user.id):
await update.message.reply_text("❌ У вас нет прав для просмотра постов.") await update.message.reply_text(SCHEDULER_MESSAGES['no_permission_view'])
return return
posts = db.get_scheduled_posts(chat_id=update.effective_chat.id) posts = db.get_scheduled_posts(chat_id=update.effective_chat.id)
if not posts: if not posts:
await update.message.reply_text("📭 Нет запланированных постов.") await update.message.reply_text(SCHEDULER_MESSAGES['no_posts'])
return return
response = "📋 <b>Запланированные посты:</b>\n\n" response = "📋 <b>Запланированные посты:</b>\n\n"
@@ -983,25 +841,25 @@ ID: {user_info['ID']}
# Проверка прав администратора # Проверка прав администратора
if not await self.is_admin(update.effective_chat, user.id): if not await self.is_admin(update.effective_chat, user.id):
await update.message.reply_text("❌ У вас нет прав для удаления постов.") await update.message.reply_text(SCHEDULER_MESSAGES['no_permission_delete'])
return return
if len(context.args) < 1: if len(context.args) < 1:
await update.message.reply_text("Использование: /delete_post [ID поста]") await update.message.reply_text(SCHEDULER_MESSAGES['usage_delete'])
return return
try: try:
post_id = int(context.args[0]) post_id = int(context.args[0])
except ValueError: except ValueError:
await update.message.reply_text("❌ ID поста должен быть числом.") await update.message.reply_text(SCHEDULER_MESSAGES['invalid_id'])
return return
success = db.delete_scheduled_post(post_id, user.id) success = db.delete_scheduled_post(post_id, user.id)
if success: if success:
await update.message.reply_text(f"✅ Пост {post_id} удален.") await update.message.reply_text(SCHEDULER_MESSAGES['post_deleted'].format(post_id=post_id))
else: else:
await update.message.reply_text(f"❌ Пост {post_id} не найден или у вас нет прав для его удаления.") await update.message.reply_text(SCHEDULER_MESSAGES['post_not_found'].format(post_id=post_id))
async def publish_now(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def publish_now(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Опубликовать пост немедленно""" """Опубликовать пост немедленно"""
@@ -1009,17 +867,17 @@ ID: {user_info['ID']}
# Проверка прав администратора # Проверка прав администратора
if not await self.is_admin(update.effective_chat, user.id): if not await self.is_admin(update.effective_chat, user.id):
await update.message.reply_text("❌ У вас нет прав для публикации постов.") await update.message.reply_text(SCHEDULER_MESSAGES['no_permission_publish'])
return return
if len(context.args) < 1: if len(context.args) < 1:
await update.message.reply_text("Использование: /publish_now [ID поста]") await update.message.reply_text(SCHEDULER_MESSAGES['usage_publish'])
return return
try: try:
post_id = int(context.args[0]) post_id = int(context.args[0])
except ValueError: except ValueError:
await update.message.reply_text("❌ ID поста должен быть числом.") await update.message.reply_text(SCHEDULER_MESSAGES['invalid_id'])
return return
# Получаем пост из базы данных # Получаем пост из базы данных
@@ -1031,7 +889,7 @@ ID: {user_info['ID']}
break break
if not post: if not post:
await update.message.reply_text(f"❌ Пост {post_id} не найден.") await update.message.reply_text(SCHEDULER_MESSAGES['post_not_found'].format(post_id=post_id))
return return
post_id, chat_id, text, image_path, schedule_time, created_by, status, published_at, created_at, creator_name = post post_id, chat_id, text, image_path, schedule_time, created_by, status, published_at, created_at, creator_name = post
@@ -1046,10 +904,10 @@ ID: {user_info['ID']}
# Отмечаем пост как опубликованный # Отмечаем пост как опубликованный
db.mark_post_published(post_id) db.mark_post_published(post_id)
await update.message.reply_text(f"✅ Пост {post_id} опубликован немедленно!") await update.message.reply_text(SCHEDULER_MESSAGES['post_published'].format(post_id=post_id))
except Exception as e: except Exception as e:
await update.message.reply_text(f"❌ Ошибка при публикации поста: {str(e)}") await update.message.reply_text(SCHEDULER_MESSAGES['publish_error'].format(error=str(e)))
def parse_schedule_time(self, time_str): def parse_schedule_time(self, time_str):
"""Парсинг времени публикации из строки""" """Парсинг времени публикации из строки"""
@@ -1113,17 +971,20 @@ ID: {user_info['ID']}
rank_update = db.update_rank(user.id, update.effective_chat.id, user.first_name) rank_update = db.update_rank(user.id, update.effective_chat.id, user.first_name)
if rank_update and rank_update.get("promoted"): if rank_update and rank_update.get("promoted"):
await update.effective_chat.send_message( await update.effective_chat.send_message(
f"🌟 За активное участие в группе {rank_update['name']} получил(-а) новое звание {rank_update['new_rank']}!" RANK_MESSAGES['promotion_message'].format(
name=rank_update['name'],
new_rank=rank_update['new_rank']
)
) )
# Проверка на слова "реквизиты" # Проверка на слова "реквизиты"
if "реквизиты" in message_text or "реквизит" in message_text: if "реквизиты" in message_text or "реквизит" in message_text:
await update.message.reply_text(BANK_DETAILS_TEXT, reply_to_message_id=update.message.message_id) await update.message.reply_text(messages.BANK_DETAILS_TEXT, reply_to_message_id=update.message.message_id)
return return
# Предопределенные ответы # Предопределенные ответы
response_found = False response_found = False
for key, response in PREDEFINED_RESPONSES.items(): for key, response in messages.PREDEFINED_RESPONSES.items():
if key in message_text: if key in message_text:
await update.message.reply_text(response) await update.message.reply_text(response)
response_found = True response_found = True
@@ -1131,40 +992,27 @@ ID: {user_info['ID']}
if not response_found: if not response_found:
# Если нет предопределенного ответа, показываем сообщение и help # Если нет предопределенного ответа, показываем сообщение и help
keyboard = [ keyboard = GAME_KEYBOARDS['main_menu']
[InlineKeyboardButton("📋 Помощь", callback_data='cmd_help')],
[InlineKeyboardButton("🎮 Мини игры", callback_data='cmd_play_game')]
]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text( await update.message.reply_text(
"🤖 Извините, я не понял ваше сообщение. Вот что я умею:\n\n" USER_MESSAGES['unknown_message'],
"• Отвечать на простые вопросы\n"
"• Вести рейтинг участников\n"
"• Предоставлять погоду и новости\n"
"• Играть в мини-игры\n"
"• Помогать с модерацией\n\n"
"Используйте /help для подробной информации.",
reply_markup=reply_markup reply_markup=reply_markup
) )
return return
# Случайное начисление очков от 1 до 5 вместо случайных ответов # Случайное начисление очков от 1 до 5 вместо случайных ответов
if random.random() < 0.1: # 10% шанс if random.random() < 0.1: # 10% шанс
bonus_points = random.randint(1, 5) bonus_points = random.randint(SCORE_VALUES['bonus_min'], SCORE_VALUES['bonus_max'])
db.update_score(user.id, bonus_points) db.update_score(user.id, bonus_points)
await update.message.reply_text(f"🎁 Бонус! Вам начислено {bonus_points} очков!") await update.message.reply_text(USER_MESSAGES['bonus_points'].format(points=bonus_points))
async def handle_new_chat_members(self, update: Update, context: ContextTypes.DEFAULT_TYPE): async def handle_new_chat_members(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработка новых участников чата""" """Обработка новых участников чата"""
for member in update.chat_member.new_chat_members: for member in update.chat_member.new_chat_members:
db.add_user(member.id, member.username, member.first_name, member.last_name) db.add_user(member.id, member.username, member.first_name, member.last_name)
welcome_text = f""" welcome_text = USER_MESSAGES['welcome_new_user'].format(name=member.first_name)
🎉 Добро пожаловать в чат, {member.first_name}!
Мы рады видеть вас здесь! Используйте /help чтобы узнать о возможностях бота.
"""
await update.effective_chat.send_message(welcome_text) await update.effective_chat.send_message(welcome_text)
@@ -1217,10 +1065,7 @@ ID: {user_info['ID']}
async def show_start_menu(self, query): async def show_start_menu(self, query):
"""Показать стартовое меню""" """Показать стартовое меню"""
keyboard = [ keyboard = GAME_KEYBOARDS['main_menu']
[InlineKeyboardButton("📋 Помощь", callback_data='cmd_help')],
[InlineKeyboardButton("🎮 Мини игры", callback_data='cmd_play_game')]
]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
welcome_text = f""" welcome_text = f"""
@@ -1240,15 +1085,10 @@ ID: {user_info['ID']}
async def show_help_menu(self, query): async def show_help_menu(self, query):
"""Показать меню помощи""" """Показать меню помощи"""
keyboard = [ keyboard = GAME_KEYBOARDS['help_menu']
[InlineKeyboardButton("🚀 Старт", callback_data='cmd_start')],
[InlineKeyboardButton("🔄 Начать заново", callback_data='cmd_restart')]
]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
help_text = HELP_TEXT await query.edit_message_text(messages.HELP_TEXT, parse_mode='HTML', reply_markup=reply_markup)
await query.edit_message_text(help_text, parse_mode='HTML', reply_markup=reply_markup)
async def show_games_menu(self, query): async def show_games_menu(self, query):
"""Показать меню выбора игр""" """Показать меню выбора игр"""
@@ -1260,7 +1100,7 @@ ID: {user_info['ID']}
] ]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("🎮 Выберите игру:", reply_markup=reply_markup) await query.edit_message_text(GAME_MESSAGES['select_game'], reply_markup=reply_markup)
def run(self): def run(self):
"""Запуск бота""" """Запуск бота"""
File diff suppressed because it is too large Load Diff
+20 -39
View File
@@ -2,6 +2,7 @@ import sqlite3
import os import os
from datetime import datetime from datetime import datetime
import csv import csv
from messages import TECH_MESSAGES
class Database: class Database:
def __init__(self, db_file='telegram_bot.db'): def __init__(self, db_file='telegram_bot.db'):
@@ -14,9 +15,9 @@ class Database:
"""Установка соединения с SQLite базой данных""" """Установка соединения с SQLite базой данных"""
try: try:
self.connection = sqlite3.connect(self.db_file) self.connection = sqlite3.connect(self.db_file)
print("Успешное подключение к SQLite базе данных") print(TECH_MESSAGES['db_connected'])
except sqlite3.Error as error: except sqlite3.Error as error:
print(f"Ошибка при подключении к SQLite: {error}") print(TECH_MESSAGES['db_connection_error'].format(error=error))
self.connection = None self.connection = None
def create_tables(self): def create_tables(self):
@@ -87,9 +88,9 @@ class Database:
""") """)
self.connection.commit() self.connection.commit()
print("Таблицы созданы успешно") print(TECH_MESSAGES['db_tables_created'])
except sqlite3.Error as error: except sqlite3.Error as error:
print(f"Ошибка при создании таблиц: {error}") print(TECH_MESSAGES['db_tables_error'].format(error=error))
def add_user(self, user_id, username, first_name, last_name): def add_user(self, user_id, username, first_name, last_name):
"""Добавление нового пользователя""" """Добавление нового пользователя"""
@@ -101,7 +102,7 @@ class Database:
""", (user_id, username, first_name, last_name)) """, (user_id, username, first_name, last_name))
self.connection.commit() self.connection.commit()
except sqlite3.Error as error: except sqlite3.Error as error:
print(f"Ошибка при добавлении пользователя: {error}") print(TECH_MESSAGES['user_added_error'].format(error=error))
def update_score(self, user_id, points=1): def update_score(self, user_id, points=1):
"""Обновление очков пользователя""" """Обновление очков пользователя"""
@@ -112,7 +113,7 @@ class Database:
""", (points, user_id)) """, (points, user_id))
self.connection.commit() self.connection.commit()
except sqlite3.Error as error: except sqlite3.Error as error:
print(f"Ошибка при обновлении очков: {error}") print(TECH_MESSAGES['score_update_error'].format(error=error))
def update_reputation(self, user_id, rep_points=1): def update_reputation(self, user_id, rep_points=1):
"""Обновление репутации пользователя""" """Обновление репутации пользователя"""
@@ -125,7 +126,7 @@ class Database:
# Обновление ранга на основе репутации # Обновление ранга на основе репутации
self.update_rank(user_id) self.update_rank(user_id)
except sqlite3.Error as error: except sqlite3.Error as error:
print(f"Ошибка при обновлении репутации: {error}") print(TECH_MESSAGES['reputation_update_error'].format(error=error))
def update_rank(self, user_id, chat_id=None, first_name=None): def update_rank(self, user_id, chat_id=None, first_name=None):
"""Обновление ранга пользователя на основе репутации""" """Обновление ранга пользователя на основе репутации"""
@@ -150,35 +151,15 @@ class Database:
else: else:
self.connection.commit() self.connection.commit()
except sqlite3.Error as error: except sqlite3.Error as error:
print(f"Ошибка при обновлении ранга: {error}") print(TECH_MESSAGES['rank_update_error'].format(error=error))
return None return None
def calculate_rank(self, reputation): def calculate_rank(self, reputation):
"""Расчет ранга на основе репутации""" """Расчет ранга на основе репутации"""
ranks = [ # Используем константы из messages.py
(0, "Рядовой"), from messages import RANK_THRESHOLDS
(100, "Ефрейтор"),
(235, "Младший сержант"),
(505, "Сержант"),
(810, "Старший сержант"),
(1250, "Старшина"),
(1725, "Прапорщик"),
(2335, "Старший прапорщик"),
(2980, "Младший лейтенант"),
(3760, "Лейтенант"),
(4575, "Старший лейтенант"),
(5525, "Капитан"),
(6510, "Майор"),
(7630, "Подполковник"),
(8785, "Полковник"),
(16075, "Генерал майор"),
(32150, "Генерал лейтенант"),
(64300, "Генерал полковник"),
(128600, "Генерал армии"),
(256000, "Маршал")
]
for threshold, rank_name in reversed(ranks): for threshold, rank_name in reversed(RANK_THRESHOLDS):
if reputation >= threshold: if reputation >= threshold:
return rank_name return rank_name
@@ -196,7 +177,7 @@ class Database:
""", (limit,)) """, (limit,))
return cursor.fetchall() return cursor.fetchall()
except sqlite3.Error as error: except sqlite3.Error as error:
print(f"Ошибка при получении топ пользователей: {error}") print(TECH_MESSAGES['top_users_error'].format(error=error))
return [] return []
def add_warning(self, user_id, reason, issued_by): def add_warning(self, user_id, reason, issued_by):
@@ -214,7 +195,7 @@ class Database:
self.connection.commit() self.connection.commit()
except sqlite3.Error as error: except sqlite3.Error as error:
print(f"Ошибка при добавлении предупреждения: {error}") print(TECH_MESSAGES['warning_add_error'].format(error=error))
def get_user_warnings(self, user_id): def get_user_warnings(self, user_id):
"""Получение количества предупреждений пользователя""" """Получение количества предупреждений пользователя"""
@@ -226,7 +207,7 @@ class Database:
result = cursor.fetchone() result = cursor.fetchone()
return result[0] if result else 0 return result[0] if result else 0
except sqlite3.Error as error: except sqlite3.Error as error:
print(f"Ошибка при получении предупреждений: {error}") print(TECH_MESSAGES['warnings_get_error'].format(error=error))
return 0 return 0
def get_user_info(self, user_id): def get_user_info(self, user_id):
@@ -259,14 +240,14 @@ class Database:
} }
return None return None
except sqlite3.Error as error: except sqlite3.Error as error:
print(f"Ошибка при получении информации о пользователе: {error}") print(TECH_MESSAGES['user_info_error'].format(error=error))
return None return None
def import_users_from_csv(self, csv_file_path): def import_users_from_csv(self, csv_file_path):
"""Импорт пользователей из CSV файла""" """Импорт пользователей из CSV файла"""
try: try:
if not os.path.exists(csv_file_path): if not os.path.exists(csv_file_path):
print(f"Файл {csv_file_path} не найден") print(TECH_MESSAGES['csv_file_not_found'].format(file=csv_file_path))
return False return False
imported_count = 0 imported_count = 0
@@ -312,14 +293,14 @@ class Database:
self.connection.commit() self.connection.commit()
except (ValueError, KeyError) as e: except (ValueError, KeyError) as e:
print(f"Ошибка обработки строки: {row}. Ошибка: {e}") print(TECH_MESSAGES['csv_row_error'].format(row=row, error=e))
continue continue
print(f"Импорт завершен. Добавлено: {imported_count}, Обновлено: {updated_count}") print(f"Импорт завершен. Добавлено: {imported_count}, Обновлено: {updated_count}")
return True return True
except sqlite3.Error as error: except sqlite3.Error as error:
print(f"Ошибка при импорте пользователей из CSV: {error}") print(TECH_MESSAGES['csv_import_error'].format(error=error))
return False return False
def add_scheduled_post(self, chat_id, text, schedule_time, created_by, image_path=None): def add_scheduled_post(self, chat_id, text, schedule_time, created_by, image_path=None):
@@ -419,4 +400,4 @@ class Database:
"""Закрытие соединения с базой данных""" """Закрытие соединения с базой данных"""
if self.connection: if self.connection:
self.connection.close() self.connection.close()
print("Соединение с SQLite закрыто") print(TECH_MESSAGES['db_closed'])
+385
View File
@@ -0,0 +1,385 @@
# -*- coding: utf-8 -*-
"""
Константы и текстовые сообщения для Telegram бота
"""
# Предопределенные ответы
PREDEFINED_RESPONSES = {
'привет': 'Привет! Как дела?',
'как дела': 'Отлично! А у тебя?',
'что делаешь': 'Отвечаю на сообщения в чате 😊',
'спасибо': 'Пожалуйста! Рад помочь!',
'пока': 'До свидания! Заходи еще!',
}
# Текст справки
HELP_TEXT = """
🤖 <b>Команды бота:</b>
<b>Основные:</b>
/start - Начать работу с ботом
/help - Показать это сообщение
/info - Информация о вас
<b>Рейтинг:</b>
/rank - Ваш текущий рейтинг
/ranks_info - Информация о системе рангов
/leaderboard - Топ-10 участников
<b>Информация:</b>
/weather [город] - Погода в городе
/news - Последние новости
/translate [текст] [язык] - Перевод текста
<b>Прочее:</b>
Введите "реквизиты" - Получить реквизиты для оплаты
<b>Игры:</b>
/play_game - Запустить мини-игру
<b>Посты по расписанию (только для админов):</b>
/schedule_post [время] [текст] - Запланировать пост
/list_posts - Показать запланированные посты
/delete_post [ID] - Удалить пост по расписанию
/publish_now [ID] - Опубликовать пост немедленно
<b>Модерация (только для админов):</b>
/warn [пользователь] [причина] - Выдать предупреждение
/mute [пользователь] [время] - Заглушить пользователя
/unmute [пользователь] - Снять заглушку
/ban [пользователь] [причина] - Забанить пользователя
/unban [пользователь] - Разбанить пользователя
/kick [пользователь] [причина] - Кикнуть пользователя
/promote [пользователь] - Повысить до модератора
/demote [пользователь] - Понизить с модератора
/import_csv [файл] - Импорт пользователей из CSV файла
"""
# Текст реквизитов
BANK_DETAILS_TEXT = "Денежные средства можно перечислить по номерам: \r\n💳 89066935474 Елена, \r\n💳89207144698 Людмила\r\nБольшая просьба не писать сообщения при переводе, карту могут заблокировать!!!"
# Сообщения планировщика постов
SCHEDULER_MESSAGES = {
'no_permission': '❌ У вас нет прав для планирования постов.',
'invalid_format': '❌ Ошибка формата времени: {error}',
'time_in_past': '❌ Время публикации должно быть в будущем!',
'post_scheduled': '✅ Пост запланирован!\n\n📅 Время публикации: {time}\n📝 Текст: {text}\n🆔 ID поста: {post_id}',
'save_error': '❌ Ошибка при сохранении поста в базу данных.',
'no_posts': '📭 Нет запланированных постов.',
'usage_schedule': 'Использование: /schedule_post [время] [текст поста]\n\nФормат времени:\n• Абсолютное: 2024-01-15 14:30:00\n• Относительное: +30m (через 30 минут), +2h (через 2 часа), +1d (через день)\n\nПример: /schedule_post +2h Привет всем!',
'usage_delete': 'Использование: /delete_post [ID поста]',
'usage_publish': 'Использование: /publish_now [ID поста]',
'invalid_id': '❌ ID поста должен быть числом.',
'post_deleted': '✅ Пост {post_id} удален.',
'post_not_found': '❌ Пост {post_id} не найден или у вас нет прав для его удаления.',
'post_published': '✅ Пост {post_id} опубликован немедленно!',
'publish_error': '❌ Ошибка при публикации поста: {error}',
'no_permission_view': '❌ У вас нет прав для просмотра постов.',
'no_permission_delete': '❌ У вас нет прав для удаления постов.',
'no_permission_publish': '❌ У вас нет прав для публикации постов.',
}
# Сообщения ранговой системы
RANK_MESSAGES = {
'rank_info': """
🏆 <b>Военная система рангов</b>
<b>Как работает система:</b>
• Каждый участник начинает с звания "Рядовой"
• Звание повышается автоматически при достижении определенного количества очков
• Очки начисляются за активность в чате и победы в играх
<b>Иерархия званий:</b>
🪖 Рядовой (0 очков)
🪖 Ефрейтор (100 очков)
🪖 Младший сержант (235 очков)
🪖 Сержант (505 очков)
🪖 Старший сержант (810 очков)
🪖 Старшина (1,250 очков)
🪖 Прапорщик (1,725 очков)
🪖 Старший прапорщик (2,335 очков)
🪖 Младший лейтенант (2,980 очков)
🪖 Лейтенант (3,760 очков)
🪖 Старший лейтенант (4,575 очков)
🪖 Капитан (5,525 очков)
🪖 Майор (6,510 очков)
🪖 Подполковник (7,630 очков)
🪖 Полковник (8,785 очков)
🪖 Генерал майор (16,075 очков)
🪖 Генерал лейтенант (32,150 очков)
🪖 Генерал полковник (64,300 очков)
🪖 Генерал армии (128,600 очков)
🪖 Маршал (256,000 очков)
<b>Как получить очки:</b>
• +1 очко за каждое сообщение в чате
• +5 очков за победу в мини-игре
• +15 очков за победу в крестиках-ноликах
• Бонусные очки за активность (случайно 1-5 очков)
<b>Команды для работы с рангом:</b>
/rank - Показать ваш текущий ранг и очки
/leaderboard - Топ-10 участников по очкам
/info - Детальная информация о вашем профиле
Служите чату верой и правдой, чтобы достичь высших воинских званий! 🎖️
""",
'rank_title': '🏆 <b>Ваш рейтинг:</b>\n\nОчки: {score}\nПредупреждений: {warnings}\nРоль: {role}',
'rank_not_found': 'Информация о вас не найдена.',
'leaderboard_title': '🏆 <b>Топ-10 участников:</b>\n\n',
'leaderboard_empty': 'Рейтинг пока пуст.',
'leaderboard_entry': '{position}. {name} - {score} очков\n',
'promotion_message': '🌟 За активное участие в группе {name} получил(-а) новое звание {new_rank}!',
}
# Сообщения модерации
MODERATION_MESSAGES = {
'no_permission': '❌ У вас нет прав для выполнения этой команды.',
'user_banned': '🚫 Пользователь {user_id} забанен.\nПричина: {reason}',
'user_unbanned': '✅ Пользователь {user_id} разбанен.',
'user_muted': '🔇 Пользователь {user_id} заглушен на {time} секунд.',
'user_unmuted': '🔊 Заглушка с пользователя {user_id} снята.',
'user_kicked': '👢 Пользователь {user_id} кикнут.\nПричина: {reason}',
'user_promoted': '⬆️ Пользователь {user_id} повышен до модератора.',
'user_demoted': '⬇️ Пользователь {user_id} понижен с модератора.',
'warning_issued': '⚠️ Предупреждение выдано пользователю {user_id} по причине: {reason}',
'ban_usage': 'Использование: /ban [пользователь] [причина]',
'unban_usage': 'Использование: /unban [пользователь]',
'mute_usage': 'Использование: /mute [пользователь] [время_в_секундах]',
'kick_usage': 'Использование: /kick [пользователь] [причина]',
'promote_usage': 'Использование: /promote [пользователь]',
'demote_usage': 'Использование: /demote [пользователь]',
'warn_usage': 'Использование: /warn [пользователь] [причина]',
'mute_invalid_time': '❌ Время должно быть числом в секундах.',
'ban_error': '❌ Ошибка при бане пользователя: {error}',
'unban_error': '❌ Ошибка при разбане пользователя: {error}',
'mute_error': '❌ Ошибка при mute пользователя: {error}',
'unmute_error': '❌ Ошибка при unmute пользователя: {error}',
'kick_error': '❌ Ошибка при кике пользователя: {error}',
'promote_error': '❌ Ошибка при повышении пользователя: {error}',
'demote_error': '❌ Ошибка при понижении пользователя: {error}',
}
# Сообщения игр
GAME_MESSAGES = {
'select_game': '🎮 Выберите игру:',
'rps_game': '🤖 Камень, ножницы, бумага!\n\nВыберите ваш ход:',
'tic_tac_toe_game': '❌⭕ Крестики-нолики!\n\nВы ходите первым. Выберите клетку:',
'quiz_game': '🧠 Викторина:\n\n{question}',
'rps_win': '🎉 Вы выиграли!\n\nВаш выбор: {user_choice}\nМой выбор: {bot_choice}',
'rps_lose': '😞 Вы проиграли!\n\nВаш выбор: {user_choice}\nМой выбор: {bot_choice}',
'rps_draw': '🤝 Ничья!\n\nВаш выбор: {user_choice}\nМой выбор: {bot_choice}',
'tic_tac_toe_win': '🎉 Вы выиграли! 🏆',
'tic_tac_toe_lose': '😞 Вы проиграли. Бот победил!',
'tic_tac_toe_draw': '🤝 Ничья!',
'quiz_correct': '🎉 Правильно! Вы заработали 5 очков!\n\nВопрос: {question}\nОтвет: {answer}',
'quiz_wrong': '❌ Неправильно!\n\nВопрос: {question}\nПравильный ответ: {correct_answer}',
'quiz_not_found': 'Вопрос викторины не найден.',
'invalid_move': 'Неверный ход!',
'game_over': 'Неверный формат хода!',
}
# Сообщения погоды
WEATHER_MESSAGES = {
'no_api_key': 'API ключ для погоды не настроен.',
'weather_info': '🌤️ <b>Погода в {city}:</b>\n\nТемпература: {temp}°C\nОщущается как: {feels_like}°C\nВлажность: {humidity}%\nОписание: {description}',
'city_not_found': 'Город не найден.',
'weather_error': 'Ошибка при получении погоды: {error}',
}
# Сообщения новостей
NEWS_MESSAGES = {
'no_api_key': 'API ключ для новостей не настроен.',
'news_title': '📰 <b>Последние новости:</b>\n\n',
'news_not_found': 'Новости не найдены.',
'news_error': 'Ошибка при получении новостей: {error}',
}
# Сообщения перевода
TRANSLATE_MESSAGES = {
'usage': 'Использование: /translate [текст] [язык] (например: /translate hello en)',
'result': 'Перевод "{text}" на {lang}: [здесь будет перевод]',
'error': 'Ошибка при переводе: {error}',
}
# Сообщения импорта CSV
IMPORT_MESSAGES = {
'no_permission': '❌ У вас нет прав для выполнения этой команды.',
'usage': 'Использование: /import_csv [путь_к_файлу]\n\nПо умолчанию: /import_csv chat_-1001519866478_users_full_20251014.csv',
'start_import': '🔄 Начинаю импорт пользователей из файла: {file}',
'success': '✅ Импорт пользователей успешно завершен!',
'error': '❌ Произошла ошибка при импорте пользователей.',
'file_error': '❌ Ошибка при импорте: {error}',
}
# Сообщения пользователя
USER_MESSAGES = {
'welcome': '''Привет, {name}! 👋
Я ваш помощник в чате. Вот что я умею:
• Отвечать на сообщения
• Вести рейтинг участников
• Предоставлять погоду и новости
• Играть в мини-игры
• Помогать с модерацией
Используйте /help для подробной информации.''',
'help_welcome': '''Привет, {name}! 👋
Я ваш помощник в чате. Вот что я умею:
• Отвечать на сообщения
• Вести рейтинг участников
• Предоставлять погоду и новости
• Играть в мини-игры
• Помогать с модерацией
Используйте /help для подробной информации.''',
'unknown_message': '''🤖 Извините, я не понял ваше сообщение. Вот что я умею:
• Отвечать на простые вопросы
• Вести рейтинг участников
• Предоставлять погоду и новости
• Играть в мини-игры
• Помогать с модерацией
Используйте /help для подробной информации.''',
'bonus_points': '🎁 Бонус! Вам начислено {points} очков!',
'welcome_new_user': '''🎉 Добро пожаловать в чат, {name}!
Мы рады видеть вас здесь! Используйте /help чтобы узнать о возможностях бота.''',
}
# Сообщения инлайнового поиска
INLINE_MESSAGES = {
'weather_title': 'Погода в {city}',
'weather_text': '🌤️ Погода в {city}:\nТемпература: {temp}°C\nОщущается как: {feels_like}°C\nВлажность: {humidity}%\nОписание: {description}',
'weather_not_found': 'Город не найден',
'translate_title': 'Перевод на {lang}',
'translate_text': 'Перевод "{text}" на {lang}: [здесь будет перевод]',
}
# Технические сообщения
TECH_MESSAGES = {
'db_connected': 'Успешное подключение к SQLite базе данных',
'db_tables_created': 'Таблицы созданы успешно',
'db_connection_error': 'Ошибка при подключении к SQLite: {error}',
'db_tables_error': 'Ошибка при создании таблиц: {error}',
'user_added_error': 'Ошибка при добавлении пользователя: {error}',
'score_update_error': 'Ошибка при обновлении очков: {error}',
'reputation_update_error': 'Ошибка при обновлении репутации: {error}',
'rank_update_error': 'Ошибка при обновлении ранга: {error}',
'warning_add_error': 'Ошибка при добавлении предупреждения: {error}',
'user_info_error': 'Ошибка при получении информации о пользователе: {error}',
'top_users_error': 'Ошибка при получении топ пользователей: {error}',
'warnings_get_error': 'Ошибка при получении предупреждений: {error}',
'csv_import_success': 'Импорт завершен. Добавлено: {added}, Обновлено: {updated}',
'csv_import_error': 'Ошибка при импорте пользователей из CSV: {error}',
'csv_file_not_found': 'Файл {file} не найден',
'csv_row_error': 'Ошибка обработки строки: {row}. Ошибка: {error}',
'scheduler_started': 'Планировщик постов запущен',
'scheduler_stopped': 'Планировщик постов остановлен',
'scheduler_error': 'Ошибка в планировщике: {error}',
'post_published': 'Пост {post_id} опубликован в чат {chat_id}',
'post_publish_error': 'Ошибка при публикации поста {post_id}: {error}',
'posts_check_error': 'Ошибка при проверке постов: {error}',
'callback_error': 'Ошибка при обработке инлайнового запроса: {error}',
'db_closed': 'Соединение с SQLite закрыто',
}
# Игровые элементы
GAME_CHOICES = {
'rock': '🪨 Камень',
'paper': '📄 Бумага',
'scissors': '✂️ Ножницы',
}
# Клавиатуры для игр
GAME_KEYBOARDS = {
'main_menu': [
['📋 Помощь', '🎮 Мини игры']
],
'games_menu': [
['Камень-ножницы-бумага', 'Крестики-нолики'],
['Викторина'],
['⬅️ Назад']
],
'rps_game': [
['🪨 Камень', '📄 Бумага'],
['✂️ Ножницы']
],
'help_menu': [
['🚀 Старт', '🔄 Начать заново']
],
}
# Викторина
QUIZ_QUESTIONS = [
{"question": "Столица Франции?", "answers": ["Париж", "Лондон", "Берлин"], "correct": 0},
{"question": "2 + 2 = ?", "answers": ["3", "4", "5"], "correct": 1},
{"question": "Цвет неба?", "answers": ["Зеленый", "Синий", "Красный"], "correct": 1},
{"question": "Сколько планет в Солнечной системе?", "answers": ["8", "9", "10"], "correct": 0},
{"question": "Какой язык программирования мы используем?", "answers": ["JavaScript", "Python", "Java"], "correct": 1}
]
# Константы для рангов
RANK_THRESHOLDS = [
(0, "Рядовой"),
(100, "Ефрейтор"),
(235, "Младший сержант"),
(505, "Сержант"),
(810, "Старший сержант"),
(1250, "Старшина"),
(1725, "Прапорщик"),
(2335, "Старший прапорщик"),
(2980, "Младший лейтенант"),
(3760, "Лейтенант"),
(4575, "Старший лейтенант"),
(5525, "Капитан"),
(6510, "Майор"),
(7630, "Подполковник"),
(8785, "Полковник"),
(16075, "Генерал майор"),
(32150, "Генерал лейтенант"),
(64300, "Генерал полковник"),
(128600, "Генерал армии"),
(256000, "Маршал")
]
# Константы для очков
SCORE_VALUES = {
'message': 1,
'game_win': 5,
'tic_tac_toe_win': 15,
'bonus_min': 1,
'bonus_max': 5,
'reputation_per_message': 1,
}
# Константы планировщика
SCHEDULER_CONFIG = {
'check_interval': 30, # секунд
'time_format': '%Y-%m-%d %H:%M:%S',
}
# Статусы постов
POST_STATUSES = {
'scheduled': 'scheduled',
'published': 'published',
}
# Роли пользователей
USER_ROLES = {
'user': 'user',
'moderator': 'moderator',
'admin': 'admin',
}
# Статусы пользователей Telegram
TELEGRAM_STATUSES = {
'member': 'member',
'administrator': 'administrator',
'creator': 'creator',
}
+8 -7
View File
@@ -13,6 +13,7 @@ from datetime import datetime
from telegram import Bot from telegram import Bot
from database_sqlite import Database from database_sqlite import Database
from config import BOT_TOKEN from config import BOT_TOKEN
from messages import TECH_MESSAGES, SCHEDULER_CONFIG
# Настройка логирования # Настройка логирования
logging.basicConfig( logging.basicConfig(
@@ -30,20 +31,20 @@ class PostScheduler:
async def start(self): async def start(self):
"""Запуск планировщика""" """Запуск планировщика"""
self.running = True self.running = True
logger.info("Планировщик постов запущен") logger.info(TECH_MESSAGES['scheduler_started'])
while self.running: while self.running:
try: try:
await self.check_and_publish_posts() await self.check_and_publish_posts()
await asyncio.sleep(30) # Проверка каждые 30 секунд await asyncio.sleep(SCHEDULER_CONFIG['check_interval']) # Проверка каждые 30 секунд
except Exception as e: except Exception as e:
logger.error(f"Ошибка в планировщике: {e}") logger.error(TECH_MESSAGES['scheduler_error'].format(error=e))
await asyncio.sleep(60) # При ошибке ждем минуту await asyncio.sleep(60) # При ошибке ждем минуту
async def stop(self): async def stop(self):
"""Остановка планировщика""" """Остановка планировщика"""
self.running = False self.running = False
logger.info("Планировщик постов остановлен") logger.info(TECH_MESSAGES['scheduler_stopped'])
async def check_and_publish_posts(self): async def check_and_publish_posts(self):
"""Проверка и публикация постов по расписанию""" """Проверка и публикация постов по расписанию"""
@@ -65,13 +66,13 @@ class PostScheduler:
# Отмечаем пост как опубликованный # Отмечаем пост как опубликованный
self.db.mark_post_published(post_id) self.db.mark_post_published(post_id)
logger.info(f"Пост {post_id} опубликован в чат {chat_id}") logger.info(TECH_MESSAGES['post_published'].format(post_id=post_id, chat_id=chat_id))
except Exception as e: except Exception as e:
logger.error(f"Ошибка при публикации поста {post_id}: {e}") logger.error(TECH_MESSAGES['post_publish_error'].format(post_id=post_id, error=e))
except Exception as e: except Exception as e:
logger.error(f"Ошибка при проверке постов: {e}") logger.error(TECH_MESSAGES['posts_check_error'].format(error=e))
async def main(): async def main():
"""Главная функция""" """Главная функция"""
BIN
View File
Binary file not shown.