Merge remote-tracking branch 'origin/voice-1' into merge-voice-1

This commit was merged in pull request #6.
This commit is contained in:
2025-08-26 18:27:20 +03:00
12 changed files with 1676 additions and 1307 deletions

View File

View File

@@ -0,0 +1,68 @@
import time
from datetime import datetime
from pathlib import Path
from aiogram import Router, F
from aiogram.types import CallbackQuery
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
callback_router = Router()
bdf = BaseDependencyFactory()
GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs']
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs']
PREVIEW_LINK = bdf.settings['Telegram']['preview_link']
LOGS = bdf.settings['Settings']['logs']
TEST = bdf.settings['Settings']['test']
BotDB = bdf.get_db()
@callback_router.callback_query(
F.data == "save"
)
async def save_voice_message(call: CallbackQuery):
file_name = ''
file_id = 1
user_id = BotDB.get_user_id_by_message_id_for_voice_bot(call.message.message_id)
# Проверяем что запись о файле есть в базе данных
is_having_audio_from_user = BotDB.get_last_user_audio_record(user_id=user_id)
if is_having_audio_from_user is False:
# Если нет, то генерируем имя файла
file_name = f'message_from_{user_id}_number_{file_id}'
else:
# Иначе берем последнюю запись из БД, добавляем к ней 1, и создаем новую запись
file_name = BotDB.get_path_for_audio_record(user_id=user_id)
file_id = BotDB.get_id_for_audio_record(user_id) + 1
path = Path(f'voice_users/{file_name}.ogg')
if path.exists():
file_name = f'message_from_{user_id}_number_{file_id}'
else:
pass
# Собираем инфо о сообщении
time_UTC = int(time.time())
date_added = datetime.fromtimestamp(time_UTC)
# Сохраняем в базку
BotDB.add_audio_record(file_name, user_id, date_added, 0, file_id)
file_info = await call.message.bot.get_file(file_id=call.message.voice.file_id)
downloaded_file = await call.message.bot.download_file(file_path=file_info.file_path)
with open(f'voice_users/{file_name}.ogg', 'wb') as new_file:
new_file.write(downloaded_file.read())
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
await call.answer(text='Сохранено!', cache_time=3)
@callback_router.callback_query(
F.data == "delete"
)
async def delete_voice_message(call: CallbackQuery):
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
await call.answer(text='Удалено!', cache_time=3)

View File

@@ -0,0 +1,236 @@
import random
import asyncio
from datetime import datetime
from pathlib import Path
from aiogram import Router, types, F
from aiogram.filters import Command, StateFilter
from aiogram.fsm.context import FSMContext
from aiogram.types import FSInputFile
from helper_bot.filters.main import ChatTypeFilter
from helper_bot.middlewares.blacklist_middleware import BlacklistMiddleware
from helper_bot.utils.base_dependency_factory import get_global_instance
from helper_bot.utils.helper_func import update_user_info, check_user_emoji, send_voice_message
from logs.custom_logger import logger
from voice_bot.keyboards.keyboards import get_main_keyboard, get_reply_keyboard_for_voice
from voice_bot.utils.helper_func import last_message
voice_router = Router()
bdf = get_global_instance()
GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs']
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs']
PREVIEW_LINK = bdf.settings['Telegram']['preview_link']
LOGS = bdf.settings['Settings']['logs']
TEST = bdf.settings['Settings']['test']
BotDB = bdf.get_db()
voice_router.message.middleware(BlacklistMiddleware())
@voice_router.message(
ChatTypeFilter(chat_type=["private"]),
Command("restart")
)
async def restart_function(message: types.Message, state: FSMContext):
await message.forward(chat_id=GROUP_FOR_LOGS)
await update_user_info('voice', message)
check_user_emoji(message)
markup = get_main_keyboard()
await message.answer(text='Я перезапущен!',
reply_markup=markup)
await state.set_state('START')
@voice_router.message(
ChatTypeFilter(chat_type=["private"]),
Command("emoji")
)
async def handle_emoji_message(message: types.Message, state: FSMContext):
await message.forward(chat_id=GROUP_FOR_LOGS)
user_emoji = check_user_emoji(message)
await state.set_state("START")
if user_emoji is not None:
await message.answer(f'Твоя эмодзя - {user_emoji}', parse_mode='HTML')
@voice_router.message(
ChatTypeFilter(chat_type=["private"]),
Command("help")
)
async def help_function(message: types.Message, state: FSMContext):
await message.forward(chat_id=GROUP_FOR_LOGS)
await update_user_info('voice', message)
await message.answer(
text='Скорее всего ответы на твои вопросы есть здесь, ознакомься: https://telegra.ph/Instrukciya-k-botu-Golosa-Bijsk-10-11-2'
'\nЕсли это не поможет, пиши в личку: @Kerrad1', disable_web_page_preview=not PREVIEW_LINK)
await state.set_state('START')
@voice_router.message(
ChatTypeFilter(chat_type=["private"]),
Command("start")
)
async def start(message: types.Message, state: FSMContext):
await state.set_state("START")
await message.forward(chat_id=GROUP_FOR_LOGS)
await update_user_info('voice', message)
user_emoji = check_user_emoji(message)
try:
name_stick_hello = list(Path('Stick').rglob('Hello_*'))
random_stick_hello = random.choice(name_stick_hello)
random_stick_hello = FSInputFile(path=random_stick_hello)
logger.info(f"Стикер успешно получен из БД. Наименование стикера: {name_stick_hello}")
await message.answer_sticker(random_stick_hello)
await asyncio.sleep(0.3)
except Exception as e:
if LOGS:
await message.bot.send_message(IMPORTANT_LOGS, f'Отправка приветственных стикеров лажает. Ошибка: {e}')
markup = get_main_keyboard()
await message.answer(text="<b>Привет.</b>", parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK)
await asyncio.sleep(0.3)
await message.answer(text="<i>Здесь можно послушать голосовые сообщения от совершенно незнакомых людей из "
"Бийска</i>",
parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK)
await asyncio.sleep(1)
await message.answer(text="Это почти как написать письмо, положить его в бутылку и швырнуть в океан. Никогда не "
"узнаешь, послушал его кто-то или нет и ответить тоже не получится..",
parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK)
await asyncio.sleep(0.8)
await message.answer(text="Записывать можно всё что угодно — никаких правил нет. Главное — твой голос, <i>хотя "
"бы на 5-10 секунд</i>",
parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK)
await asyncio.sleep(1.5)
await message.answer(text="Здесь всё анонимно: тот, кому я отправлю твое сообщение, не узнает ни твое имя, "
"ни твой аккаунт (так что можно не стесняться говорить то, что не стал(а) бы "
"выкладывать в собственные соцсети)",
parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK)
await asyncio.sleep(1.3)
await message.answer(text="Если не знаешь, что сказать, можешь просто прочитать любое текстовое сообщение из "
"недавно полученных или отправленных (или спеть, рассказать стихотворенье)",
parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK)
await asyncio.sleep(0.8)
await message.answer(text=f"Любые войсы будут помечены эмоджи. <b>Твой эмоджи - </b>{user_emoji}"
f"Таким эмоджи будут помечены твои сообщения для других "
f"Но другие люди не узнают кто за каким эмоджи скрывается:)",
parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK)
await asyncio.sleep(0.8)
await message.answer(text="Так же можешь ознакомиться с инструкцией к боту по команде /help",
parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK)
await asyncio.sleep(0.8)
await message.answer(text="<b>Ну всё, достаточно инструкций. записывайся! Микрофон твой - </b> 🎤",
parse_mode='html', reply_markup=markup,
disable_web_page_preview=not PREVIEW_LINK)
@voice_router.message(
ChatTypeFilter(chat_type=["private"]),
Command("refresh")
)
async def refresh_listen_function(message: types.Message, state: FSMContext):
await message.forward(chat_id=GROUP_FOR_LOGS)
await update_user_info('voice', message)
markup = get_main_keyboard()
BotDB.delete_listen_count_for_user(message.from_user.id)
await message.answer(
text='Прослушивания очищены. Можешь начать слушать заново🤗', disable_web_page_preview=not PREVIEW_LINK,
markup=markup)
await state.set_state('START')
@voice_router.message(
StateFilter("START"),
ChatTypeFilter(chat_type=["private"]),
F.text == '🎤Высказаться'
)
async def standup_write(message: types.Message, state: FSMContext):
await message.forward(chat_id=GROUP_FOR_LOGS)
markup = types.ReplyKeyboardRemove()
await message.answer(text='Хорошо, теперь пришли мне свое голосовое сообщение', reply_markup=markup)
try:
message_with_date = last_message()
await message.answer(text=message_with_date, parse_mode="html")
except Exception as e:
logger.error(f'Не удалось получить дату последнего сообщения - {e}')
await state.set_state('STANDUP_WRITE')
@voice_router.message(
StateFilter("STANDUP_WRITE"),
ChatTypeFilter(chat_type=["private"]),
)
async def suggest_voice(message: types.Message, state: FSMContext):
logger.info(
f"Вызов функции suggest_voice. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}")
await message.forward(chat_id=GROUP_FOR_LOGS)
markup = get_main_keyboard()
if message.content_type == 'voice':
markup_for_voice = get_reply_keyboard_for_voice()
# Отправляем аудио в приватный канал
sent_message = await send_voice_message(GROUP_FOR_POST, message,
message.voice.file_id, markup_for_voice)
# Сохраняем в базу инфо о посте
BotDB.set_user_id_and_message_id_for_voice_bot(sent_message.message_id, message.from_user.id)
# Отправляем юзеру ответ и возвращаем его в меню
await message.answer(text='Окей, сохранил!👌', reply_markup=markup)
await state.set_state('START')
else:
# TODO: Если пришлют фото, он не работает
await message.forward(chat_id=GROUP_FOR_LOGS)
await message.answer(text='Я тебя не понимаю🤷‍♀️ запиши голосовое', reply_markup=markup)
await state.set_state('STANDUP_WRITE')
@voice_router.message(
StateFilter("START"),
ChatTypeFilter(chat_type=["private"]),
F.text == '🎧Послушать'
)
async def standup_listen_audio(message: types.Message):
check_audio = BotDB.check_listen_audio(user_id=message.from_user.id)
list_audio = list(check_audio)
markup = get_main_keyboard()
if not list_audio:
await message.answer(text='Прости, ты прослушал все аудио😔. Возвращайся позже, возможно наша база пополнится',
reply_markup=markup)
try:
message_with_date = last_message()
await message.answer(text=message_with_date, parse_mode="html")
except Exception as e:
logger.error(f'Не удалось получить последнюю дату {e}')
else:
# Получаем ссылку на аудио сообщение пользователя
number_element = random.randint(0, len(list_audio) - 1)
audio_for_user = check_audio[number_element]
# Получаем автора записи + эмодзи по нему
user_id = BotDB.get_user_id_by_file_name(audio_for_user)
date_added = BotDB.get_date_by_file_name(audio_for_user)
user_emoji = BotDB.check_emoji_for_user(user_id)
path = Path(f'voice_users/{audio_for_user}.ogg')
voice = FSInputFile(path)
# Маркируем сообщение как прослушанное
BotDB.mark_listened_audio(audio_for_user, user_id=message.from_user.id)
# Формируем подпись
if user_emoji:
caption = f'{user_emoji}\nДата записи: {date_added}'
else:
caption = f'Дата записи: {date_added}'
await message.bot.send_voice(chat_id=message.chat.id, voice=voice, caption=caption, reply_markup=markup)
await message.answer(text=f'Осталось непрослушанных: <b>{len(check_audio) - 1}</b>', reply_markup=markup)