nahj_rag/bale_leaderbot.py
2026-02-17 16:52:37 +00:00

307 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
# from fastapi import FastAPI, Request
from pydantic import BaseModel
import requests
import logging
# import uvicorn
import time
import chatbot_khamenei as leader_chat
import query_pipeline
# ===========================
# پیکربندی اولیه
# ===========================
# TOKEN = '2052165365:Tt7u2qXB9oRTPISeZ0wmoAGpPsIgjq-vAxM'
# TOKEN = '1162038067:qK9yIm9wxRNkrMHTiCENtnUlpd47g8zsE7w'
TOKEN = "1162038067:qK9yIm9wxRNkrMHTiCENtnUlpd47g8zsE7w"
API_URL = f"https://tapi.bale.ai/bot{TOKEN}/"
# راه‌اندازی لاگر
logging.basicConfig(
filename="./baleqabot/bot.log",
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
# ===========================
# define import model class
# ===========================
class Message(BaseModel):
chat: dict
text: str | None = None
class Update(BaseModel):
message: Message | None = None
# ===========================
# کلاس اصلی ربات بله
# ===========================
class BaleBot:
def __init__(self, token: str):
# self.api_url = f"https://api.bale.ai/bot{token}/"
self.api_url = API_URL
self.user_states = {}
async def get_updates(self, offset = None):
params = {"timeout": 20}
if offset:
params["offset"] = offset
# print("get_updates function")
resp = requests.get(f"{self.api_url}getUpdates", params=params)
return resp.json().get("result", [])
async def send_message(self, chat_id: int, text: str, keyboard=None):
payload = {
"chat_id": chat_id,
"text": text,
"parse_mode": "HTML"
}
if keyboard:
payload["reply_markup"] = keyboard
try:
response = requests.post(self.api_url + "sendMessage", json=payload)
return response.json()
except Exception as e:
logging.error(f"Error sending message: {e}")
return None
async def get_latest_req_id(self):
latest_req_id = 0
with open('./baleqabot/requests.json', 'r', encoding='utf-8') as file:
prev_reqs = json.load(file)
if prev_reqs:
latest_req_id = prev_reqs[-1]['update_id']
return latest_req_id + 1
async def save_entery(self, update):
all_reqs = []
prev_reqs = []
try:
with open('./baleqabot/requests.json', 'r', encoding='utf-8') as file:
prev_reqs = json.load(file)
all_reqs.extend(prev_reqs)
except:
prev_reqs = []
all_reqs.extend(update)
with open('./baleqabot/requests.json', 'w', encoding='utf-8') as file:
data = json.dumps(all_reqs, ensure_ascii=False, indent=2)
file.write(data)
return True
async def split_text_into_chunks(self, text, max_length=4000):
"""
تقسیم یک متن به چانک‌های حداکثر max_length کاراکتری، بدون خراب کردن معنا با رعایت انتهای جمله‌ها.
:param text: متن ورودی
:param max_length: حداکثر طول هر چانک (پیش‌فرض 4000)
:return: لیستی از چانک‌های متن
"""
chunks = [] # لیستی برای ذخیره چانک‌ها
start = 0 # شروع متن برای هر چانک
while start < len(text):
# اگر متن باقی‌مانده کوتاه‌تر از max_length باشد، کل آن را اضافه کنید
if len(text) - start <= max_length:
chunks.append(text[start:])
break
# پیدا کردن نقطه پایانی چانک (حداکثر تا max_length کاراکتر جلو بروید)
end = start + max_length
# اگر در وسط یک جمله هستیم، به عقب برگردید تا انتهای جمله پیدا شود
while end > start and text[end - 1] not in '.!?':
end -= 1
# اگر هیچ انتهای جمله پیدا نشد، متن را تا max_length ببرید
if end == start:
end = start + max_length
# اضافه کردن چانک به لیست
chunks.append(text[start:end])
# شروع چانک بعدی
start = end
return chunks
async def save_chat_data(self,query, answer, first_name, username):
chat_data = f'''username: {username}\nfirstname: {first_name}\nquery: {query}\nanswer:{answer}\n+ + + + + + + + + + + + + + + + + + + + \n+ + + + + + + + + + + + + + + + + + + + \n\n'''
with open('./baleqabot/chat-data.txt', 'a+', encoding='utf-8') as file:
file.write(chat_data)
async def handle_update(self, update_reqs: dict):
print(f"handle update ...")
data = update_reqs
if "message" not in data:
return
message = data["message"]
chat_id = message["chat"]["id"]
text = message.get("text", "").strip()
fromm = message['from']
first_name = fromm['first_name']
username = fromm['username']
logging.info(f"Received message from {chat_id}: {text}")
keyboard = {
"keyboard": [["جستجو","پرسش","شبکه معنایی","مشاوره سیاستی"]],
"resize_keyboard": True,
"one_time_keyboard": True
}
if text == "/start":
reply = "سلام، من دستیار هوشمند گفتگو با پرسونای رهبر معظم انقلاب هستم. لطفا یکی از گزینه‌های زیر را انتخاب نمائید ..."
await self.send_message(chat_id, reply, keyboard)
return
elif text == "پرسش":
# حذف نوع درخواست قبلی کاربر
self.user_states.pop(chat_id, None)
# ایجاد وضعیت پرسش برای کاربر جاری
self.user_states[chat_id] = "simple_question"
reply = "لطفا متن پرسش را وارد نمائید ..."
await self.send_message(chat_id, reply, keyboard)
return
elif text == "جستجو":
# حذف نوع درخواست قبلی کاربر
self.user_states.pop(chat_id, None)
# ایجاد وضعیت جستجو برای کاربر جاری
self.user_states[chat_id] = "search"
reply = "لطفا متن موردنظر جهت جستجو را وارد نمائید ..."
await self.send_message(chat_id, reply, keyboard)
return
elif text == "شبکه معنایی":
# حذف نوع درخواست قبلی کاربر
self.user_states.pop(chat_id, None)
# ایجاد وضعیت شبکه معنایی برای کاربر جاری
self.user_states[chat_id] = "semantic-network"
reply = "لطفا کلمه موردنظر جهت ترسیم شبکه معنایی را وارد نمائید ..."
await self.send_message(chat_id, reply, keyboard)
return
elif text == "مشاوره سیاستی":
# حذف نوع درخواست قبلی کاربر
self.user_states.pop(chat_id, None)
# ایجاد وضعیت پرسش برای کاربر جاری
self.user_states[chat_id] = "political_question"
reply = "لطفا پرسش سیاستی را وارد نمائید ..."
await self.send_message(chat_id, reply, keyboard)
return
# elif text == "/help":
# reply = (
# "دستورهای موجود:\n"
# "/start - شروع ربات\n"
# "/chat - گفت‌گو با دستیار هوشمند قانون\n"
# "/status - وضعیت ربات"
# )
# self.send_message(chat_id, reply)
elif text == "راهنما":
reply = "ربات فعال است ✅"
await self.send_message(chat_id, reply, keyboard)
return
elif self.user_states.get(chat_id) == "semantic-network":
await self.send_message(chat_id, f"⏳ در حال ایجاد شبکه معنایی برای کلمه «{text}» ...")
reply = 'با عرض پوزش؛ این امکان، در حال حاضر در دسترس نیست'
elif self.user_states.get(chat_id) == "search":
await self.send_message(chat_id, f"⏳ در حال جستجو برای «{text}» ...")
answer = leader_chat.bale_search(text)
if answer:
reply = answer
else:
reply = 'خطا در تولید پاسخ!'
elif self.user_states.get(chat_id) == "simple_question":
await self.send_message(chat_id, f"⏳ در حال آماده‌سازی پاسخ به «{text}» ...")
answer = leader_chat.bale_chat(text)
if answer:
reply = answer
else:
reply = 'خطا در تولید پاسخ!'
elif self.user_states.get(chat_id) == "political_question":
await self.send_message(chat_id, f"⏳ در حال آماده‌سازی پاسخ به «{text}» ...")
# answer = leader_chat.bale_chat(text)
final_result = await leader_chat.bale_complex_chat(text)
if final_result:
sub_questions = 'سوالات جزئی مرتبط با سوال کاربر:\n'
for i, q in enumerate(final_result.get('sub_qa'),1):
sub_questions += f'{i}. {q.get("question")}\n'
sub_qa_text = ''
for i, qa in enumerate(final_result.get('sub_qa'),1):
sub_qa_text += f'{i}. {qa.get("question")}\n{qa.get("answer")}\n\n'
# reply_content = f'''سوال اصلی: {text}\n\n{sub_questions}\n\n* * * * *سوالات جزئی:\n{sub_qa_text.strip()}\n\nپاسخ نهائی:\n{final_result.get('final_answer',0)}'''
reply_content = f'''{final_result.get('final_answer',0)}'''
reply = reply_content.strip()
else:
reply = 'خطا در تولید پاسخ!'
else:
reply = "لطفا یکی از گزینه‌های زیر را انتخاب نمائید"
await self.send_message(chat_id, reply, keyboard)
return
reply_len = len(reply.split())
print(f"len answer: {reply_len}")
print(f"ready for next ...")
print('+'*20)
print('+'*20)
reply_chuncs = []
reply_chuncs = await self.split_text_into_chunks(reply)
for i, paragraph in enumerate(reply_chuncs):
resultt = await self.send_message(chat_id, paragraph, keyboard)
# print(f"sent to bale server result {i+1}: {resultt}")
await self.save_chat_data(text, reply, first_name, username)
async def main():
print(f'bale-leader-bot is Readey!!!')
while True:
last_req_id = await bale_bot.get_latest_req_id()
# print(f"last req id: {last_req_id}")
update = await bale_bot.get_updates(last_req_id)
# print(f"reading bale-bot server. len update: {len(update)}")
if update:
print(f'{len(update)} requests recognized!')
await bale_bot.save_entery(update)
for i, item in enumerate(update, 1):
print(f'handle input {i}/{len(update)}')
await bale_bot.handle_update(item)
time.sleep(1)
# ===========================
# ساخت اپلیکیشن FastAPI
# ===========================
# app = FastAPI()
bale_bot = BaleBot(TOKEN)
# ===========================
# (local execution)
# ===========================
if __name__ == "__main__":
import asyncio
result = asyncio.run(main())