import json from fastapi import FastAPI, Request from pydantic import BaseModel import requests import logging import uvicorn import random import time 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 async def get_latest_req_id(self): latest_request = dm.get_last_request() 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'] username, first_name, last_name = '','','' if 'username' in fromm: username = fromm['username'] if 'first_name' in fromm: first_name = fromm['first_name'] if 'last_name' in fromm: last_name = fromm['last_name'] try: dm.insert_request(update_item['update_id'],username,message['text'], answer, message['message_id'],fromm['id'],fromm['is_bot'],message['date'],chat['id'],chat['type'],first_name,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 # =========================== # ساخت اپلیکیشن FastAPI # =========================== app = FastAPI() @app.post("/chat") async def chat(request: Request): """ دریافت مستقیم آبجکت ورودی به صورت JSON """ # دریافت بدنه درخواست body = await request.json() # دسترسی به فیلدها user_input = body.get("message") metadata = body.get("metadata", {}) # update_id = await save_entery(item) update_id = random.randint(1, 10) answer = nahj_chat.bale_chat(user_input) if not answer: reply = 'خطا در تولید پاسخ!' if answer: await update_request(update_id, answer) # برگرداندن آبجکت خروجی (خودکار به JSON تبدیل می‌شود) return { "output": answer, "status": "ok", "input_received": user_input } print(f'%%%%%%%%%%%%%%%%%%%%%%%%%%%%') print(f'!!! NAHJ-RUNNER IS READY !!!') print(f'%%%%%%%%%%%%%%%%%%%%%%%%%%%%') # =========================== # (local execution) # =========================== # if __name__ == "__main__": # import asyncio # result = asyncio.run(chat()) if __name__ == "__main__": uvicorn.run(app, host="127.0.0.1", port=8010) # uvicorn.run( # "nahj_engine_general_runner:app", # host="0.0.0.0", # port=8010, # reload=True, # فعال بودن reload برای دیباگ مفید است # log_level="debug" # ) # uvicorn nahj_engine_general_runner:app --reload --host 0.0.0.0 --port 8010