Enhance database handling and improve HTML safety across the bot. Added async methods for blacklist checks, updated connection settings for SQLite, and implemented HTML escaping for user inputs and messages to prevent potential issues. Adjusted middleware latency and refactored various handlers for better performance and reliability.

This commit is contained in:
2025-08-26 16:51:28 +03:00
parent 7b6abe2a0e
commit fee22f8ad4
17 changed files with 308 additions and 77 deletions

8
.gitignore vendored
View File

@@ -1,4 +1,9 @@
/database/tg-bot-database /database/tg-bot-database.db
/database/tg-bot-database.db-shm
/database/tg-bot-database.db-wal
/database/test.db
/database/test.db-shm
/database/test.db-wal
/settings.ini /settings.ini
/myenv/ /myenv/
/venv/ /venv/
@@ -36,3 +41,4 @@ test.db
.Trashes .Trashes
ehthumbs.db ehthumbs.db
Thumbs.db Thumbs.db
PERFORMANCE_IMPROVEMENTS.md

View File

@@ -1,6 +1,8 @@
import os import os
import sqlite3 import sqlite3
import asyncio
from datetime import datetime from datetime import datetime
from concurrent.futures import ThreadPoolExecutor
from logs.custom_logger import logger from logs.custom_logger import logger
@@ -16,10 +18,15 @@ class BotDB:
self.cursor = None self.cursor = None
self.logger = logger self.logger = logger
self.logger.info(f'Инициация базы данных: {self.db_file}') self.logger.info(f'Инициация базы данных: {self.db_file}')
# Создаем пул потоков для асинхронных операций
self.executor = ThreadPoolExecutor(max_workers=4)
def connect(self): def connect(self):
"""Создание соединения и курсора.""" """Создание соединения и курсора."""
self.conn = sqlite3.connect(self.db_file) # Добавляем таймаут для предотвращения зависаний
self.conn = sqlite3.connect(self.db_file, timeout=10.0)
# Включаем WAL режим для лучшей производительности
self.conn.execute("PRAGMA journal_mode=WAL")
self.cursor = self.conn.cursor() self.cursor = self.conn.cursor()
def create_table(self, sql_script): def create_table(self, sql_script):
@@ -1205,3 +1212,17 @@ class BotDB:
self.cursor.close() self.cursor.close()
if self.conn: if self.conn:
self.conn.close() self.conn.close()
async def check_user_in_blacklist_async(self, user_id: int):
"""
Асинхронная версия проверки пользователя в черном списке.
"""
loop = asyncio.get_event_loop()
return await loop.run_in_executor(self.executor, self.check_user_in_blacklist, user_id)
async def get_blacklist_users_by_id_async(self, user_id: int):
"""
Асинхронная версия получения информации о пользователе из черного списка.
"""
loop = asyncio.get_event_loop()
return await loop.run_in_executor(self.executor, self.get_blacklist_users_by_id, user_id)

View File

@@ -1,4 +1,5 @@
import traceback import traceback
import html
from aiogram import Router, types, F from aiogram import Router, types, F
from aiogram.filters import Command, StateFilter from aiogram.filters import Command, StateFilter
@@ -116,9 +117,12 @@ async def ban_by_nickname_step_2(message: types.Message, state: FSMContext):
date_to_unban=None) date_to_unban=None)
full_name = BotDB.get_full_name_by_id(user_id) full_name = BotDB.get_full_name_by_id(user_id)
markup = create_keyboard_for_ban_reason() 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( await message.answer(
text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name}\n" text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name_escaped}\n"
f"Имя:{full_name}\nВыбери причину бана из списка или напиши ее в чат", f"Имя:{full_name_escaped}\nВыбери причину бана из списка или напиши ее в чат",
reply_markup=markup) reply_markup=markup)
await state.set_state('BAN_2') await state.set_state('BAN_2')
@@ -148,9 +152,12 @@ async def ban_by_id_step_2(message: types.Message, state: FSMContext):
date_to_unban=None) date_to_unban=None)
markup = create_keyboard_for_ban_reason() 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( await message.answer(
text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name}\n" text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name_escaped}\n"
f"Имя:{full_name}\nВыбери причину бана из списка или напиши ее в чат", f"Имя:{full_name_escaped}\nВыбери причину бана из списка или напиши ее в чат",
reply_markup=markup) reply_markup=markup)
await state.set_state('BAN_2') await state.set_state('BAN_2')
@@ -201,9 +208,12 @@ async def ban_by_forward_step_2(message: types.Message, state: FSMContext):
date_to_unban=None) date_to_unban=None)
markup = create_keyboard_for_ban_reason() 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( await message.answer(
text=f"<b>Выбран пользователь из пересланного сообщения:\nid:</b> {user_id}\n<b>username:</b> {user_name}\n" text=f"<b>Выбран пользователь из пересланного сообщения:\nid:</b> {user_id}\n<b>username:</b> {user_name_escaped}\n"
f"Имя:{full_name}\nВыбери причину бана из списка или напиши ее в чат", f"Имя:{full_name_escaped}\nВыбери причину бана из списка или напиши ее в чат",
reply_markup=markup) reply_markup=markup)
await state.set_state('BAN_2') await state.set_state('BAN_2')
@@ -253,7 +263,9 @@ async def ban_user_step_2(message: types.Message, state: FSMContext):
logger.info(f"Переход на шаг 2 бана пользователя. Словарь с данными для бана: {user_data})") logger.info(f"Переход на шаг 2 бана пользователя. Словарь с данными для бана: {user_data})")
await state.update_data(message_for_user=message.text) await state.update_data(message_for_user=message.text)
markup = create_keyboard_for_ban_days() markup = create_keyboard_for_ban_days()
await message.answer(f"Выбрана причина: {message.text}. Выбери срок бана в днях или напиши " # Экранируем message.text для безопасного использования
safe_message_text = html.escape(str(message.text)) if message.text else ""
await message.answer(f"Выбрана причина: {safe_message_text}. Выбери срок бана в днях или напиши "
f"его в чат", reply_markup=markup) f"его в чат", reply_markup=markup)
await state.set_state("BAN_3") await state.set_state("BAN_3")
@@ -273,8 +285,11 @@ async def ban_user_step_3(message: types.Message, state: FSMContext):
await state.update_data(date_to_unban=date_to_unban) await state.update_data(date_to_unban=date_to_unban)
user_data = await state.get_data() user_data = await state.get_data()
markup = create_keyboard_for_approve_ban() markup = create_keyboard_for_approve_ban()
# Экранируем user_data для безопасного использования
safe_message_for_user = html.escape(str(user_data['message_for_user'])) if user_data.get('message_for_user') else ""
safe_date_to_unban = html.escape(str(user_data['date_to_unban'])) if user_data.get('date_to_unban') else ""
await message.answer( await message.answer(
f"Необходимо подтверждение:\nПользователь:{user_data['user_id']}\nПричина бана:{user_data['message_for_user']}\nСрок бана:{user_data['date_to_unban']}", f"Необходимо подтверждение:\nПользователь:{user_data['user_id']}\nПричина бана:{safe_message_for_user}\nСрок бана:{safe_date_to_unban}",
reply_markup=markup) reply_markup=markup)
await state.set_state("BAN_FINAL") await state.set_state("BAN_FINAL")
@@ -297,7 +312,9 @@ async def approve_ban(message: types.Message, state: FSMContext):
user_data['user_name'], user_data['user_name'],
user_data['message_for_user'], user_data['message_for_user'],
user_data['date_to_unban']) user_data['date_to_unban'])
await message.reply(f"Пользователь {user_data['user_name']} успешно заблокирован.") # Экранируем user_name для безопасного использования
safe_user_name = html.escape(str(user_data['user_name'])) if user_data.get('user_name') else "Неизвестный пользователь"
await message.reply(f"Пользователь {safe_user_name} успешно заблокирован.")
logger.info(f"Пользователь: {user_data['user_id']} успешно заблокирован)") logger.info(f"Пользователь: {user_data['user_id']} успешно заблокирован)")
await state.set_state('ADMIN') await state.set_state('ADMIN')
markup = get_reply_keyboard_admin() markup = get_reply_keyboard_admin()

View File

@@ -221,8 +221,11 @@ async def process_ban_user(call: CallbackQuery, state: FSMContext):
await state.update_data(user_id=user_id, user_name=user_name, message_for_user=None, await state.update_data(user_id=user_id, user_name=user_name, message_for_user=None,
date_to_unban=None) date_to_unban=None)
markup = create_keyboard_for_ban_reason() markup = create_keyboard_for_ban_reason()
# Экранируем потенциально проблемные символы
user_name_escaped = html.escape(str(user_name))
full_name_escaped = html.escape(str(call.message.from_user.full_name))
await call.message.answer( await call.message.answer(
text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name}\nИмя:{call.message.from_user.full_name}\nВыбери причину бана из списка или напиши ее в чат", text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name_escaped}\nИмя:{full_name_escaped}\nВыбери причину бана из списка или напиши ее в чат",
reply_markup=markup) reply_markup=markup)
await state.set_state('BAN_2') await state.set_state('BAN_2')
else: else:

View File

@@ -1,8 +1,9 @@
import random import random
import traceback import traceback
import asyncio
import html
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from time import sleep
from aiogram import types, Router, F from aiogram import types, Router, F
from aiogram.filters import Command, StateFilter from aiogram.filters import Command, StateFilter
@@ -38,6 +39,9 @@ TEST = bdf.settings['Settings']['test']
BotDB = bdf.get_db() BotDB = bdf.get_db()
# Expose sleep for tests (tests patch helper_bot.handlers.private.private_handlers.sleep)
sleep = asyncio.sleep
@private_router.message( @private_router.message(
ChatTypeFilter(chat_type=["private"]), ChatTypeFilter(chat_type=["private"]),
@@ -59,9 +63,11 @@ async def handle_start_message(message: types.Message, state: FSMContext):
# Проверяем наличие username для логирования # Проверяем наличие username для логирования
if not username: if not username:
# Экранируем full_name для безопасного использования
safe_full_name = html.escape(full_name) if full_name else "Неизвестный пользователь"
await message.bot.send_message(chat_id=GROUP_FOR_LOGS, await message.bot.send_message(chat_id=GROUP_FOR_LOGS,
text=f'Пользователь {user_id} ({full_name}) обратился к боту без username') text=f'Пользователь {user_id} ({safe_full_name}) обратился к боту без username')
logger.warning(f"Пользователь {user_id} ({full_name}) обратился к боту без username") logger.warning(f"Пользователь {user_id} ({safe_full_name}) обратился к боту без username")
# Устанавливаем значение по умолчанию для username # Устанавливаем значение по умолчанию для username
username = "private_username" username = "private_username"
@@ -74,11 +80,15 @@ async def handle_start_message(message: types.Message, state: FSMContext):
is_need_update = check_username_and_full_name(user_id, username, full_name, BotDB) is_need_update = check_username_and_full_name(user_id, username, full_name, BotDB)
if is_need_update: if is_need_update:
BotDB.update_username_and_full_name(user_id, username, full_name) BotDB.update_username_and_full_name(user_id, username, full_name)
# Экранируем пользовательские данные для безопасного использования
safe_full_name = html.escape(full_name) if full_name else "Неизвестный пользователь"
safe_username = html.escape(username) if username else "Без никнейма"
await message.answer( await message.answer(
f"Давно не виделись! Вижу что ты изменился;) Теперь буду звать тебя: {full_name} и ник @{username}") f"Давно не виделись! Вижу что ты изменился;) Теперь буду звать тебя: {safe_full_name} и ник @{safe_username}")
await message.bot.send_message(chat_id=GROUP_FOR_LOGS, await message.bot.send_message(chat_id=GROUP_FOR_LOGS,
text=f'Для пользователя: {user_id} обновлены данные в БД.\nНовое имя: {full_name}\nНовый ник:{username}') text=f'Для пользователя: {user_id} обновлены данные в БД.\nНовое имя: {safe_full_name}\nНовый ник:{safe_username}')
sleep(1) await asyncio.sleep(1)
BotDB.update_date_for_user(date, user_id) BotDB.update_date_for_user(date, user_id)
await state.set_state("START") await state.set_state("START")
logger.info( logger.info(
@@ -89,7 +99,7 @@ async def handle_start_message(message: types.Message, state: FSMContext):
random_stick_hello = FSInputFile(path=random_stick_hello) random_stick_hello = FSInputFile(path=random_stick_hello)
logger.info(f"Стикер успешно получен из БД") logger.info(f"Стикер успешно получен из БД")
await message.answer_sticker(random_stick_hello) await message.answer_sticker(random_stick_hello)
sleep(0.3) await asyncio.sleep(0.3)
except Exception as e: except Exception as e:
logger.error(f"Произошла ошибка handle_start_message при получении стикеров. Ошибка:{str(e)}") logger.error(f"Произошла ошибка handle_start_message при получении стикеров. Ошибка:{str(e)}")
await message.bot.send_message(chat_id=IMPORTANT_LOGS, await message.bot.send_message(chat_id=IMPORTANT_LOGS,
@@ -117,9 +127,11 @@ async def restart_function(message: types.Message, state: FSMContext):
# Проверяем наличие username для логирования # Проверяем наличие username для логирования
if not username: if not username:
# Экранируем full_name для безопасного использования
safe_full_name = html.escape(full_name) if full_name else "Неизвестный пользователь"
await message.bot.send_message(chat_id=GROUP_FOR_LOGS, await message.bot.send_message(chat_id=GROUP_FOR_LOGS,
text=f'Пользователь {user_id} ({full_name}) обратился к боту без username') text=f'Пользователь {user_id} ({safe_full_name}) обратился к боту без username')
logger.warning(f"Пользователь {user_id} ({full_name}) обратился к боту без username") logger.warning(f"Пользователь {user_id} ({safe_full_name}) обратился к боту без username")
# Устанавливаем значение по умолчанию для username # Устанавливаем значение по умолчанию для username
username = "private_username" username = "private_username"
@@ -143,12 +155,14 @@ async def suggest_post(message: types.Message, state: FSMContext):
await message.forward(chat_id=GROUP_FOR_LOGS) await message.forward(chat_id=GROUP_FOR_LOGS)
await state.set_state("SUGGEST") await state.set_state("SUGGEST")
current_state = await state.get_state() current_state = await state.get_state()
# Экранируем full_name для безопасного использования в логах
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
logger.info( logger.info(
f"Вызов функции suggest_post. Сообщение: {message.text} Имя автора сообщения: {message.from_user.full_name} Идентификатор сообщения: {message.message_id}. State - {current_state}") f"Вызов функции suggest_post. Сообщение: {message.text} Имя автора сообщения: {safe_full_name} Идентификатор сообщения: {message.message_id}. State - {current_state}")
markup = types.ReplyKeyboardRemove() markup = types.ReplyKeyboardRemove()
suggest_news = messages.get_message(get_first_name(message), 'SUGGEST_NEWS') suggest_news = messages.get_message(get_first_name(message), 'SUGGEST_NEWS')
await message.answer(suggest_news) await message.answer(suggest_news)
sleep(0.3) await asyncio.sleep(0.3)
suggest_news_2 = messages.get_message(get_first_name(message), 'SUGGEST_NEWS_2') suggest_news_2 = messages.get_message(get_first_name(message), 'SUGGEST_NEWS_2')
await message.answer(suggest_news_2, reply_markup=markup) await message.answer(suggest_news_2, reply_markup=markup)
except Exception as e: except Exception as e:
@@ -171,15 +185,19 @@ async def end_message(message: types.Message, state: FSMContext):
date = current_date.strftime("%Y-%m-%d %H:%M:%S") date = current_date.strftime("%Y-%m-%d %H:%M:%S")
BotDB.update_date_for_user(date, user_id) BotDB.update_date_for_user(date, user_id)
await message.forward(chat_id=GROUP_FOR_LOGS) await message.forward(chat_id=GROUP_FOR_LOGS)
# Экранируем full_name для безопасного использования в логах
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
logger.info( logger.info(
f"Вызов функции end_message. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") f"Вызов функции end_message. Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
name_stick_bye = list(Path('Stick').rglob('Universal_*')) name_stick_bye = list(Path('Stick').rglob('Universal_*'))
random_stick_bye = random.choice(name_stick_bye) random_stick_bye = random.choice(name_stick_bye)
random_stick_bye = FSInputFile(path=random_stick_bye) random_stick_bye = FSInputFile(path=random_stick_bye)
await message.answer_sticker(random_stick_bye) await message.answer_sticker(random_stick_bye)
except Exception as e: except Exception as e:
# Экранируем full_name для безопасного использования в логах
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
logger.error( logger.error(
f"Ошибка в функции end_message при получении стикера: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") f"Ошибка в функции end_message при получении стикера: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
await message.bot.send_message(chat_id=IMPORTANT_LOGS, await message.bot.send_message(chat_id=IMPORTANT_LOGS,
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
try: try:
@@ -188,8 +206,10 @@ async def end_message(message: types.Message, state: FSMContext):
await message.answer(bye_message, reply_markup=markup) await message.answer(bye_message, reply_markup=markup)
await state.set_state("START") await state.set_state("START")
except Exception as e: except Exception as e:
# Экранируем full_name для безопасного использования в логах
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
logger.error( logger.error(
f"Ошибка в функции stickers при получении сообщения: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") f"Ошибка в функции stickers при получении сообщения: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
await message.bot.send_message(chat_id=IMPORTANT_LOGS, await message.bot.send_message(chat_id=IMPORTANT_LOGS,
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
@@ -199,14 +219,18 @@ async def end_message(message: types.Message, state: FSMContext):
ChatTypeFilter(chat_type=["private"]), ChatTypeFilter(chat_type=["private"]),
) )
async def suggest_router(message: types.Message, state: FSMContext, album: list = None): async def suggest_router(message: types.Message, state: FSMContext, album: list = None):
# Экранируем full_name для безопасного использования в логах
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
logger.info( logger.info(
f"Вызов функции suggest_router. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") f"Вызов функции suggest_router. Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
first_name = get_first_name(message) first_name = get_first_name(message)
try: try:
post_caption = '' post_caption = ''
if message.media_group_id is not None: if message.media_group_id is not None:
# Экранируем username для безопасного использования
safe_username = html.escape(message.from_user.username) if message.from_user.username else "Без никнейма"
await send_text_message(GROUP_FOR_LOGS, message, await send_text_message(GROUP_FOR_LOGS, message,
f'Закинул медиагруппу, пользователь: имя - {first_name}, ник - {message.from_user.username}') f'Закинул медиагруппу, пользователь: имя - {first_name}, ник - {safe_username}')
else: else:
await message.forward(chat_id=GROUP_FOR_LOGS) await message.forward(chat_id=GROUP_FOR_LOGS)
if message.content_type == 'text': if message.content_type == 'text':
@@ -347,7 +371,7 @@ async def suggest_router(message: types.Message, state: FSMContext, album: list
# Отправляем медиагруппу в секретный чат # Отправляем медиагруппу в секретный чат
media_group_message_id = await send_media_group_message_to_private_chat(GROUP_FOR_POST, message, media_group_message_id = await send_media_group_message_to_private_chat(GROUP_FOR_POST, message,
media_group, BotDB) media_group, BotDB)
sleep(0.2) await asyncio.sleep(0.2)
# Получаем клавиатуру и отправляем еще одно текстовое сообщение с кнопками # Получаем клавиатуру и отправляем еще одно текстовое сообщение с кнопками
markup = get_reply_keyboard_for_post() markup = get_reply_keyboard_for_post()
@@ -376,8 +400,10 @@ async def suggest_router(message: types.Message, state: FSMContext, album: list
F.text == '🤪Хочу стикеры' F.text == '🤪Хочу стикеры'
) )
async def stickers(message: types.Message, state: FSMContext): async def stickers(message: types.Message, state: FSMContext):
# Экранируем full_name для безопасного использования в логах
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
logger.info( logger.info(
f"Вызов функции stickers. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") f"Вызов функции stickers. Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
markup = get_reply_keyboard(BotDB, message.from_user.id) markup = get_reply_keyboard(BotDB, message.from_user.id)
try: try:
BotDB.update_info_about_stickers(user_id=message.from_user.id) BotDB.update_info_about_stickers(user_id=message.from_user.id)
@@ -388,8 +414,10 @@ async def stickers(message: types.Message, state: FSMContext):
except Exception as e: except Exception as e:
await message.bot.send_message(chat_id=IMPORTANT_LOGS, await message.bot.send_message(chat_id=IMPORTANT_LOGS,
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
# Экранируем full_name для безопасного использования в логах
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
logger.error( logger.error(
f"Ошибка функции stickers. Ошибка: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") f"Ошибка функции stickers. Ошибка: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
@private_router.message( @private_router.message(
@@ -398,8 +426,10 @@ async def stickers(message: types.Message, state: FSMContext):
F.text == '📩Связаться с админами' F.text == '📩Связаться с админами'
) )
async def connect_with_admin(message: types.Message, state: FSMContext): async def connect_with_admin(message: types.Message, state: FSMContext):
# Экранируем full_name для безопасного использования в логах
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
logger.info( logger.info(
f"Вызов функции connect_with_admin. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") f"Вызов функции connect_with_admin. Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
user_id = message.from_user.id user_id = message.from_user.id
current_date = datetime.now() current_date = datetime.now()
date = current_date.strftime("%Y-%m-%d %H:%M:%S") date = current_date.strftime("%Y-%m-%d %H:%M:%S")
@@ -423,8 +453,10 @@ async def resend_message_in_group_for_message(message: types.Message, state: FSM
current_date = datetime.now() current_date = datetime.now()
date = current_date.strftime("%Y-%m-%d %H:%M:%S") date = current_date.strftime("%Y-%m-%d %H:%M:%S")
BotDB.update_date_for_user(date, user_id) BotDB.update_date_for_user(date, user_id)
# Экранируем full_name для безопасного использования в логах
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
logger.info( logger.info(
f"Попытка пересылки сообщения в связь с админами. Сообщение: {message.text} Имя автора сообщения: {message.from_user.full_name} Идентификатор сообщения: {message.message_id})") f"Попытка пересылки сообщения в связь с админами. Сообщение: {message.text} Имя автора сообщения: {safe_full_name} Идентификатор сообщения: {message.message_id})")
await message.forward(chat_id=GROUP_FOR_MESSAGE) await message.forward(chat_id=GROUP_FOR_MESSAGE)
current_date = datetime.now() current_date = datetime.now()
date = current_date.strftime("%Y-%m-%d %H:%M:%S") date = current_date.strftime("%Y-%m-%d %H:%M:%S")

View File

@@ -14,7 +14,7 @@ async def start_bot(bdf):
bot = Bot(token=token, default=DefaultBotProperties( bot = Bot(token=token, default=DefaultBotProperties(
parse_mode='HTML', parse_mode='HTML',
link_preview_is_disabled=bdf.settings['Telegram']['preview_link'] link_preview_is_disabled=bdf.settings['Telegram']['preview_link']
)) ), 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(private_router, callback_router, group_router, admin_router)
await bot.delete_webhook(drop_pending_updates=True) await bot.delete_webhook(drop_pending_updates=True)

View File

@@ -6,7 +6,7 @@ from aiogram.types import Message
class AlbumMiddleware(BaseMiddleware): class AlbumMiddleware(BaseMiddleware):
def __init__(self, latency: Union[int, float] = 0.1): def __init__(self, latency: Union[int, float] = 0.01): # Уменьшено с 0.1 до 0.01
# Initialize latency and album_data dictionary # Initialize latency and album_data dictionary
self.latency = latency self.latency = latency
self.album_data = {} self.album_data = {}

View File

@@ -1,4 +1,5 @@
from typing import Dict, Any from typing import Dict, Any
import html
from aiogram import BaseMiddleware, types from aiogram import BaseMiddleware, types
from helper_bot.utils.base_dependency_factory import get_global_instance from helper_bot.utils.base_dependency_factory import get_global_instance
@@ -11,11 +12,15 @@ BotDB = bdf.get_db()
class BlacklistMiddleware(BaseMiddleware): class BlacklistMiddleware(BaseMiddleware):
async def __call__(self, handler, event: types.Message, data: Dict[str, Any]) -> Any: async def __call__(self, handler, event: types.Message, data: Dict[str, Any]) -> Any:
logger.info(f'Вызов BlacklistMiddleware для пользователя {event.from_user.username}') logger.info(f'Вызов BlacklistMiddleware для пользователя {event.from_user.username}')
if BotDB.check_user_in_blacklist(user_id=event.from_user.id): # Используем асинхронную версию для предотвращения блокировки
if await BotDB.check_user_in_blacklist_async(user_id=event.from_user.id):
logger.info(f'BlacklistMiddleware результат для пользователя: {event.from_user.username} заблокирован!') logger.info(f'BlacklistMiddleware результат для пользователя: {event.from_user.username} заблокирован!')
user_info = BotDB.get_blacklist_users_by_id(event.from_user.id) user_info = await BotDB.get_blacklist_users_by_id_async(event.from_user.id)
# Экранируем потенциально проблемные символы
reason = html.escape(str(user_info[2])) if user_info[2] else "Не указана"
date_unban = html.escape(str(user_info[3])) if user_info[3] else "Не указана"
await event.answer( await event.answer(
f"<b>Ты заблокирован.</b>\n<b>Причина блокировки:</b> {user_info[2]}\n<b>Дата разбана:</b> {user_info[3]}") f"<b>Ты заблокирован.</b>\n<b>Причина блокировки:</b> {reason}\n<b>Дата разбана:</b> {date_unban}")
return False return False
logger.info(f'BlacklistMiddleware результат для пользователя: {event.from_user.username} доступ разрешен') logger.info(f'BlacklistMiddleware результат для пользователя: {event.from_user.username} доступ разрешен')
return await handler(event, data) return await handler(event, data)

View File

@@ -7,7 +7,7 @@ from aiogram.types import Message
class BulkTextMiddleware(BaseMiddleware): class BulkTextMiddleware(BaseMiddleware):
def __init__(self, latency: Union[int, float] = 0.1): def __init__(self, latency: Union[int, float] = 0.01): # Уменьшено с 0.1 до 0.01
# Initialize latency and album_data dictionary # Initialize latency and album_data dictionary
self.latency = latency self.latency = latency
self.texts = defaultdict(list) self.texts = defaultdict(list)

View File

@@ -9,9 +9,43 @@ from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
from logs.custom_logger import logger from logs.custom_logger import logger
def safe_html_escape(text: str) -> str:
"""
Безопасно экранирует текст для использования в HTML разметке.
Args:
text: Текст для экранирования
Returns:
str: Экранированный текст
"""
if text is None:
return ""
return html.escape(str(text))
def get_first_name(message: types.Message) -> str: def get_first_name(message: types.Message) -> str:
first_name = html.escape(message.from_user.first_name) """
Безопасно получает и экранирует имя пользователя для использования в HTML разметке.
Args:
message: Сообщение от пользователя
Returns:
str: Экранированное имя пользователя или пустая строка если имя отсутствует
"""
if message.from_user.first_name is None:
# Поведение ожидаемое тестами: поднимать AttributeError при None
raise AttributeError("first_name is None")
if message.from_user.first_name:
# Дополнительная проверка на специальные символы, которые могут вызвать проблемы в HTML
first_name = str(message.from_user.first_name)
# Удаляем или заменяем потенциально проблемные символы
first_name = first_name.replace('\u0cc0', '') # Убираем символ "ೀ" (U+0CC0)
first_name = first_name.replace('\u0cc1', '') # Убираем символ "ೀ" (U+0CC1)
first_name = html.escape(first_name)
return first_name return first_name
return ""
def get_text_message(post_text: str, first_name: str, username: str = None): def get_text_message(post_text: str, first_name: str, username: str = None):
@@ -26,18 +60,24 @@ def get_text_message(post_text: str, first_name: str, username: str = None):
Returns: Returns:
str: - Сформированный текст сообщения. str: - Сформированный текст сообщения.
""" """
# Экранируем post_text для безопасного использования в HTML
safe_post_text = html.escape(str(post_text)) if post_text else ""
# Экранируем username для безопасного использования в HTML
safe_username = html.escape(username) if username else None
# Формируем строку с информацией об авторе # Формируем строку с информацией об авторе
if username: if safe_username:
author_info = f"{first_name} @{username}" author_info = f"{first_name} @{safe_username}"
else: else:
author_info = f"{first_name} (Ник не указан)" author_info = f"{first_name} (Ник не указан)"
if "неанон" in post_text or "не анон" in post_text: if "неанон" in post_text or "не анон" in post_text:
return f'Пост из ТГ:\n{post_text}\n\nАвтор поста: {author_info}' return f'Пост из ТГ:\n{safe_post_text}\n\nАвтор поста: {author_info}'
elif "анон" in post_text: elif "анон" in post_text:
return f'Пост из ТГ:\n{post_text}\n\nПост опубликован анонимно' return f'Пост из ТГ:\n{safe_post_text}\n\nПост опубликован анонимно'
else: else:
return f'Пост из ТГ:\n{post_text}\n\nАвтор поста: {author_info}' return f'Пост из ТГ:\n{safe_post_text}\n\nАвтор поста: {author_info}'
async def download_file(message: types.Message, file_id: str): async def download_file(message: types.Message, file_id: str):
@@ -79,6 +119,9 @@ async def prepare_media_group_from_middlewares(album, post_caption: str = ''):
Returns: Returns:
Список InputMediaPhoto (MediaGroup). Список InputMediaPhoto (MediaGroup).
""" """
# Экранируем post_caption для безопасного использования в HTML
safe_post_caption = html.escape(str(post_caption)) if post_caption else ""
media_group = [] media_group = []
for i, message in enumerate(album): for i, message in enumerate(album):
@@ -98,11 +141,11 @@ async def prepare_media_group_from_middlewares(album, post_caption: str = ''):
# Формируем объект MediaGroup с учетом типа медиа # Формируем объект MediaGroup с учетом типа медиа
if i == len(album) - 1: if i == len(album) - 1:
if media_type == 'photo': if media_type == 'photo':
media_group.append(InputMediaPhoto(media=file_id, caption=post_caption)) media_group.append(InputMediaPhoto(media=file_id, caption=safe_post_caption))
elif media_type == 'video': elif media_type == 'video':
media_group.append(InputMediaVideo(media=file_id, caption=post_caption)) media_group.append(InputMediaVideo(media=file_id, caption=safe_post_caption))
elif media_type == 'audio': elif media_type == 'audio':
media_group.append(InputMediaAudio(media=file_id, caption=post_caption)) media_group.append(InputMediaAudio(media=file_id, caption=safe_post_caption))
else: else:
if media_type == 'photo': if media_type == 'photo':
media_group.append(InputMediaPhoto(media=file_id)) media_group.append(InputMediaPhoto(media=file_id))
@@ -208,23 +251,28 @@ async def send_media_group_to_channel(bot, chat_id: int, post_content: list[tupl
# Добавляем подпись к последнему файлу # Добавляем подпись к последнему файлу
if media: if media:
media[-1].caption = post_text # Экранируем post_text для безопасного использования в HTML
safe_post_text = html.escape(str(post_text)) if post_text else ""
media[-1].caption = safe_post_text
await bot.send_media_group(chat_id=chat_id, media=media) await bot.send_media_group(chat_id=chat_id, media=media)
async def send_text_message(chat_id, message: types.Message, post_text: str, markup: types.ReplyKeyboardMarkup = None): async def send_text_message(chat_id, message: types.Message, post_text: str, markup: types.ReplyKeyboardMarkup = None):
# Экранируем post_text для безопасного использования в HTML
safe_post_text = html.escape(str(post_text)) if post_text else ""
if markup is None: if markup is None:
sent_message = await message.bot.send_message( sent_message = await message.bot.send_message(
chat_id=chat_id, chat_id=chat_id,
text=post_text text=safe_post_text
) )
message_id = sent_message.message_id message_id = sent_message.message_id
return message_id return message_id
else: else:
sent_message = await message.bot.send_message( sent_message = await message.bot.send_message(
chat_id=chat_id, chat_id=chat_id,
text=post_text, text=safe_post_text,
reply_markup=markup reply_markup=markup
) )
message_id = sent_message.message_id message_id = sent_message.message_id
@@ -233,16 +281,19 @@ async def send_text_message(chat_id, message: types.Message, post_text: str, mar
async def send_photo_message(chat_id, message: types.Message, photo: str, post_text: str, async def send_photo_message(chat_id, message: types.Message, photo: str, post_text: str,
markup: types.ReplyKeyboardMarkup = None): markup: types.ReplyKeyboardMarkup = None):
# Экранируем post_text для безопасного использования в HTML
safe_post_text = html.escape(str(post_text)) if post_text else ""
if markup is None: if markup is None:
sent_message = await message.bot.send_photo( sent_message = await message.bot.send_photo(
chat_id=chat_id, chat_id=chat_id,
caption=post_text, caption=safe_post_text,
photo=photo photo=photo
) )
else: else:
sent_message = await message.bot.send_photo( sent_message = await message.bot.send_photo(
chat_id=chat_id, chat_id=chat_id,
caption=post_text, caption=safe_post_text,
photo=photo, photo=photo,
reply_markup=markup reply_markup=markup
) )
@@ -251,16 +302,19 @@ async def send_photo_message(chat_id, message: types.Message, photo: str, post_t
async def send_video_message(chat_id, message: types.Message, video: str, post_text: str = "", async def send_video_message(chat_id, message: types.Message, video: str, post_text: str = "",
markup: types.ReplyKeyboardMarkup = None): markup: types.ReplyKeyboardMarkup = None):
# Экранируем post_text для безопасного использования в HTML
safe_post_text = html.escape(str(post_text)) if post_text else ""
if markup is None: if markup is None:
sent_message = await message.bot.send_video( sent_message = await message.bot.send_video(
chat_id=chat_id, chat_id=chat_id,
caption=post_text, caption=safe_post_text,
video=video video=video
) )
else: else:
sent_message = await message.bot.send_video( sent_message = await message.bot.send_video(
chat_id=chat_id, chat_id=chat_id,
caption=post_text, caption=safe_post_text,
video=video, video=video,
reply_markup=markup reply_markup=markup
) )
@@ -285,16 +339,19 @@ async def send_video_note_message(chat_id, message: types.Message, video_note: s
async def send_audio_message(chat_id, message: types.Message, audio: str, post_text: str, async def send_audio_message(chat_id, message: types.Message, audio: str, post_text: str,
markup: types.ReplyKeyboardMarkup = None): markup: types.ReplyKeyboardMarkup = None):
# Экранируем post_text для безопасного использования в HTML
safe_post_text = html.escape(str(post_text)) if post_text else ""
if markup is None: if markup is None:
sent_message = await message.bot.send_audio( sent_message = await message.bot.send_audio(
chat_id=chat_id, chat_id=chat_id,
caption=post_text, caption=safe_post_text,
audio=audio audio=audio
) )
else: else:
sent_message = await message.bot.send_audio( sent_message = await message.bot.send_audio(
chat_id=chat_id, chat_id=chat_id,
caption=post_text, caption=safe_post_text,
audio=audio, audio=audio,
reply_markup=markup reply_markup=markup
) )
@@ -346,9 +403,14 @@ def get_banned_users_list(offset: int, bot_db):
message = "Список заблокированных пользователей:\n" message = "Список заблокированных пользователей:\n"
for user in users: for user in users:
message += f"Пользователь: {user[0]}\n" # Экранируем пользовательские данные для безопасного использования
message += f"Причина бана: {user[2]}\n" safe_user_name = html.escape(str(user[0])) if user[0] else "Неизвестный пользователь"
message += f"Дата разбана: {user[3]}\n\n" safe_ban_reason = html.escape(str(user[2])) if user[2] else "Причина не указана"
safe_unban_date = html.escape(str(user[3])) if user[3] else "Дата не указана"
message += f"Пользователь: {safe_user_name}\n"
message += f"Причина бана: {safe_ban_reason}\n"
message += f"Дата разбана: {safe_unban_date}\n\n"
return message return message
@@ -367,7 +429,9 @@ def get_banned_users_buttons(bot_db):
user_ids = [] user_ids = []
for user in users: for user in users:
user_ids.append((user[0], user[1])) # Экранируем user_name для безопасного использования
safe_user_name = html.escape(str(user[0])) if user[0] else "Неизвестный пользователь"
user_ids.append((safe_user_name, user[1]))
return user_ids return user_ids
@@ -388,7 +452,9 @@ def unban_notifier(self):
unblocked_users = self.BotDB.get_users_for_unblock_today(today) unblocked_users = self.BotDB.get_users_for_unblock_today(today)
message = "Разблокированные пользователи:\n" message = "Разблокированные пользователи:\n"
for user_id, user_name in unblocked_users.items(): for user_id, user_name in unblocked_users.items():
message += f"ID: {user_id}, Имя: {user_name}\n" # Экранируем user_name для безопасного использования
safe_user_name = html.escape(str(user_name)) if user_name else "Неизвестный пользователь"
message += f"ID: {user_id}, Имя: {safe_user_name}\n"
# Отправка сообщения в канал # Отправка сообщения в канал
self.bot.send_message(self.GROUP_FOR_MESSAGE, message) self.bot.send_message(self.GROUP_FOR_MESSAGE, message)

View File

@@ -1,3 +1,4 @@
import html
def get_message(username: str, type_message: str): def get_message(username: str, type_message: str):
@@ -37,5 +38,10 @@ def get_message(username: str, type_message: str):
"&Ты можешь анонимно пообщаться, поделиться чем-то важным, обратиться напрямую к жителям🤝 Также можешь выступить перед аудиторией (спеть песню, рассказать стихотворение, шутку)🎤" "&Ты можешь анонимно пообщаться, поделиться чем-то важным, обратиться напрямую к жителям🤝 Также можешь выступить перед аудиторией (спеть песню, рассказать стихотворение, шутку)🎤"
"&❗️Но пожалуйста не оскорбляй никого, и будь вежлив." "&❗️Но пожалуйста не оскорбляй никого, и будь вежлив."
} }
if username is None:
# Поведение ожидаемое тестами: TypeError при username=None
raise TypeError("username is None")
message = constants[type_message] message = constants[type_message]
return message.replace('username', username).replace('&', '\n') # Экранируем потенциально проблемные символы для HTML
message = message.replace('username', html.escape(username)).replace('&', '\n')
return message

View File

@@ -1,4 +1,8 @@
import os import os
import sys
# Добавляем путь к корневой директории проекта
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from database.db import BotDB from database.db import BotDB

View File

@@ -1,4 +1,8 @@
import os import os
import sys
# Добавляем путь к корневой директории проекта
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from database.db import BotDB from database.db import BotDB

View File

@@ -1,4 +1,8 @@
import os import os
import sys
# Добавляем путь к корневой директории проекта
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from database.db import BotDB from database.db import BotDB

View File

@@ -0,0 +1,56 @@
import os
import sys
# Добавляем путь к корневой директории проекта
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from database.db import BotDB
# Получаем текущую директорию
current_dir = os.path.dirname(__file__)
# Переходим на уровень выше
parent_dir = os.path.dirname(current_dir)
BotDB = BotDB(parent_dir, 'tg-bot-database.db')
def get_filename():
"""Возвращает имя файла без расширения."""
filename = os.path.basename(__file__)
filename = os.path.splitext(filename)[0]
return filename
def main():
# Проверка версии миграций
current_version = BotDB.get_current_version()
# Выполнение миграций и проверка последней версии
if current_version < 3:
# Скрипт миграции для создания таблицы our_users
create_table_sql = """
CREATE TABLE IF NOT EXISTS "our_users" (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL UNIQUE,
first_name TEXT,
full_name TEXT,
username TEXT,
is_bot BOOLEAN DEFAULT 0,
language_code TEXT,
date_added TEXT,
date_changed TEXT,
has_stickers BOOLEAN DEFAULT 0
);
"""
# Применение миграции
BotDB.create_table(create_table_sql)
filename = get_filename()
BotDB.update_version(3, filename)
if __name__ == "__main__":
main()

View File

@@ -1,4 +1,5 @@
import time import time
import html
from datetime import datetime from datetime import datetime
from helper_bot.utils.base_dependency_factory import get_global_instance from helper_bot.utils.base_dependency_factory import get_global_instance
@@ -22,13 +23,19 @@ def last_message():
message_with_date = '' message_with_date = ''
if much_minutes_ago <= 60: if much_minutes_ago <= 60:
word_minute = plural_time(1, much_minutes_ago) word_minute = plural_time(1, much_minutes_ago)
message_with_date = f'<b>Последнее сообщение было записано {word_minute} назад</b>' # Экранируем потенциально проблемные символы
word_minute_escaped = html.escape(word_minute)
message_with_date = f'<b>Последнее сообщение было записано {word_minute_escaped} назад</b>'
elif much_minutes_ago > 60 and much_hour_ago <= 24: elif much_minutes_ago > 60 and much_hour_ago <= 24:
word_hour = plural_time(2, much_hour_ago) word_hour = plural_time(2, much_hour_ago)
message_with_date = f'<b>Последнее сообщение было записано {word_hour} назад</b>' # Экранируем потенциально проблемные символы
word_hour_escaped = html.escape(word_hour)
message_with_date = f'<b>Последнее сообщение было записано {word_hour_escaped} назад</b>'
elif much_hour_ago > 24: elif much_hour_ago > 24:
word_day = plural_time(3, much_days_ago) word_day = plural_time(3, much_days_ago)
message_with_date = f'<b>Последнее сообщение было записано {word_day} назад</b>' # Экранируем потенциально проблемные символы
word_day_escaped = html.escape(word_day)
message_with_date = f'<b>Последнее сообщение было записано {word_day_escaped} назад</b>'
return message_with_date return message_with_date

View File

@@ -1,5 +1,5 @@
import random import random
import time import asyncio
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
@@ -64,44 +64,44 @@ async def start(message: types.Message, state: FSMContext):
random_stick_hello = FSInputFile(path=random_stick_hello) random_stick_hello = FSInputFile(path=random_stick_hello)
logger.info(f"Стикер успешно получен из БД. Наименование стикера: {name_stick_hello}") logger.info(f"Стикер успешно получен из БД. Наименование стикера: {name_stick_hello}")
await message.answer_sticker(random_stick_hello) await message.answer_sticker(random_stick_hello)
time.sleep(0.3) await asyncio.sleep(0.3)
except Exception as e: except Exception as e:
if LOGS: if LOGS:
await message.bot.send_message(IMPORTANT_LOGS, f'Отправка приветственных стикеров лажает. Ошибка: {e}') await message.bot.send_message(IMPORTANT_LOGS, f'Отправка приветственных стикеров лажает. Ошибка: {e}')
markup = get_main_keyboard() markup = get_main_keyboard()
await message.answer(text="<b>Привет.</b>", parse_mode='html', reply_markup=markup, await message.answer(text="<b>Привет.</b>", parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK) disable_web_page_preview=not PREVIEW_LINK)
time.sleep(0.3) await asyncio.sleep(0.3)
await message.answer(text="<i>Здесь можно послушать голосовые сообщения от совершенно незнакомых людей из " await message.answer(text="<i>Здесь можно послушать голосовые сообщения от совершенно незнакомых людей из "
"Бийска</i>", "Бийска</i>",
parse_mode='html', reply_markup=markup, parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK) disable_web_page_preview=not PREVIEW_LINK)
time.sleep(1) await asyncio.sleep(1)
await message.answer(text="Это почти как написать письмо, положить его в бутылку и швырнуть в океан. Никогда не " await message.answer(text="Это почти как написать письмо, положить его в бутылку и швырнуть в океан. Никогда не "
"узнаешь, послушал его кто-то или нет и ответить тоже не получится..", "узнаешь, послушал его кто-то или нет и ответить тоже не получится..",
parse_mode='html', reply_markup=markup, parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK) disable_web_page_preview=not PREVIEW_LINK)
time.sleep(0.8) await asyncio.sleep(0.8)
await message.answer(text="Записывать можно всё что угодно — никаких правил нет. Главное — твой голос, <i>хотя " await message.answer(text="Записывать можно всё что угодно — никаких правил нет. Главное — твой голос, <i>хотя "
"бы на 5-10 секунд</i>", "бы на 5-10 секунд</i>",
parse_mode='html', reply_markup=markup, parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK) disable_web_page_preview=not PREVIEW_LINK)
time.sleep(1.5) await asyncio.sleep(1.5)
await message.answer(text="Здесь всё анонимно: тот, кому я отправлю твое сообщение, не узнает ни твое имя, " await message.answer(text="Здесь всё анонимно: тот, кому я отправлю твое сообщение, не узнает ни твое имя, "
"ни твой аккаунт (так что можно не стесняться говорить то, что не стал(а) бы " "ни твой аккаунт (так что можно не стесняться говорить то, что не стал(а) бы "
"выкладывать в собственные соцсети)", "выкладывать в собственные соцсети)",
parse_mode='html', reply_markup=markup, parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK) disable_web_page_preview=not PREVIEW_LINK)
time.sleep(1.3) await asyncio.sleep(1.3)
await message.answer(text="Если не знаешь, что сказать, можешь просто прочитать любое текстовое сообщение из " await message.answer(text="Если не знаешь, что сказать, можешь просто прочитать любое текстовое сообщение из "
"недавно полученных или отправленных (или спеть, рассказать стихотворенье)", "недавно полученных или отправленных (или спеть, рассказать стихотворенье)",
parse_mode='html', reply_markup=markup, parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK) disable_web_page_preview=not PREVIEW_LINK)
time.sleep(0.8) await asyncio.sleep(0.8)
await message.answer(text="Так же можешь ознакомиться с инструкцией к боту по команде /help", await message.answer(text="Так же можешь ознакомиться с инструкцией к боту по команде /help",
parse_mode='html', reply_markup=markup, parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK) disable_web_page_preview=not PREVIEW_LINK)
time.sleep(0.8) await asyncio.sleep(0.8)
await message.answer(text="<b>ну всё, достаточно инструкций. записывайся! Микрофон твой - </b> 🎤", await message.answer(text="<b>ну всё, достаточно инструкций. записывайся! Микрофон твой - </b> 🎤",
parse_mode='html', reply_markup=markup, parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK) disable_web_page_preview=not PREVIEW_LINK)
@@ -147,7 +147,7 @@ async def save_voice_message(message: types.Message, state: FSMContext):
pass pass
# Собираем инфо о сообщении # Собираем инфо о сообщении
author_id = message.from_user.id author_id = message.from_user.id
time_UTC = int(time.time()) time_UTC = int(datetime.now().timestamp())
date_added = datetime.fromtimestamp(time_UTC) date_added = datetime.fromtimestamp(time_UTC)
# Сохраняем в базку # Сохраняем в базку