From a9dd790676b23bd9cb9a12b44a9a89d833c68e88 Mon Sep 17 00:00:00 2001 From: DBinK Date: Thu, 9 Mar 2023 14:49:19 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E4=B8=BA=E5=BE=AE=E4=BF=A1=E7=BE=A4?= =?UTF-8?q?=E8=81=8A=E5=92=8C=E7=A7=81=E8=81=8A=E5=A2=9E=E5=8A=A0=E6=95=8F?= =?UTF-8?q?=E6=84=9F=E8=AF=8D=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 +++++ README.md | 17 ++++++----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 6520724..2095ccc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,8 @@ device.json go-cqhttp logs/ session.token +*venv +chatgpt.bat +configtmp.json +config-template.json +sensitive_words.txt \ No newline at end of file diff --git a/README.md b/README.md index 153e9c5..696f4b1 100644 --- a/README.md +++ b/README.md @@ -395,9 +395,8 @@ https://slack.dev/bolt-python/tutorial/getting-started ``` ### 9.Web -#### http -**需要:** 服务器 +**Contributor:** [RegimenArsenic](https://github.com/RegimenArsenic) **依赖** @@ -411,17 +410,13 @@ pip3 install PyJWT flask "channel": { "type": "http", "http": { - "http_auth_secret_key": "6d25a684-9558-11e9-aa94-efccd7a0659b",//jwt认证秘钥 - "http_auth_password": "6.67428e-11",//认证密码,仅仅只是自用,最初步的防御别人扫描端口后DDOS浪费tokens - "port": "80"//端口 + "http_auth_secret_key": "6d25a684-9558-11e9-aa94-efccd7a0659b", //jwt认证秘钥 + "http_auth_password": "6.67428e-11", //认证密码,仅仅只是自用,最初步的防御别人扫描端口后DDOS浪费tokens + "port": "80" //端口 } } ``` +本地运行:`python3 app.py`运行后访问 `http://127.0.0.1:80` - -URL,如果端口是 80 ,可不填 - -``` -http:/你的固定公网ip或者域名:端口/ -``` +服务器运行:部署后访问 `http://公网域名或IP:端口` From 567b824333c34d512e1996da1c6f0c83ced888c9 Mon Sep 17 00:00:00 2001 From: DBinK Date: Sat, 11 Mar 2023 19:16:47 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=99=BE=E5=BA=A6?= =?UTF-8?q?=E4=BA=91=E6=8E=A5=E5=8F=A3=E5=A2=9E=E5=8A=A0=E6=95=8F=E6=84=9F?= =?UTF-8?q?=E8=AF=8D=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 ++- channel/wechat/wechat_channel.py | 61 +++++++++++++++++++++++++-- common/sensitive_word.py | 71 ++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 common/sensitive_word.py diff --git a/.gitignore b/.gitignore index 2095ccc..e03e83d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,8 @@ session.token chatgpt.bat configtmp.json config-template.json -sensitive_words.txt \ No newline at end of file +sensitive_words.txt +common/test_sensitive_word copy.py +common/test_sensitive_word.py +.vscode/launch.json +sensitive_words.txt diff --git a/channel/wechat/wechat_channel.py b/channel/wechat/wechat_channel.py index ea3f3ff..a571805 100644 --- a/channel/wechat/wechat_channel.py +++ b/channel/wechat/wechat_channel.py @@ -3,6 +3,7 @@ """ wechat channel """ + import itchat import json from itchat.content import * @@ -12,11 +13,22 @@ from common.log import logger from common import const from config import channel_conf_val, channel_conf import requests +from urllib.parse import urlencode, quote + +from common.sensitive_word import SensitiveWord + import io + thread_pool = ThreadPoolExecutor(max_workers=8) + +sw = SensitiveWord() + +# ... + + @itchat.msg_register(TEXT) def handler_single_msg(msg): WechatChannel().handle(msg) @@ -29,6 +41,9 @@ def handler_group_msg(msg): return None + + + class WechatChannel(Channel): def __init__(self): pass @@ -40,12 +55,21 @@ class WechatChannel(Channel): # start message listener itchat.run() + + + def handle(self, msg): logger.debug("[WX]receive msg: " + json.dumps(msg, ensure_ascii=False)) from_user_id = msg['FromUserName'] to_user_id = msg['ToUserName'] # 接收人id other_user_id = msg['User']['UserName'] # 对手方id content = msg['Text'] + + # 调用敏感词检测函数 + if sw.process_text(content): + self.send('请注意文明用语', from_user_id) + return + match_prefix = self.check_prefix(content, channel_conf_val(const.WECHAT, 'single_chat_prefix')) if from_user_id == other_user_id and match_prefix is not None: # 好友向自己发送消息 @@ -79,7 +103,7 @@ class WechatChannel(Channel): group_name = msg['User'].get('NickName', None) group_id = msg['User'].get('UserName', None) if not group_name: - return "" + return None origin_content = msg['Content'] content = msg['Content'] content_list = content.split(' ', 1) @@ -89,16 +113,24 @@ class WechatChannel(Channel): elif len(content_list) == 2: content = content_list[1] - match_prefix = (msg['IsAt'] and not channel_conf_val(const.WECHAT, "group_at_off", False)) or self.check_prefix(origin_content, channel_conf_val(const.WECHAT, 'group_chat_prefix')) \ - or self.check_contain(origin_content, channel_conf_val(const.WECHAT, 'group_chat_keyword')) + # 调用敏感词检测函数 + if sw.process_text(content): + self.send('请注意文明用语', group_id) + return + + match_prefix = (msg['IsAt'] and not channel_conf_val(const.WECHAT, "group_at_off", False)) or self.check_prefix(origin_content, channel_conf_val(const.WECHAT, 'group_chat_prefix')) or self.check_contain(origin_content, channel_conf_val(const.WECHAT, 'group_chat_keyword')) + group_white_list = channel_conf_val(const.WECHAT, 'group_name_white_list') + if ('ALL_GROUP' in group_white_list or group_name in group_white_list or self.check_contain(group_name, channel_conf_val(const.WECHAT, 'group_name_keyword_white_list'))) and match_prefix: + img_match_prefix = self.check_prefix(content, channel_conf_val(const.WECHAT, 'image_create_prefix')) if img_match_prefix: content = content.split(img_match_prefix, 1)[1].strip() thread_pool.submit(self._do_send_img, content, group_id) else: thread_pool.submit(self._do_send_group, content, msg) + return None def send(self, msg, receiver): logger.info('[WX] sendMsg={}, receiver={}'.format(msg, receiver)) @@ -164,3 +196,26 @@ class WechatChannel(Channel): if content.find(ky) != -1: return True return None + + +''' +这是一个基于itchat库的微信机器人实现,支持单聊和群聊消息的自动回复和图片发送等功能。代码中使用了线程池技术和异步回调函数等方式来提高程序的性能和并发处理能力。 + +其中,WechatChannel 类实现了 Channel 接口,并定义了一些额外的方法,如发送消息、检测敏感词汇、处理单聊和群聊消息等。 + +send() 函数用于向指定用户发送文本消息; +_do_send() 函数用于处理接收到的文本消息并回复相应的内容; +_do_send_img() 函数用于处理接收到的图片消息并发送相应的图片内容; +_do_send_group() 函数用于处理接收到的群组消息并回复相应的内容; + +check_prefix() 函数用于检查消息是否以指定前缀开头; +check_contain() 函数用于检查消息是否包含指定关键字。 + +handler_single_msg() 函数和 handler_group_msg() 函数分别用于处理接收到的单聊和群聊消息,并回复相应的内容。 + +在handle() 函数中,先根据消息类型和内容进行分类和处理,然后利用线程池并发处理多个消息,提高程序的处理效率。 + +整体上来说,这段代码实现了一个简单的微信机器人,并且具有较好的可扩展性,可以通过增加不同的处理函数或者修改匹配规则等方式来实现更为丰富的功能。 +''' + + diff --git a/common/sensitive_word.py b/common/sensitive_word.py new file mode 100644 index 0000000..24f0e83 --- /dev/null +++ b/common/sensitive_word.py @@ -0,0 +1,71 @@ +import requests +import json +import os + + +class SensitiveWord: + def __init__(self): + + + # 计算配置文件绝对路径 + config_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "config.json")) + + # 读取配置文件 + with open(config_path, "r", encoding="utf-8") as f: + self.config = json.load(f) + #print(self.config) # 输出配置文件内容以进行调试 + + + # 设置请求 URL + self.url = "https://aip.baidubce.com/rest/2.0/antispam/v2/spam" + + # 获取 access token + self.access_token = self.get_access_token() + + def get_access_token(self): + """ + 获取百度云接口的 access token + + :return: str access token + """ + url = "https://aip.baidubce.com/oauth/2.0/token" + params = { + "grant_type": "client_credentials", + "client_id": self.config["common"]["client_id"], + "client_secret": self.config["common"]["client_secret"] + } + response = requests.post(url, params=params) + response_json = response.json() + + access_token = response_json.get("access_token") + + if not access_token: + raise ValueError(f"获取 access_token 失败: {response_json.get('error_description')}") + + #print(f"Access token: {access_token}") # 输出访问令牌以进行调试 + + return access_token + + + def process_text(self, text): + url = "https://aip.baidubce.com/rest/2.0/solution/v1/text_censor/v2/user_defined" # 填写 API 请求地址 + access_token = self.get_access_token() + headers = {"content-type": "application/x-www-form-urlencoded"} + params = { + "text": text.encode("utf-8"), + "access_token": access_token + } + response = requests.post(url, data=params, headers=headers) + + if response.status_code != 200: + raise ValueError(f"Failed to check sensitive words: {response.json().get('error_msg')}") + + conclusion_type = response.json().get("conclusionType") + + + print(response.json()) # 输出完整的 API 响应结果 + + if conclusion_type in [1, None]: + return False + else: + return True From b91b50bc22257a5085e5f9551cdc49257ad38f3e Mon Sep 17 00:00:00 2001 From: DBinK Date: Sun, 12 Mar 2023 12:20:50 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E9=80=BB=E8=BE=91=EF=BC=8C=E5=BD=93=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E9=85=8D=E7=BD=AE=E4=B8=BA=E7=A9=BA=E6=97=B6=E4=B8=8D?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- channel/wechat/wechat_channel.py | 18 ++++---- common/sensitive_word.py | 75 +++++++++++++++++++------------- 2 files changed, 54 insertions(+), 39 deletions(-) diff --git a/channel/wechat/wechat_channel.py b/channel/wechat/wechat_channel.py index a571805..672d5d7 100644 --- a/channel/wechat/wechat_channel.py +++ b/channel/wechat/wechat_channel.py @@ -11,9 +11,9 @@ from channel.channel import Channel from concurrent.futures import ThreadPoolExecutor from common.log import logger from common import const -from config import channel_conf_val, channel_conf +from config import channel_conf_val import requests -from urllib.parse import urlencode, quote +from urllib.parse import urlencode from common.sensitive_word import SensitiveWord @@ -67,7 +67,7 @@ class WechatChannel(Channel): # 调用敏感词检测函数 if sw.process_text(content): - self.send('请注意文明用语', from_user_id) + self.send('请检查您的输入是否有违规内容', from_user_id) return match_prefix = self.check_prefix(content, channel_conf_val(const.WECHAT, 'single_chat_prefix')) @@ -113,13 +113,16 @@ class WechatChannel(Channel): elif len(content_list) == 2: content = content_list[1] - # 调用敏感词检测函数 - if sw.process_text(content): - self.send('请注意文明用语', group_id) - return + match_prefix = (msg['IsAt'] and not channel_conf_val(const.WECHAT, "group_at_off", False)) or self.check_prefix(origin_content, channel_conf_val(const.WECHAT, 'group_chat_prefix')) or self.check_contain(origin_content, channel_conf_val(const.WECHAT, 'group_chat_keyword')) + # 如果在群里被at了 或 触发机器人关键字,则调用敏感词检测函数 + if match_prefix is True: + if sw.process_text(content): + self.send('请检查您的输入是否有违规内容', group_id) + return + group_white_list = channel_conf_val(const.WECHAT, 'group_name_white_list') if ('ALL_GROUP' in group_white_list or group_name in group_white_list or self.check_contain(group_name, channel_conf_val(const.WECHAT, 'group_name_keyword_white_list'))) and match_prefix: @@ -218,4 +221,3 @@ handler_single_msg() 函数和 handler_group_msg() 函数分别用于处理接 整体上来说,这段代码实现了一个简单的微信机器人,并且具有较好的可扩展性,可以通过增加不同的处理函数或者修改匹配规则等方式来实现更为丰富的功能。 ''' - diff --git a/common/sensitive_word.py b/common/sensitive_word.py index 24f0e83..4369eb3 100644 --- a/common/sensitive_word.py +++ b/common/sensitive_word.py @@ -27,45 +27,58 @@ class SensitiveWord: 获取百度云接口的 access token :return: str access token - """ - url = "https://aip.baidubce.com/oauth/2.0/token" - params = { - "grant_type": "client_credentials", - "client_id": self.config["common"]["client_id"], - "client_secret": self.config["common"]["client_secret"] - } - response = requests.post(url, params=params) - response_json = response.json() - - access_token = response_json.get("access_token") - - if not access_token: - raise ValueError(f"获取 access_token 失败: {response_json.get('error_description')}") - #print(f"Access token: {access_token}") # 输出访问令牌以进行调试 + """ + + #检测敏感词配置是否存在 + if self.config is not None and self.config.get("common") is not None and self.config["common"].get("type") is not None: - return access_token + url = "https://aip.baidubce.com/oauth/2.0/token" + params = { + "grant_type": "client_credentials", + "client_id": self.config["common"]["client_id"], + "client_secret": self.config["common"]["client_secret"] + } + response = requests.post(url, params=params) + response_json = response.json() + + access_token = response_json.get("access_token") + + if not access_token: + raise ValueError(f"获取 access_token 失败: {response_json.get('error_description')}") + + #print(f"Access token: {access_token}") # 输出访问令牌以进行调试 + return access_token + else: + print("百度云接口配置不存在") def process_text(self, text): - url = "https://aip.baidubce.com/rest/2.0/solution/v1/text_censor/v2/user_defined" # 填写 API 请求地址 - access_token = self.get_access_token() - headers = {"content-type": "application/x-www-form-urlencoded"} - params = { - "text": text.encode("utf-8"), - "access_token": access_token - } - response = requests.post(url, data=params, headers=headers) - if response.status_code != 200: - raise ValueError(f"Failed to check sensitive words: {response.json().get('error_msg')}") + #检测敏感词配置是否存在 + if self.config is not None and self.config.get("common") is not None and self.config["common"].get("type") is not None: + #存在则执行正常检测流程 + url = "https://aip.baidubce.com/rest/2.0/solution/v1/text_censor/v2/user_defined" # API 请求地址 + access_token = self.get_access_token() + headers = {"content-type": "application/x-www-form-urlencoded"} + params = { + "text": text.encode("utf-8"), + "access_token": access_token + } + response = requests.post(url, data=params, headers=headers) - conclusion_type = response.json().get("conclusionType") + if response.status_code != 200: + raise ValueError(f"无法连接到接口,请检查你的网络: {response.json().get('error_msg')}") + + conclusion_type = response.json().get("conclusionType") - print(response.json()) # 输出完整的 API 响应结果 + print(response.json()) # 输出完整的 API 响应结果 - if conclusion_type in [1, None]: - return False + if conclusion_type in [1, None]: + return False + else: + return True + #不存在则直接返回无敏感词 else: - return True + return False From 0376858d8733a2b3223273ff48746a34fbc29cb2 Mon Sep 17 00:00:00 2001 From: DBinK Date: Tue, 14 Mar 2023 02:51:12 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 ----- common/sensitive_word.py | 24 ++++++++++++------------ config.py | 3 +++ 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index e03e83d..06b35da 100644 --- a/.gitignore +++ b/.gitignore @@ -11,11 +11,6 @@ go-cqhttp logs/ session.token *venv -chatgpt.bat -configtmp.json -config-template.json -sensitive_words.txt -common/test_sensitive_word copy.py common/test_sensitive_word.py .vscode/launch.json sensitive_words.txt diff --git a/common/sensitive_word.py b/common/sensitive_word.py index 4369eb3..ce4012a 100644 --- a/common/sensitive_word.py +++ b/common/sensitive_word.py @@ -1,20 +1,19 @@ import requests import json import os - +import config class SensitiveWord: def __init__(self): - - - # 计算配置文件绝对路径 - config_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "config.json")) - # 读取配置文件 - with open(config_path, "r", encoding="utf-8") as f: - self.config = json.load(f) - #print(self.config) # 输出配置文件内容以进行调试 + try: + self.config = config.load_config() # 加载配置文件 + #print(self.config) # 输出配置文件内容以进行调试 + except Exception as e: + print(e) # 打印错误信息 + + print(self.config) # 设置请求 URL self.url = "https://aip.baidubce.com/rest/2.0/antispam/v2/spam" @@ -31,7 +30,7 @@ class SensitiveWord: """ #检测敏感词配置是否存在 - if self.config is not None and self.config.get("common") is not None and self.config["common"].get("type") is not None: + if self.config is not None and "common" in self.config and "type" in self.config["common"] and self.config["common"]["type"]: url = "https://aip.baidubce.com/oauth/2.0/token" params = { @@ -47,16 +46,17 @@ class SensitiveWord: if not access_token: raise ValueError(f"获取 access_token 失败: {response_json.get('error_description')}") - #print(f"Access token: {access_token}") # 输出访问令牌以进行调试 + print(f"Access token: {access_token}") # 输出访问令牌以进行调试 return access_token else: print("百度云接口配置不存在") + print(self.config) def process_text(self, text): #检测敏感词配置是否存在 - if self.config is not None and self.config.get("common") is not None and self.config["common"].get("type") is not None: + if self.config is not None and "common" in self.config and "type" in self.config["common"] and self.config["common"]["type"]: #存在则执行正常检测流程 url = "https://aip.baidubce.com/rest/2.0/solution/v1/text_censor/v2/user_defined" # API 请求地址 access_token = self.get_access_token() diff --git a/config.py b/config.py index 755421a..9509d95 100644 --- a/config.py +++ b/config.py @@ -15,6 +15,9 @@ def load_config(): config_str = read_file(config_path) # 将json字符串反序列化为dict类型 config = json.loads(config_str) + print("载入环节" ) + print(config) + return config def get_root(): return os.path.dirname(os.path.abspath( __file__ ))