Добавлена возможность пересылки другого сообщения при обращении к боту.

This commit is contained in:
Kirill Kirilenko 2025-12-29 23:41:37 +03:00
parent 3db3f13cda
commit 2296176b1c
3 changed files with 60 additions and 24 deletions

View file

@ -31,14 +31,27 @@ class ChatContext:
self.messages.pop() self.messages.pop()
@dataclass()
class AiMessage:
user_name: str = None
text: str = None
class AiAgent: class AiAgent:
def __init__(self, api_token: str): def __init__(self, api_token: str):
self.client = OpenRouter(api_key=api_token) self.client = OpenRouter(api_key=api_token)
self.chat_contexts: Dict[int, ChatContext] = {} self.chat_contexts: Dict[int, ChatContext] = {}
async def get_reply(self, chat_id: int, chat_prompt: str, user_name: str, message: str) -> str: async def get_reply(self, chat_id: int, chat_prompt: str,
message: AiMessage, forwarded_message: Optional[AiMessage]) -> str:
message_text = message.text
if forwarded_message:
message_text += '\n<Цитируемое сообщение от {}>\n'.format(forwarded_message.user_name)
message_text += forwarded_message.text + '\n'
message_text += '<Конец цитаты>'
context = self._get_chat_context(chat_id, chat_prompt) context = self._get_chat_context(chat_id, chat_prompt)
context.add_message(role="user", content=f"[{user_name}]: {message}") context.add_message(role="user", content=f"[{message.user_name}]: {message_text}")
try: try:
# Get response from OpenRouter # Get response from OpenRouter

View file

@ -6,7 +6,7 @@ from aiogram.types.user import User
from aiogram.enums.content_type import ContentType from aiogram.enums.content_type import ContentType
import utils import utils
from ai_agent import AiAgent from ai_agent import AiAgent, AiMessage
import tg.tg_database as database import tg.tg_database as database
router = Router() router = Router()
@ -34,6 +34,17 @@ ACCEPTED_CONTENT_TYPES: list[ContentType] = [
] ]
async def get_user_name_for_ai(user: User):
if user.first_name and user.last_name:
return "{} {}".format(user.first_name, user.last_name)
elif user.first_name:
return user.first_name
elif user.username:
return user.username
else:
return str(user.id)
@router.message(F.content_type.in_(ACCEPTED_CONTENT_TYPES)) @router.message(F.content_type.in_(ACCEPTED_CONTENT_TYPES))
async def any_message_handler(message: Message): async def any_message_handler(message: Message):
chat_id = message.chat.id chat_id = message.chat.id
@ -54,26 +65,26 @@ async def any_message_handler(message: Message):
if bot_user is None: if bot_user is None:
bot_user = await message.bot.get_me() bot_user = await message.bot.get_me()
ai_message = AiMessage()
ai_fwd_message: Optional[AiMessage] = None
# Ответ на сообщение бота # Ответ на сообщение бота
if message.reply_to_message and message.reply_to_message.from_user.id == bot_user.id: if message.reply_to_message and message.reply_to_message.from_user.id == bot_user.id:
message_text = message.text ai_message.text = message.text
else: else:
# Сообщение содержит @bot_username # Сообщение содержит @bot_username
bot_username_mention = '@' + bot_user.username bot_username_mention = '@' + bot_user.username
if message.content_type == ContentType.TEXT and message.text.find(bot_username_mention) != -1: if message.content_type == ContentType.TEXT and message.text.find(bot_username_mention) != -1:
message_text = message.text.replace(bot_username_mention, bot_user.first_name) ai_message.text = message.text.replace(bot_username_mention, bot_user.first_name)
else: else:
return return
if message.from_user.first_name and message.from_user.last_name: if message.reply_to_message and message.reply_to_message.content_type == ContentType.TEXT:
user_name = "{} {}".format(message.from_user.first_name, message.from_user.last_name) ai_fwd_message = (
elif message.from_user.first_name: AiMessage(user_name=await get_user_name_for_ai(message.reply_to_message.from_user),
user_name = message.from_user.first_name text=message.reply_to_message.text))
elif message.from_user.username:
user_name = message.from_user.username
else:
user_name = str(message.from_user.id)
ai_message.user_name = await get_user_name_for_ai(message.from_user)
chat_prompt = chat['ai_prompt'] chat_prompt = chat['ai_prompt']
global agent global agent
@ -82,7 +93,7 @@ async def any_message_handler(message: Message):
agent = AiAgent(message.bot.config['openrouter_token']) agent = AiAgent(message.bot.config['openrouter_token'])
await message.bot.send_chat_action(chat_id, 'typing') await message.bot.send_chat_action(chat_id, 'typing')
await message.reply(await agent.get_reply(chat_id, chat_prompt, user_name, message_text)) await message.reply(await agent.get_reply(chat_id, chat_prompt, ai_message, ai_fwd_message))
def clear_ai_chat_context(chat_id: int): def clear_ai_chat_context(chat_id: int):

View file

@ -1,12 +1,13 @@
import re import re
from typing import Optional from typing import Optional
from vkbottle import API
from vkbottle.bot import Message from vkbottle.bot import Message
from vkbottle.framework.labeler import BotLabeler from vkbottle.framework.labeler import BotLabeler
from vkbottle_types.codegen.objects import GroupsGroup from vkbottle_types.codegen.objects import GroupsGroup
import utils import utils
from ai_agent import AiAgent from ai_agent import AiAgent, AiMessage
import vk.vk_database as database import vk.vk_database as database
labeler = BotLabeler() labeler = BotLabeler()
@ -15,6 +16,14 @@ agent: Optional[AiAgent] = None
bot_user: Optional[GroupsGroup] = None bot_user: Optional[GroupsGroup] = None
async def get_user_name_for_ai(api: API, user_id: int):
user = await api.users.get(user_ids=[user_id])
if len(user) == 1:
return "{} {}".format(user[0].first_name, user[0].last_name)
else:
return '@id' + str(user_id)
# Обычные сообщения (не команды и не действия) # Обычные сообщения (не команды и не действия)
@labeler.chat_message() @labeler.chat_message()
async def any_message_handler(message: Message): async def any_message_handler(message: Message):
@ -40,28 +49,31 @@ async def any_message_handler(message: Message):
if bot_user is None: if bot_user is None:
bot_user = (await message.ctx_api.groups.get_by_id()).groups[0] bot_user = (await message.ctx_api.groups.get_by_id()).groups[0]
ai_message = AiMessage()
ai_fwd_message: Optional[AiMessage] = None
# Ответ на сообщение бота # Ответ на сообщение бота
if message.reply_message and message.reply_message.from_id == -bot_user.id: if message.reply_message and message.reply_message.from_id == -bot_user.id:
message_text = message.text ai_message.text = message.text
else: else:
# Сообщение содержит @bot_username # Сообщение содержит @bot_username
bot_username_mention = '@' + bot_user.screen_name bot_username_mention = '@' + bot_user.screen_name
if message.text is not None and message.text.find(bot_username_mention) != -1: if message.text is not None and message.text.find(bot_username_mention) != -1:
message_text = message.text.replace(bot_username_mention, bot_user.name) ai_message.text = message.text.replace(bot_username_mention, bot_user.name)
else: else:
# Сообщение содержит [club<bot_id>|<some_name>] # Сообщение содержит [club<bot_id>|<some_name>]
pattern = r"\[club" + str(bot_user.id) + r"\|(.+)]" pattern = r"\[club" + str(bot_user.id) + r"\|(.+)]"
if message.text is not None and re.search(pattern, message.text) is not None: if message.text is not None and re.search(pattern, message.text) is not None:
message_text = re.sub(pattern, r'\1', message.text) ai_message.text = re.sub(pattern, r'\1', message.text)
else: else:
return return
user = await message.ctx_api.users.get(user_ids=[message.from_id]) if message.reply_message and message.reply_message.text is not None:
if len(user) == 1: ai_fwd_message = (
user_name = "{} {}".format(user[0].first_name, user[0].last_name) AiMessage(user_name=await get_user_name_for_ai(message.ctx_api, message.reply_message.from_id),
else: text=message.reply_message.text))
user_name = '@id' + str(message.from_id)
ai_message.user_name = await get_user_name_for_ai(message.ctx_api, message.from_id)
chat_prompt = chat['ai_prompt'] chat_prompt = chat['ai_prompt']
global agent global agent
@ -70,7 +82,7 @@ async def any_message_handler(message: Message):
agent = AiAgent(message.ctx_api.config['openrouter_token']) agent = AiAgent(message.ctx_api.config['openrouter_token'])
await message.ctx_api.messages.set_activity(peer_id=chat_id, type='typing') await message.ctx_api.messages.set_activity(peer_id=chat_id, type='typing')
await message.reply(await agent.get_reply(chat_id, chat_prompt, user_name, message_text)) await message.reply(await agent.get_reply(chat_id, chat_prompt, ai_message, ai_fwd_message))
def clear_ai_chat_context(chat_id: int): def clear_ai_chat_context(chat_id: int):