From 70e71af84c65083a6b0324fd3ede3e6de0f473c0 Mon Sep 17 00:00:00 2001 From: DyAxy Date: Tue, 14 May 2024 06:44:48 +0100 Subject: [PATCH] upgrade --- README.md | 9 ++- bot.py | 33 ++++++----- config.yml.example | 9 +-- handler.py | 133 +++++++++++++++++++++------------------------ test.py | 6 ++ 5 files changed, 96 insertions(+), 94 deletions(-) create mode 100644 test.py diff --git a/README.md b/README.md index aa4bede..97d4c00 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,7 @@ Python 版本需求 >= 3.9 ## 现有功能 - 基于Crisp客服系统 - 自动推送文字、图片到指定聊天 -- 自动基于关键词回复对应内容 -- 支持回复后推送回对应客户 +- 基于Telegram话题群将消息分栏 ## 计划功能 - 回复图片功能(需要Crisp订阅) @@ -38,6 +37,12 @@ python3 bot.py 3. 接着为你的bot设置一个username,但是一定要以bot结尾,例如:`v2board_bot` 4. 最后你就能得到bot的token了,看起来应该像这样:`123456789:gaefadklwdqojdoiqwjdiwqdo` +## 创建 Telegram Topic 群 + +1. 创建一个群聊,并将申请的 Bot 拉进去 +2. 在管理群中,打开话题 (Topic),并将 Bot 设为管理员 +3. 将 # 的话题设为置顶 (Pin) + ## 申请 Crisp 以及 MarketPlace 插件 1. 注册 [https://app.crisp.chat/initiate/signup](https://app.crisp.chat/initiate/signup) diff --git a/bot.py b/bot.py index 8b02f4f..3cd24f7 100644 --- a/bot.py +++ b/bot.py @@ -39,20 +39,23 @@ async def onReply(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: msg = update.effective_message - website_id = config['crisp']['website'] - if msg.reply_to_message.text is not None: - session_id = re.search( - 'session_\w{8}(-\w{4}){3}-\w{12}', msg.reply_to_message.text).group() - elif msg.reply_to_message.caption is not None: - session_id = re.search( - 'session_\w{8}(-\w{4}){3}-\w{12}', msg.reply_to_message.caption).group() - query = { - "type": "text", - "content": msg.text, - "from": "operator", - "origin": "chat" - } - client.website.send_message_in_conversation(website_id, session_id, query) + + if msg.chat_id != config['bot']['groupId']: + return + for sessionId in context.bot_data: + if context.bot_data[sessionId]['topicId'] == msg.message_thread_id: + query = { + "type": "text", + "content": msg.text, + "from": "operator", + "origin": "chat" + } + client.website.send_message_in_conversation( + config['crisp']['website'], + sessionId, + query + ) + return def main(): try: @@ -60,7 +63,7 @@ def main(): # 启动 Bot if os.getenv('RUNNER_NAME') is not None: return - app.add_handler(MessageHandler(filters.REPLY & filters.TEXT, onReply)) + app.add_handler(MessageHandler(filters.TEXT, onReply)) app.job_queue.run_once(handler.exec,5,name='RTM') app.run_polling(drop_pending_updates=True) except Exception as error: diff --git a/config.yml.example b/config.yml.example index c736d6f..baef06e 100644 --- a/config.yml.example +++ b/config.yml.example @@ -1,10 +1,8 @@ bot: # Bot Token token: 1234:1234567890abcdef - # 发送至,可以设置群或私聊 - send_id: - - 0 - - 0 + # 发送至群 + groupId: 0 crisp: # 插件 ID id: @@ -12,6 +10,3 @@ crisp: key: # 网站 ID website: -autoreply: - # 自动关键词回复,你可以复制成多行,每个关键词用 `|` 隔开即可,在 `:` 后输入自动回复内容 - "在吗|你好": "欢迎使用客服系统,请等待客服回复你~" diff --git a/handler.py b/handler.py index f9b9509..e185548 100644 --- a/handler.py +++ b/handler.py @@ -1,26 +1,19 @@ + import bot import json import base64 import socketio import requests from telegram.ext import ContextTypes +from telegram import InlineKeyboardButton, InlineKeyboardMarkup config = bot.config client = bot.client -website_id = config["crisp"]["website"] - -def getKey(content: str): - if len(config["autoreply"]) > 0: - for x in config["autoreply"]: - keyword = x.split("|") - for key in keyword: - if key in content: - return True, config["autoreply"][x] - return False, "" +groupId = config["bot"]["groupId"] +websiteId = config["crisp"]["website"] -async def sendTextMessage(message): - session_id = message["session_id"] - metas = client.website.get_conversation_metas(website_id, session_id) +def getMetas(sessionId): + metas = client.website.get_conversation_metas(websiteId, sessionId) flow = ['📠Crisp消息推送',''] if len(metas["email"]) > 0: @@ -34,52 +27,59 @@ async def sendTextMessage(message): UsedTraffic = metas["data"]["UsedTraffic"] AllTraffic = metas["data"]["AllTraffic"] flow.append(f"🗒流量信息:{UsedTraffic} / {AllTraffic}") + if len(flow) > 2: + return '\n'.join(flow) + return '无额外信息' + +async def createSession(data): + bot = callbackContext.bot + botData = callbackContext.bot_data + sessionId = data["session_id"] + session = botData.get(sessionId) - flow.append(f"🧾消息内容:{message['content']}") - result, autoreply = getKey(message["content"]) - if result is True: - flow.append("") - flow.append(f"💡自动回复:{autoreply}") - query = { - "type": "text", - "content": autoreply, - "from": "operator", - "origin": "chat", + metas = getMetas(sessionId) + if session is None: + topic = await bot.create_forum_topic( + groupId,data["user"]["nickname"]) + msg = await bot.send_message( + groupId, + metas, + message_thread_id=topic.message_thread_id + ) + botData[sessionId] = { + 'topicId': topic.message_thread_id, + 'messageId': msg.message_id, } - client.website.send_message_in_conversation(website_id, session_id, query) - - flow.append("") - flow.append(f"🧷Session{session_id}") - - text = '\n'.join(flow) - for send_id in config["bot"]["send_id"]: - await callbackContext.bot.send_message( - chat_id=send_id, text=text) - client.website.mark_messages_read_in_conversation( - website_id, - session_id, - {"from": "user", "origin": "chat", "fingerprints": [message["fingerprint"]]}, - ) + else: + await bot.edit_message_text('加载中',groupId,session['messageId']) + await bot.edit_message_text(metas,groupId,session['messageId']) -async def sendImageMessage(message): - session_id = message["session_id"] +async def sendMessage(data): + bot = callbackContext.bot + botData = callbackContext.bot_data + sessionId = data["session_id"] + session = botData.get(sessionId) - flow = ['📠Crisp消息推送',''] - flow.append("") - flow.append(f"🧷Session{session_id}") - text = '\n'.join(flow) + client.website.mark_messages_read_in_conversation(websiteId,sessionId, + {"from": "user", "origin": "chat", "fingerprints": [data["fingerprint"]]} + ) - for send_id in config["bot"]["send_id"]: - await callbackContext.bot.send_photo( - chat_id=send_id, - photo=message["content"]["url"], - caption=text + if data["type"] == "text": + flow = ['📠消息推送',''] + flow.append(f"🧾消息内容:{data['content']}") + await bot.send_message( + groupId, + '\n'.join(flow), + message_thread_id=session["topicId"] ) - client.website.mark_messages_read_in_conversation( - website_id, - session_id, - {"from": "user", "origin": "chat", "fingerprints": [message["fingerprint"]]}, - ) + elif data["type"] == "file" and str(data["content"]["type"]).count("image") > 0: + await bot.send_photo( + groupId, + data["content"]["url"], + message_thread_id=session["topicId"] + ) + else: + print("Unhandled Message Type : ", data["type"]) sio = socketio.AsyncClient(reconnection_attempts=5, logger=True) # Def Event Handlers @@ -104,15 +104,10 @@ async def disconnect(): print("Disconnected from server.") @sio.on("message:send") async def messageForward(data): - try: - if data["type"] == "text": - await sendTextMessage(data) - elif data["type"] == "file" and str(data["content"]["type"]).count("image") > 0: - await sendImageMessage(data) - else: - print("Unhandled Message Type : ", data["type"]) - except Exception as err: - print(err) + if data["website_id"] != websiteId: + return + await createSession(data) + await sendMessage(data) # Meow! def getCrispConnectEndpoints(): @@ -126,17 +121,15 @@ def getCrispConnectEndpoints(): response = requests.request("GET", url, headers=headers, data=payload) endPoint = json.loads(response.text).get("data").get("socket").get("app") return endPoint + # Connecting to Crisp RTM(WSS) Server -async def start_server(): +async def exec(context: ContextTypes.DEFAULT_TYPE): + global callbackContext + callbackContext = context + # await sendAllUnread() await sio.connect( getCrispConnectEndpoints(), transports="websocket", wait_timeout=10, ) - await sio.wait() - -async def exec(context: ContextTypes.DEFAULT_TYPE): - global callbackContext - callbackContext = context - # await sendAllUnread() - await start_server() \ No newline at end of file + await sio.wait() \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..f8add8e --- /dev/null +++ b/test.py @@ -0,0 +1,6 @@ +a = {} +a['a'] = 1 +a['b'] = 2 + +for i in a: + print(i) \ No newline at end of file