From 2c040cfafe9f8616bff34b40148c17848381516e Mon Sep 17 00:00:00 2001 From: Jetsung Chan Date: Fri, 19 Jan 2024 17:07:57 +0800 Subject: [PATCH] feat: add wecom --- README.md | 27 ++++++----- docs/README.md | 87 +++++++++++++++++++++------------- pyproject.toml | 6 +-- src/ipush/__init__.py | 1 + src/ipush/provider/chanify.py | 6 +-- src/ipush/provider/dingtalk.py | 18 +++++-- src/ipush/provider/juhe.py | 8 ++++ src/ipush/provider/pushdeer.py | 12 +++-- src/ipush/provider/pushplus.py | 10 +++- src/ipush/provider/wecom.py | 56 ++++++++++++++++++++++ tests/conftest.py | 5 ++ tests/test_dingtalk.py | 12 +++++ tests/test_juhe.py | 15 +++++- tests/test_pushdeer.py | 18 +++++-- tests/test_pushplus.py | 12 +++++ tests/test_wecom.py | 31 ++++++++++++ 16 files changed, 259 insertions(+), 65 deletions(-) create mode 100644 src/ipush/provider/wecom.py create mode 100644 tests/test_wecom.py diff --git a/README.md b/README.md index 7c35ad1..a8db696 100644 --- a/README.md +++ b/README.md @@ -32,19 +32,20 @@ notify.send("iPush test") ## 支持平台 -| 状态 | **国内**平台 | 官网 | 备注 | -| :------- | :---------------- | :-------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------- | -| ✔ **荐** | **钉钉群机器人** | [https://open.dingtalk.com/](https://open.dingtalk.com/document/robots/customize-robot-security-settings) | -| ✔ **荐** | **飞书群机器人** | [https://open.feishu.cn/](https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot) | | -| ✔ **荐** | **Lark 群机器人** | [https://open.larksuite.com/](https://open.larksuite.com/document/client-docs/bot-v3/add-custom-bot) | | -| ✔ **荐** | **Bark** | [https://day.app/2021/06/barkfaq/](https://day.app/2021/06/barkfaq/) | 仅支持 `iOS` | -| ✔ | **Chanify** | [https://www.chanify.net/](https://www.chanify.net/) | 仅支持 `iOS` | -| ✔ | **PushDeer** | https://www.pushdeer.com/ | | -| // | // | **基于微信公众号** | \\\\ | -| ✔ | **PushPlus** | [https://www.pushplus.plus/](https://www.pushplus.plus/doc) | | -| ✔ | **Showdoc** | [https://push.showdoc.com.cn/](https://www.showdoc.com.cn/push) | | -| ✔ | **息知** | [https://xz.qqoq.net/](https://xz.qqoq.net/) | | -| ✔ | **聚合云推** | [https://tui.juhe.cn/](https://tui.juhe.cn/docs) | 聚合推送。支持 `邮箱`、`微信公众号`、`钉钉机器人`、`WebHook`、`企业微信`、`Bark` | +| 状态 | **国内**平台 | 官网 | 备注 | +| :------- | :------------------- | :--------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------- | +| ✔ **荐** | **钉钉群机器人** | [https://open.dingtalk.com/](https://open.dingtalk.com/document/robots/customize-robot-security-settings) | +| ✔ **荐** | **飞书群机器人** | [https://open.feishu.cn/](https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot) | | +| ✔ **荐** | **Lark 群机器人** | [https://open.larksuite.com/](https://open.larksuite.com/document/client-docs/bot-v3/add-custom-bot) | | +| ✔ **荐** | **Bark** | [https://day.app/2021/06/barkfaq/](https://day.app/2021/06/barkfaq/) | 仅支持 `iOS` | +| ✔ | **Chanify** | [https://www.chanify.net/](https://www.chanify.net/) | 仅支持 `iOS` | +| ✔ | **PushDeer** | https://www.pushdeer.com/ | | +| ✔ **荐** | **企业微信群机器人** | [https://developer.work.weixin.qq.com](https://developer.work.weixin.qq.com/document/path/91770?notreplace=true) | | +| // | // | **基于微信公众号** | \\\\ | +| ✔ | **PushPlus** | [https://www.pushplus.plus/](https://www.pushplus.plus/doc) | | +| ✔ | **Showdoc** | [https://push.showdoc.com.cn/](https://www.showdoc.com.cn/push) | | +| ✔ | **息知** | [https://xz.qqoq.net/](https://xz.qqoq.net/) | | +| ✔ | **聚合云推** | [https://tui.juhe.cn/](https://tui.juhe.cn/docs) | 聚合推送。支持 `邮箱`、`微信公众号`、`钉钉机器人`、`WebHook`、`企业微信`、`Bark` | | 状态 | **国外**平台 | 官网 | 备注 | | :------- | :----------- | :-------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/docs/README.md b/docs/README.md index 0d7ffae..772cf46 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,8 +15,10 @@ pip install ipush ```python from ipush import Dingtalk -notify = Dingtalk("token", "secret") -notify.send("ipush test") +notify = Dingtalk('token', "secret") +notify.send('ipush test') +# or 'text', 'markdown' +notify.settype('markdown').send('**ipush** markdown test') ``` - **飞书群** @@ -24,8 +26,8 @@ notify.send("ipush test") ```python from ipush import Feishu -notify = Feishu("token", "secret") -notify.send("ipush test") +notify = Feishu('token', "secret") +notify.send('ipush test') ``` - **Lark 群** @@ -33,8 +35,8 @@ notify.send("ipush test") ```python from ipush import Lark -notify = Lark("token", "secret") -notify.send("ipush test") +notify = Lark('token', "secret") +notify.send('ipush test') ``` - **Bark** @@ -42,10 +44,10 @@ notify.send("ipush test") ```python from ipush import Bark -notify = Bark("token") -notify.send("ipush test", "title") -notify.seturl("https://self-hosted") -notify.send("ipush test custom url") +notify = Bark('token') +notify.send('ipush test', 'title') +notify.seturl('https://self-hosted') +notify.send('ipush test custom url') ``` - **Chanify** @@ -53,10 +55,10 @@ notify.send("ipush test custom url") ```python from ipush import Chanify -notify = Chanify("token") -notify.send("ipush test") -notify.seturl("https://self-hosted") -notify.send("ipush test custom url") +notify = Chanify('token') +notify.send('ipush test') +notify.seturl('https://self-hosted') +notify.send('ipush test custom url') ``` - **PushDeer** @@ -64,10 +66,12 @@ notify.send("ipush test custom url") ```python from ipush import PushDeer -notify = PushDeer("token") -notify.send("ipush test") -notify.seturl("https://self-hosted") -notify.send("ipush test custom url") +notify = PushDeer('token') +notify.send('ipush test') +notify.seturl('https://self-hosted') +notify.send('ipush test custom url') +# or 'text', 'markdown' +notify.settype('text').send('ipush test') ``` - **PushPlus** @@ -75,8 +79,10 @@ notify.send("ipush test custom url") ```python from ipush import PushPlus -notify = PushPlus("token") -notify.send("ipush test", "title") +notify = PushPlus('token') +notify.send('ipush test', 'title') +# or 'html', 'markdown', 'txt', 'json' +notify.settemplate('markdown').send('**ipush** markdown test') ``` - **Showdoc** @@ -84,8 +90,8 @@ notify.send("ipush test", "title") ```python from ipush import Showdoc -notify = Showdoc("token") -notify.send("ipush test", "title") +notify = Showdoc('token') +notify.send('ipush test', 'title') ``` - **Xizhi** @@ -93,8 +99,8 @@ notify.send("ipush test", "title") ```python from ipush import Xizhi -notify = Xizhi("token") -notify.send("ipush test", "title") +notify = Xizhi('token') +notify.send('ipush test', 'title') ``` - **Telegram** @@ -102,10 +108,10 @@ notify.send("ipush test", "title") ```python from ipush import Telegram -notify = Telegram("token") -notify.send("ipush test", "chat_id") -notify.seturl("https://self-hosted") -notify.send("ipush test custom url") +notify = Telegram('token') +notify.send('ipush test', 'chat_id') +notify.seturl('https://self-hosted') +notify.send('ipush test custom url') ``` - **Alertzy** @@ -113,8 +119,8 @@ notify.send("ipush test custom url") ```python from ipush import Alertzy -notify = Alertzy("token") -notify.send("ipush test", "title") +notify = Alertzy('token') +notify.send('ipush test', 'title') ``` - **Notify** @@ -122,8 +128,8 @@ notify.send("ipush test", "title") ```python from ipush import Notify -notify = Notify("token", "user_id") -notify.send("ipush test", "title") +notify = Notify('token', "user_id") +notify.send('ipush test', 'title') ``` - **Juhe** @@ -131,6 +137,19 @@ notify.send("ipush test", "title") ```python from ipush import Juhe -notify = Juhe("token", "service_id") -notify.send("ipush test", "title") +notify = Juhe('token', 'service_id') +notify.send('ipush test', 'title') +# or 'html', 'markdown', 'txt', 'json' +notify.setdoctype('markdown').send('**ipush** markdown test') +``` + +- **WeCom** + +```python +from ipush import WeCom + +notify = WeCom('token') +notify.send('ipush test', 'title') +# or 'text', 'markdown' +notify.setmsgtype('markdown').send('**ipush** markdown test') ``` diff --git a/pyproject.toml b/pyproject.toml index 54a8308..6c9503f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "ipush" -version = "0.5.0" -description = "向 APP、微信平台推送通知。支持 Telegram、钉钉群、飞书群、Lark 群、Bark 等平台。" +version = "0.6.0" +description = "向 APP、微信平台推送通知。支持 Telegram、钉钉群、飞书群、企业微信群、Lark 群、Bark 等平台。" authors = [ { name = "Jetsung Chan", email = "jetsungchan@gmail.com" } ] @@ -10,7 +10,7 @@ dependencies = [ "lxml>=4.9.4", ] readme = "README.md" -keywords = ["push", "notify", "telegram", "dingtalk", "feishu", "lark", "bark", "chanify", "pushdeer", "pushplus", "showdoc", "xizhi"] +keywords = ["push", "notify", "telegram", "dingtalk", "feishu", "wechat", "lark", "bark", "chanify", "pushdeer", "pushplus", "showdoc", "xizhi"] requires-python = ">= 3.8" classifiers = [ 'Development Status :: 4 - Beta', diff --git a/src/ipush/__init__.py b/src/ipush/__init__.py index e150d30..029d9a3 100644 --- a/src/ipush/__init__.py +++ b/src/ipush/__init__.py @@ -10,4 +10,5 @@ from .provider.pushplus import PushPlus from .provider.showdoc import Showdoc from .provider.telegram import Telegram +from .provider.wecom import WeCom from .provider.xizhi import Xizhi diff --git a/src/ipush/provider/chanify.py b/src/ipush/provider/chanify.py index 416c0e5..6e855a9 100644 --- a/src/ipush/provider/chanify.py +++ b/src/ipush/provider/chanify.py @@ -16,15 +16,15 @@ def __init__(self, token=''): def _signature(self): pass - def seturl(self, url): - self.url = url - def _geturl(self): """ 生成请求的 URL """ return f'{self.url}/v1/sender/{self.token}' + def seturl(self, url): + self.url = url + def send(self, message): """ 发送通知 diff --git a/src/ipush/provider/dingtalk.py b/src/ipush/provider/dingtalk.py index 5a675b5..d2ed247 100644 --- a/src/ipush/provider/dingtalk.py +++ b/src/ipush/provider/dingtalk.py @@ -14,6 +14,7 @@ class Dingtalk(Provider): def __init__(self, token='', secret=''): self.token = token self.secret = secret + self.msgtype = 'text' def _signature(self): """ @@ -32,7 +33,15 @@ def _geturl(self, sign): """ return f'https://oapi.dingtalk.com/robot/send?access_token={self.token}{sign}' - def send(self, message): + def setmsgtype(self, msgtype): + """ + 设置消息类型 + :param msgtype: 消息类型 + """ + self.msgtype = msgtype if msgtype in ['text', 'markdown'] else 'text' + return self + + def send(self, message, title=''): """ 发送通知 :param message: 消息内容 @@ -49,9 +58,10 @@ def send(self, message): req.update_headers(headers) data = { - 'msgtype': 'text', - 'text': { - 'content': message, + 'msgtype': self.msgtype, + self.msgtype: { + 'title': '新消息' if title == '' else title, + 'content' if self.msgtype != 'markdown' else 'text': message, }, } data = json.dumps(data, indent=4) diff --git a/src/ipush/provider/juhe.py b/src/ipush/provider/juhe.py index c7957a5..e97106f 100644 --- a/src/ipush/provider/juhe.py +++ b/src/ipush/provider/juhe.py @@ -10,6 +10,7 @@ class Juhe(Provider): def __init__(self, token='', service_id=''): self.token = token self.service_id = service_id + self.doc_type = 'txt' def _signature(self): pass @@ -20,6 +21,12 @@ def _geturl(self): """ return 'https://tui.juhe.cn/api/plus/pushApi' + def setdoctype(self, doc_type): + self.doc_type = ( + doc_type if doc_type in ['html', 'markdown', 'txt', 'json'] else 'txt' + ) + return self + def send(self, message, title=''): """ 发送通知 @@ -34,6 +41,7 @@ def send(self, message, title=''): 'service_id': self.service_id, 'title': '新消息' if title == '' else title, 'content': message, + 'doc_type': self.doc_type, } req.post(req_url, data) return req.response diff --git a/src/ipush/provider/pushdeer.py b/src/ipush/provider/pushdeer.py index e8c9c16..88e2348 100644 --- a/src/ipush/provider/pushdeer.py +++ b/src/ipush/provider/pushdeer.py @@ -11,20 +11,26 @@ class PushDeer(Provider): def __init__(self, token=''): self.token = token + self.type = 'markdown' self.url = 'https://api2.pushdeer.com' def _signature(self): pass - def seturl(self, url): - self.url = url - def _geturl(self): """ 生成请求的 URL """ return f'{self.url}/message/push' + def seturl(self, url): + self.url = url + return self + + def settype(self, type): + self.type = type if type in ['text', 'markdown'] else 'markdown' + return self + def send(self, message): """ 发送通知 diff --git a/src/ipush/provider/pushplus.py b/src/ipush/provider/pushplus.py index e296a99..5d78e9e 100644 --- a/src/ipush/provider/pushplus.py +++ b/src/ipush/provider/pushplus.py @@ -11,6 +11,7 @@ class PushPlus(Provider): def __init__(self, token=''): self.token = token + self.template = 'html' def _signature(self): pass @@ -21,6 +22,12 @@ def _geturl(self): """ return 'https://www.pushplus.plus/send' + def settemplate(self, template): + self.template = ( + template if template in ['html', 'markdown', 'txt', 'json'] else 'html' + ) + return self + def send(self, message, title=''): """ 发送通知 @@ -35,9 +42,10 @@ def send(self, message, title=''): req.update_headers(headers) data = { + 'token': self.token, 'title': '新消息' if title == '' else title, 'content': message, - 'token': self.token, + 'template': self.template, } data = json.dumps(data, indent=4) req.post(req_url, data=data.encode('utf-8')) diff --git a/src/ipush/provider/wecom.py b/src/ipush/provider/wecom.py new file mode 100644 index 0000000..625291a --- /dev/null +++ b/src/ipush/provider/wecom.py @@ -0,0 +1,56 @@ +import json +import time + +from ..utils.fetch import Fetch +from ._provider import Provider + + +class WeCom(Provider): + """ + 企业微信通知 + """ + + def __init__(self, token=''): + self.token = token + self.msgtype = 'text' + + def _signature(self): + pass + + def _geturl(self): + """ + 生成请求的 URL + :param sign: 签名 + """ + return f'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={self.token}' + + def setmsgtype(self, msgtype): + """ + 设置消息类型 + :param msgtype: 消息类型 + """ + self.msgtype = msgtype if msgtype in ['text', 'markdown'] else 'text' + return self + + def send(self, message): + """ + 发送通知 + :param message: 消息内容 + """ + req_url = self._geturl() + + headers = { + 'content-type': 'application/json', + } + req = Fetch() + req.update_headers(headers) + + data = { + 'msgtype': self.msgtype, + self.msgtype: { + 'content': message, + }, + } + data = json.dumps(data, indent=4) + req.post(req_url, data=data.encode('utf-8')) + return req.response diff --git a/tests/conftest.py b/tests/conftest.py index 873dab6..3d4f1fd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,3 +14,8 @@ def message(): @pytest.fixture def custom_message(): return 'PyPush test custom url' + + +@pytest.fixture +def markdown_message(): + return '[**PyPush**](https://git.jetsung.com/idev/pypush) test markdown' diff --git a/tests/test_dingtalk.py b/tests/test_dingtalk.py index e0fb207..bed37d7 100644 --- a/tests/test_dingtalk.py +++ b/tests/test_dingtalk.py @@ -22,3 +22,15 @@ def test_dintalk(access_token, message): assert res.status_code == 200 json = res.json() assert json['errcode'] == 0 + + +@pytest.mark.skipif( + not os.environ.get('DingtalkToken'), reason='Dingtalk Token not provided' +) +def test_dintalk_markdown(access_token, markdown_message, title): + token, secret = access_token + notify = Dingtalk(token, secret) + res = notify.setmsgtype('markdown').send(markdown_message, title) + assert res.status_code == 200 + json = res.json() + assert json['errcode'] == 0 diff --git a/tests/test_juhe.py b/tests/test_juhe.py index 43f9d8a..4025975 100644 --- a/tests/test_juhe.py +++ b/tests/test_juhe.py @@ -29,10 +29,23 @@ def test_juhe(access_token, message): not os.environ.get('JuheToken') or not os.environ.get('JuheServiceID'), reason='Juhe Token not provided', ) -def test_juhe_title(access_token, title, message): +def test_juhe_title(access_token, message, title): token, service_id = access_token notify = Juhe(token, service_id) res = notify.send(message, title) assert res.status_code == 200 json = res.json() assert json['code'] == 200 + + +@pytest.mark.skipif( + not os.environ.get('JuheToken') or not os.environ.get('JuheServiceID'), + reason='Juhe Token not provided', +) +def test_juhe_markdown(access_token, markdown_message, title): + token, service_id = access_token + notify = Juhe(token, service_id) + res = notify.setdoctype('markdown').send(markdown_message, title) + assert res.status_code == 200 + json = res.json() + assert json['code'] == 200 diff --git a/tests/test_pushdeer.py b/tests/test_pushdeer.py index 8bfe05a..1967e4f 100644 --- a/tests/test_pushdeer.py +++ b/tests/test_pushdeer.py @@ -15,10 +15,10 @@ def access_token(): @pytest.mark.skipif( not os.environ.get('PushDeerToken'), reason='PushDeer Token not provided' ) -def test_PushDeer(access_token, message): +def test_pushdeer(access_token, markdown_message): token, _ = access_token notify = PushDeer(token) - res = notify.send(message) + res = notify.send(markdown_message) assert res.status_code == 200 json = res.json() assert json['code'] == 0 @@ -28,7 +28,7 @@ def test_PushDeer(access_token, message): not os.environ.get('PushDeerToken') or not os.environ.get('PushDeerCustomURL'), reason='PushDeer Token not provided', ) -def test_PushDeer_custom_url(access_token, custom_message): +def test_pushdeer_custom_url(access_token, custom_message): token, custom_url = access_token notify = PushDeer(token) notify.seturl(custom_url) @@ -36,3 +36,15 @@ def test_PushDeer_custom_url(access_token, custom_message): assert res.status_code == 200 json = res.json() assert json['code'] == 0 + + +@pytest.mark.skipif( + not os.environ.get('PushDeerToken'), reason='PushDeer Token not provided' +) +def test_pushdeer_text(access_token, message): + token, _ = access_token + notify = PushDeer(token) + res = notify.settype('text').send(message) + assert res.status_code == 200 + json = res.json() + assert json['code'] == 0 diff --git a/tests/test_pushplus.py b/tests/test_pushplus.py index 784bf67..9652aac 100644 --- a/tests/test_pushplus.py +++ b/tests/test_pushplus.py @@ -33,3 +33,15 @@ def test_pushplus_title(access_token, message, title): assert res.status_code == 200 json = res.json() assert json['code'] == 200 + + +@pytest.mark.skipif( + not os.environ.get('PushPlusToken'), reason='PushPlus Token not provided' +) +def test_pushplus_markdown(access_token, markdown_message, title): + token = access_token + notify = PushPlus(token) + res = notify.settemplate('markdown').send(markdown_message, title) + assert res.status_code == 200 + json = res.json() + assert json['code'] == 200 diff --git a/tests/test_wecom.py b/tests/test_wecom.py new file mode 100644 index 0000000..ce03a84 --- /dev/null +++ b/tests/test_wecom.py @@ -0,0 +1,31 @@ +import os + +import pytest + +from ipush import WeCom + + +@pytest.fixture +def access_token(): + token = os.environ.get('WeComToken') + return token + + +@pytest.mark.skipif(not os.environ.get('WeComToken'), reason='WeCom Token not provided') +def test_wecom(access_token, message): + token = access_token + notify = WeCom(token) + res = notify.send(message) + assert res.status_code == 200 + json = res.json() + assert json['errcode'] == 0 + + +@pytest.mark.skipif(not os.environ.get('WeComToken'), reason='WeCom Token not provided') +def test_wecom_markdown(access_token, markdown_message): + token = access_token + notify = WeCom(token) + res = notify.setmsgtype('markdown').send(markdown_message) + assert res.status_code == 200 + json = res.json() + assert json['errcode'] == 0