import json # from fastapi import FastAPI, Request from pydantic import BaseModel import requests import logging # import uvicorn import time import nahj_engine_bge as nahj_chat # برای مدلهای غیر bge کامنت شود و به جای آن خط زیر را از کامنت دربیاورید # import nahj_engine as nahj_chat import data_model as dm # =========================== # پیکربندی اولیه # =========================== TOKEN = "602738113:OcVhjcsXqvE6D9FUytdoMZ096DPKYIUwnrk" API_URL = f"https://tapi.bale.ai/bot{TOKEN}/" # راه‌اندازی لاگر logging.basicConfig( filename="./bale_bot/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_request = dm.get_last_request() # latest_request ={} # latest_request['update_id']=831 latest_req_id = latest_request['update_id'] if not latest_req_id: latest_req_id = 0 return latest_req_id + 1 async def save_entery(self, update_item): is_active = True answer = '' message = update_item['message'] fromm = message['from'] chat = message['chat'] try: dm.insert_request(update_item['update_id'],fromm['username'],message['text'], answer, message['message_id'],fromm['id'],fromm['is_bot'],message['date'],chat['id'],chat['type'],fromm['first_name'],fromm['last_name'], is_active) except: return update_item['update_id'] return update_item['update_id'] async def update_request(self, update_id, answer): dm.update_request(update_id= update_id, answer= answer) 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''' # # should write in DATABASE with open('./bale_bot/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] = "deep_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 = nahj_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 = nahj_chat.bale_chat(text) if answer: reply = answer else: reply = 'خطا در تولید پاسخ!' elif self.user_states.get(chat_id) == "deep_question": await self.send_message(chat_id, f"⏳ در حال آماده‌سازی پاسخ به «{text}» ...") # answer = nahj_chat.bale_chat(text) final_result = await nahj_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): await self.send_message(chat_id, paragraph, keyboard) # await self.save_chat_data(text, reply, first_name, username) return reply async def main(): print(f'!!! BALE-NAHJ-BOT IS READY !!!') 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!') for i, item in enumerate(update, 1): print(f'handle input {i}/{len(update)}') update_id = await bale_bot.save_entery(item) answer = await bale_bot.handle_update(item) if answer: await bale_bot.update_request(update_id, answer) time.sleep(1) # =========================== # ساخت اپلیکیشن FastAPI # =========================== # app = FastAPI() bale_bot = BaleBot(TOKEN) # =========================== # (local execution) # =========================== if __name__ == "__main__": import asyncio result = asyncio.run(main())