forked from hanfangyuan4396/dify-on-wechat
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge Pull Request zhayujie#686 into master
- Loading branch information
Showing
20 changed files
with
659 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,4 +10,5 @@ nohup.out | |
tmp | ||
plugins.json | ||
itchat.pkl | ||
*.log | ||
*.log | ||
user_datas.pkl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# 个人微信公众号channel | ||
|
||
鉴于个人微信号在服务器上通过itchat登录有封号风险,这里新增了个人微信公众号channel,提供无风险的服务。 | ||
但是由于个人微信公众号的众多接口限制,目前支持的功能有限,实现简陋,提供了一个最基本的文本对话服务,支持加载插件,优化了命令格式,支持私有api_key。暂未实现图片输入输出、语音输入输出等交互形式。 | ||
如有公众号是企业主体且可以通过微信认证,即可获得更多接口,解除大多数限制。欢迎大家提供更多的支持。 | ||
|
||
## 使用方法 | ||
|
||
在开始部署前,你需要一个拥有公网IP的服务器,以提供微信服务器和我们自己服务器的连接。或者你需要进行内网穿透,否则微信服务器无法将消息发送给我们的服务器。 | ||
|
||
此外,需要在我们的服务器上安装python的web框架web.py。 | ||
以ubuntu为例(在ubuntu 22.04上测试): | ||
``` | ||
pip3 install web.py | ||
``` | ||
|
||
然后在[微信公众平台](https://mp.weixin.qq.com)注册一个自己的公众号,类型选择订阅号,主体为个人即可。 | ||
|
||
然后根据[接入指南](https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html)的说明,在[微信公众平台](https://mp.weixin.qq.com)的“设置与开发”-“基本配置”-“服务器配置”中填写服务器地址`URL`和令牌`Token`。这里的`URL`是`example.com/wx`的形式,不可以使用IP,`Token`是你自己编的一个特定的令牌。消息加解密方式目前选择的是明文模式。 | ||
|
||
相关的服务器验证代码已经写好,你不需要再添加任何代码。你只需要在本项目根目录的`config.json`中添加`"channel_type": "wechatmp", "wechatmp_token": "your Token", ` 然后运行`python3 app.py`启动web服务器,然后在刚才的“服务器配置”中点击`提交`即可验证你的服务器。 | ||
|
||
随后在[微信公众平台](https://mp.weixin.qq.com)启用服务器,关闭手动填写规则的自动回复,即可实现ChatGPT的自动回复。 | ||
|
||
## 个人微信公众号的限制 | ||
由于目前测试的公众号不是企业主体,所以没有客服接口,因此公众号无法主动发出消息,只能被动回复。而微信官方对被动回复有5秒的时间限制,最多重试2次,因此最多只有15秒的自动回复时间窗口。因此如果问题比较复杂或者我们的服务器比较忙,ChatGPT的回答就没办法及时回复给用户。为了解决这个问题,这里做了回答缓存,它需要你在回复超时后,再次主动发送任意文字(例如1)来尝试拿到回答缓存。为了优化使用体验,目前设置了两分钟(120秒)的timeout,用户在至多两分钟后即可得到查询到回复或者错误原因。 | ||
|
||
另外,由于微信官方的限制,自动回复有长度限制。因此这里将ChatGPT的回答拆分,分成每段600字回复(限制大约在700字)。 | ||
|
||
## 私有api_key | ||
公共api有访问频率限制(免费账号每分钟最多20次ChatGPT的API调用),这在服务多人的时候会遇到问题。因此这里多加了一个设置私有api_key的功能。目前通过godcmd插件的命令来设置私有api_key。 | ||
|
||
## 测试范围 | ||
目前在`RoboStyle`这个公众号上进行了测试,感兴趣的可以关注并体验。开启了godcmd, Banwords, role, dungeon, finish这五个插件,其他的插件还没有测试。百度的接口暂未测试。语音对话没有测试。图片直接以链接形式回复(没有临时素材上传接口的权限)。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# -*- coding: utf-8 -*-# | ||
# filename: receive.py | ||
import xml.etree.ElementTree as ET | ||
|
||
|
||
def parse_xml(web_data): | ||
if len(web_data) == 0: | ||
return None | ||
xmlData = ET.fromstring(web_data) | ||
msg_type = xmlData.find('MsgType').text | ||
if msg_type == 'text': | ||
return TextMsg(xmlData) | ||
elif msg_type == 'image': | ||
return ImageMsg(xmlData) | ||
elif msg_type == 'event': | ||
return Event(xmlData) | ||
|
||
|
||
class Msg(object): | ||
def __init__(self, xmlData): | ||
self.ToUserName = xmlData.find('ToUserName').text | ||
self.FromUserName = xmlData.find('FromUserName').text | ||
self.CreateTime = xmlData.find('CreateTime').text | ||
self.MsgType = xmlData.find('MsgType').text | ||
self.MsgId = xmlData.find('MsgId').text | ||
|
||
|
||
class TextMsg(Msg): | ||
def __init__(self, xmlData): | ||
Msg.__init__(self, xmlData) | ||
self.Content = xmlData.find('Content').text.encode("utf-8") | ||
|
||
|
||
class ImageMsg(Msg): | ||
def __init__(self, xmlData): | ||
Msg.__init__(self, xmlData) | ||
self.PicUrl = xmlData.find('PicUrl').text | ||
self.MediaId = xmlData.find('MediaId').text | ||
|
||
|
||
class Event(object): | ||
def __init__(self, xmlData): | ||
self.ToUserName = xmlData.find('ToUserName').text | ||
self.FromUserName = xmlData.find('FromUserName').text | ||
self.CreateTime = xmlData.find('CreateTime').text | ||
self.MsgType = xmlData.find('MsgType').text | ||
self.Event = xmlData.find('Event').text |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# -*- coding: utf-8 -*-# | ||
# filename: reply.py | ||
import time | ||
|
||
class Msg(object): | ||
def __init__(self): | ||
pass | ||
|
||
def send(self): | ||
return "success" | ||
|
||
class TextMsg(Msg): | ||
def __init__(self, toUserName, fromUserName, content): | ||
self.__dict = dict() | ||
self.__dict['ToUserName'] = toUserName | ||
self.__dict['FromUserName'] = fromUserName | ||
self.__dict['CreateTime'] = int(time.time()) | ||
self.__dict['Content'] = content | ||
|
||
def send(self): | ||
XmlForm = """ | ||
<xml> | ||
<ToUserName><![CDATA[{ToUserName}]]></ToUserName> | ||
<FromUserName><![CDATA[{FromUserName}]]></FromUserName> | ||
<CreateTime>{CreateTime}</CreateTime> | ||
<MsgType><![CDATA[text]]></MsgType> | ||
<Content><![CDATA[{Content}]]></Content> | ||
</xml> | ||
""" | ||
return XmlForm.format(**self.__dict) | ||
|
||
class ImageMsg(Msg): | ||
def __init__(self, toUserName, fromUserName, mediaId): | ||
self.__dict = dict() | ||
self.__dict['ToUserName'] = toUserName | ||
self.__dict['FromUserName'] = fromUserName | ||
self.__dict['CreateTime'] = int(time.time()) | ||
self.__dict['MediaId'] = mediaId | ||
|
||
def send(self): | ||
XmlForm = """ | ||
<xml> | ||
<ToUserName><![CDATA[{ToUserName}]]></ToUserName> | ||
<FromUserName><![CDATA[{FromUserName}]]></FromUserName> | ||
<CreateTime>{CreateTime}</CreateTime> | ||
<MsgType><![CDATA[image]]></MsgType> | ||
<Image> | ||
<MediaId><![CDATA[{MediaId}]]></MediaId> | ||
</Image> | ||
</xml> | ||
""" | ||
return XmlForm.format(**self.__dict) |
Oops, something went wrong.