Compare commits
No commits in common. "651f755b2d6e6cb80d0bf6e0624373819b108fe1" and "d8338b035147b49716094bded293c0090806b712" have entirely different histories.
651f755b2d
...
d8338b0351
10 changed files with 6 additions and 229 deletions
|
|
@ -13,7 +13,6 @@ async def main() -> None:
|
||||||
print('Конфигурация загружена.')
|
print('Конфигурация загружена.')
|
||||||
|
|
||||||
bot = Bot(token=config['api_token'])
|
bot = Bot(token=config['api_token'])
|
||||||
bot.config = config
|
|
||||||
|
|
||||||
dp = Dispatcher()
|
dp = Dispatcher()
|
||||||
dp.include_router(handlers.router)
|
dp.include_router(handlers.router)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ async def user_join_handler(message: Message):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
chat = database.DB.create_chat_if_not_exists(chat_id)
|
chat = database.DB.create_chat_if_not_exists(chat_id)
|
||||||
if chat['active'] == 0:
|
if chat['active'] == 0:
|
||||||
|
await message.answer(MESSAGE_CHAT_NOT_ACTIVE)
|
||||||
return
|
return
|
||||||
|
|
||||||
for member in message.new_chat_members:
|
for member in message.new_chat_members:
|
||||||
|
|
@ -29,6 +30,7 @@ async def user_join_handler(message: Message):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
chat = database.DB.create_chat_if_not_exists(chat_id)
|
chat = database.DB.create_chat_if_not_exists(chat_id)
|
||||||
if chat['active'] == 0:
|
if chat['active'] == 0:
|
||||||
|
await message.answer(MESSAGE_CHAT_NOT_ACTIVE)
|
||||||
return
|
return
|
||||||
|
|
||||||
member = message.left_chat_member
|
member = message.left_chat_member
|
||||||
|
|
|
||||||
|
|
@ -95,9 +95,9 @@ async def warning_handler(message: Message, bot: Bot):
|
||||||
user = database.DB.get_user(chat_id, user_id)
|
user = database.DB.get_user(chat_id, user_id)
|
||||||
|
|
||||||
user_info = message.reply_to_message.from_user
|
user_info = message.reply_to_message.from_user
|
||||||
# TODO: родительный падеж имени и фамилии, если возможно
|
await message.answer('У {} {} {} {}.'.format(
|
||||||
await message.answer('У {} {} {}.'.format(
|
user_info.first_name,
|
||||||
utils.full_name(user_info.first_name, user_info.last_name),
|
user_info.last_name,
|
||||||
user['warnings'],
|
user['warnings'],
|
||||||
utils.make_word_agree_with_number(user['warnings'], 'предупреждение'))
|
utils.make_word_agree_with_number(user['warnings'], 'предупреждение'))
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,112 +1,12 @@
|
||||||
from typing import Dict, List, Optional
|
|
||||||
|
|
||||||
from aiogram import Router, F
|
from aiogram import Router, F
|
||||||
from aiogram.types import Message
|
from aiogram.types import Message
|
||||||
from aiogram.types.user import User
|
|
||||||
from aiogram.enums.content_type import ContentType
|
from aiogram.enums.content_type import ContentType
|
||||||
|
|
||||||
from openrouter import OpenRouter
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
import utils
|
import utils
|
||||||
import tg.tg_database as database
|
import tg.tg_database as database
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ChatContext:
|
|
||||||
def __init__(self, max_messages: int):
|
|
||||||
self.max_messages: int = max_messages
|
|
||||||
self.messages: List[Dict[str, str]] = []
|
|
||||||
|
|
||||||
def add_message(self, role: str, content: str):
|
|
||||||
if len(self.messages) == self.max_messages:
|
|
||||||
# Всегда сохраняем в контексте системное сообщение
|
|
||||||
self.messages.pop(1)
|
|
||||||
self.messages.append({"role": role, "content": content})
|
|
||||||
|
|
||||||
def get_messages_for_api(self) -> List[Dict[str, str]]:
|
|
||||||
return self.messages
|
|
||||||
|
|
||||||
|
|
||||||
chat_contexts: Dict[int, ChatContext] = {}
|
|
||||||
bot_user: Optional[User] = None
|
|
||||||
|
|
||||||
system_prompt = """
|
|
||||||
Ты - помощник в групповом чате Telegram.
|
|
||||||
Отвечай на вопросы и поддерживай контекст беседы.
|
|
||||||
Ты не можешь обсуждать политику и религию.
|
|
||||||
Сообщения пользователей будут приходить в следующем формате: '[Имя]: текст сообщения'
|
|
||||||
При ответе НЕ нужно указывать пользователя, которому он предназначен.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def get_ai_chat_context(chat_id: int) -> ChatContext:
|
|
||||||
"""Get or create chat context for a specific chat"""
|
|
||||||
if chat_id not in chat_contexts:
|
|
||||||
chat_contexts[chat_id] = ChatContext(10)
|
|
||||||
chat = database.DB.get_chat(chat_id)
|
|
||||||
prompt = system_prompt
|
|
||||||
if chat['ai_prompt'] is not None:
|
|
||||||
prompt += '\n\n' + chat['ai_prompt']
|
|
||||||
chat_contexts[chat_id].add_message(role="system", content=prompt)
|
|
||||||
return chat_contexts[chat_id]
|
|
||||||
|
|
||||||
|
|
||||||
async def ai_message_handler(message: Message):
|
|
||||||
chat_id = message.chat.id
|
|
||||||
|
|
||||||
# Extract user information and message content
|
|
||||||
if message.from_user.first_name and message.from_user.last_name:
|
|
||||||
user_name = "{} {}".format(message.from_user.first_name, message.from_user.last_name)
|
|
||||||
elif message.from_user.first_name:
|
|
||||||
user_name = message.from_user.first_name
|
|
||||||
elif message.from_user.username:
|
|
||||||
user_name = message.from_user.username
|
|
||||||
else:
|
|
||||||
user_name = str(message.from_user.id)
|
|
||||||
|
|
||||||
bot_mention = '@' + bot_user.username
|
|
||||||
message_text = message.text.replace(bot_mention, bot_user.first_name)
|
|
||||||
|
|
||||||
context = get_ai_chat_context(chat_id)
|
|
||||||
context.add_message(
|
|
||||||
role="user",
|
|
||||||
content=f"[{user_name}]: {message_text}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# noinspection PyUnresolvedReferences
|
|
||||||
api_key = message.bot.config['openrouter_token']
|
|
||||||
|
|
||||||
client = OpenRouter(api_key=api_key)
|
|
||||||
messages_for_api = context.get_messages_for_api()
|
|
||||||
|
|
||||||
await message.bot.send_chat_action(chat_id, 'typing')
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Get response from OpenRouter
|
|
||||||
response = await client.chat.send_async(
|
|
||||||
model="meta-llama/llama-3.3-70b-instruct:free",
|
|
||||||
messages=messages_for_api,
|
|
||||||
max_tokens=500,
|
|
||||||
temperature=0.7
|
|
||||||
)
|
|
||||||
|
|
||||||
# Extract AI response
|
|
||||||
ai_response = response.choices[0].message.content
|
|
||||||
|
|
||||||
# Add AI response to context
|
|
||||||
context.add_message(role="assistant", content=ai_response)
|
|
||||||
|
|
||||||
# Send response back to chat
|
|
||||||
await message.reply(ai_response)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error processing message: {e}")
|
|
||||||
await message.reply("Извините, при обработке запроса произошла ошибка.")
|
|
||||||
|
|
||||||
|
|
||||||
ACCEPTED_CONTENT_TYPES: list[ContentType] = [
|
ACCEPTED_CONTENT_TYPES: list[ContentType] = [
|
||||||
ContentType.TEXT,
|
ContentType.TEXT,
|
||||||
ContentType.ANIMATION,
|
ContentType.ANIMATION,
|
||||||
|
|
@ -142,11 +42,3 @@ async def any_message_handler(message: Message):
|
||||||
database.DB.create_user_if_not_exists(chat_id, user_id)
|
database.DB.create_user_if_not_exists(chat_id, user_id)
|
||||||
database.DB.user_set_last_message(chat_id, user_id, utils.posix_time())
|
database.DB.user_set_last_message(chat_id, user_id, utils.posix_time())
|
||||||
database.DB.user_increment_messages(chat_id, user_id)
|
database.DB.user_increment_messages(chat_id, user_id)
|
||||||
|
|
||||||
global bot_user
|
|
||||||
if bot_user is None:
|
|
||||||
bot_user = await message.bot.get_me()
|
|
||||||
|
|
||||||
bot_mention = '@' + bot_user.username
|
|
||||||
if message.content_type == ContentType.TEXT and message.text.find(bot_mention) != -1:
|
|
||||||
await ai_message_handler(message)
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ from aiogram import Bot, Router, F
|
||||||
from aiogram.types import Message
|
from aiogram.types import Message
|
||||||
from aiogram.utils.formatting import Bold, Italic
|
from aiogram.utils.formatting import Bold, Italic
|
||||||
|
|
||||||
import utils
|
|
||||||
from messages import *
|
from messages import *
|
||||||
import tg.tg_database as database
|
import tg.tg_database as database
|
||||||
|
|
||||||
|
|
@ -17,7 +16,7 @@ async def format_rating(chat_id: int, top_users: List[Any], bot: Bot) -> str:
|
||||||
i = 1
|
i = 1
|
||||||
for user in top_users:
|
for user in top_users:
|
||||||
info = await bot.get_chat_member(chat_id, user['user_id'])
|
info = await bot.get_chat_member(chat_id, user['user_id'])
|
||||||
result += '{}. {} - {}\n'.format(i, utils.full_name(info.user.first_name, info.user.last_name), user['value'])
|
result += '{}. {} {} - {}\n'.format(i, info.user.first_name, info.user.last_name, user['value'])
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ class TgDatabase(database.BasicDatabase):
|
||||||
"active" INTEGER NOT NULL DEFAULT 0,
|
"active" INTEGER NOT NULL DEFAULT 0,
|
||||||
"rules" TEXT,
|
"rules" TEXT,
|
||||||
"greeting_join" TEXT,
|
"greeting_join" TEXT,
|
||||||
"ai_prompt" TEXT,
|
|
||||||
PRIMARY KEY("id"))
|
PRIMARY KEY("id"))
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
|
||||||
8
utils.py
8
utils.py
|
|
@ -1,6 +1,4 @@
|
||||||
from calendar import timegm
|
from calendar import timegm
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from pymorphy3 import MorphAnalyzer
|
from pymorphy3 import MorphAnalyzer
|
||||||
from time import gmtime
|
from time import gmtime
|
||||||
|
|
||||||
|
|
@ -15,9 +13,3 @@ def make_word_agree_with_number(n: int, word: str) -> str:
|
||||||
|
|
||||||
def posix_time():
|
def posix_time():
|
||||||
return timegm(gmtime())
|
return timegm(gmtime())
|
||||||
|
|
||||||
|
|
||||||
def full_name(first_name: str, last_name: Optional[str]) -> str:
|
|
||||||
if last_name is not None:
|
|
||||||
return f"{first_name} {last_name}"
|
|
||||||
return first_name
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ if __name__ == '__main__':
|
||||||
print('Конфигурация загружена.')
|
print('Конфигурация загружена.')
|
||||||
|
|
||||||
bot = VkBot(config['api_token'], labeler=handlers.labeler)
|
bot = VkBot(config['api_token'], labeler=handlers.labeler)
|
||||||
bot.api.config = config
|
|
||||||
bot.loop_wrapper.on_startup.append(tasks.startup_task(bot.api))
|
bot.loop_wrapper.on_startup.append(tasks.startup_task(bot.api))
|
||||||
bot.loop_wrapper.add_task(tasks.daily_maintenance_task(bot.api))
|
bot.loop_wrapper.add_task(tasks.daily_maintenance_task(bot.api))
|
||||||
bot.run_forever()
|
bot.run_forever()
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
from typing import Dict, List, Optional
|
|
||||||
|
|
||||||
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 openrouter import OpenRouter
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
import utils
|
import utils
|
||||||
import vk.vk_database as database
|
import vk.vk_database as database
|
||||||
|
|
@ -13,96 +7,6 @@ import vk.vk_database as database
|
||||||
labeler = BotLabeler()
|
labeler = BotLabeler()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ChatContext:
|
|
||||||
def __init__(self, max_messages: int):
|
|
||||||
self.max_messages: int = max_messages
|
|
||||||
self.messages: List[Dict[str, str]] = []
|
|
||||||
|
|
||||||
def add_message(self, role: str, content: str):
|
|
||||||
if len(self.messages) == self.max_messages:
|
|
||||||
# Всегда сохраняем в контексте системное сообщение
|
|
||||||
self.messages.pop(1)
|
|
||||||
self.messages.append({"role": role, "content": content})
|
|
||||||
|
|
||||||
def get_messages_for_api(self) -> List[Dict[str, str]]:
|
|
||||||
return self.messages
|
|
||||||
|
|
||||||
|
|
||||||
chat_contexts: Dict[int, ChatContext] = {}
|
|
||||||
bot_user: Optional[GroupsGroup] = None
|
|
||||||
|
|
||||||
system_prompt = """
|
|
||||||
Ты - помощник в групповом чате Telegram.
|
|
||||||
Отвечай на вопросы и поддерживай контекст беседы.
|
|
||||||
Ты не можешь обсуждать политику и религию.
|
|
||||||
Сообщения пользователей будут приходить в следующем формате: '[Имя]: текст сообщения'
|
|
||||||
При ответе НЕ нужно указывать пользователя, которому он предназначен.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def get_ai_chat_context(chat_id: int) -> ChatContext:
|
|
||||||
"""Get or create chat context for a specific chat"""
|
|
||||||
if chat_id not in chat_contexts:
|
|
||||||
chat_contexts[chat_id] = ChatContext(10)
|
|
||||||
chat = database.DB.get_chat(chat_id)
|
|
||||||
prompt = system_prompt
|
|
||||||
if chat['ai_prompt'] is not None:
|
|
||||||
prompt += '\n\n' + chat['ai_prompt']
|
|
||||||
chat_contexts[chat_id].add_message(role="system", content=prompt)
|
|
||||||
return chat_contexts[chat_id]
|
|
||||||
|
|
||||||
|
|
||||||
async def ai_message_handler(message: Message):
|
|
||||||
chat_id = message.peer_id
|
|
||||||
|
|
||||||
# Extract user information and message content
|
|
||||||
user = await message.ctx_api.users.get(user_ids=[message.from_id])
|
|
||||||
if len(user) == 1:
|
|
||||||
user_name = "{} {}".format(user[0].first_name, user[0].last_name)
|
|
||||||
else:
|
|
||||||
user_name = '@id' + str(message.from_id)
|
|
||||||
|
|
||||||
bot_mention = '@' + bot_user.screen_name
|
|
||||||
message_text = message.text.replace(bot_mention, bot_user.name)
|
|
||||||
|
|
||||||
context = get_ai_chat_context(chat_id)
|
|
||||||
context.add_message(
|
|
||||||
role="user",
|
|
||||||
content=f"[{user_name}]: {message_text}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# noinspection PyUnresolvedReferences
|
|
||||||
api_key = message.ctx_api.config['openrouter_token']
|
|
||||||
|
|
||||||
client = OpenRouter(api_key=api_key)
|
|
||||||
messages_for_api = context.get_messages_for_api()
|
|
||||||
|
|
||||||
await message.ctx_api.messages.set_activity(peer_id=chat_id, type='typing')
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Get response from OpenRouter
|
|
||||||
response = await client.chat.send_async(
|
|
||||||
model="meta-llama/llama-3.3-70b-instruct:free",
|
|
||||||
messages=messages_for_api,
|
|
||||||
max_tokens=500,
|
|
||||||
temperature=0.7
|
|
||||||
)
|
|
||||||
|
|
||||||
# Extract AI response
|
|
||||||
ai_response = response.choices[0].message.content
|
|
||||||
|
|
||||||
# Add AI response to context
|
|
||||||
context.add_message(role="assistant", content=ai_response)
|
|
||||||
|
|
||||||
# Send response back to chat
|
|
||||||
await message.reply(ai_response)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error processing message: {e}")
|
|
||||||
await message.reply("Извините, при обработке запроса произошла ошибка.")
|
|
||||||
|
|
||||||
|
|
||||||
# Обычные сообщения (не команды и не действия)
|
# Обычные сообщения (не команды и не действия)
|
||||||
@labeler.chat_message()
|
@labeler.chat_message()
|
||||||
async def any_message_handler(message: Message):
|
async def any_message_handler(message: Message):
|
||||||
|
|
@ -123,11 +27,3 @@ async def any_message_handler(message: Message):
|
||||||
database.DB.create_user_if_not_exists(chat_id, user_id)
|
database.DB.create_user_if_not_exists(chat_id, user_id)
|
||||||
database.DB.user_set_last_message(chat_id, user_id, utils.posix_time())
|
database.DB.user_set_last_message(chat_id, user_id, utils.posix_time())
|
||||||
database.DB.user_increment_messages(chat_id, user_id)
|
database.DB.user_increment_messages(chat_id, user_id)
|
||||||
|
|
||||||
global bot_user
|
|
||||||
if bot_user is None:
|
|
||||||
bot_user = (await message.ctx_api.groups.get_by_id()).groups[0]
|
|
||||||
|
|
||||||
bot_mention = '@' + bot_user.screen_name
|
|
||||||
if message.text is not None and message.text.find(bot_mention) != -1:
|
|
||||||
await ai_message_handler(message)
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ class VkDatabase(database.BasicDatabase):
|
||||||
"greeting_join" TEXT,
|
"greeting_join" TEXT,
|
||||||
"greeting_rejoin" TEXT,
|
"greeting_rejoin" TEXT,
|
||||||
"birthday_message" TEXT,
|
"birthday_message" TEXT,
|
||||||
"ai_prompt" TEXT,
|
|
||||||
PRIMARY KEY("id"))
|
PRIMARY KEY("id"))
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue