mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-20 21:57:14 +08:00
Merge branch 'master' into feat-client
This commit is contained in:
@@ -36,6 +36,9 @@ def create_channel(channel_type) -> Channel:
|
||||
elif channel_type == const.FEISHU:
|
||||
from channel.feishu.feishu_channel import FeiShuChanel
|
||||
ch = FeiShuChanel()
|
||||
elif channel_type == const.DINGTALK:
|
||||
from channel.dingtalk.dingtalk_channel import DingTalkChanel
|
||||
ch = DingTalkChanel()
|
||||
else:
|
||||
raise RuntimeError
|
||||
ch.channel_type = channel_type
|
||||
|
||||
164
channel/dingtalk/dingtalk_channel.py
Normal file
164
channel/dingtalk/dingtalk_channel.py
Normal file
@@ -0,0 +1,164 @@
|
||||
"""
|
||||
钉钉通道接入
|
||||
|
||||
@author huiwen
|
||||
@Date 2023/11/28
|
||||
"""
|
||||
|
||||
# -*- coding=utf-8 -*-
|
||||
import uuid
|
||||
|
||||
import requests
|
||||
import web
|
||||
from channel.dingtalk.dingtalk_message import DingTalkMessage
|
||||
from bridge.context import Context
|
||||
from bridge.reply import Reply, ReplyType
|
||||
from common.log import logger
|
||||
from common.singleton import singleton
|
||||
from config import conf
|
||||
from common.expired_dict import ExpiredDict
|
||||
from bridge.context import ContextType
|
||||
from channel.chat_channel import ChatChannel, check_prefix
|
||||
from common import utils
|
||||
import json
|
||||
import os
|
||||
|
||||
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
from dingtalk_stream import AckMessage
|
||||
import dingtalk_stream
|
||||
|
||||
@singleton
|
||||
class DingTalkChanel(ChatChannel,dingtalk_stream.ChatbotHandler):
|
||||
dingtalk_client_id = conf().get('dingtalk_client_id')
|
||||
dingtalk_client_secret = conf().get('dingtalk_client_secret')
|
||||
|
||||
def setup_logger(self):
|
||||
logger = logging.getLogger()
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(
|
||||
logging.Formatter('%(asctime)s %(name)-8s %(levelname)-8s %(message)s [%(filename)s:%(lineno)d]'))
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.INFO)
|
||||
return logger
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
super(dingtalk_stream.ChatbotHandler, self).__init__()
|
||||
|
||||
self.logger = self.setup_logger()
|
||||
# 历史消息id暂存,用于幂等控制
|
||||
self.receivedMsgs = ExpiredDict(60 * 60 * 7.1)
|
||||
|
||||
logger.info("[dingtalk] client_id={}, client_secret={} ".format(
|
||||
self.dingtalk_client_id, self.dingtalk_client_secret))
|
||||
# 无需群校验和前缀
|
||||
conf()["group_name_white_list"] = ["ALL_GROUP"]
|
||||
|
||||
|
||||
|
||||
def startup(self):
|
||||
|
||||
credential = dingtalk_stream.Credential( self.dingtalk_client_id, self.dingtalk_client_secret)
|
||||
client = dingtalk_stream.DingTalkStreamClient(credential)
|
||||
client.register_callback_handler(dingtalk_stream.chatbot.ChatbotMessage.TOPIC,self)
|
||||
client.start_forever()
|
||||
|
||||
def handle_single(self, cmsg:DingTalkMessage):
|
||||
# 处理单聊消息
|
||||
#
|
||||
|
||||
if cmsg.ctype == ContextType.VOICE:
|
||||
|
||||
logger.debug("[dingtalk]receive voice msg: {}".format(cmsg.content))
|
||||
elif cmsg.ctype == ContextType.IMAGE:
|
||||
logger.debug("[dingtalk]receive image msg: {}".format(cmsg.content))
|
||||
elif cmsg.ctype == ContextType.PATPAT:
|
||||
logger.debug("[dingtalk]receive patpat msg: {}".format(cmsg.content))
|
||||
elif cmsg.ctype == ContextType.TEXT:
|
||||
expression = cmsg.my_msg
|
||||
|
||||
cmsg.content = conf()["single_chat_prefix"][0] + cmsg.content
|
||||
|
||||
context = self._compose_context(cmsg.ctype, cmsg.content, isgroup=False, msg=cmsg)
|
||||
|
||||
if context:
|
||||
self.produce(context)
|
||||
|
||||
def handle_group(self, cmsg:DingTalkMessage):
|
||||
# 处理群聊消息
|
||||
#
|
||||
|
||||
if cmsg.ctype == ContextType.VOICE:
|
||||
|
||||
logger.debug("[dingtalk]receive voice msg: {}".format(cmsg.content))
|
||||
elif cmsg.ctype == ContextType.IMAGE:
|
||||
logger.debug("[dingtalk]receive image msg: {}".format(cmsg.content))
|
||||
elif cmsg.ctype == ContextType.PATPAT:
|
||||
logger.debug("[dingtalk]receive patpat msg: {}".format(cmsg.content))
|
||||
elif cmsg.ctype == ContextType.TEXT:
|
||||
expression = cmsg.my_msg
|
||||
|
||||
cmsg.content = conf()["group_chat_prefix"][0] + cmsg.content
|
||||
context = self._compose_context(cmsg.ctype, cmsg.content, isgroup=True, msg=cmsg)
|
||||
context['no_need_at']=True
|
||||
if context:
|
||||
self.produce(context)
|
||||
|
||||
|
||||
async def process(self, callback: dingtalk_stream.CallbackMessage):
|
||||
|
||||
|
||||
|
||||
try:
|
||||
|
||||
incoming_message = dingtalk_stream.ChatbotMessage.from_dict(callback.data)
|
||||
dingtalk_msg = DingTalkMessage(incoming_message)
|
||||
if incoming_message.conversation_type == '1':
|
||||
self.handle_single(dingtalk_msg)
|
||||
else:
|
||||
self.handle_group(dingtalk_msg)
|
||||
return AckMessage.STATUS_OK, 'OK'
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return self.FAILED_MSG
|
||||
|
||||
|
||||
def send(self, reply: Reply, context: Context):
|
||||
|
||||
|
||||
incoming_message = context.kwargs['msg'].incoming_message
|
||||
self.reply_text(reply.content, incoming_message)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# def _compose_context(self, ctype: ContextType, content, **kwargs):
|
||||
# context = Context(ctype, content)
|
||||
# context.kwargs = kwargs
|
||||
# if "origin_ctype" not in context:
|
||||
# context["origin_ctype"] = ctype
|
||||
|
||||
# cmsg = context["msg"]
|
||||
# context["session_id"] = cmsg.from_user_id
|
||||
# context["receiver"] = cmsg.other_user_id
|
||||
|
||||
# if ctype == ContextType.TEXT:
|
||||
# # 1.文本请求
|
||||
# # 图片生成处理
|
||||
# img_match_prefix = check_prefix(content, conf().get("image_create_prefix"))
|
||||
# if img_match_prefix:
|
||||
# content = content.replace(img_match_prefix, "", 1)
|
||||
# context.type = ContextType.IMAGE_CREATE
|
||||
# else:
|
||||
# context.type = ContextType.TEXT
|
||||
# context.content = content.strip()
|
||||
|
||||
# elif context.type == ContextType.VOICE:
|
||||
# # 2.语音请求
|
||||
# if "desire_rtype" not in context and conf().get("voice_reply_voice"):
|
||||
# context["desire_rtype"] = ReplyType.VOICE
|
||||
|
||||
# return context
|
||||
40
channel/dingtalk/dingtalk_message.py
Normal file
40
channel/dingtalk/dingtalk_message.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from bridge.context import ContextType
|
||||
from channel.chat_message import ChatMessage
|
||||
import json
|
||||
import requests
|
||||
from common.log import logger
|
||||
from common.tmp_dir import TmpDir
|
||||
from common import utils
|
||||
from dingtalk_stream import ChatbotMessage
|
||||
|
||||
class DingTalkMessage(ChatMessage):
|
||||
def __init__(self, event: ChatbotMessage):
|
||||
super().__init__(event)
|
||||
|
||||
self.msg_id = event.message_id
|
||||
msg_type = event.message_type
|
||||
self.incoming_message =event
|
||||
self.sender_staff_id = event.sender_staff_id
|
||||
|
||||
self.create_time = event.create_at
|
||||
if event.conversation_type=="1":
|
||||
self.is_group = False
|
||||
else:
|
||||
self.is_group = True
|
||||
|
||||
|
||||
if msg_type == "text":
|
||||
self.ctype = ContextType.TEXT
|
||||
|
||||
self.content = event.text.content.strip()
|
||||
|
||||
self.from_user_id = event.sender_id
|
||||
self.to_user_id = event.chatbot_user_id
|
||||
self.other_user_nickname = event.conversation_title
|
||||
|
||||
user_id = event.sender_id
|
||||
nickname =event.sender_nick
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -46,35 +46,6 @@ class FeishuMessage(ChatMessage):
|
||||
else:
|
||||
logger.info(f"[FeiShu] Failed to download file, key={file_key}, res={response.text}")
|
||||
self._prepare_fn = _download_file
|
||||
|
||||
# elif msg.type == "voice":
|
||||
# self.ctype = ContextType.VOICE
|
||||
# self.content = TmpDir().path() + msg.media_id + "." + msg.format # content直接存临时目录路径
|
||||
#
|
||||
# def download_voice():
|
||||
# # 如果响应状态码是200,则将响应内容写入本地文件
|
||||
# response = client.media.download(msg.media_id)
|
||||
# if response.status_code == 200:
|
||||
# with open(self.content, "wb") as f:
|
||||
# f.write(response.content)
|
||||
# else:
|
||||
# logger.info(f"[wechatcom] Failed to download voice file, {response.content}")
|
||||
#
|
||||
# self._prepare_fn = download_voice
|
||||
# elif msg.type == "image":
|
||||
# self.ctype = ContextType.IMAGE
|
||||
# self.content = TmpDir().path() + msg.media_id + ".png" # content直接存临时目录路径
|
||||
#
|
||||
# def download_image():
|
||||
# # 如果响应状态码是200,则将响应内容写入本地文件
|
||||
# response = client.media.download(msg.media_id)
|
||||
# if response.status_code == 200:
|
||||
# with open(self.content, "wb") as f:
|
||||
# f.write(response.content)
|
||||
# else:
|
||||
# logger.info(f"[wechatcom] Failed to download image file, {response.content}")
|
||||
#
|
||||
# self._prepare_fn = download_image
|
||||
else:
|
||||
raise NotImplementedError("Unsupported message type: Type:{} ".format(msg_type))
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ def _check(func):
|
||||
|
||||
|
||||
@wework.msg_register(
|
||||
[ntwork.MT_RECV_TEXT_MSG, ntwork.MT_RECV_IMAGE_MSG, 11072, ntwork.MT_RECV_VOICE_MSG])
|
||||
[ntwork.MT_RECV_TEXT_MSG, ntwork.MT_RECV_IMAGE_MSG, 11072, ntwork.MT_RECV_LINK_CARD_MSG,ntwork.MT_RECV_FILE_MSG, ntwork.MT_RECV_VOICE_MSG])
|
||||
def all_msg_handler(wework_instance: ntwork.WeWork, message):
|
||||
logger.debug(f"收到消息: {message}")
|
||||
if 'data' in message:
|
||||
|
||||
@@ -128,6 +128,18 @@ class WeworkMessage(ChatMessage):
|
||||
self.ctype = ContextType.IMAGE
|
||||
self.content = os.path.join(current_dir, "tmp", file_name)
|
||||
self._prepare_fn = lambda: cdn_download(wework, wework_msg, file_name)
|
||||
elif wework_msg["type"] == 11045: # 文件消息
|
||||
print("文件消息")
|
||||
print(wework_msg)
|
||||
file_name = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
|
||||
file_name = file_name + wework_msg['data']['cdn']['file_name']
|
||||
current_dir = os.getcwd()
|
||||
self.ctype = ContextType.FILE
|
||||
self.content = os.path.join(current_dir, "tmp", file_name)
|
||||
self._prepare_fn = lambda: cdn_download(wework, wework_msg, file_name)
|
||||
elif wework_msg["type"] == 11047: # 链接消息
|
||||
self.ctype = ContextType.SHARING
|
||||
self.content = wework_msg['data']['url']
|
||||
elif wework_msg["type"] == 11072: # 新成员入群通知
|
||||
self.ctype = ContextType.JOIN_GROUP
|
||||
member_list = wework_msg['data']['member_list']
|
||||
@@ -179,6 +191,7 @@ class WeworkMessage(ChatMessage):
|
||||
if conversation_id:
|
||||
room_info = get_room_info(wework=wework, conversation_id=conversation_id)
|
||||
self.other_user_nickname = room_info.get('nickname', None) if room_info else None
|
||||
self.from_user_nickname = room_info.get('nickname', None) if room_info else None
|
||||
at_list = data.get('at_list', [])
|
||||
tmp_list = []
|
||||
for at in at_list:
|
||||
|
||||
Reference in New Issue
Block a user