mirror of
https://github.com/FerraSoft/bottohelp.git
synced 2026-08-06 21:55:03 +00:00
Initial commit: Telegram bot with scheduler and database functionality
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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():
|
||||||
"""Главная функция"""
|
"""Главная функция"""
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user