diff --git a/sendNotify.py b/sendNotify.py
index 56d6aa5..032bb97 100644
--- a/sendNotify.py
+++ b/sendNotify.py
@@ -1,339 +1,538 @@
#!/usr/bin/env python3
# _*_ coding:utf-8 _*_
-
-#Modify: Kirin
-
-import sys
-import os, re
-import requests
-import json
-import time
-import hmac
-import hashlib
import base64
+import hashlib
+import hmac
+import json
+import os
+import re
+import threading
+import time
import urllib.parse
-from requests.adapters import HTTPAdapter
-from urllib3.util import Retry
+import smtplib
+from email.mime.text import MIMEText
+from email.header import Header
+from email.utils import formataddr
+
+import requests
+
+# 原先的 print 函数和主线程的锁
+_print = print
+mutex = threading.Lock()
+
+
+# 定义新的 print 函数
+def print(text, *args, **kw):
+ """
+ 使输出有序进行,不出现多线程同一时间输出导致错乱的问题。
+ """
+ with mutex:
+ _print(text, *args, **kw)
-cur_path = os.path.abspath(os.path.dirname(__file__))
-root_path = os.path.split(cur_path)[0]
-sys.path.append(root_path)
# 通知服务
-BARK = '' # bark服务,自行搜索; secrets可填;
-BARK_PUSH='' # bark自建服务器,要填完整链接,结尾的/不要
-SCKEY = '' # Server酱的SCKEY; secrets可填
-TG_BOT_TOKEN = '' # tg机器人的TG_BOT_TOKEN; secrets可填1407203283:AAG9rt-6RDaaX0HBLZQq0laNOh898iFYaRQ
-TG_USER_ID = '' # tg机器人的TG_USER_ID; secrets可填 1434078534
-TG_API_HOST='' # tg 代理api
-TG_PROXY_IP = '' # tg机器人的TG_PROXY_IP; secrets可填
-TG_PROXY_PORT = '' # tg机器人的TG_PROXY_PORT; secrets可填
-DD_BOT_ACCESS_TOKEN = '' # 钉钉机器人的DD_BOT_ACCESS_TOKEN; secrets可填
-DD_BOT_SECRET = '' # 钉钉机器人的DD_BOT_SECRET; secrets可填
-QQ_SKEY = '' # qq机器人的QQ_SKEY; secrets可填
-QQ_MODE = '' # qq机器人的QQ_MODE; secrets可填
-QYWX_AM = '' # 企业微信
-QYWX_KEY = '' # 企业微信BOT
-PUSH_PLUS_TOKEN = '' # 微信推送Plus+
+# fmt: off
+push_config = {
+ 'HITOKOTO': True, # 启用一言(随机句子)
-notify_mode = []
+ 'BARK_PUSH': '', # bark IP 或设备码,例:https://api.day.app/DxHcxxxxxRxxxxxxcm/
+ 'BARK_ARCHIVE': '', # bark 推送是否存档
+ 'BARK_GROUP': '', # bark 推送分组
+ 'BARK_SOUND': '', # bark 推送声音
+ 'BARK_ICON': '', # bark 推送图标
+ 'BARK_LEVEL': '', # bark 推送时效性
+ 'BARK_URL': '', # bark 推送跳转URL
-message_info = ''''''
+ 'CONSOLE': False, # 控制台输出
-# GitHub action运行需要填写对应的secrets
-if "BARK" in os.environ and os.environ["BARK"]:
- BARK = os.environ["BARK"]
-if "BARK_PUSH" in os.environ and os.environ["BARK_PUSH"]:
- BARK_PUSH = os.environ["BARK_PUSH"]
-if "SCKEY" in os.environ and os.environ["SCKEY"]:
- SCKEY = os.environ["SCKEY"]
-if "TG_BOT_TOKEN" in os.environ and os.environ["TG_BOT_TOKEN"] and "TG_USER_ID" in os.environ and os.environ["TG_USER_ID"]:
- TG_BOT_TOKEN = os.environ["TG_BOT_TOKEN"]
- TG_USER_ID = os.environ["TG_USER_ID"]
-if "TG_API_HOST" in os.environ and os.environ["TG_API_HOST"]:
- TG_API_HOST = os.environ["TG_API_HOST"]
-if "DD_BOT_ACCESS_TOKEN" in os.environ and os.environ["DD_BOT_ACCESS_TOKEN"] and "DD_BOT_SECRET" in os.environ and os.environ["DD_BOT_SECRET"]:
- DD_BOT_ACCESS_TOKEN = os.environ["DD_BOT_ACCESS_TOKEN"]
- DD_BOT_SECRET = os.environ["DD_BOT_SECRET"]
-if "QQ_SKEY" in os.environ and os.environ["QQ_SKEY"] and "QQ_MODE" in os.environ and os.environ["QQ_MODE"]:
- QQ_SKEY = os.environ["QQ_SKEY"]
- QQ_MODE = os.environ["QQ_MODE"]
-# 获取pushplus+ PUSH_PLUS_TOKEN
-if "PUSH_PLUS_TOKEN" in os.environ:
- if len(os.environ["PUSH_PLUS_TOKEN"]) > 1:
- PUSH_PLUS_TOKEN = os.environ["PUSH_PLUS_TOKEN"]
- # print("已获取并使用Env环境 PUSH_PLUS_TOKEN")
-# 获取企业微信应用推送 QYWX_AM
-if "QYWX_AM" in os.environ:
- if len(os.environ["QYWX_AM"]) > 1:
- QYWX_AM = os.environ["QYWX_AM"]
-
+ 'DD_BOT_SECRET': '', # 钉钉机器人的 DD_BOT_SECRET
+ 'DD_BOT_TOKEN': '', # 钉钉机器人的 DD_BOT_TOKEN
-if "QYWX_KEY" in os.environ:
- if len(os.environ["QYWX_KEY"]) > 1:
- QYWX_KEY = os.environ["QYWX_KEY"]
- # print("已获取并使用Env环境 QYWX_AM")
+ 'FSKEY': '', # 飞书机器人的 FSKEY
-if BARK:
- notify_mode.append('bark')
- # print("BARK 推送打开")
-if BARK_PUSH:
- notify_mode.append('bark')
- # print("BARK 推送打开")
-if SCKEY:
- notify_mode.append('sc_key')
- # print("Server酱 推送打开")
-if TG_BOT_TOKEN and TG_USER_ID:
- notify_mode.append('telegram_bot')
- # print("Telegram 推送打开")
-if DD_BOT_ACCESS_TOKEN and DD_BOT_SECRET:
- notify_mode.append('dingding_bot')
- # print("钉钉机器人 推送打开")
-if QQ_SKEY and QQ_MODE:
- notify_mode.append('coolpush_bot')
- # print("QQ机器人 推送打开")
+ 'GOBOT_URL': '', # go-cqhttp
+ # 推送到个人QQ:http://127.0.0.1/send_private_msg
+ # 群:http://127.0.0.1/send_group_msg
+ 'GOBOT_QQ': '', # go-cqhttp 的推送群或用户
+ # GOBOT_URL 设置 /send_private_msg 时填入 user_id=个人QQ
+ # /send_group_msg 时填入 group_id=QQ群
+ 'GOBOT_TOKEN': '', # go-cqhttp 的 access_token
-if PUSH_PLUS_TOKEN:
- notify_mode.append('pushplus_bot')
- # print("微信推送Plus机器人 推送打开")
-if QYWX_AM:
- notify_mode.append('wecom_app')
- # print("企业微信机器人 推送打开")
+ 'GOTIFY_URL': '', # gotify地址,如https://push.example.de:8080
+ 'GOTIFY_TOKEN': '', # gotify的消息应用token
+ 'GOTIFY_PRIORITY': 0, # 推送消息优先级,默认为0
-if QYWX_KEY:
- notify_mode.append('wecom_key')
- # print("企业微信机器人 推送打开")
+ 'IGOT_PUSH_KEY': '', # iGot 聚合推送的 IGOT_PUSH_KEY
+
+ 'PUSH_KEY': '', # server 酱的 PUSH_KEY,兼容旧版与 Turbo 版
+
+ 'DEER_KEY': '', # PushDeer 的 PUSHDEER_KEY
+ 'DEER_URL': '', # PushDeer 的 PUSHDEER_URL
+
+ 'CHAT_URL': '', # synology chat url
+ 'CHAT_TOKEN': '', # synology chat token
+
+ 'PUSH_PLUS_TOKEN': '', # pushplus 推送的用户令牌
+ 'PUSH_PLUS_USER': '', # pushplus 推送的群组编码
+ 'PUSH_PLUS_TEMPLATE': 'html', # pushplus 发送模板,支持html,txt,json,markdown,cloudMonitor,jenkins,route,pay
+ 'PUSH_PLUS_CHANNEL': 'wechat', # pushplus 发送渠道,支持wechat,webhook,cp,mail,sms
+ 'PUSH_PLUS_WEBHOOK': '', # pushplus webhook编码,可在pushplus公众号上扩展配置出更多渠道
+ 'PUSH_PLUS_CALLBACKURL': '', # pushplus 发送结果回调地址,会把推送最终结果通知到这个地址上
+ 'PUSH_PLUS_TO': '', # pushplus 好友令牌,微信公众号渠道填写好友令牌,企业微信渠道填写企业微信用户id
+
+ 'WE_PLUS_BOT_TOKEN': '', # 微加机器人的用户令牌
+ 'WE_PLUS_BOT_RECEIVER': '', # 微加机器人的消息接收者
+ 'WE_PLUS_BOT_VERSION': 'pro', # 微加机器人的调用版本
+
+ 'QMSG_KEY': '', # qmsg 酱的 QMSG_KEY
+ 'QMSG_TYPE': '', # qmsg 酱的 QMSG_TYPE
+
+ 'QYWX_ORIGIN': '', # 企业微信代理地址
+
+ 'QYWX_AM': '', # 企业微信应用
+
+ 'QYWX_KEY': '', # 企业微信机器人
+
+ 'TG_BOT_TOKEN': '', # tg 机器人的 TG_BOT_TOKEN,例:1407203283:AAG9rt-6RDaaX0HBLZQq0laNOh898iFYaRQ
+ 'TG_USER_ID': '', # tg 机器人的 TG_USER_ID,例:1434078534
+ 'TG_API_HOST': '', # tg 代理 api
+ 'TG_PROXY_AUTH': '', # tg 代理认证参数
+ 'TG_PROXY_HOST': '', # tg 机器人的 TG_PROXY_HOST
+ 'TG_PROXY_PORT': '', # tg 机器人的 TG_PROXY_PORT
+
+ 'AIBOTK_KEY': '', # 智能微秘书 个人中心的apikey 文档地址:http://wechat.aibotk.com/docs/about
+ 'AIBOTK_TYPE': '', # 智能微秘书 发送目标 room 或 contact
+ 'AIBOTK_NAME': '', # 智能微秘书 发送群名 或者好友昵称和type要对应好
+
+ 'SMTP_SERVER': '', # SMTP 发送邮件服务器,形如 smtp.exmail.qq.com:465
+ 'SMTP_SSL': 'false', # SMTP 发送邮件服务器是否使用 SSL,填写 true 或 false
+ 'SMTP_EMAIL': '', # SMTP 收发件邮箱,通知将会由自己发给自己
+ 'SMTP_PASSWORD': '', # SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定
+ 'SMTP_NAME': '', # SMTP 收发件人姓名,可随意填写
+
+ 'PUSHME_KEY': '', # PushMe 的 PUSHME_KEY
+ 'PUSHME_URL': '', # PushMe 的 PUSHME_URL
+
+ 'CHRONOCAT_QQ': '', # qq号
+ 'CHRONOCAT_TOKEN': '', # CHRONOCAT 的token
+ 'CHRONOCAT_URL': '', # CHRONOCAT的url地址
+
+ 'WEBHOOK_URL': '', # 自定义通知 请求地址
+ 'WEBHOOK_BODY': '', # 自定义通知 请求体
+ 'WEBHOOK_HEADERS': '', # 自定义通知 请求头
+ 'WEBHOOK_METHOD': '', # 自定义通知 请求方法
+ 'WEBHOOK_CONTENT_TYPE': '', # 自定义通知 content-type
+
+ 'NTFY_URL': '', # ntfy地址,如https://ntfy.sh
+ 'NTFY_TOPIC': '', # ntfy的消息应用topic
+ 'NTFY_PRIORITY':'3', # 推送消息优先级,默认为3
+ 'NTFY_TOKEN': '', # 推送token,可选
+ 'NTFY_USERNAME': '', # 推送用户名称,可选
+ 'NTFY_PASSWORD': '', # 推送用户密码,可选
+ 'NTFY_ACTIONS': '', # 推送用户动作,可选
+
+ 'WXPUSHER_APP_TOKEN': '', # wxpusher 的 appToken 官方文档: https://wxpusher.zjiecode.com/docs/ 管理后台: https://wxpusher.zjiecode.com/admin/
+ 'WXPUSHER_TOPIC_IDS': '', # wxpusher 的 主题ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
+ 'WXPUSHER_UIDS': '', # wxpusher 的 用户ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
+}
+# fmt: on
+
+for k in push_config:
+ if os.getenv(k):
+ v = os.getenv(k)
+ push_config[k] = v
-def message(str_msg):
- global message_info
- print(str_msg)
- message_info = "{}\n{}".format(message_info, str_msg)
- sys.stdout.flush()
-
-def bark(title, content):
- print("\n")
- if BARK:
- try:
- response = requests.get(
- f"""https://api.day.app/{BARK}/{title}/{urllib.parse.quote_plus(content)}""").json()
- if response['code'] == 200:
- print('推送成功!')
- else:
- print('推送失败!')
- except:
- print('推送失败!')
- if BARK_PUSH:
- try:
- response = requests.get(
- f"""{BARK_PUSH}/{title}/{urllib.parse.quote_plus(content)}""").json()
- if response['code'] == 200:
- print('推送成功!')
- else:
- print('推送失败!')
- except:
- print('推送失败!')
- print("bark服务启动")
- if BARK=='' and BARK_PUSH=='':
- print("bark服务的bark_token未设置!!\n取消推送")
+def bark(title: str, content: str) -> None:
+ """
+ 使用 bark 推送消息。
+ """
+ if not push_config.get("BARK_PUSH"):
return
+ print("bark 服务启动")
-def serverJ(title, content):
- print("\n")
- if not SCKEY:
- print("server酱服务的SCKEY未设置!!\n取消推送")
+ if push_config.get("BARK_PUSH").startswith("http"):
+ url = f'{push_config.get("BARK_PUSH")}'
+ else:
+ url = f'https://api.day.app/{push_config.get("BARK_PUSH")}'
+
+ bark_params = {
+ "BARK_ARCHIVE": "isArchive",
+ "BARK_GROUP": "group",
+ "BARK_SOUND": "sound",
+ "BARK_ICON": "icon",
+ "BARK_LEVEL": "level",
+ "BARK_URL": "url",
+ }
+ data = {
+ "title": title,
+ "body": content,
+ }
+ for pair in filter(
+ lambda pairs: pairs[0].startswith("BARK_")
+ and pairs[0] != "BARK_PUSH"
+ and pairs[1]
+ and bark_params.get(pairs[0]),
+ push_config.items(),
+ ):
+ data[bark_params.get(pair[0])] = pair[1]
+ headers = {"Content-Type": "application/json;charset=utf-8"}
+ response = requests.post(
+ url=url, data=json.dumps(data), headers=headers, timeout=15
+ ).json()
+
+ if response["code"] == 200:
+ print("bark 推送成功!")
+ else:
+ print("bark 推送失败!")
+
+
+def console(title: str, content: str) -> None:
+ """
+ 使用 控制台 推送消息。
+ """
+ print(f"{title}\n\n{content}")
+
+
+def dingding_bot(title: str, content: str) -> None:
+ """
+ 使用 钉钉机器人 推送消息。
+ """
+ if not push_config.get("DD_BOT_SECRET") or not push_config.get("DD_BOT_TOKEN"):
return
- print("serverJ服务启动")
+ print("钉钉机器人 服务启动")
+
+ timestamp = str(round(time.time() * 1000))
+ secret_enc = push_config.get("DD_BOT_SECRET").encode("utf-8")
+ string_to_sign = "{}\n{}".format(timestamp, push_config.get("DD_BOT_SECRET"))
+ string_to_sign_enc = string_to_sign.encode("utf-8")
+ hmac_code = hmac.new(
+ secret_enc, string_to_sign_enc, digestmod=hashlib.sha256
+ ).digest()
+ sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
+ url = f'https://oapi.dingtalk.com/robot/send?access_token={push_config.get("DD_BOT_TOKEN")}×tamp={timestamp}&sign={sign}'
+ headers = {"Content-Type": "application/json;charset=utf-8"}
+ data = {"msgtype": "text", "text": {"content": f"{title}\n\n{content}"}}
+ response = requests.post(
+ url=url, data=json.dumps(data), headers=headers, timeout=15
+ ).json()
+
+ if not response["errcode"]:
+ print("钉钉机器人 推送成功!")
+ else:
+ print("钉钉机器人 推送失败!")
+
+
+def feishu_bot(title: str, content: str) -> None:
+ """
+ 使用 飞书机器人 推送消息。
+ """
+ if not push_config.get("FSKEY"):
+ return
+ print("飞书 服务启动")
+
+ url = f'https://open.feishu.cn/open-apis/bot/v2/hook/{push_config.get("FSKEY")}'
+ data = {"msg_type": "text", "content": {"text": f"{title}\n\n{content}"}}
+ response = requests.post(url, data=json.dumps(data)).json()
+
+ if response.get("StatusCode") == 0 or response.get("code") == 0:
+ print("飞书 推送成功!")
+ else:
+ print("飞书 推送失败!错误信息如下:\n", response)
+
+
+def go_cqhttp(title: str, content: str) -> None:
+ """
+ 使用 go_cqhttp 推送消息。
+ """
+ if not push_config.get("GOBOT_URL") or not push_config.get("GOBOT_QQ"):
+ return
+ print("go-cqhttp 服务启动")
+
+ url = f'{push_config.get("GOBOT_URL")}?access_token={push_config.get("GOBOT_TOKEN")}&{push_config.get("GOBOT_QQ")}&message=标题:{title}\n内容:{content}'
+ response = requests.get(url).json()
+
+ if response["status"] == "ok":
+ print("go-cqhttp 推送成功!")
+ else:
+ print("go-cqhttp 推送失败!")
+
+
+def gotify(title: str, content: str) -> None:
+ """
+ 使用 gotify 推送消息。
+ """
+ if not push_config.get("GOTIFY_URL") or not push_config.get("GOTIFY_TOKEN"):
+ return
+ print("gotify 服务启动")
+
+ url = f'{push_config.get("GOTIFY_URL")}/message?token={push_config.get("GOTIFY_TOKEN")}'
+ data = {
+ "title": title,
+ "message": content,
+ "priority": push_config.get("GOTIFY_PRIORITY"),
+ }
+ response = requests.post(url, data=data).json()
+
+ if response.get("id"):
+ print("gotify 推送成功!")
+ else:
+ print("gotify 推送失败!")
+
+
+def iGot(title: str, content: str) -> None:
+ """
+ 使用 iGot 推送消息。
+ """
+ if not push_config.get("IGOT_PUSH_KEY"):
+ return
+ print("iGot 服务启动")
+
+ url = f'https://push.hellyw.com/{push_config.get("IGOT_PUSH_KEY")}'
+ data = {"title": title, "content": content}
+ headers = {"Content-Type": "application/x-www-form-urlencoded"}
+ response = requests.post(url, data=data, headers=headers).json()
+
+ if response["ret"] == 0:
+ print("iGot 推送成功!")
+ else:
+ print(f'iGot 推送失败!{response["errMsg"]}')
+
+
+def serverJ(title: str, content: str) -> None:
+ """
+ 通过 serverJ 推送消息。
+ """
+ if not push_config.get("PUSH_KEY"):
+ return
+ print("serverJ 服务启动")
+
+ data = {"text": title, "desp": content.replace("\n", "\n\n")}
+
+ match = re.match(r"sctp(\d+)t", push_config.get("PUSH_KEY"))
+ if match:
+ num = match.group(1)
+ url = f'https://{num}.push.ft07.com/send/{push_config.get("PUSH_KEY")}.send'
+ else:
+ url = f'https://sctapi.ftqq.com/{push_config.get("PUSH_KEY")}.send'
+
+ response = requests.post(url, data=data).json()
+
+ if response.get("errno") == 0 or response.get("code") == 0:
+ print("serverJ 推送成功!")
+ else:
+ print(f'serverJ 推送失败!错误码:{response["message"]}')
+
+
+def pushdeer(title: str, content: str) -> None:
+ """
+ 通过PushDeer 推送消息
+ """
+ if not push_config.get("DEER_KEY"):
+ return
+ print("PushDeer 服务启动")
data = {
"text": title,
- "desp": content.replace("\n", "\n\n")
+ "desp": content,
+ "type": "markdown",
+ "pushkey": push_config.get("DEER_KEY"),
}
- response = requests.post(f"https://sc.ftqq.com/{SCKEY}.send", data=data).json()
- if response['errno'] == 0:
- print('推送成功!')
+ url = "https://api2.pushdeer.com/message/push"
+ if push_config.get("DEER_URL"):
+ url = push_config.get("DEER_URL")
+
+ response = requests.post(url, data=data).json()
+
+ if len(response.get("content").get("result")) > 0:
+ print("PushDeer 推送成功!")
else:
- print('推送失败!')
+ print("PushDeer 推送失败!错误信息:", response)
-# tg通知
-def telegram_bot(title, content):
- try:
- print("\n")
- bot_token = TG_BOT_TOKEN
- user_id = TG_USER_ID
- if not bot_token or not user_id:
- print("tg服务的bot_token或者user_id未设置!!\n取消推送")
- return
- print("tg服务启动")
- if TG_API_HOST:
- if 'http' in TG_API_HOST:
- url = f"{TG_API_HOST}/bot{TG_BOT_TOKEN}/sendMessage"
- else:
- url = f"https://{TG_API_HOST}/bot{TG_BOT_TOKEN}/sendMessage"
- else:
- url = f"https://api.telegram.org/bot{TG_BOT_TOKEN}/sendMessage"
- headers = {'Content-Type': 'application/x-www-form-urlencoded'}
- payload = {'chat_id': str(TG_USER_ID), 'text': f'{title}\n\n{content}', 'disable_web_page_preview': 'true'}
- proxies = None
- if TG_PROXY_IP and TG_PROXY_PORT:
- proxyStr = "http://{}:{}".format(TG_PROXY_IP, TG_PROXY_PORT)
- proxies = {"http": proxyStr, "https": proxyStr}
- try:
- response = requests.post(url=url, headers=headers, params=payload, proxies=proxies).json()
- except:
- print('推送失败!')
- if response['ok']:
- print('推送成功!')
- else:
- print('推送失败!')
- except Exception as e:
- print(e)
+def chat(title: str, content: str) -> None:
+ """
+ 通过Chat 推送消息
+ """
+ if not push_config.get("CHAT_URL") or not push_config.get("CHAT_TOKEN"):
+ return
+ print("chat 服务启动")
+ data = "payload=" + json.dumps({"text": title + "\n" + content})
+ url = push_config.get("CHAT_URL") + push_config.get("CHAT_TOKEN")
+ response = requests.post(url, data=data)
-def dingding_bot(title, content):
- timestamp = str(round(time.time() * 1000)) # 时间戳
- secret_enc = DD_BOT_SECRET.encode('utf-8')
- string_to_sign = '{}\n{}'.format(timestamp, DD_BOT_SECRET)
- string_to_sign_enc = string_to_sign.encode('utf-8')
- hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
- sign = urllib.parse.quote_plus(base64.b64encode(hmac_code)) # 签名
- print('开始使用 钉钉机器人 推送消息...', end='')
- url = f'https://oapi.dingtalk.com/robot/send?access_token={DD_BOT_ACCESS_TOKEN}×tamp={timestamp}&sign={sign}'
- headers = {'Content-Type': 'application/json;charset=utf-8'}
+ if response.status_code == 200:
+ print("Chat 推送成功!")
+ else:
+ print("Chat 推送失败!错误信息:", response)
+
+
+def pushplus_bot(title: str, content: str) -> None:
+ """
+ 通过 pushplus 推送消息。
+ """
+ if not push_config.get("PUSH_PLUS_TOKEN"):
+ return
+ print("PUSHPLUS 服务启动")
+
+ url = "https://www.pushplus.plus/send"
data = {
- 'msgtype': 'text',
- 'text': {'content': f'{title}\n\n{content}'}
+ "token": push_config.get("PUSH_PLUS_TOKEN"),
+ "title": title,
+ "content": content,
+ "topic": push_config.get("PUSH_PLUS_USER"),
+ "template": push_config.get("PUSH_PLUS_TEMPLATE"),
+ "channel": push_config.get("PUSH_PLUS_CHANNEL"),
+ "webhook": push_config.get("PUSH_PLUS_WEBHOOK"),
+ "callbackUrl": push_config.get("PUSH_PLUS_CALLBACKURL"),
+ "to": push_config.get("PUSH_PLUS_TO"),
}
- response = requests.post(url=url, data=json.dumps(data), headers=headers, timeout=15).json()
- if not response['errcode']:
- print('推送成功!')
- else:
- print('推送失败!')
+ body = json.dumps(data).encode(encoding="utf-8")
+ headers = {"Content-Type": "application/json"}
+ response = requests.post(url=url, data=body, headers=headers).json()
-def coolpush_bot(title, content):
- print("\n")
- if not QQ_SKEY or not QQ_MODE:
- print("qq服务的QQ_SKEY或者QQ_MODE未设置!!\n取消推送")
+ code = response["code"]
+ if code == 200:
+ print("PUSHPLUS 推送请求成功,可根据流水号查询推送结果:" + response["data"])
+ print(
+ "注意:请求成功并不代表推送成功,如未收到消息,请到pushplus官网使用流水号查询推送最终结果"
+ )
+ elif code == 900 or code == 903 or code == 905 or code == 999:
+ print(response["msg"])
+
+ else:
+ url_old = "http://pushplus.hxtrip.com/send"
+ headers["Accept"] = "application/json"
+ response = requests.post(url=url_old, data=body, headers=headers).json()
+
+ if response["code"] == 200:
+ print("PUSHPLUS(hxtrip) 推送成功!")
+
+ else:
+ print("PUSHPLUS 推送失败!")
+
+
+def weplus_bot(title: str, content: str) -> None:
+ """
+ 通过 微加机器人 推送消息。
+ """
+ if not push_config.get("WE_PLUS_BOT_TOKEN"):
return
- print("qq服务启动")
- url=f"https://qmsg.zendee.cn/{QQ_MODE}/{QQ_SKEY}"
- payload = {'msg': f"{title}\n\n{content}".encode('utf-8')}
+ print("微加机器人 服务启动")
+
+ template = "txt"
+ if len(content) > 800:
+ template = "html"
+
+ url = "https://www.weplusbot.com/send"
+ data = {
+ "token": push_config.get("WE_PLUS_BOT_TOKEN"),
+ "title": title,
+ "content": content,
+ "template": template,
+ "receiver": push_config.get("WE_PLUS_BOT_RECEIVER"),
+ "version": push_config.get("WE_PLUS_BOT_VERSION"),
+ }
+ body = json.dumps(data).encode(encoding="utf-8")
+ headers = {"Content-Type": "application/json"}
+ response = requests.post(url=url, data=body, headers=headers).json()
+
+ if response["code"] == 200:
+ print("微加机器人 推送成功!")
+ else:
+ print("微加机器人 推送失败!")
+
+
+def qmsg_bot(title: str, content: str) -> None:
+ """
+ 使用 qmsg 推送消息。
+ """
+ if not push_config.get("QMSG_KEY") or not push_config.get("QMSG_TYPE"):
+ return
+ print("qmsg 服务启动")
+
+ url = f'https://qmsg.zendee.cn/{push_config.get("QMSG_TYPE")}/{push_config.get("QMSG_KEY")}'
+ payload = {"msg": f'{title}\n\n{content.replace("----", "-")}'.encode("utf-8")}
response = requests.post(url=url, params=payload).json()
- if response['code'] == 0:
- print('推送成功!')
+
+ if response["code"] == 0:
+ print("qmsg 推送成功!")
else:
- print('推送失败!')
-# push推送
-def pushplus_bot(title, content):
- try:
- print("\n")
- if not PUSH_PLUS_TOKEN:
- print("PUSHPLUS服务的token未设置!!\n取消推送")
- return
- print("PUSHPLUS服务启动")
- url = 'http://www.pushplus.plus/send'
- data = {
- "token": PUSH_PLUS_TOKEN,
- "title": title,
- "content": content
- }
- body = json.dumps(data).encode(encoding='utf-8')
- headers = {'Content-Type': 'application/json'}
- response = requests.post(url=url, data=body, headers=headers).json()
- if response['code'] == 200:
- print('推送成功!')
- else:
- print('推送失败!')
- except Exception as e:
- print(e)
+ print(f'qmsg 推送失败!{response["reason"]}')
-
-print("xxxxxxxxxxxx")
-def wecom_key(title, content):
- print("\n")
- if not QYWX_KEY:
- print("QYWX_KEY未设置!!\n取消推送")
+def wecom_app(title: str, content: str) -> None:
+ """
+ 通过 企业微信 APP 推送消息。
+ """
+ if not push_config.get("QYWX_AM"):
return
- print("QYWX_KEY服务启动")
- print("content"+content)
- headers = {'Content-Type': 'application/json'}
- data = {
- "msgtype":"text",
- "text":{
- "content":title+"\n"+content.replace("\n", "\n\n")
- }
- }
-
- print(f"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={QYWX_KEY}")
- response = requests.post(f"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={QYWX_KEY}", json=data,headers=headers).json()
- print(response)
+ QYWX_AM_AY = re.split(",", push_config.get("QYWX_AM"))
+ if 4 < len(QYWX_AM_AY) > 5:
+ print("QYWX_AM 设置错误!!")
+ return
+ print("企业微信 APP 服务启动")
-
-# 企业微信 APP 推送
-def wecom_app(title, content):
+ corpid = QYWX_AM_AY[0]
+ corpsecret = QYWX_AM_AY[1]
+ touser = QYWX_AM_AY[2]
+ agentid = QYWX_AM_AY[3]
try:
- if not QYWX_AM:
- print("QYWX_AM 并未设置!!\n取消推送")
- return
- QYWX_AM_AY = re.split(',', QYWX_AM)
- if 4 < len(QYWX_AM_AY) > 5:
- print("QYWX_AM 设置错误!!\n取消推送")
- return
- corpid = QYWX_AM_AY[0]
- corpsecret = QYWX_AM_AY[1]
- touser = QYWX_AM_AY[2]
- agentid = QYWX_AM_AY[3]
- try:
- media_id = QYWX_AM_AY[4]
- except:
- media_id = ''
- wx = WeCom(corpid, corpsecret, agentid)
- # 如果没有配置 media_id 默认就以 text 方式发送
- if not media_id:
- message = title + '\n\n' + content
- response = wx.send_text(message, touser)
- else:
- response = wx.send_mpnews(title, content, media_id, touser)
- if response == 'ok':
- print('推送成功!')
- else:
- print('推送失败!错误信息如下:\n', response)
- except Exception as e:
- print(e)
+ media_id = QYWX_AM_AY[4]
+ except IndexError:
+ media_id = ""
+ wx = WeCom(corpid, corpsecret, agentid)
+ # 如果没有配置 media_id 默认就以 text 方式发送
+ if not media_id:
+ message = title + "\n\n" + content
+ response = wx.send_text(message, touser)
+ else:
+ response = wx.send_mpnews(title, content, media_id, touser)
+
+ if response == "ok":
+ print("企业微信推送成功!")
+ else:
+ print("企业微信推送失败!错误信息如下:\n", response)
+
class WeCom:
def __init__(self, corpid, corpsecret, agentid):
self.CORPID = corpid
self.CORPSECRET = corpsecret
self.AGENTID = agentid
+ self.ORIGIN = "https://qyapi.weixin.qq.com"
+ if push_config.get("QYWX_ORIGIN"):
+ self.ORIGIN = push_config.get("QYWX_ORIGIN")
def get_access_token(self):
- url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken'
- values = {'corpid': self.CORPID,
- 'corpsecret': self.CORPSECRET,
- }
+ url = f"{self.ORIGIN}/cgi-bin/gettoken"
+ values = {
+ "corpid": self.CORPID,
+ "corpsecret": self.CORPSECRET,
+ }
req = requests.post(url, params=values)
data = json.loads(req.text)
return data["access_token"]
def send_text(self, message, touser="@all"):
- send_url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=' + self.get_access_token()
+ send_url = (
+ f"{self.ORIGIN}/cgi-bin/message/send?access_token={self.get_access_token()}"
+ )
send_values = {
"touser": touser,
"msgtype": "text",
"agentid": self.AGENTID,
- "text": {
- "content": message
- },
- "safe": "0"
+ "text": {"content": message},
+ "safe": "0",
}
- send_msges = (bytes(json.dumps(send_values), 'utf-8'))
+ send_msges = bytes(json.dumps(send_values), "utf-8")
respone = requests.post(send_url, send_msges)
respone = respone.json()
return respone["errmsg"]
def send_mpnews(self, title, message, media_id, touser="@all"):
- send_url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=' + self.get_access_token()
+ send_url = (
+ f"{self.ORIGIN}/cgi-bin/message/send?access_token={self.get_access_token()}"
+ )
send_values = {
"touser": touser,
"msgtype": "mpnews",
@@ -345,85 +544,549 @@ class WeCom:
"thumb_media_id": media_id,
"author": "Author",
"content_source_url": "",
- "content": message.replace('\n', '
'),
- "digest": message
+ "content": message.replace("\n", "
"),
+ "digest": message,
}
]
- }
+ },
}
- send_msges = (bytes(json.dumps(send_values), 'utf-8'))
+ send_msges = bytes(json.dumps(send_values), "utf-8")
respone = requests.post(send_url, send_msges)
respone = respone.json()
return respone["errmsg"]
-def send(title, content):
+
+def wecom_bot(title: str, content: str) -> None:
"""
- 使用 bark, telegram bot, dingding bot, serverJ 发送手机推送
- :param title:
- :param content:
- :return:
+ 通过 企业微信机器人 推送消息。
+ """
+ if not push_config.get("QYWX_KEY"):
+ return
+ print("企业微信机器人服务启动")
+
+ origin = "https://qyapi.weixin.qq.com"
+ if push_config.get("QYWX_ORIGIN"):
+ origin = push_config.get("QYWX_ORIGIN")
+
+ url = f"{origin}/cgi-bin/webhook/send?key={push_config.get('QYWX_KEY')}"
+ headers = {"Content-Type": "application/json;charset=utf-8"}
+ data = {"msgtype": "text", "text": {"content": f"{title}\n\n{content}"}}
+ response = requests.post(
+ url=url, data=json.dumps(data), headers=headers, timeout=15
+ ).json()
+
+ if response["errcode"] == 0:
+ print("企业微信机器人推送成功!")
+ else:
+ print("企业微信机器人推送失败!")
+
+
+def telegram_bot(title: str, content: str) -> None:
+ """
+ 使用 telegram 机器人 推送消息。
+ """
+ if not push_config.get("TG_BOT_TOKEN") or not push_config.get("TG_USER_ID"):
+ return
+ print("tg 服务启动")
+
+ if push_config.get("TG_API_HOST"):
+ url = f"{push_config.get('TG_API_HOST')}/bot{push_config.get('TG_BOT_TOKEN')}/sendMessage"
+ else:
+ url = (
+ f"https://api.telegram.org/bot{push_config.get('TG_BOT_TOKEN')}/sendMessage"
+ )
+ headers = {"Content-Type": "application/x-www-form-urlencoded"}
+ payload = {
+ "chat_id": str(push_config.get("TG_USER_ID")),
+ "text": f"{title}\n\n{content}",
+ "disable_web_page_preview": "true",
+ }
+ proxies = None
+ if push_config.get("TG_PROXY_HOST") and push_config.get("TG_PROXY_PORT"):
+ if push_config.get("TG_PROXY_AUTH") is not None and "@" not in push_config.get(
+ "TG_PROXY_HOST"
+ ):
+ push_config["TG_PROXY_HOST"] = (
+ push_config.get("TG_PROXY_AUTH")
+ + "@"
+ + push_config.get("TG_PROXY_HOST")
+ )
+ proxyStr = "http://{}:{}".format(
+ push_config.get("TG_PROXY_HOST"), push_config.get("TG_PROXY_PORT")
+ )
+ proxies = {"http": proxyStr, "https": proxyStr}
+ response = requests.post(
+ url=url, headers=headers, params=payload, proxies=proxies
+ ).json()
+
+ if response["ok"]:
+ print("tg 推送成功!")
+ else:
+ print("tg 推送失败!")
+
+
+def aibotk(title: str, content: str) -> None:
+ """
+ 使用 智能微秘书 推送消息。
+ """
+ if (
+ not push_config.get("AIBOTK_KEY")
+ or not push_config.get("AIBOTK_TYPE")
+ or not push_config.get("AIBOTK_NAME")
+ ):
+ return
+ print("智能微秘书 服务启动")
+
+ if push_config.get("AIBOTK_TYPE") == "room":
+ url = "https://api-bot.aibotk.com/openapi/v1/chat/room"
+ data = {
+ "apiKey": push_config.get("AIBOTK_KEY"),
+ "roomName": push_config.get("AIBOTK_NAME"),
+ "message": {"type": 1, "content": f"【青龙快讯】\n\n{title}\n{content}"},
+ }
+ else:
+ url = "https://api-bot.aibotk.com/openapi/v1/chat/contact"
+ data = {
+ "apiKey": push_config.get("AIBOTK_KEY"),
+ "name": push_config.get("AIBOTK_NAME"),
+ "message": {"type": 1, "content": f"【青龙快讯】\n\n{title}\n{content}"},
+ }
+ body = json.dumps(data).encode(encoding="utf-8")
+ headers = {"Content-Type": "application/json"}
+ response = requests.post(url=url, data=body, headers=headers).json()
+ print(response)
+ if response["code"] == 0:
+ print("智能微秘书 推送成功!")
+ else:
+ print(f'智能微秘书 推送失败!{response["error"]}')
+
+
+def smtp(title: str, content: str) -> None:
+ """
+ 使用 SMTP 邮件 推送消息。
+ """
+ if (
+ not push_config.get("SMTP_SERVER")
+ or not push_config.get("SMTP_SSL")
+ or not push_config.get("SMTP_EMAIL")
+ or not push_config.get("SMTP_PASSWORD")
+ or not push_config.get("SMTP_NAME")
+ ):
+ return
+ print("SMTP 邮件 服务启动")
+
+ message = MIMEText(content, "plain", "utf-8")
+ message["From"] = formataddr(
+ (
+ Header(push_config.get("SMTP_NAME"), "utf-8").encode(),
+ push_config.get("SMTP_EMAIL"),
+ )
+ )
+ message["To"] = formataddr(
+ (
+ Header(push_config.get("SMTP_NAME"), "utf-8").encode(),
+ push_config.get("SMTP_EMAIL"),
+ )
+ )
+ message["Subject"] = Header(title, "utf-8")
+
+ try:
+ smtp_server = (
+ smtplib.SMTP_SSL(push_config.get("SMTP_SERVER"))
+ if push_config.get("SMTP_SSL") == "true"
+ else smtplib.SMTP(push_config.get("SMTP_SERVER"))
+ )
+ smtp_server.login(
+ push_config.get("SMTP_EMAIL"), push_config.get("SMTP_PASSWORD")
+ )
+ smtp_server.sendmail(
+ push_config.get("SMTP_EMAIL"),
+ push_config.get("SMTP_EMAIL"),
+ message.as_bytes(),
+ )
+ smtp_server.close()
+ print("SMTP 邮件 推送成功!")
+ except Exception as e:
+ print(f"SMTP 邮件 推送失败!{e}")
+
+
+def pushme(title: str, content: str) -> None:
+ """
+ 使用 PushMe 推送消息。
+ """
+ if not push_config.get("PUSHME_KEY"):
+ return
+ print("PushMe 服务启动")
+
+ url = (
+ push_config.get("PUSHME_URL")
+ if push_config.get("PUSHME_URL")
+ else "https://push.i-i.me/"
+ )
+ data = {
+ "push_key": push_config.get("PUSHME_KEY"),
+ "title": title,
+ "content": content,
+ "date": push_config.get("date") if push_config.get("date") else "",
+ "type": push_config.get("type") if push_config.get("type") else "",
+ }
+ response = requests.post(url, data=data)
+
+ if response.status_code == 200 and response.text == "success":
+ print("PushMe 推送成功!")
+ else:
+ print(f"PushMe 推送失败!{response.status_code} {response.text}")
+
+
+def chronocat(title: str, content: str) -> None:
+ """
+ 使用 CHRONOCAT 推送消息。
+ """
+ if (
+ not push_config.get("CHRONOCAT_URL")
+ or not push_config.get("CHRONOCAT_QQ")
+ or not push_config.get("CHRONOCAT_TOKEN")
+ ):
+ return
+
+ print("CHRONOCAT 服务启动")
+
+ user_ids = re.findall(r"user_id=(\d+)", push_config.get("CHRONOCAT_QQ"))
+ group_ids = re.findall(r"group_id=(\d+)", push_config.get("CHRONOCAT_QQ"))
+
+ url = f'{push_config.get("CHRONOCAT_URL")}/api/message/send'
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f'Bearer {push_config.get("CHRONOCAT_TOKEN")}',
+ }
+
+ for chat_type, ids in [(1, user_ids), (2, group_ids)]:
+ if not ids:
+ continue
+ for chat_id in ids:
+ data = {
+ "peer": {"chatType": chat_type, "peerUin": chat_id},
+ "elements": [
+ {
+ "elementType": 1,
+ "textElement": {"content": f"{title}\n\n{content}"},
+ }
+ ],
+ }
+ response = requests.post(url, headers=headers, data=json.dumps(data))
+ if response.status_code == 200:
+ if chat_type == 1:
+ print(f"QQ个人消息:{ids}推送成功!")
+ else:
+ print(f"QQ群消息:{ids}推送成功!")
+ else:
+ if chat_type == 1:
+ print(f"QQ个人消息:{ids}推送失败!")
+ else:
+ print(f"QQ群消息:{ids}推送失败!")
+
+
+def ntfy(title: str, content: str) -> None:
+ """
+ 通过 Ntfy 推送消息
"""
- for i in notify_mode:
- if i == 'bark':
- if BARK or BARK_PUSH:
- bark(title=title, content=content)
- else:
- print('未启用 bark')
- continue
- if i == 'sc_key':
- if SCKEY:
- serverJ(title=title, content=content)
- else:
- print('未启用 Server酱')
- continue
- elif i == 'dingding_bot':
- if DD_BOT_ACCESS_TOKEN and DD_BOT_SECRET:
- dingding_bot(title=title, content=content)
- else:
- print('未启用 钉钉机器人')
- continue
- elif i == 'telegram_bot':
- if TG_BOT_TOKEN and TG_USER_ID:
- telegram_bot(title=title, content=content)
- else:
- print('未启用 telegram机器人')
- continue
- elif i == 'coolpush_bot':
- if QQ_SKEY and QQ_MODE:
- coolpush_bot(title=title, content=content)
- else:
- print('未启用 QQ机器人')
- continue
- elif i == 'pushplus_bot':
- if PUSH_PLUS_TOKEN:
- pushplus_bot(title=title, content=content)
- else:
- print('未启用 PUSHPLUS机器人')
- continue
- elif i == 'wecom_app':
- if QYWX_AM:
- wecom_app(title=title, content=content)
- else:
- print('未启用企业微信应用消息推送')
- continue
- elif i == 'wecom_key':
- if QYWX_KEY:
-
- for i in range(int(len(content)/2000)+1):
- wecom_key(title=title, content=content[i*2000:(i+1)*2000])
-
-
- else:
- print('未启用企业微信应用消息推送')
+ def encode_rfc2047(text: str) -> str:
+ """将文本编码为符合 RFC 2047 标准的格式"""
+ encoded_bytes = base64.b64encode(text.encode("utf-8"))
+ encoded_str = encoded_bytes.decode("utf-8")
+ return f"=?utf-8?B?{encoded_str}?="
+
+ if not push_config.get("NTFY_TOPIC"):
+ return
+ print("ntfy 服务启动")
+ priority = "3"
+ if not push_config.get("NTFY_PRIORITY"):
+ print("ntfy 服务的NTFY_PRIORITY 未设置!!默认设置为3")
+ else:
+ priority = push_config.get("NTFY_PRIORITY")
+
+ # 使用 RFC 2047 编码 title
+ encoded_title = encode_rfc2047(title)
+
+ data = content.encode(encoding="utf-8")
+ headers = {"Title": encoded_title, "Priority": priority, "Icon": "https://qn.whyour.cn/logo.png"} # 使用编码后的 title
+ if push_config.get("NTFY_TOKEN"):
+ headers['Authorization'] = "Bearer " + push_config.get("NTFY_TOKEN")
+ elif push_config.get("NTFY_USERNAME") and push_config.get("NTFY_PASSWORD"):
+ authStr = push_config.get("NTFY_USERNAME") + ":" + push_config.get("NTFY_PASSWORD")
+ headers['Authorization'] = "Basic " + base64.b64encode(authStr.encode('utf-8')).decode('utf-8')
+ if push_config.get("NTFY_ACTIONS"):
+ headers['Actions'] = encode_rfc2047(push_config.get("NTFY_ACTIONS"))
+
+ url = push_config.get("NTFY_URL") + "/" + push_config.get("NTFY_TOPIC")
+ response = requests.post(url, data=data, headers=headers)
+ if response.status_code == 200: # 使用 response.status_code 进行检查
+ print("Ntfy 推送成功!")
+ else:
+ print("Ntfy 推送失败!错误信息:", response.text)
+
+
+def wxpusher_bot(title: str, content: str) -> None:
+ """
+ 通过 wxpusher 推送消息。
+ 支持的环境变量:
+ - WXPUSHER_APP_TOKEN: appToken
+ - WXPUSHER_TOPIC_IDS: 主题ID, 多个用英文分号;分隔
+ - WXPUSHER_UIDS: 用户ID, 多个用英文分号;分隔
+ """
+ if not push_config.get("WXPUSHER_APP_TOKEN"):
+ return
+
+ url = "https://wxpusher.zjiecode.com/api/send/message"
+
+ # 处理topic_ids和uids,将分号分隔的字符串转为数组
+ topic_ids = []
+ if push_config.get("WXPUSHER_TOPIC_IDS"):
+ topic_ids = [
+ int(id.strip())
+ for id in push_config.get("WXPUSHER_TOPIC_IDS").split(";")
+ if id.strip()
+ ]
+
+ uids = []
+ if push_config.get("WXPUSHER_UIDS"):
+ uids = [
+ uid.strip()
+ for uid in push_config.get("WXPUSHER_UIDS").split(";")
+ if uid.strip()
+ ]
+
+ # topic_ids uids 至少有一个
+ if not topic_ids and not uids:
+ print("wxpusher 服务的 WXPUSHER_TOPIC_IDS 和 WXPUSHER_UIDS 至少设置一个!!")
+ return
+
+ print("wxpusher 服务启动")
+
+ data = {
+ "appToken": push_config.get("WXPUSHER_APP_TOKEN"),
+ "content": f"