Dev 6 #9
3
__init__.py
Normal file
3
__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# This file makes the root directory a Python package
|
||||||
|
|
||||||
|
|
||||||
@@ -41,10 +41,12 @@ async def admin_panel(message: types.Message, state: FSMContext):
|
|||||||
reply_markup=markup)
|
reply_markup=markup)
|
||||||
else:
|
else:
|
||||||
await message.answer('Доступ запрещен, досвидания!')
|
await message.answer('Доступ запрещен, досвидания!')
|
||||||
|
await state.set_state("START")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при запуске админ панели: {e}")
|
logger.error(f"Ошибка при запуске админ панели: {e}")
|
||||||
await message.bot.send_message(IMPORTANT_LOGS,
|
await message.bot.send_message(IMPORTANT_LOGS,
|
||||||
f'Ошибка в функции admin_panel {e}. Traceback: {traceback.format_exc()}')
|
f'Ошибка в функции admin_panel {e}. Traceback: {traceback.format_exc()}')
|
||||||
|
await state.set_state("START")
|
||||||
|
|
||||||
|
|
||||||
@admin_router.message(
|
@admin_router.message(
|
||||||
@@ -52,7 +54,12 @@ async def admin_panel(message: types.Message, state: FSMContext):
|
|||||||
StateFilter("ADMIN"),
|
StateFilter("ADMIN"),
|
||||||
F.text == 'Бан (Список)'
|
F.text == 'Бан (Список)'
|
||||||
)
|
)
|
||||||
async def get_last_users(message: types.Message):
|
async def get_last_users(message: types.Message, state: FSMContext):
|
||||||
|
# Дополнительная проверка на админские права
|
||||||
|
if not check_access(message.from_user.id, BotDB):
|
||||||
|
await message.answer('Доступ запрещен!')
|
||||||
|
await state.set_state("START")
|
||||||
|
return
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Попытка получения списка последних пользователей. Текст сообщения: {message.text} Имя автора сообщения: {message.from_user.full_name})")
|
f"Попытка получения списка последних пользователей. Текст сообщения: {message.text} Имя автора сообщения: {message.from_user.full_name})")
|
||||||
list_users = BotDB.get_last_users_from_db()
|
list_users = BotDB.get_last_users_from_db()
|
||||||
@@ -67,6 +74,11 @@ async def get_last_users(message: types.Message):
|
|||||||
F.text == 'Бан по нику'
|
F.text == 'Бан по нику'
|
||||||
)
|
)
|
||||||
async def ban_by_nickname(message: types.Message, state: FSMContext):
|
async def ban_by_nickname(message: types.Message, state: FSMContext):
|
||||||
|
# Дополнительная проверка на админские права
|
||||||
|
if not check_access(message.from_user.id, BotDB):
|
||||||
|
await message.answer('Доступ запрещен!')
|
||||||
|
await state.set_state("START")
|
||||||
|
return
|
||||||
await message.answer('Пришли мне username блокируемого пользователя')
|
await message.answer('Пришли мне username блокируемого пользователя')
|
||||||
await state.set_state('PRE_BAN')
|
await state.set_state('PRE_BAN')
|
||||||
|
|
||||||
@@ -77,25 +89,29 @@ async def ban_by_nickname(message: types.Message, state: FSMContext):
|
|||||||
F.text == 'Бан по ID'
|
F.text == 'Бан по ID'
|
||||||
)
|
)
|
||||||
async def ban_by_id(message: types.Message, state: FSMContext):
|
async def ban_by_id(message: types.Message, state: FSMContext):
|
||||||
|
# Дополнительная проверка на админские права
|
||||||
|
if not check_access(message.from_user.id, BotDB):
|
||||||
|
await message.answer('Доступ запрещен!')
|
||||||
|
await state.set_state("START")
|
||||||
|
return
|
||||||
await message.answer('Пришли мне ID блокируемого пользователя')
|
await message.answer('Пришли мне ID блокируемого пользователя')
|
||||||
await state.set_state('PRE_BAN_ID')
|
await state.set_state('PRE_BAN_ID')
|
||||||
|
|
||||||
|
|
||||||
@admin_router.message(
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
StateFilter("ADMIN"),
|
|
||||||
F.text == 'Тестовый бан'
|
|
||||||
)
|
|
||||||
async def ban_by_forward(message: types.Message, state: FSMContext):
|
|
||||||
await message.answer('Перешлите мне сообщение от пользователя, которого хотите заблокировать')
|
|
||||||
await state.set_state('PRE_BAN_FORWARD')
|
|
||||||
|
|
||||||
|
|
||||||
@admin_router.message(
|
@admin_router.message(
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
|
StateFilter("PRE_BAN", "PRE_BAN_ID", "BAN_2"),
|
||||||
F.text == 'Отменить'
|
F.text == 'Отменить'
|
||||||
)
|
)
|
||||||
async def decline_ban(message: types.Message, state: FSMContext):
|
async def decline_ban(message: types.Message, state: FSMContext):
|
||||||
|
# Дополнительная проверка на админские права
|
||||||
|
if not check_access(message.from_user.id, BotDB):
|
||||||
|
await message.answer('Доступ запрещен!')
|
||||||
|
await state.set_state("START")
|
||||||
|
return
|
||||||
current_state = await state.get_state()
|
current_state = await state.get_state()
|
||||||
await state.set_data({})
|
await state.set_data({})
|
||||||
await state.set_state("ADMIN")
|
await state.set_state("ADMIN")
|
||||||
@@ -109,6 +125,11 @@ async def decline_ban(message: types.Message, state: FSMContext):
|
|||||||
StateFilter("PRE_BAN")
|
StateFilter("PRE_BAN")
|
||||||
)
|
)
|
||||||
async def ban_by_nickname_step_2(message: types.Message, state: FSMContext):
|
async def ban_by_nickname_step_2(message: types.Message, state: FSMContext):
|
||||||
|
# Дополнительная проверка на админские права
|
||||||
|
if not check_access(message.from_user.id, BotDB):
|
||||||
|
await message.answer('Доступ запрещен!')
|
||||||
|
await state.set_state("START")
|
||||||
|
return
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Функция ban_by_nickname_2. Получен никнейм пользователя: {message.text}")
|
f"Функция ban_by_nickname_2. Получен никнейм пользователя: {message.text}")
|
||||||
user_name = message.text
|
user_name = message.text
|
||||||
@@ -132,6 +153,11 @@ async def ban_by_nickname_step_2(message: types.Message, state: FSMContext):
|
|||||||
StateFilter("PRE_BAN_ID")
|
StateFilter("PRE_BAN_ID")
|
||||||
)
|
)
|
||||||
async def ban_by_id_step_2(message: types.Message, state: FSMContext):
|
async def ban_by_id_step_2(message: types.Message, state: FSMContext):
|
||||||
|
# Дополнительная проверка на админские права
|
||||||
|
if not check_access(message.from_user.id, BotDB):
|
||||||
|
await message.answer('Доступ запрещен!')
|
||||||
|
await state.set_state("START")
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
user_id = int(message.text)
|
user_id = int(message.text)
|
||||||
logger.info(f"Функция ban_by_id_step_2. Получен ID пользователя: {user_id}")
|
logger.info(f"Функция ban_by_id_step_2. Получен ID пользователя: {user_id}")
|
||||||
@@ -168,73 +194,10 @@ async def ban_by_id_step_2(message: types.Message, state: FSMContext):
|
|||||||
await message.answer('Вернулись в меню', reply_markup=markup)
|
await message.answer('Вернулись в меню', reply_markup=markup)
|
||||||
|
|
||||||
|
|
||||||
@admin_router.message(
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
StateFilter("PRE_BAN_FORWARD"),
|
|
||||||
F.forward_from
|
|
||||||
)
|
|
||||||
async def ban_by_forward_step_2(message: types.Message, state: FSMContext):
|
|
||||||
"""Обработчик пересланных сообщений для бана пользователя"""
|
|
||||||
try:
|
|
||||||
# Получаем информацию о пользователе из пересланного сообщения
|
|
||||||
forwarded_user = message.forward_from
|
|
||||||
|
|
||||||
if not forwarded_user:
|
|
||||||
await message.answer("Не удалось получить информацию о пользователе из пересланного сообщения. Возможно, пользователь скрыл возможность пересылки своих сообщений.")
|
|
||||||
await state.set_state('ADMIN')
|
|
||||||
markup = get_reply_keyboard_admin()
|
|
||||||
await message.answer('Вернулись в меню', reply_markup=markup)
|
|
||||||
return
|
|
||||||
|
|
||||||
user_id = forwarded_user.id
|
|
||||||
user_name = forwarded_user.username or "private_username"
|
|
||||||
full_name = forwarded_user.full_name or "Неизвестно"
|
|
||||||
|
|
||||||
logger.info(f"Функция ban_by_forward_step_2. Получен пользователь из пересланного сообщения: ID={user_id}, username={user_name}, full_name={full_name}")
|
|
||||||
|
|
||||||
# Проверяем, существует ли пользователь в базе
|
|
||||||
user_info = BotDB.get_user_info_by_id(user_id)
|
|
||||||
if not user_info:
|
|
||||||
# Если пользователя нет в базе, используем информацию из пересланного сообщения
|
|
||||||
logger.info(f"Пользователь с ID {user_id} не найден в базе данных, используем данные из пересланного сообщения")
|
|
||||||
user_name = user_name
|
|
||||||
full_name = full_name
|
|
||||||
else:
|
|
||||||
# Если пользователь есть в базе, используем данные из базы
|
|
||||||
user_name = user_info.get('username', user_name)
|
|
||||||
full_name = user_info.get('full_name', full_name)
|
|
||||||
|
|
||||||
await state.update_data(user_id=user_id, user_name=user_name, message_for_user=None,
|
|
||||||
date_to_unban=None)
|
|
||||||
|
|
||||||
markup = create_keyboard_for_ban_reason()
|
|
||||||
# Экранируем потенциально проблемные символы
|
|
||||||
user_name_escaped = html.escape(str(user_name))
|
|
||||||
full_name_escaped = html.escape(str(full_name))
|
|
||||||
await message.answer(
|
|
||||||
text=f"<b>Выбран пользователь из пересланного сообщения:\nid:</b> {user_id}\n<b>username:</b> {user_name_escaped}\n"
|
|
||||||
f"Имя:{full_name_escaped}\nВыбери причину бана из списка или напиши ее в чат",
|
|
||||||
reply_markup=markup)
|
|
||||||
await state.set_state('BAN_2')
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Ошибка при обработке пересланного сообщения: {e}")
|
|
||||||
await message.answer("Произошла ошибка при обработке пересланного сообщения.")
|
|
||||||
await state.set_state('ADMIN')
|
|
||||||
markup = get_reply_keyboard_admin()
|
|
||||||
await message.answer('Вернулись в меню', reply_markup=markup)
|
|
||||||
|
|
||||||
|
|
||||||
@admin_router.message(
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
StateFilter("PRE_BAN_FORWARD")
|
|
||||||
)
|
|
||||||
async def ban_by_forward_invalid(message: types.Message, state: FSMContext):
|
|
||||||
"""Обработчик для случаев, когда сообщение не является пересланным или не содержит информацию о пользователе"""
|
|
||||||
if message.forward_from_chat:
|
|
||||||
await message.answer("Пересланное сообщение из канала или группы не содержит информацию о конкретном пользователе. Пожалуйста, перешлите сообщение из приватного чата.")
|
|
||||||
else:
|
|
||||||
await message.answer("Пожалуйста, перешлите сообщение от пользователя, которого хотите заблокировать. Обычное сообщение не подходит.")
|
|
||||||
|
|
||||||
|
|
||||||
@admin_router.message(
|
@admin_router.message(
|
||||||
|
|||||||
@@ -1,284 +1,322 @@
|
|||||||
import html
|
import html
|
||||||
import traceback
|
import traceback
|
||||||
|
from datetime import datetime, timedelta
|
||||||
from aiogram import Router, F
|
|
||||||
from aiogram.fsm.context import FSMContext
|
from aiogram import Router, F
|
||||||
from aiogram.types import CallbackQuery
|
from aiogram.fsm.context import FSMContext
|
||||||
|
from aiogram.types import CallbackQuery
|
||||||
from helper_bot.keyboards.keyboards import create_keyboard_with_pagination, get_reply_keyboard_admin, \
|
|
||||||
create_keyboard_for_ban_reason
|
from helper_bot.keyboards.keyboards import create_keyboard_with_pagination, get_reply_keyboard_admin, \
|
||||||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
create_keyboard_for_ban_reason
|
||||||
from helper_bot.utils.helper_func import send_text_message, send_photo_message, get_banned_users_list, \
|
from helper_bot.utils.base_dependency_factory import get_global_instance
|
||||||
get_banned_users_buttons, delete_user_blacklist, send_media_group_to_channel, \
|
from helper_bot.utils.helper_func import send_text_message, send_photo_message, get_banned_users_list, \
|
||||||
send_video_message, send_video_note_message, send_audio_message, send_voice_message
|
get_banned_users_buttons, delete_user_blacklist, send_media_group_to_channel, \
|
||||||
from logs.custom_logger import logger
|
send_video_message, send_video_note_message, send_audio_message, send_voice_message
|
||||||
|
from logs.custom_logger import logger
|
||||||
callback_router = Router()
|
|
||||||
|
callback_router = Router()
|
||||||
bdf = get_global_instance()
|
|
||||||
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
|
bdf = get_global_instance()
|
||||||
GROUP_FOR_MESSAGE = bdf.settings['Telegram']['group_for_message']
|
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
|
||||||
MAIN_PUBLIC = bdf.settings['Telegram']['main_public']
|
GROUP_FOR_MESSAGE = bdf.settings['Telegram']['group_for_message']
|
||||||
GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs']
|
MAIN_PUBLIC = bdf.settings['Telegram']['main_public']
|
||||||
IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs']
|
GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs']
|
||||||
PREVIEW_LINK = bdf.settings['Telegram']['preview_link']
|
IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs']
|
||||||
LOGS = bdf.settings['Settings']['logs']
|
PREVIEW_LINK = bdf.settings['Telegram']['preview_link']
|
||||||
TEST = bdf.settings['Settings']['test']
|
LOGS = bdf.settings['Settings']['logs']
|
||||||
|
TEST = bdf.settings['Settings']['test']
|
||||||
BotDB = bdf.get_db()
|
|
||||||
|
BotDB = bdf.get_db()
|
||||||
|
|
||||||
@callback_router.callback_query(
|
|
||||||
F.data == "publish"
|
@callback_router.callback_query(
|
||||||
)
|
F.data == "publish"
|
||||||
async def post_for_group(call: CallbackQuery, state: FSMContext):
|
)
|
||||||
logger.info(
|
async def post_for_group(call: CallbackQuery, state: FSMContext):
|
||||||
f'Получен callback-запрос с действием: {call.data} от пользователя {call.from_user.full_name} (ID сообщения: {call.message.message_id})')
|
logger.info(
|
||||||
text_post = html.escape(str(call.message.text))
|
f'Получен callback-запрос с действием: {call.data} от пользователя {call.from_user.full_name} (ID сообщения: {call.message.message_id})')
|
||||||
text_post_with_photo = html.escape(str(call.message.caption))
|
text_post = html.escape(str(call.message.text))
|
||||||
if call.message.content_type == 'text' and call.message.text != "^":
|
text_post_with_photo = html.escape(str(call.message.caption))
|
||||||
try:
|
if call.message.content_type == 'text' and call.message.text != "^":
|
||||||
# Пересылаем сообщение в канал
|
try:
|
||||||
await send_text_message(MAIN_PUBLIC, call.message, text_post)
|
# Пересылаем сообщение в канал
|
||||||
|
await send_text_message(MAIN_PUBLIC, call.message, text_post)
|
||||||
# Получаем из базы автора
|
|
||||||
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
# Получаем из базы автора
|
||||||
|
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
||||||
# Очищаем предложку и удаляем оттуда пост
|
|
||||||
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
# Очищаем предложку и удаляем оттуда пост
|
||||||
logger.info(f'Текст сообщения опубликован в канале {MAIN_PUBLIC}.')
|
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
||||||
await call.answer(text='Выложено!', cache_time=3)
|
logger.info(f'Текст сообщения опубликован в канале {MAIN_PUBLIC}.')
|
||||||
|
await call.answer(text='Выложено!', cache_time=3)
|
||||||
# Отвечаем пользователю
|
|
||||||
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
# Отвечаем пользователю
|
||||||
except Exception as e:
|
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
||||||
if e.message != 'Forbidden: bot was blocked by the user':
|
except Exception as e:
|
||||||
await call.bot.send_message(chat_id=IMPORTANT_LOGS,
|
if e.message != 'Forbidden: bot was blocked by the user':
|
||||||
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
await call.bot.send_message(chat_id=IMPORTANT_LOGS,
|
||||||
logger.error(f'Ошибка при публикации текста в канал {MAIN_PUBLIC}: {str(e)}')
|
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
logger.error(f'Ошибка при публикации текста в канал {MAIN_PUBLIC}: {str(e)}')
|
||||||
elif call.message.content_type == 'photo':
|
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
||||||
try:
|
elif call.message.content_type == 'photo':
|
||||||
await send_photo_message(MAIN_PUBLIC, call.message, call.message.photo[-1].file_id, text_post_with_photo)
|
try:
|
||||||
|
await send_photo_message(MAIN_PUBLIC, call.message, call.message.photo[-1].file_id, text_post_with_photo)
|
||||||
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
|
||||||
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
||||||
|
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
||||||
# Удаляем пост из предложки
|
|
||||||
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
# Удаляем пост из предложки
|
||||||
logger.info(f'Пост с фото опубликован в канале {MAIN_PUBLIC}.')
|
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
||||||
await call.answer(text='Выложено!', cache_time=3)
|
logger.info(f'Пост с фото опубликован в канале {MAIN_PUBLIC}.')
|
||||||
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
await call.answer(text='Выложено!', cache_time=3)
|
||||||
except Exception as e:
|
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
||||||
if e.message != 'Forbidden: bot was blocked by the user':
|
except Exception as e:
|
||||||
await call.bot.send_message(chat_id=IMPORTANT_LOGS,
|
if e.message != 'Forbidden: bot was blocked by the user':
|
||||||
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
await call.bot.send_message(chat_id=IMPORTANT_LOGS,
|
||||||
logger.error(f'Ошибка при публикации фотографии в канал {MAIN_PUBLIC}: {str(e)}')
|
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
logger.error(f'Ошибка при публикации фотографии в канал {MAIN_PUBLIC}: {str(e)}')
|
||||||
elif call.message.content_type == 'video':
|
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
||||||
try:
|
elif call.message.content_type == 'video':
|
||||||
await send_video_message(MAIN_PUBLIC, call.message, call.message.video.file_id, text_post_with_photo)
|
try:
|
||||||
|
await send_video_message(MAIN_PUBLIC, call.message, call.message.video.file_id, text_post_with_photo)
|
||||||
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
|
||||||
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
||||||
|
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
||||||
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
|
||||||
logger.info(f'Пост с видео опубликован в канале {MAIN_PUBLIC}.')
|
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
||||||
await call.answer(text='Выложено!', cache_time=3)
|
logger.info(f'Пост с видео опубликован в канале {MAIN_PUBLIC}.')
|
||||||
|
await call.answer(text='Выложено!', cache_time=3)
|
||||||
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
|
||||||
except Exception as e:
|
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
||||||
if e.message != 'Forbidden: bot was blocked by the user':
|
except Exception as e:
|
||||||
await call.bot.send_message(chat_id=IMPORTANT_LOGS,
|
if e.message != 'Forbidden: bot was blocked by the user':
|
||||||
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
await call.bot.send_message(chat_id=IMPORTANT_LOGS,
|
||||||
logger.error(f'Ошибка при публикации видео в канал {MAIN_PUBLIC}: {str(e)}')
|
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
logger.error(f'Ошибка при публикации видео в канал {MAIN_PUBLIC}: {str(e)}')
|
||||||
elif call.message.content_type == 'video_note':
|
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
||||||
try:
|
elif call.message.content_type == 'video_note':
|
||||||
await send_video_note_message(MAIN_PUBLIC, call.message, call.message.video_note.file_id)
|
try:
|
||||||
|
await send_video_note_message(MAIN_PUBLIC, call.message, call.message.video_note.file_id)
|
||||||
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
|
||||||
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
||||||
|
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
||||||
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
|
||||||
logger.info(f'Пост с кружком опубликован в канале {MAIN_PUBLIC}.')
|
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
||||||
await call.answer(text='Выложено!', cache_time=3)
|
logger.info(f'Пост с кружком опубликован в канале {MAIN_PUBLIC}.')
|
||||||
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
await call.answer(text='Выложено!', cache_time=3)
|
||||||
except Exception as e:
|
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
||||||
if e.message != 'Forbidden: bot was blocked by the user':
|
except Exception as e:
|
||||||
await call.bot.send_message(chat_id=IMPORTANT_LOGS,
|
if e.message != 'Forbidden: bot was blocked by the user':
|
||||||
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
await call.bot.send_message(chat_id=IMPORTANT_LOGS,
|
||||||
logger.error(f'Ошибка при публикации кружка в канал {MAIN_PUBLIC}: {str(e)}')
|
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
logger.error(f'Ошибка при публикации кружка в канал {MAIN_PUBLIC}: {str(e)}')
|
||||||
elif call.message.content_type == 'audio':
|
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
||||||
try:
|
elif call.message.content_type == 'audio':
|
||||||
await send_audio_message(MAIN_PUBLIC, call.message, call.message.audio.file_id, text_post_with_photo)
|
try:
|
||||||
|
await send_audio_message(MAIN_PUBLIC, call.message, call.message.audio.file_id, text_post_with_photo)
|
||||||
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
|
||||||
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
||||||
|
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
||||||
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
|
||||||
logger.info(f'Пост с аудио опубликован в канале {MAIN_PUBLIC}.')
|
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
||||||
await call.answer(text='Выложено!', cache_time=3)
|
logger.info(f'Пост с аудио опубликован в канале {MAIN_PUBLIC}.')
|
||||||
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
await call.answer(text='Выложено!', cache_time=3)
|
||||||
except Exception as e:
|
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
||||||
if e.message != 'Forbidden: bot was blocked by the user':
|
except Exception as e:
|
||||||
await call.bot.send_message(chat_id=IMPORTANT_LOGS,
|
if e.message != 'Forbidden: bot was blocked by the user':
|
||||||
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
await call.bot.send_message(chat_id=IMPORTANT_LOGS,
|
||||||
logger.error(f'Ошибка при публикации аудио в канал {MAIN_PUBLIC}: {str(e)}')
|
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
logger.error(f'Ошибка при публикации аудио в канал {MAIN_PUBLIC}: {str(e)}')
|
||||||
elif call.message.content_type == 'voice':
|
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
||||||
try:
|
elif call.message.content_type == 'voice':
|
||||||
await send_voice_message(MAIN_PUBLIC, call.message, call.message.voice.file_id)
|
try:
|
||||||
|
await send_voice_message(MAIN_PUBLIC, call.message, call.message.voice.file_id)
|
||||||
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
|
||||||
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
||||||
|
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
||||||
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
|
||||||
logger.info(f'Пост с войсом опубликован в канале {MAIN_PUBLIC}.')
|
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
||||||
await call.answer(text='Выложено!', cache_time=3)
|
logger.info(f'Пост с войсом опубликован в канале {MAIN_PUBLIC}.')
|
||||||
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
await call.answer(text='Выложено!', cache_time=3)
|
||||||
except Exception as e:
|
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
||||||
if e.message != 'Forbidden: bot was blocked by the user':
|
except Exception as e:
|
||||||
await call.bot.send_message(chat_id=IMPORTANT_LOGS,
|
if e.message != 'Forbidden: bot was blocked by the user':
|
||||||
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
await call.bot.send_message(chat_id=IMPORTANT_LOGS,
|
||||||
logger.error(f'Ошибка при публикации войса в канал {MAIN_PUBLIC}: {str(e)}')
|
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
logger.error(f'Ошибка при публикации войса в канал {MAIN_PUBLIC}: {str(e)}')
|
||||||
elif call.message.text == "^":
|
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
||||||
# Получаем контент медиагруппы и текст для публикации
|
elif call.message.text == "^":
|
||||||
post_content = BotDB.get_post_content_from_telegram_by_last_id(call.message.message_id)
|
# Получаем контент медиагруппы и текст для публикации
|
||||||
pre_text = BotDB.get_post_text_from_telegram_by_last_id(call.message.message_id)
|
post_content = BotDB.get_post_content_from_telegram_by_last_id(call.message.message_id)
|
||||||
post_text = html.escape(str(pre_text))
|
pre_text = BotDB.get_post_text_from_telegram_by_last_id(call.message.message_id)
|
||||||
|
post_text = html.escape(str(pre_text))
|
||||||
# Готовим список для удаления
|
|
||||||
post_ids = BotDB.get_post_ids_from_telegram_by_last_id(call.message.message_id)
|
# Готовим список для удаления
|
||||||
message_ids = [row[0] for row in post_ids]
|
post_ids = BotDB.get_post_ids_from_telegram_by_last_id(call.message.message_id)
|
||||||
message_ids.append(call.message.message_id)
|
message_ids = [row[0] for row in post_ids]
|
||||||
|
message_ids.append(call.message.message_id)
|
||||||
# Выкладываем пост в канал
|
|
||||||
await send_media_group_to_channel(bot=call.bot, chat_id=MAIN_PUBLIC, post_content=post_content,
|
# Выкладываем пост в канал
|
||||||
post_text=post_text)
|
await send_media_group_to_channel(bot=call.bot, chat_id=MAIN_PUBLIC, post_content=post_content,
|
||||||
|
post_text=post_text)
|
||||||
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
|
||||||
author_id = BotDB.get_author_id_by_helper_message_id(call.message.message_id)
|
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
||||||
|
author_id = BotDB.get_author_id_by_helper_message_id(call.message.message_id)
|
||||||
# TODO: Удалить фотки с локалки после выкладки?
|
|
||||||
await call.bot.delete_messages(chat_id=GROUP_FOR_POST, message_ids=message_ids)
|
# TODO: Удалить фотки с локалки после выкладки?
|
||||||
await call.answer(text='Выложено!', cache_time=3)
|
await call.bot.delete_messages(chat_id=GROUP_FOR_POST, message_ids=message_ids)
|
||||||
|
await call.answer(text='Выложено!', cache_time=3)
|
||||||
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
|
||||||
|
await send_text_message(author_id, call.message, 'Твой пост был выложен🥰')
|
||||||
|
|
||||||
@callback_router.callback_query(
|
|
||||||
F.data == "decline"
|
@callback_router.callback_query(
|
||||||
)
|
F.data == "decline"
|
||||||
async def decline_post_for_group(call: CallbackQuery, state: FSMContext):
|
)
|
||||||
logger.info(
|
async def decline_post_for_group(call: CallbackQuery, state: FSMContext):
|
||||||
f'Получен callback-запрос с данными: {call.data} от пользователя {call.from_user.full_name} (ID: {call.from_user.id})')
|
logger.info(
|
||||||
try:
|
f'Получен callback-запрос с данными: {call.data} от пользователя {call.from_user.full_name} (ID: {call.from_user.id})')
|
||||||
if call.message.content_type == 'text' and call.message.text != "^" or call.message.content_type == 'photo' \
|
try:
|
||||||
or call.message.content_type == 'audio' or call.message.content_type == 'voice' \
|
if call.message.content_type == 'text' and call.message.text != "^" or call.message.content_type == 'photo' \
|
||||||
or call.message.content_type == 'video' or call.message.content_type == 'video_note':
|
or call.message.content_type == 'audio' or call.message.content_type == 'voice' \
|
||||||
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
or call.message.content_type == 'video' or call.message.content_type == 'video_note':
|
||||||
|
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
||||||
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
|
||||||
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
||||||
|
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
||||||
logger.info(
|
|
||||||
f'Сообщение отклонено админом {call.from_user.full_name} (ID: {call.from_user.id}).')
|
logger.info(
|
||||||
await call.answer(text='Отклонено!', cache_time=3)
|
f'Сообщение отклонено админом {call.from_user.full_name} (ID: {call.from_user.id}).')
|
||||||
await send_text_message(author_id, call.message, 'Твой пост был отклонен😔')
|
await call.answer(text='Отклонено!', cache_time=3)
|
||||||
if call.message.text == '^':
|
await send_text_message(author_id, call.message, 'Твой пост был отклонен😔')
|
||||||
post_ids = BotDB.get_post_ids_from_telegram_by_last_id(call.message.message_id)
|
if call.message.text == '^':
|
||||||
message_ids = [row[0] for row in post_ids]
|
post_ids = BotDB.get_post_ids_from_telegram_by_last_id(call.message.message_id)
|
||||||
message_ids.append(call.message.message_id)
|
message_ids = [row[0] for row in post_ids]
|
||||||
|
message_ids.append(call.message.message_id)
|
||||||
await call.bot.delete_messages(chat_id=GROUP_FOR_POST, message_ids=message_ids)
|
|
||||||
|
await call.bot.delete_messages(chat_id=GROUP_FOR_POST, message_ids=message_ids)
|
||||||
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
|
||||||
author_id = BotDB.get_author_id_by_helper_message_id(call.message.message_id)
|
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
||||||
|
author_id = BotDB.get_author_id_by_helper_message_id(call.message.message_id)
|
||||||
await call.answer(text='Удалено!', cache_time=3)
|
|
||||||
|
await call.answer(text='Удалено!', cache_time=3)
|
||||||
await send_text_message(author_id, call.message, 'Твой пост был отклонен😔')
|
|
||||||
except Exception as e:
|
await send_text_message(author_id, call.message, 'Твой пост был отклонен😔')
|
||||||
if e.message != 'Forbidden: bot was blocked by the user':
|
except Exception as e:
|
||||||
await call.bot.send_message(IMPORTANT_LOGS,
|
if e.message != 'Forbidden: bot was blocked by the user':
|
||||||
f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
await call.bot.send_message(IMPORTANT_LOGS,
|
||||||
logger.error(f'Ошибка при удалении сообщения в группе {GROUP_FOR_POST}: {str(e)}')
|
f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
logger.error(f'Ошибка при удалении сообщения в группе {GROUP_FOR_POST}: {str(e)}')
|
||||||
|
await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3)
|
||||||
|
|
||||||
@callback_router.callback_query(
|
|
||||||
F.data.contains('ban')
|
@callback_router.callback_query(
|
||||||
)
|
F.data == "ban"
|
||||||
async def process_ban_user(call: CallbackQuery, state: FSMContext):
|
)
|
||||||
user_id = call.data[4:]
|
async def ban_user_from_post(call: CallbackQuery):
|
||||||
logger.info(
|
try:
|
||||||
f"Вызов функции process_ban_user. Данные callback: {call.data} пользователь: {user_id}")
|
# Получаем информацию о пользователе из сообщения
|
||||||
user_name = BotDB.get_username(user_id=user_id)
|
author_id = BotDB.get_author_id_by_message_id(call.message.message_id)
|
||||||
if user_name:
|
user_name = BotDB.get_username(user_id=author_id)
|
||||||
await state.update_data(user_id=user_id, user_name=user_name, message_for_user=None,
|
full_name = call.message.from_user.full_name if call.message.from_user else "Неизвестно"
|
||||||
date_to_unban=None)
|
|
||||||
markup = create_keyboard_for_ban_reason()
|
# Устанавливаем причину бана и дату разблокировки (+7 дней)
|
||||||
# Экранируем потенциально проблемные символы
|
current_date = datetime.now()
|
||||||
user_name_escaped = html.escape(str(user_name))
|
date_to_unban = current_date + timedelta(days=7)
|
||||||
full_name_escaped = html.escape(str(call.message.from_user.full_name))
|
|
||||||
await call.message.answer(
|
# Записываем в базу данных
|
||||||
text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name_escaped}\nИмя:{full_name_escaped}\nВыбери причину бана из списка или напиши ее в чат",
|
BotDB.set_user_blacklist(
|
||||||
reply_markup=markup)
|
user_id=author_id,
|
||||||
await state.set_state('BAN_2')
|
user_name=user_name,
|
||||||
else:
|
message_for_user="Спам",
|
||||||
markup = get_reply_keyboard_admin()
|
date_to_unban=date_to_unban
|
||||||
await call.message.answer(text='Пользователь с таким ID не найден в базе', markup=markup)
|
)
|
||||||
await state.set_state('ADMIN')
|
|
||||||
|
# Удаляем пост из предложки
|
||||||
|
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
||||||
@callback_router.callback_query(
|
|
||||||
F.data.contains('unlock')
|
# Отправляем сообщение пользователю о блокировке
|
||||||
)
|
date_str = date_to_unban.strftime("%d.%m.%Y %H:%M")
|
||||||
async def process_unlock_user(call: CallbackQuery):
|
await send_text_message(author_id, call.message, f"Ты заблокирован за спам. Дата разблокировки: {date_str}")
|
||||||
user_id = call.data[7:]
|
|
||||||
user_name = BotDB.get_username(user_id=user_id)
|
logger.info(f"Пользователь {author_id} заблокирован за спам до {date_str}")
|
||||||
delete_user_blacklist(user_id, BotDB)
|
await call.answer(text='Пользователь заблокирован!', cache_time=3)
|
||||||
logger.info(f"Разблокирован пользователь с ID: {user_id} username:{user_name}")
|
|
||||||
username = BotDB.get_username(user_id)
|
except Exception as e:
|
||||||
await call.answer(f'Пользователь разблокирован {username}', show_alert=True)
|
logger.error(f'Ошибка при блокировке пользователя: {str(e)}')
|
||||||
|
await call.answer(text='Ошибка при блокировке!', show_alert=True, cache_time=3)
|
||||||
|
|
||||||
@callback_router.callback_query(
|
|
||||||
F.data == 'return'
|
@callback_router.callback_query(
|
||||||
)
|
F.data.contains('ban')
|
||||||
async def return_to_main_menu(call: CallbackQuery):
|
)
|
||||||
await call.message.delete()
|
async def process_ban_user(call: CallbackQuery, state: FSMContext):
|
||||||
logger.info(f"Запуск админ панели для пользователя: {call.message.from_user.id}")
|
user_id = call.data[4:]
|
||||||
markup = get_reply_keyboard_admin()
|
logger.info(
|
||||||
await call.message.answer("Добро пожаловать в админку. Выбери что хочешь:",
|
f"Вызов функции process_ban_user. Данные callback: {call.data} пользователь: {user_id}")
|
||||||
reply_markup=markup)
|
user_name = BotDB.get_username(user_id=user_id)
|
||||||
|
if user_name:
|
||||||
|
await state.update_data(user_id=user_id, user_name=user_name, message_for_user=None,
|
||||||
@callback_router.callback_query(
|
date_to_unban=None)
|
||||||
F.data.contains('page')
|
markup = create_keyboard_for_ban_reason()
|
||||||
)
|
# Экранируем потенциально проблемные символы
|
||||||
async def change_page(call: CallbackQuery):
|
user_name_escaped = html.escape(str(user_name))
|
||||||
page_number = int(call.data[5:])
|
full_name_escaped = html.escape(str(call.message.from_user.full_name))
|
||||||
logger.info(f"Переход на страницу {page_number}")
|
await call.message.answer(
|
||||||
if call.message.text == 'Список пользователей которые последними обращались к боту':
|
text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name_escaped}\nИмя:{full_name_escaped}\nВыбери причину бана из списка или напиши ее в чат",
|
||||||
list_users = BotDB.get_last_users_from_db()
|
reply_markup=markup)
|
||||||
# TODO: Здесь где-то надо добавить обработку ошибки IndexError: list index out of range
|
await state.set_state('BAN_2')
|
||||||
keyboard = create_keyboard_with_pagination(int(page_number), len(list_users), list_users,
|
else:
|
||||||
'ban')
|
markup = get_reply_keyboard_admin()
|
||||||
|
await call.message.answer(text='Пользователь с таким ID не найден в базе', reply_markup=markup)
|
||||||
await call.bot.edit_message_reply_markup(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
await state.set_state('ADMIN')
|
||||||
reply_markup=keyboard)
|
|
||||||
else:
|
|
||||||
# Готовим сообщения
|
@callback_router.callback_query(
|
||||||
message_user = get_banned_users_list(int(page_number) * 7 - 7, BotDB)
|
F.data.contains('unlock')
|
||||||
await call.bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
)
|
||||||
text=message_user)
|
async def process_unlock_user(call: CallbackQuery):
|
||||||
|
user_id = call.data[7:]
|
||||||
# Готовим клавиатуру
|
user_name = BotDB.get_username(user_id=user_id)
|
||||||
buttons = get_banned_users_buttons(BotDB)
|
delete_user_blacklist(user_id, BotDB)
|
||||||
keyboard = create_keyboard_with_pagination(int(call.data[5:]), len(buttons), buttons, 'unlock')
|
logger.info(f"Разблокирован пользователь с ID: {user_id} username:{user_name}")
|
||||||
await call.bot.edit_message_reply_markup(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
username = BotDB.get_username(user_id)
|
||||||
reply_markup=keyboard)
|
await call.answer(f'Пользователь разблокирован {username}', show_alert=True)
|
||||||
|
|
||||||
|
|
||||||
|
@callback_router.callback_query(
|
||||||
|
F.data == 'return'
|
||||||
|
)
|
||||||
|
async def return_to_main_menu(call: CallbackQuery):
|
||||||
|
await call.message.delete()
|
||||||
|
logger.info(f"Запуск админ панели для пользователя: {call.message.from_user.id}")
|
||||||
|
markup = get_reply_keyboard_admin()
|
||||||
|
await call.message.answer("Добро пожаловать в админку. Выбери что хочешь:",
|
||||||
|
reply_markup=markup)
|
||||||
|
|
||||||
|
|
||||||
|
@callback_router.callback_query(
|
||||||
|
F.data.contains('page')
|
||||||
|
)
|
||||||
|
async def change_page(call: CallbackQuery):
|
||||||
|
page_number = int(call.data[5:])
|
||||||
|
logger.info(f"Переход на страницу {page_number}")
|
||||||
|
if call.message.text == 'Список пользователей которые последними обращались к боту':
|
||||||
|
list_users = BotDB.get_last_users_from_db()
|
||||||
|
# TODO: Здесь где-то надо добавить обработку ошибки IndexError: list index out of range
|
||||||
|
keyboard = create_keyboard_with_pagination(int(page_number), len(list_users), list_users,
|
||||||
|
'ban')
|
||||||
|
|
||||||
|
await call.bot.edit_message_reply_markup(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
||||||
|
reply_markup=keyboard)
|
||||||
|
else:
|
||||||
|
# Готовим сообщения
|
||||||
|
message_user = get_banned_users_list(int(page_number) * 7 - 7, BotDB)
|
||||||
|
await call.bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
||||||
|
text=message_user)
|
||||||
|
|
||||||
|
# Готовим клавиатуру
|
||||||
|
buttons = get_banned_users_buttons(BotDB)
|
||||||
|
keyboard = create_keyboard_with_pagination(int(call.data[5:]), len(buttons), buttons, 'unlock')
|
||||||
|
await call.bot.edit_message_reply_markup(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
||||||
|
reply_markup=keyboard)
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ from aiogram.utils.keyboard import ReplyKeyboardBuilder, InlineKeyboardBuilder
|
|||||||
def get_reply_keyboard_for_post():
|
def get_reply_keyboard_for_post():
|
||||||
builder = InlineKeyboardBuilder()
|
builder = InlineKeyboardBuilder()
|
||||||
builder.row(types.InlineKeyboardButton(
|
builder.row(types.InlineKeyboardButton(
|
||||||
text="Опубликовать", callback_data="publish")
|
text="Опубликовать", callback_data="publish"),
|
||||||
|
types.InlineKeyboardButton(
|
||||||
|
text="Отклонить", callback_data="decline")
|
||||||
)
|
)
|
||||||
builder.row(types.InlineKeyboardButton(
|
builder.row(types.InlineKeyboardButton(
|
||||||
text="Отклонить", callback_data="decline")
|
text="👮♂️ Забанить", callback_data="ban")
|
||||||
)
|
)
|
||||||
markup = builder.as_markup(resize_keyboard=True, one_time_keyboard=True)
|
markup = builder.as_markup(resize_keyboard=True, one_time_keyboard=True)
|
||||||
return markup
|
return markup
|
||||||
@@ -37,7 +39,7 @@ def get_reply_keyboard_admin():
|
|||||||
builder.add(types.KeyboardButton(text="Бан (Список)"))
|
builder.add(types.KeyboardButton(text="Бан (Список)"))
|
||||||
builder.add(types.KeyboardButton(text="Бан по нику"))
|
builder.add(types.KeyboardButton(text="Бан по нику"))
|
||||||
builder.add(types.KeyboardButton(text="Бан по ID"))
|
builder.add(types.KeyboardButton(text="Бан по ID"))
|
||||||
builder.add(types.KeyboardButton(text="Тестовый бан"))
|
builder.row()
|
||||||
builder.add(types.KeyboardButton(text="Разбан (список)"))
|
builder.add(types.KeyboardButton(text="Разбан (список)"))
|
||||||
builder.add(types.KeyboardButton(text="Вернуться в бота"))
|
builder.add(types.KeyboardButton(text="Вернуться в бота"))
|
||||||
markup = builder.as_markup(resize_keyboard=True, one_time_keyboard=True)
|
markup = builder.as_markup(resize_keyboard=True, one_time_keyboard=True)
|
||||||
@@ -49,7 +51,7 @@ def create_keyboard_with_pagination(page: int, total_items: int, array_items: li
|
|||||||
Создает клавиатуру с пагинацией для заданного набора элементов и устанавливает необходимый callback
|
Создает клавиатуру с пагинацией для заданного набора элементов и устанавливает необходимый callback
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
page: Номер текущей страницы.
|
page: Номер текущей страницы (начинается с 1).
|
||||||
total_items: Общее количество элементов.
|
total_items: Общее количество элементов.
|
||||||
array_items: Лист кортежей. Содержит в себе user_name: user_id
|
array_items: Лист кортежей. Содержит в себе user_name: user_id
|
||||||
callback: Действие в коллбеке. Вернет callback вида ({callback}_{user_id})
|
callback: Действие в коллбеке. Вернет callback вида ({callback}_{user_id})
|
||||||
@@ -57,34 +59,75 @@ def create_keyboard_with_pagination(page: int, total_items: int, array_items: li
|
|||||||
Returns:
|
Returns:
|
||||||
InlineKeyboardMarkup: Клавиатура с кнопками пагинации.
|
InlineKeyboardMarkup: Клавиатура с кнопками пагинации.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Проверяем валидность входных данных
|
||||||
|
if page < 1:
|
||||||
|
page = 1
|
||||||
|
if not array_items:
|
||||||
|
# Если нет элементов, возвращаем только кнопку "Назад"
|
||||||
|
keyboard = InlineKeyboardBuilder()
|
||||||
|
home_button = types.InlineKeyboardButton(text="🏠 Назад", callback_data="return")
|
||||||
|
keyboard.row(home_button)
|
||||||
|
return keyboard.as_markup()
|
||||||
|
|
||||||
# Определяем общее количество страниц
|
# Определяем общее количество страниц
|
||||||
total_pages = (total_items + 9 - 1) // 9
|
items_per_page = 9
|
||||||
|
total_pages = (total_items + items_per_page - 1) // items_per_page
|
||||||
|
|
||||||
|
# Ограничиваем страницу максимальным значением
|
||||||
|
if page > total_pages:
|
||||||
|
page = total_pages
|
||||||
|
|
||||||
# Создаем билдер для клавиатуры
|
# Создаем билдер для клавиатуры
|
||||||
keyboard = InlineKeyboardBuilder()
|
keyboard = InlineKeyboardBuilder()
|
||||||
|
|
||||||
# Вычисляем стартовый номер для текущей страницы
|
# Вычисляем стартовый номер для текущей страницы
|
||||||
start_index = (page - 1) * 9
|
start_index = (page - 1) * items_per_page
|
||||||
|
|
||||||
# Кнопки с номерами страниц
|
# Кнопки с элементами текущей страницы
|
||||||
for i in range(start_index, min(start_index + 9, len(array_items))):
|
end_index = min(start_index + items_per_page, len(array_items))
|
||||||
keyboard.add(types.InlineKeyboardButton(
|
current_row = []
|
||||||
|
|
||||||
|
for i in range(start_index, end_index):
|
||||||
|
current_row.append(types.InlineKeyboardButton(
|
||||||
text=f"{array_items[i][0]}", callback_data=f"{callback}_{array_items[i][1]}"
|
text=f"{array_items[i][0]}", callback_data=f"{callback}_{array_items[i][1]}"
|
||||||
))
|
))
|
||||||
keyboard.adjust(3)
|
|
||||||
|
# Когда набирается 3 кнопки, добавляем ряд
|
||||||
next_button = types.InlineKeyboardButton(
|
if len(current_row) == 3:
|
||||||
text="➡️ Следующая", callback_data=f"page_{page + 1}"
|
keyboard.row(*current_row)
|
||||||
)
|
current_row = []
|
||||||
prev_button = types.InlineKeyboardButton(
|
|
||||||
text="⬅️ Предыдущая", callback_data=f"page_{page - 1}"
|
# Добавляем оставшиеся кнопки, если они есть
|
||||||
)
|
if current_row:
|
||||||
keyboard.row(prev_button, next_button)
|
keyboard.row(*current_row)
|
||||||
home_button = types.InlineKeyboardButton(
|
|
||||||
text="🏠 Назад", callback_data="return")
|
# Создаем кнопки навигации только если нужно
|
||||||
|
navigation_buttons = []
|
||||||
|
|
||||||
|
# Кнопка "Предыдущая" - показываем только если не первая страница
|
||||||
|
if page > 1:
|
||||||
|
prev_button = types.InlineKeyboardButton(
|
||||||
|
text="⬅️ Предыдущая", callback_data=f"page_{page - 1}"
|
||||||
|
)
|
||||||
|
navigation_buttons.append(prev_button)
|
||||||
|
|
||||||
|
# Кнопка "Следующая" - показываем только если не последняя страница
|
||||||
|
if page < total_pages:
|
||||||
|
next_button = types.InlineKeyboardButton(
|
||||||
|
text="➡️ Следующая", callback_data=f"page_{page + 1}"
|
||||||
|
)
|
||||||
|
navigation_buttons.append(next_button)
|
||||||
|
|
||||||
|
# Добавляем кнопки навигации, если они есть
|
||||||
|
if navigation_buttons:
|
||||||
|
keyboard.row(*navigation_buttons)
|
||||||
|
|
||||||
|
# Кнопка "Назад"
|
||||||
|
home_button = types.InlineKeyboardButton(text="🏠 Назад", callback_data="return")
|
||||||
keyboard.row(home_button)
|
keyboard.row(home_button)
|
||||||
k = keyboard.as_markup()
|
|
||||||
return k
|
return keyboard.as_markup()
|
||||||
|
|
||||||
|
|
||||||
def create_keyboard_for_ban_reason():
|
def create_keyboard_for_ban_reason():
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ async def start_bot(bdf):
|
|||||||
link_preview_is_disabled=bdf.settings['Telegram']['preview_link']
|
link_preview_is_disabled=bdf.settings['Telegram']['preview_link']
|
||||||
), timeout=30.0) # Добавляем таймаут для предотвращения зависаний
|
), timeout=30.0) # Добавляем таймаут для предотвращения зависаний
|
||||||
dp = Dispatcher(storage=MemoryStorage(), fsm_strategy=FSMStrategy.GLOBAL_USER)
|
dp = Dispatcher(storage=MemoryStorage(), fsm_strategy=FSMStrategy.GLOBAL_USER)
|
||||||
dp.include_routers(private_router, callback_router, group_router, admin_router)
|
dp.include_routers(admin_router, private_router, callback_router, group_router)
|
||||||
await bot.delete_webhook(drop_pending_updates=True)
|
await bot.delete_webhook(drop_pending_updates=True)
|
||||||
await dp.start_polling(bot, skip_updates=True)
|
await dp.start_polling(bot, skip_updates=True)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ class StateUser(StatesGroup):
|
|||||||
PRE_CHAT = State()
|
PRE_CHAT = State()
|
||||||
PRE_BAN = State()
|
PRE_BAN = State()
|
||||||
PRE_BAN_ID = State()
|
PRE_BAN_ID = State()
|
||||||
PRE_BAN_FORWARD = State()
|
|
||||||
BAN_2 = State()
|
BAN_2 = State()
|
||||||
BAN_3 = State()
|
BAN_3 = State()
|
||||||
BAN_4 = State()
|
BAN_4 = State()
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, InlineKeyboardMar
|
|||||||
from helper_bot.keyboards.keyboards import (
|
from helper_bot.keyboards.keyboards import (
|
||||||
get_reply_keyboard,
|
get_reply_keyboard,
|
||||||
get_reply_keyboard_for_post,
|
get_reply_keyboard_for_post,
|
||||||
get_reply_keyboard_leave_chat
|
get_reply_keyboard_leave_chat,
|
||||||
|
create_keyboard_with_pagination
|
||||||
)
|
)
|
||||||
from helper_bot.filters.main import ChatTypeFilter
|
from helper_bot.filters.main import ChatTypeFilter
|
||||||
from database.db import BotDB
|
from database.db import BotDB
|
||||||
@@ -326,5 +327,125 @@ class TestKeyboardIntegration:
|
|||||||
assert 'Выйти из чата' in leave_buttons
|
assert 'Выйти из чата' in leave_buttons
|
||||||
|
|
||||||
|
|
||||||
|
class TestPagination:
|
||||||
|
"""Тесты для функции create_keyboard_with_pagination"""
|
||||||
|
|
||||||
|
def test_pagination_empty_list(self):
|
||||||
|
"""Тест с пустым списком элементов"""
|
||||||
|
keyboard = create_keyboard_with_pagination(1, 0, [], 'test')
|
||||||
|
assert keyboard is not None
|
||||||
|
# Проверяем, что есть только кнопка "Назад"
|
||||||
|
assert len(keyboard.inline_keyboard) == 1
|
||||||
|
assert keyboard.inline_keyboard[0][0].text == "🏠 Назад"
|
||||||
|
|
||||||
|
def test_pagination_single_page(self):
|
||||||
|
"""Тест с одной страницей"""
|
||||||
|
items = [("User1", 1), ("User2", 2), ("User3", 3)]
|
||||||
|
keyboard = create_keyboard_with_pagination(1, 3, items, 'test')
|
||||||
|
|
||||||
|
# Проверяем количество кнопок (3 пользователя + кнопка "Назад")
|
||||||
|
assert len(keyboard.inline_keyboard) == 2 # 1 ряд с пользователями + 1 ряд с "Назад"
|
||||||
|
assert len(keyboard.inline_keyboard[0]) == 3 # 3 пользователя в первом ряду
|
||||||
|
assert keyboard.inline_keyboard[1][0].text == "🏠 Назад"
|
||||||
|
|
||||||
|
# Проверяем, что нет кнопок навигации
|
||||||
|
assert len(keyboard.inline_keyboard[0]) == 3 # только пользователи
|
||||||
|
|
||||||
|
def test_pagination_multiple_pages(self):
|
||||||
|
"""Тест с несколькими страницами"""
|
||||||
|
items = [("User" + str(i), i) for i in range(1, 15)] # 14 пользователей
|
||||||
|
keyboard = create_keyboard_with_pagination(1, 14, items, 'test')
|
||||||
|
|
||||||
|
# На первой странице должно быть 9 пользователей (3 ряда по 3) + кнопка "Следующая" + "Назад"
|
||||||
|
assert len(keyboard.inline_keyboard) == 5 # 3 ряда пользователей + навигация + назад
|
||||||
|
assert len(keyboard.inline_keyboard[0]) == 3 # первый ряд: 3 пользователя
|
||||||
|
assert len(keyboard.inline_keyboard[1]) == 3 # второй ряд: 3 пользователя
|
||||||
|
assert len(keyboard.inline_keyboard[2]) == 3 # третий ряд: 3 пользователя
|
||||||
|
assert keyboard.inline_keyboard[3][0].text == "➡️ Следующая" # кнопка навигации
|
||||||
|
assert keyboard.inline_keyboard[4][0].text == "🏠 Назад" # кнопка назад
|
||||||
|
|
||||||
|
def test_pagination_second_page(self):
|
||||||
|
"""Тест второй страницы"""
|
||||||
|
items = [("User" + str(i), i) for i in range(1, 15)] # 14 пользователей
|
||||||
|
keyboard = create_keyboard_with_pagination(2, 14, items, 'test')
|
||||||
|
|
||||||
|
# На второй странице должно быть 5 пользователей (2 ряда: 3+2) + кнопки "Предыдущая" и "Назад"
|
||||||
|
assert len(keyboard.inline_keyboard) == 4 # 2 ряда пользователей + навигация + назад
|
||||||
|
assert len(keyboard.inline_keyboard[0]) == 3 # первый ряд: 3 пользователя
|
||||||
|
assert len(keyboard.inline_keyboard[1]) == 2 # второй ряд: 2 пользователя
|
||||||
|
assert keyboard.inline_keyboard[2][0].text == "⬅️ Предыдущая"
|
||||||
|
assert keyboard.inline_keyboard[3][0].text == "🏠 Назад"
|
||||||
|
|
||||||
|
def test_pagination_middle_page(self):
|
||||||
|
"""Тест средней страницы"""
|
||||||
|
items = [("User" + str(i), i) for i in range(1, 25)] # 24 пользователя
|
||||||
|
keyboard = create_keyboard_with_pagination(2, 24, items, 'test')
|
||||||
|
|
||||||
|
# На второй странице должно быть 9 пользователей (3 ряда по 3) + кнопки "Предыдущая" и "Следующая"
|
||||||
|
assert len(keyboard.inline_keyboard) == 5 # 3 ряда пользователей + навигация + назад
|
||||||
|
assert len(keyboard.inline_keyboard[0]) == 3 # первый ряд: 3 пользователя
|
||||||
|
assert len(keyboard.inline_keyboard[1]) == 3 # второй ряд: 3 пользователя
|
||||||
|
assert len(keyboard.inline_keyboard[2]) == 3 # третий ряд: 3 пользователя
|
||||||
|
assert keyboard.inline_keyboard[3][0].text == "⬅️ Предыдущая"
|
||||||
|
assert keyboard.inline_keyboard[3][1].text == "➡️ Следующая"
|
||||||
|
|
||||||
|
def test_pagination_invalid_page_number(self):
|
||||||
|
"""Тест с некорректным номером страницы"""
|
||||||
|
items = [("User" + str(i), i) for i in range(1, 10)] # 9 пользователей
|
||||||
|
keyboard = create_keyboard_with_pagination(0, 9, items, 'test') # страница 0
|
||||||
|
|
||||||
|
# Должна вернуться первая страница
|
||||||
|
assert len(keyboard.inline_keyboard) == 4 # 3 ряда пользователей + назад
|
||||||
|
assert len(keyboard.inline_keyboard[0]) == 3 # первый ряд: 3 пользователя
|
||||||
|
assert len(keyboard.inline_keyboard[1]) == 3 # второй ряд: 3 пользователя
|
||||||
|
assert len(keyboard.inline_keyboard[2]) == 3 # третий ряд: 3 пользователя
|
||||||
|
|
||||||
|
def test_pagination_page_out_of_range(self):
|
||||||
|
"""Тест с номером страницы больше максимального"""
|
||||||
|
items = [("User" + str(i), i) for i in range(1, 10)] # 9 пользователей
|
||||||
|
keyboard = create_keyboard_with_pagination(5, 9, items, 'test') # страница 5 при 1 странице
|
||||||
|
|
||||||
|
# Должна вернуться первая страница
|
||||||
|
assert len(keyboard.inline_keyboard) == 4 # 3 ряда пользователей + назад
|
||||||
|
assert len(keyboard.inline_keyboard[0]) == 3 # первый ряд: 3 пользователя
|
||||||
|
assert len(keyboard.inline_keyboard[1]) == 3 # второй ряд: 3 пользователя
|
||||||
|
assert len(keyboard.inline_keyboard[2]) == 3 # третий ряд: 3 пользователя
|
||||||
|
|
||||||
|
def test_pagination_callback_data_format(self):
|
||||||
|
"""Тест формата callback_data"""
|
||||||
|
items = [("User1", 123), ("User2", 456)]
|
||||||
|
keyboard = create_keyboard_with_pagination(1, 2, items, 'ban')
|
||||||
|
|
||||||
|
# Проверяем формат callback_data для пользователей
|
||||||
|
assert keyboard.inline_keyboard[0][0].callback_data == "ban_123"
|
||||||
|
assert keyboard.inline_keyboard[0][1].callback_data == "ban_456"
|
||||||
|
|
||||||
|
# Проверяем формат callback_data для кнопки "Назад"
|
||||||
|
assert keyboard.inline_keyboard[1][0].callback_data == "return"
|
||||||
|
|
||||||
|
def test_pagination_navigation_callback_data(self):
|
||||||
|
"""Тест callback_data для кнопок навигации"""
|
||||||
|
items = [("User" + str(i), i) for i in range(1, 15)] # 14 пользователей
|
||||||
|
keyboard = create_keyboard_with_pagination(2, 14, items, 'test')
|
||||||
|
|
||||||
|
# Проверяем callback_data для кнопки "Предыдущая"
|
||||||
|
assert keyboard.inline_keyboard[2][0].callback_data == "page_1"
|
||||||
|
|
||||||
|
# Проверяем callback_data для кнопки "Назад"
|
||||||
|
assert keyboard.inline_keyboard[3][0].callback_data == "return"
|
||||||
|
|
||||||
|
def test_pagination_exactly_items_per_page(self):
|
||||||
|
"""Тест когда количество элементов точно равно items_per_page"""
|
||||||
|
items = [("User" + str(i), i) for i in range(1, 10)] # ровно 9 пользователей
|
||||||
|
keyboard = create_keyboard_with_pagination(1, 9, items, 'test')
|
||||||
|
|
||||||
|
# Должна быть только одна страница без кнопок навигации
|
||||||
|
assert len(keyboard.inline_keyboard) == 4 # 3 ряда пользователей + назад
|
||||||
|
assert len(keyboard.inline_keyboard[0]) == 3 # первый ряд: 3 пользователя
|
||||||
|
assert len(keyboard.inline_keyboard[1]) == 3 # второй ряд: 3 пользователя
|
||||||
|
assert len(keyboard.inline_keyboard[2]) == 3 # третий ряд: 3 пользователя
|
||||||
|
assert keyboard.inline_keyboard[3][0].text == "🏠 Назад"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
pytest.main([__file__, '-v'])
|
pytest.main([__file__, '-v'])
|
||||||
|
|||||||
@@ -1,11 +1,33 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch, AsyncMock
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import os
|
||||||
|
|
||||||
from helper_bot.utils.helper_func import (
|
from helper_bot.utils.helper_func import (
|
||||||
get_first_name,
|
get_first_name,
|
||||||
get_text_message,
|
get_text_message,
|
||||||
check_username_and_full_name
|
check_username_and_full_name,
|
||||||
|
safe_html_escape,
|
||||||
|
download_file,
|
||||||
|
prepare_media_group_from_middlewares,
|
||||||
|
add_in_db_media_mediagroup,
|
||||||
|
add_in_db_media,
|
||||||
|
send_media_group_message_to_private_chat,
|
||||||
|
send_media_group_to_channel,
|
||||||
|
send_text_message,
|
||||||
|
send_photo_message,
|
||||||
|
send_video_message,
|
||||||
|
send_video_note_message,
|
||||||
|
send_audio_message,
|
||||||
|
send_voice_message,
|
||||||
|
check_access,
|
||||||
|
add_days_to_date,
|
||||||
|
get_banned_users_list,
|
||||||
|
get_banned_users_buttons,
|
||||||
|
delete_user_blacklist,
|
||||||
|
update_user_info,
|
||||||
|
check_user_emoji,
|
||||||
|
get_random_emoji
|
||||||
)
|
)
|
||||||
from helper_bot.utils.messages import get_message
|
from helper_bot.utils.messages import get_message
|
||||||
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory, get_global_instance
|
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory, get_global_instance
|
||||||
@@ -83,6 +105,40 @@ class TestHelperFunctions:
|
|||||||
assert result is True
|
assert result is True
|
||||||
|
|
||||||
|
|
||||||
|
class TestSafeHtmlEscape:
|
||||||
|
"""Тесты для функции безопасного экранирования HTML"""
|
||||||
|
|
||||||
|
def test_safe_html_escape_normal_text(self):
|
||||||
|
"""Тест экранирования обычного текста"""
|
||||||
|
result = safe_html_escape("Hello World")
|
||||||
|
assert result == "Hello World"
|
||||||
|
|
||||||
|
def test_safe_html_escape_html_tags(self):
|
||||||
|
"""Тест экранирования HTML тегов"""
|
||||||
|
result = safe_html_escape("<script>alert('xss')</script>")
|
||||||
|
assert result == "<script>alert('xss')</script>"
|
||||||
|
|
||||||
|
def test_safe_html_escape_special_chars(self):
|
||||||
|
"""Тест экранирования специальных символов"""
|
||||||
|
result = safe_html_escape("& < > \" '")
|
||||||
|
assert result == "& < > " '"
|
||||||
|
|
||||||
|
def test_safe_html_escape_none_input(self):
|
||||||
|
"""Тест экранирования None значения"""
|
||||||
|
result = safe_html_escape(None)
|
||||||
|
assert result == ""
|
||||||
|
|
||||||
|
def test_safe_html_escape_empty_string(self):
|
||||||
|
"""Тест экранирования пустой строки"""
|
||||||
|
result = safe_html_escape("")
|
||||||
|
assert result == ""
|
||||||
|
|
||||||
|
def test_safe_html_escape_non_string_input(self):
|
||||||
|
"""Тест экранирования нестрокового ввода"""
|
||||||
|
result = safe_html_escape(123)
|
||||||
|
assert result == "123"
|
||||||
|
|
||||||
|
|
||||||
class TestMessages:
|
class TestMessages:
|
||||||
"""Тесты для системы сообщений"""
|
"""Тесты для системы сообщений"""
|
||||||
|
|
||||||
@@ -204,5 +260,422 @@ class TestConfigurationHandling:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestDownloadFile:
|
||||||
|
"""Тесты для функции скачивания файлов"""
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_download_file_success(self):
|
||||||
|
"""Тест успешного скачивания файла"""
|
||||||
|
mock_message = Mock()
|
||||||
|
mock_message.bot = AsyncMock()
|
||||||
|
|
||||||
|
# Мокаем get_file
|
||||||
|
mock_file = Mock()
|
||||||
|
mock_file.file_path = "photos/file_123.jpg"
|
||||||
|
mock_message.bot.get_file.return_value = mock_file
|
||||||
|
|
||||||
|
# Мокаем download_file
|
||||||
|
mock_message.bot.download_file = AsyncMock()
|
||||||
|
|
||||||
|
# Мокаем os.makedirs
|
||||||
|
with patch('os.makedirs') as mock_makedirs:
|
||||||
|
with patch('os.path.join', return_value="files/photos/file_123.jpg"):
|
||||||
|
result = await download_file(mock_message, "file_id_123")
|
||||||
|
|
||||||
|
assert result == "files/photos/file_123.jpg"
|
||||||
|
mock_makedirs.assert_called()
|
||||||
|
mock_message.bot.get_file.assert_called_once_with("file_id_123")
|
||||||
|
mock_message.bot.download_file.assert_called_once()
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_download_file_exception(self):
|
||||||
|
"""Тест обработки ошибки при скачивании"""
|
||||||
|
mock_message = Mock()
|
||||||
|
mock_message.bot = AsyncMock()
|
||||||
|
mock_message.bot.get_file.side_effect = Exception("Network error")
|
||||||
|
|
||||||
|
with patch('os.makedirs'):
|
||||||
|
with patch('helper_bot.utils.helper_func.logger') as mock_logger:
|
||||||
|
result = await download_file(mock_message, "file_id_123")
|
||||||
|
|
||||||
|
assert result is None
|
||||||
|
mock_logger.error.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
class TestPrepareMediaGroup:
|
||||||
|
"""Тесты для подготовки медиагрупп"""
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_prepare_media_group_photos(self):
|
||||||
|
"""Тест подготовки медиагруппы с фотографиями"""
|
||||||
|
album = []
|
||||||
|
for i in range(3):
|
||||||
|
message = Mock()
|
||||||
|
message.photo = [Mock()]
|
||||||
|
message.photo[-1].file_id = f"photo_{i}"
|
||||||
|
album.append(message)
|
||||||
|
|
||||||
|
result = await prepare_media_group_from_middlewares(album, "Тестовая подпись")
|
||||||
|
|
||||||
|
assert len(result) == 3
|
||||||
|
assert result[0].media == "photo_0"
|
||||||
|
assert result[1].media == "photo_1"
|
||||||
|
assert result[2].media == "photo_2"
|
||||||
|
assert result[2].caption == "Тестовая подпись"
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_prepare_media_group_mixed_types(self):
|
||||||
|
"""Тест подготовки медиагруппы с разными типами медиа"""
|
||||||
|
album = []
|
||||||
|
|
||||||
|
# Фото
|
||||||
|
photo_message = Mock()
|
||||||
|
photo_message.photo = [Mock()]
|
||||||
|
photo_message.photo[-1].file_id = "photo_1"
|
||||||
|
album.append(photo_message)
|
||||||
|
|
||||||
|
# Видео
|
||||||
|
video_message = Mock()
|
||||||
|
video_message.photo = None
|
||||||
|
video_message.video = Mock()
|
||||||
|
video_message.video.file_id = "video_1"
|
||||||
|
album.append(video_message)
|
||||||
|
|
||||||
|
# Аудио
|
||||||
|
audio_message = Mock()
|
||||||
|
audio_message.photo = None
|
||||||
|
audio_message.video = None
|
||||||
|
audio_message.audio = Mock()
|
||||||
|
audio_message.audio.file_id = "audio_1"
|
||||||
|
album.append(audio_message)
|
||||||
|
|
||||||
|
result = await prepare_media_group_from_middlewares(album, "Смешанная группа")
|
||||||
|
|
||||||
|
assert len(result) == 3
|
||||||
|
assert result[0].media == "photo_1"
|
||||||
|
assert result[1].media == "video_1"
|
||||||
|
assert result[2].media == "audio_1"
|
||||||
|
assert result[2].caption == "Смешанная группа"
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_prepare_media_group_empty_album(self):
|
||||||
|
"""Тест подготовки пустой медиагруппы"""
|
||||||
|
album = []
|
||||||
|
result = await prepare_media_group_from_middlewares(album, "Пустая группа")
|
||||||
|
assert result == []
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_prepare_media_group_unsupported_type(self):
|
||||||
|
"""Тест подготовки медиагруппы с неподдерживаемым типом"""
|
||||||
|
album = []
|
||||||
|
message = Mock()
|
||||||
|
message.photo = None
|
||||||
|
message.video = None
|
||||||
|
message.audio = None
|
||||||
|
album.append(message)
|
||||||
|
|
||||||
|
result = await prepare_media_group_from_middlewares(album, "Тест")
|
||||||
|
assert result == []
|
||||||
|
|
||||||
|
|
||||||
|
class TestMediaDatabaseOperations:
|
||||||
|
"""Тесты для операций с медиа в базе данных"""
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_add_in_db_media_mediagroup(self):
|
||||||
|
"""Тест добавления медиагруппы в базу данных"""
|
||||||
|
sent_message = []
|
||||||
|
for i in range(2):
|
||||||
|
message = Mock()
|
||||||
|
message.message_id = i + 1
|
||||||
|
message.photo = [Mock()]
|
||||||
|
message.photo[-1].file_id = f"photo_{i}"
|
||||||
|
sent_message.append(message)
|
||||||
|
|
||||||
|
mock_db = Mock()
|
||||||
|
|
||||||
|
with patch('helper_bot.utils.helper_func.download_file', return_value=f"files/photo_{i}.jpg"):
|
||||||
|
await add_in_db_media_mediagroup(sent_message, mock_db)
|
||||||
|
|
||||||
|
assert mock_db.add_post_content_in_db.call_count == 2
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_add_in_db_media_photo(self):
|
||||||
|
"""Тест добавления фото в базу данных"""
|
||||||
|
mock_message = Mock()
|
||||||
|
mock_message.message_id = 123
|
||||||
|
mock_message.photo = [Mock()]
|
||||||
|
mock_message.photo[-1].file_id = "photo_123"
|
||||||
|
|
||||||
|
mock_db = Mock()
|
||||||
|
|
||||||
|
with patch('helper_bot.utils.helper_func.download_file', return_value="files/photo_123.jpg"):
|
||||||
|
await add_in_db_media(mock_message, mock_db)
|
||||||
|
|
||||||
|
mock_db.add_post_content_in_db.assert_called_once_with(
|
||||||
|
123, 123, "files/photo_123.jpg", 'photo'
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_add_in_db_media_video(self):
|
||||||
|
"""Тест добавления видео в базу данных"""
|
||||||
|
mock_message = Mock()
|
||||||
|
mock_message.message_id = 123
|
||||||
|
mock_message.photo = None # У видео нет фото
|
||||||
|
mock_message.video = Mock()
|
||||||
|
mock_message.video.file_id = "video_123"
|
||||||
|
|
||||||
|
mock_db = Mock()
|
||||||
|
|
||||||
|
with patch('helper_bot.utils.helper_func.download_file', return_value="files/video_123.mp4"):
|
||||||
|
await add_in_db_media(mock_message, mock_db)
|
||||||
|
|
||||||
|
mock_db.add_post_content_in_db.assert_called_once_with(
|
||||||
|
123, 123, "files/video_123.mp4", 'video'
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_add_in_db_media_voice(self):
|
||||||
|
"""Тест добавления голосового сообщения в базу данных"""
|
||||||
|
mock_message = Mock()
|
||||||
|
mock_message.message_id = 123
|
||||||
|
mock_message.photo = None # У голосового сообщения нет фото
|
||||||
|
mock_message.video = None # У голосового сообщения нет видео
|
||||||
|
mock_message.voice = Mock()
|
||||||
|
mock_message.voice.file_id = "voice_123"
|
||||||
|
|
||||||
|
mock_db = Mock()
|
||||||
|
|
||||||
|
with patch('helper_bot.utils.helper_func.download_file', return_value="files/voice_123.ogg"):
|
||||||
|
await add_in_db_media(mock_message, mock_db)
|
||||||
|
|
||||||
|
mock_db.add_post_content_in_db.assert_called_once_with(
|
||||||
|
123, 123, "files/voice_123.ogg", 'voice'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestSendMessageFunctions:
|
||||||
|
"""Тесты для функций отправки сообщений"""
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_send_text_message_without_markup(self):
|
||||||
|
"""Тест отправки текстового сообщения без разметки"""
|
||||||
|
mock_message = Mock()
|
||||||
|
mock_message.bot = AsyncMock()
|
||||||
|
mock_message.bot.send_message = AsyncMock()
|
||||||
|
|
||||||
|
mock_sent_message = Mock()
|
||||||
|
mock_sent_message.message_id = 456
|
||||||
|
mock_message.bot.send_message.return_value = mock_sent_message
|
||||||
|
|
||||||
|
result = await send_text_message(123, mock_message, "Тестовое сообщение")
|
||||||
|
|
||||||
|
assert result == 456
|
||||||
|
mock_message.bot.send_message.assert_called_once_with(
|
||||||
|
chat_id=123,
|
||||||
|
text="Тестовое сообщение"
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_send_text_message_with_markup(self):
|
||||||
|
"""Тест отправки текстового сообщения с разметкой"""
|
||||||
|
mock_message = Mock()
|
||||||
|
mock_message.bot = AsyncMock()
|
||||||
|
mock_message.bot.send_message = AsyncMock()
|
||||||
|
|
||||||
|
mock_markup = Mock()
|
||||||
|
mock_sent_message = Mock()
|
||||||
|
mock_sent_message.message_id = 456
|
||||||
|
mock_message.bot.send_message.return_value = mock_sent_message
|
||||||
|
|
||||||
|
result = await send_text_message(123, mock_message, "Тестовое сообщение", mock_markup)
|
||||||
|
|
||||||
|
assert result == 456
|
||||||
|
mock_message.bot.send_message.assert_called_once_with(
|
||||||
|
chat_id=123,
|
||||||
|
text="Тестовое сообщение",
|
||||||
|
reply_markup=mock_markup
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_send_photo_message(self):
|
||||||
|
"""Тест отправки фото"""
|
||||||
|
mock_message = Mock()
|
||||||
|
mock_message.bot = AsyncMock()
|
||||||
|
mock_message.bot.send_photo = AsyncMock()
|
||||||
|
|
||||||
|
mock_sent_message = Mock()
|
||||||
|
mock_message.bot.send_photo.return_value = mock_sent_message
|
||||||
|
|
||||||
|
result = await send_photo_message(123, mock_message, "photo.jpg", "Подпись к фото")
|
||||||
|
|
||||||
|
assert result == mock_sent_message
|
||||||
|
mock_message.bot.send_photo.assert_called_once_with(
|
||||||
|
chat_id=123,
|
||||||
|
caption="Подпись к фото",
|
||||||
|
photo="photo.jpg"
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_send_video_message(self):
|
||||||
|
"""Тест отправки видео"""
|
||||||
|
mock_message = Mock()
|
||||||
|
mock_message.bot = AsyncMock()
|
||||||
|
mock_message.bot.send_video = AsyncMock()
|
||||||
|
|
||||||
|
mock_sent_message = Mock()
|
||||||
|
mock_message.bot.send_video.return_value = mock_sent_message
|
||||||
|
|
||||||
|
result = await send_video_message(123, mock_message, "video.mp4", "Подпись к видео")
|
||||||
|
|
||||||
|
assert result == mock_sent_message
|
||||||
|
mock_message.bot.send_video.assert_called_once_with(
|
||||||
|
chat_id=123,
|
||||||
|
caption="Подпись к видео",
|
||||||
|
video="video.mp4"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestUtilityFunctions:
|
||||||
|
"""Тесты для утилитарных функций"""
|
||||||
|
|
||||||
|
def test_check_access(self):
|
||||||
|
"""Тест проверки доступа"""
|
||||||
|
mock_db = Mock()
|
||||||
|
mock_db.is_admin.return_value = True
|
||||||
|
|
||||||
|
result = check_access(123, mock_db)
|
||||||
|
assert result is True
|
||||||
|
|
||||||
|
mock_db.is_admin.return_value = False
|
||||||
|
result = check_access(123, mock_db)
|
||||||
|
assert result is False
|
||||||
|
|
||||||
|
def test_add_days_to_date(self):
|
||||||
|
"""Тест добавления дней к дате"""
|
||||||
|
with patch('helper_bot.utils.helper_func.datetime') as mock_datetime:
|
||||||
|
from datetime import timedelta
|
||||||
|
mock_now = datetime(2024, 1, 1)
|
||||||
|
mock_datetime.now.return_value = mock_now
|
||||||
|
mock_datetime.timedelta = timedelta
|
||||||
|
|
||||||
|
result = add_days_to_date(5)
|
||||||
|
expected_date = (mock_now + timedelta(days=5)).strftime("%d-%m-%Y")
|
||||||
|
assert result == expected_date
|
||||||
|
|
||||||
|
def test_get_banned_users_list(self):
|
||||||
|
"""Тест получения списка заблокированных пользователей"""
|
||||||
|
mock_db = Mock()
|
||||||
|
mock_db.get_banned_users_from_db_with_limits.return_value = [
|
||||||
|
("User1", 123, "Spam", "01-01-2025"),
|
||||||
|
("User2", 456, "Violation", "02-01-2025")
|
||||||
|
]
|
||||||
|
|
||||||
|
result = get_banned_users_list(0, mock_db)
|
||||||
|
|
||||||
|
assert "Список заблокированных пользователей:" in result
|
||||||
|
assert "User1" in result
|
||||||
|
assert "User2" in result
|
||||||
|
assert "Spam" in result
|
||||||
|
assert "Violation" in result
|
||||||
|
|
||||||
|
def test_get_banned_users_buttons(self):
|
||||||
|
"""Тест получения кнопок заблокированных пользователей"""
|
||||||
|
mock_db = Mock()
|
||||||
|
mock_db.get_banned_users_from_db.return_value = [
|
||||||
|
("User1", 123),
|
||||||
|
("User2", 456)
|
||||||
|
]
|
||||||
|
|
||||||
|
result = get_banned_users_buttons(mock_db)
|
||||||
|
|
||||||
|
assert len(result) == 2
|
||||||
|
assert result[0] == ("User1", 123)
|
||||||
|
assert result[1] == ("User2", 456)
|
||||||
|
|
||||||
|
def test_delete_user_blacklist(self):
|
||||||
|
"""Тест удаления пользователя из черного списка"""
|
||||||
|
mock_db = Mock()
|
||||||
|
mock_db.delete_user_blacklist.return_value = True
|
||||||
|
|
||||||
|
result = delete_user_blacklist(123, mock_db)
|
||||||
|
assert result is True
|
||||||
|
|
||||||
|
mock_db.delete_user_blacklist.assert_called_once_with(user_id=123)
|
||||||
|
|
||||||
|
|
||||||
|
class TestUserManagement:
|
||||||
|
"""Тесты для управления пользователями"""
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_update_user_info_new_user(self):
|
||||||
|
"""Тест обновления информации о новом пользователе"""
|
||||||
|
mock_message = Mock()
|
||||||
|
mock_message.from_user.id = 123
|
||||||
|
mock_message.from_user.full_name = "Test User"
|
||||||
|
mock_message.from_user.username = "testuser"
|
||||||
|
mock_message.from_user.is_bot = False
|
||||||
|
mock_message.from_user.language_code = "ru"
|
||||||
|
mock_message.answer = AsyncMock()
|
||||||
|
mock_message.bot.send_message = AsyncMock()
|
||||||
|
|
||||||
|
with patch('helper_bot.utils.helper_func.get_first_name', return_value="Test"):
|
||||||
|
with patch('helper_bot.utils.helper_func.get_random_emoji', return_value="😀"):
|
||||||
|
with patch('helper_bot.utils.helper_func.BotDB') as mock_bot_db:
|
||||||
|
mock_bot_db.user_exists.return_value = False
|
||||||
|
mock_bot_db.add_new_user_in_db = Mock()
|
||||||
|
mock_bot_db.update_date_for_user = Mock()
|
||||||
|
|
||||||
|
await update_user_info("test", mock_message)
|
||||||
|
|
||||||
|
mock_bot_db.add_new_user_in_db.assert_called_once()
|
||||||
|
mock_bot_db.update_date_for_user.assert_called_once()
|
||||||
|
|
||||||
|
def test_check_user_emoji_existing(self):
|
||||||
|
"""Тест проверки эмодзи пользователя (существующий)"""
|
||||||
|
mock_message = Mock()
|
||||||
|
mock_message.from_user.id = 123
|
||||||
|
|
||||||
|
with patch('helper_bot.utils.helper_func.BotDB') as mock_bot_db:
|
||||||
|
mock_bot_db.check_emoji_for_user.return_value = "😀"
|
||||||
|
|
||||||
|
result = check_user_emoji(mock_message)
|
||||||
|
assert result == "😀"
|
||||||
|
|
||||||
|
def test_check_user_emoji_new(self):
|
||||||
|
"""Тест проверки эмодзи пользователя (новый)"""
|
||||||
|
mock_message = Mock()
|
||||||
|
mock_message.from_user.id = 123
|
||||||
|
|
||||||
|
with patch('helper_bot.utils.helper_func.BotDB') as mock_bot_db:
|
||||||
|
mock_bot_db.check_emoji_for_user.return_value = None
|
||||||
|
mock_bot_db.update_emoji_for_user = Mock()
|
||||||
|
|
||||||
|
with patch('helper_bot.utils.helper_func.get_random_emoji', return_value="😀"):
|
||||||
|
result = check_user_emoji(mock_message)
|
||||||
|
assert result == "😀"
|
||||||
|
mock_bot_db.update_emoji_for_user.assert_called_once_with(user_id=123, emoji="😀")
|
||||||
|
|
||||||
|
def test_get_random_emoji_success(self):
|
||||||
|
"""Тест получения случайного эмодзи (успех)"""
|
||||||
|
with patch('helper_bot.utils.helper_func.BotDB') as mock_bot_db:
|
||||||
|
mock_bot_db.check_emoji.return_value = False
|
||||||
|
|
||||||
|
with patch('helper_bot.utils.helper_func.random.choice', return_value="😀"):
|
||||||
|
result = get_random_emoji()
|
||||||
|
assert result == "😀"
|
||||||
|
|
||||||
|
def test_get_random_emoji_fallback(self):
|
||||||
|
"""Тест получения случайного эмодзи (fallback)"""
|
||||||
|
with patch('helper_bot.utils.helper_func.BotDB') as mock_bot_db:
|
||||||
|
mock_bot_db.check_emoji.return_value = True # Все эмодзи заняты
|
||||||
|
|
||||||
|
with patch('helper_bot.utils.helper_func.random.choice', return_value="😀"):
|
||||||
|
with patch('helper_bot.utils.helper_func.logger') as mock_logger:
|
||||||
|
result = get_random_emoji()
|
||||||
|
assert result == "Эмоджи не определен"
|
||||||
|
mock_logger.error.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
pytest.main([__file__, '-v'])
|
pytest.main([__file__, '-v'])
|
||||||
|
|||||||
Reference in New Issue
Block a user