mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-09-12 04:27:39 +02:00
Update to 7.7.0
Since version 7.7.0, we recommend yours update python to 3.12. [+] Using nginx technology to load static files improves access speed [+] Refactor homepage, website, FTP, and database using vue3 [+] Table loading changed to skeleton screen [+] Add Website statistics-v2 professional plug-in [+] Add Home page - top 5 resource occupancy [+] Add protection for Files management (requires Tamper-proof for Enterprise 3.7) [+] Website, FTP, Databases page add program status [+] Add FTP log analysis (only supports Centos) [+] Add password-free login to phpMyAdmin [+] Add Proxy Project in Website (Supported when web service uses Nginx) [+] Add WP Toolkit (Pro version only) [+] Redesigned Docker module [+] Add WP Toolkit Protection [+] Add WP Toolkit Backup and Restore [+] Add WP Toolkit Migrated [+] Add WP Toolkit Clone site (supports new domain and subdomain) [+] Add WP Toolkit Create site from backup of other panel [+] Add WP Toolkit support for Cron automatic backup (only save Local disk) [+] Add WP Toolkit operation log [+] Add Integrity check for WP Toolkit [+] Add WP Toolkit plug-in management and themes management [*] Optimize phpMyAdmin formula access method [*] Optimize Home page PHP display problem [*] Optimize jump to the login interface after the login expires [*] Optimize automatic renewal of SSL at some times [*] Optimize Let's Encrypt to increase application success rate [-] Fix Logs Audit cannot be opened [-] Fix apache URL rewrite issue [-] Fix phpmyadmin installation problem [-] Fix the problem that some servers cannot install software [-] Fix upload file error [-] Fix left menu hiding problem [-] Fix aaPanel Mobile QR code display problem [-] Fix problem that third-party plug-ins are not displayed in the App Store [-] Fix issue where the menu bar is blank when opening new tabs [-] Fixed panel not being accessible in some cases [-] Fix the issue where Curl warning caused the inability to apply for SSL [-] Fix Quota issues for Website, FTP, Databases [-] Fix file interface display problem on mobile terminal
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
import json
|
||||
import os.path
|
||||
|
||||
from .weixin_msg import WeiXinMsg
|
||||
from .mail_msg import MailMsg
|
||||
from .web_hook_msg import WebHookMsg
|
||||
from .feishu_msg import FeiShuMsg
|
||||
from .dingding_msg import DingDingMsg
|
||||
from .sms_msg import SMSMsg
|
||||
from .wx_account_msg import WeChatAccountMsg
|
||||
from .manager import SenderManager
|
||||
from .util import read_file
|
||||
|
||||
from mod.base.push_mod import SenderConfig, PUSH_DATA_PATH
|
||||
|
||||
|
||||
# 把旧地告警系统的信息通道更新
|
||||
def update_mod_push_msg():
|
||||
|
||||
if os.path.exists(PUSH_DATA_PATH + "/update_sender.pl"):
|
||||
return
|
||||
with open(PUSH_DATA_PATH + "/update_sender.pl", "w") as f:
|
||||
f.write("")
|
||||
|
||||
WeChatAccountMsg.refresh_config(force=True)
|
||||
sms_status = False
|
||||
sc = SenderConfig()
|
||||
for conf in sc.config:
|
||||
if conf["sender_type"] == "sms":
|
||||
sms_status = True
|
||||
break
|
||||
|
||||
if not sms_status:
|
||||
sc.config.append({
|
||||
"id": sc.nwe_id(),
|
||||
"used": True,
|
||||
"sender_type": "sms",
|
||||
"data": {},
|
||||
"original": True # 标记这个通道是该类型 旧有的通道, 同时也是默认通道
|
||||
})
|
||||
|
||||
panel_data_path = "/www/server/panel/data"
|
||||
|
||||
# weixin
|
||||
if os.path.exists(panel_data_path + "/weixin.json"):
|
||||
try:
|
||||
weixin_data = json.loads(read_file(panel_data_path + "/weixin.json"))
|
||||
except:
|
||||
weixin_data = None
|
||||
|
||||
if isinstance(weixin_data, dict) and "weixin_url" in weixin_data:
|
||||
sc.config.append({
|
||||
"id": sc.nwe_id(),
|
||||
"used": True,
|
||||
"sender_type": "weixin",
|
||||
"data": {
|
||||
"url": weixin_data["weixin_url"],
|
||||
"title": "企业微信" if "title" not in weixin_data else weixin_data["title"]
|
||||
},
|
||||
"original": True
|
||||
})
|
||||
|
||||
# mail
|
||||
stmp_file = panel_data_path + "/stmp_mail.json"
|
||||
mail_list_file = panel_data_path + "/mail_list.json"
|
||||
if os.path.exists(stmp_file) and os.path.exists(mail_list_file):
|
||||
stmp_data = None
|
||||
try:
|
||||
stmp_data = json.loads(read_file(stmp_file))
|
||||
mail_list_data = json.loads(read_file(mail_list_file))
|
||||
except:
|
||||
mail_list_data = None
|
||||
|
||||
if isinstance(stmp_data, dict):
|
||||
if 'qq_mail' in stmp_data or 'qq_stmp_pwd' in stmp_data or 'hosts' in stmp_data:
|
||||
sc.config.append({
|
||||
"id": sc.nwe_id(),
|
||||
"used": True,
|
||||
"sender_type": "mail",
|
||||
"data": {
|
||||
"send": stmp_data,
|
||||
"title": "邮箱",
|
||||
"receive": [] if not mail_list_data else mail_list_data,
|
||||
},
|
||||
"original": True
|
||||
})
|
||||
|
||||
# webhook
|
||||
webhook_file = panel_data_path + "/hooks_msg.json"
|
||||
if os.path.exists(stmp_file) and os.path.exists(mail_list_file):
|
||||
try:
|
||||
webhook_data = json.loads(read_file(webhook_file))
|
||||
except:
|
||||
webhook_data = None
|
||||
|
||||
if isinstance(webhook_data, list):
|
||||
for i in webhook_data:
|
||||
i["title"] = i["name"]
|
||||
sc.config.append({
|
||||
"id": sc.nwe_id(),
|
||||
"used": True,
|
||||
"sender_type": "webhook",
|
||||
"data": i,
|
||||
})
|
||||
|
||||
# feishu
|
||||
if os.path.exists(panel_data_path + "/feishu.json"):
|
||||
try:
|
||||
feishu_data = json.loads(read_file(panel_data_path + "/feishu.json"))
|
||||
except:
|
||||
feishu_data = None
|
||||
|
||||
if isinstance(feishu_data, dict) and "feishu_url" in feishu_data:
|
||||
sc.config.append({
|
||||
"id": sc.nwe_id(),
|
||||
"used": True,
|
||||
"sender_type": "feishu",
|
||||
"data": {
|
||||
"url": feishu_data["feishu_url"],
|
||||
"title": "飞书" if "title" not in feishu_data else feishu_data["title"]
|
||||
},
|
||||
"original": True
|
||||
})
|
||||
|
||||
# dingding
|
||||
if os.path.exists(panel_data_path + "/dingding.json"):
|
||||
try:
|
||||
dingding_data = json.loads(read_file(panel_data_path + "/dingding.json"))
|
||||
except:
|
||||
dingding_data = None
|
||||
|
||||
if isinstance(dingding_data, dict) and "dingding_url" in dingding_data:
|
||||
sc.config.append({
|
||||
"id": sc.nwe_id(),
|
||||
"used": True,
|
||||
"sender_type": "dingding",
|
||||
"data": {
|
||||
"url": dingding_data["dingding_url"],
|
||||
"title": "钉钉" if "title" not in dingding_data else dingding_data["title"]
|
||||
},
|
||||
"original": True
|
||||
})
|
||||
|
||||
sc.save_config()
|
||||
read_file(PUSH_DATA_PATH + "/update_sender.pl", "")
|
||||
@@ -0,0 +1,155 @@
|
||||
# coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | 宝塔Linux面板
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2020 宝塔软件(http://www.bt.cn) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: baozi <
|
||||
# | 消息通道邮箱模块(新)
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
import re
|
||||
import json
|
||||
import requests
|
||||
import traceback
|
||||
import socket
|
||||
|
||||
import requests.packages.urllib3.util.connection as urllib3_cn
|
||||
from requests.packages import urllib3
|
||||
from typing import Optional, Union
|
||||
|
||||
from .util import write_push_log, get_test_msg
|
||||
|
||||
# 关闭警告
|
||||
urllib3.disable_warnings()
|
||||
|
||||
|
||||
class DingDingMsg:
|
||||
def __init__(self, dingding_data):
|
||||
self.id = dingding_data["id"]
|
||||
self.config = dingding_data["data"]
|
||||
|
||||
def send_msg(self, msg: str, title) -> Optional[str]:
|
||||
"""
|
||||
钉钉发送信息
|
||||
@msg 消息正文
|
||||
"""
|
||||
if not self.config:
|
||||
return '未正确配置钉钉信息'
|
||||
|
||||
# user没有时默认为空
|
||||
if "user" not in self.config:
|
||||
self.config['user'] = []
|
||||
|
||||
if "isAtAll" not in self.config:
|
||||
self.config['isAtAll'] = []
|
||||
|
||||
if not isinstance(self.config['url'], str):
|
||||
return '钉钉配置错误,请重新配置钉钉机器人'
|
||||
|
||||
at_info = ''
|
||||
for user in self.config['user']:
|
||||
if re.match(r"^[0-9]{11}$", str(user)):
|
||||
at_info += '@' + user + ' '
|
||||
|
||||
if at_info:
|
||||
msg = msg + '\n\n>' + at_info
|
||||
|
||||
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
data = {
|
||||
"msgtype": "markdown",
|
||||
"markdown": {
|
||||
"title": "服务器通知",
|
||||
"text": msg
|
||||
},
|
||||
"at": {
|
||||
"atMobiles": self.config['user'],
|
||||
"isAtAll": self.config['isAtAll']
|
||||
}
|
||||
}
|
||||
status = False
|
||||
error = None
|
||||
try:
|
||||
def allowed_gai_family():
|
||||
family = socket.AF_INET
|
||||
return family
|
||||
|
||||
allowed_gai_family_lib = urllib3_cn.allowed_gai_family
|
||||
urllib3_cn.allowed_gai_family = allowed_gai_family
|
||||
|
||||
response = requests.post(
|
||||
url=self.config["url"],
|
||||
data=json.dumps(data),
|
||||
verify=False,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
urllib3_cn.allowed_gai_family = allowed_gai_family_lib
|
||||
|
||||
if response.json()["errcode"] == 0:
|
||||
status = True
|
||||
except:
|
||||
error = traceback.format_exc()
|
||||
status = False
|
||||
|
||||
write_push_log("钉钉", status, title)
|
||||
return error
|
||||
|
||||
@classmethod
|
||||
def check_args(cls, args: dict) -> Union[dict, str]:
|
||||
if "url" not in args or "title" not in args:
|
||||
return "信息不完整"
|
||||
|
||||
title = args["title"]
|
||||
if len(title) > 15:
|
||||
return '备注名称不能超过15个字符'
|
||||
|
||||
if "user" in args and isinstance(args["user"], list):
|
||||
user = args["user"]
|
||||
else:
|
||||
user = []
|
||||
|
||||
if "atall" in args and isinstance(args["atall"], bool):
|
||||
atall = args["atall"]
|
||||
else:
|
||||
atall = True
|
||||
|
||||
data = {
|
||||
"url": args["url"],
|
||||
"user": user,
|
||||
"title": title,
|
||||
"isAtAll": atall,
|
||||
}
|
||||
|
||||
test_obj = cls({"data": data, "id": None})
|
||||
test_msg = {
|
||||
"msg_list": ['>配置状态:<font color=#20a53a>成功</font>\n\n']
|
||||
}
|
||||
|
||||
test_task = get_test_msg("面板消息通道配置提醒")
|
||||
|
||||
res = test_obj.send_msg(
|
||||
test_task.to_dingding_msg(test_msg, test_task.the_push_public_data()),
|
||||
"面板消息通道配置提醒"
|
||||
)
|
||||
if res is None:
|
||||
return data
|
||||
|
||||
return res
|
||||
|
||||
def test_send_msg(self) -> Optional[str]:
|
||||
|
||||
test_msg = {
|
||||
"msg_list": ['>配置状态:<font color=#20a53a>成功</font>\n\n']
|
||||
}
|
||||
test_task = get_test_msg("面板消息通道配置提醒")
|
||||
res = self.send_msg(
|
||||
test_task.to_dingding_msg(test_msg, test_task.the_push_public_data()),
|
||||
"面板消息通道配置提醒"
|
||||
)
|
||||
if res is None:
|
||||
return None
|
||||
return res
|
||||
@@ -0,0 +1,139 @@
|
||||
#coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | 宝塔Linux面板
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2020 宝塔软件(http://www.bt.cn) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: lx
|
||||
# | 消息通道飞书通知模块
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
import re
|
||||
import json
|
||||
import requests
|
||||
import traceback
|
||||
import socket
|
||||
|
||||
import requests.packages.urllib3.util.connection as urllib3_cn
|
||||
from requests.packages import urllib3
|
||||
from typing import Optional, Union
|
||||
|
||||
from .util import write_push_log, get_test_msg
|
||||
|
||||
# 关闭警告
|
||||
urllib3.disable_warnings()
|
||||
|
||||
|
||||
class FeiShuMsg:
|
||||
|
||||
def __init__(self, feishu_data):
|
||||
self.id = feishu_data["id"]
|
||||
self.config = feishu_data["data"]
|
||||
|
||||
@classmethod
|
||||
def check_args(cls, args: dict) -> Union[dict, str]:
|
||||
if "url" not in args or "title" not in args:
|
||||
return "信息不完整"
|
||||
|
||||
title = args["title"]
|
||||
if len(title) > 15:
|
||||
return '备注名称不能超过15个字符'
|
||||
|
||||
if "user" in args and isinstance(args["user"], list):
|
||||
user = args["user"]
|
||||
else:
|
||||
user = []
|
||||
|
||||
if "atall" in args and isinstance(args["atall"], bool):
|
||||
atall = args["atall"]
|
||||
else:
|
||||
atall = True
|
||||
|
||||
data = {
|
||||
"url": args["url"],
|
||||
"user": user,
|
||||
"title": title,
|
||||
"isAtAll": atall,
|
||||
}
|
||||
|
||||
test_obj = cls({"data": data, "id": None})
|
||||
test_msg = {
|
||||
"msg_list": ['>配置状态:成功\n\n']
|
||||
}
|
||||
|
||||
test_task = get_test_msg("消息通道配置提醒")
|
||||
|
||||
res = test_obj.send_msg(
|
||||
test_task.to_feishu_msg(test_msg, test_task.the_push_public_data()),
|
||||
"消息通道配置提醒"
|
||||
)
|
||||
if res is None:
|
||||
return data
|
||||
|
||||
return res
|
||||
|
||||
def send_msg(self, msg: str, title: str) -> Optional[str]:
|
||||
"""
|
||||
飞书发送信息
|
||||
@msg 消息正文
|
||||
"""
|
||||
if not self.config:
|
||||
return '未正确配置飞书信息。'
|
||||
|
||||
reg = '<font.+>(.+)</font>'
|
||||
tmp = re.search(reg, msg)
|
||||
if tmp:
|
||||
tmp = tmp.groups()[0]
|
||||
msg = re.sub(reg, tmp, msg)
|
||||
|
||||
if "isAtAll" not in self.config:
|
||||
self.config["isAtAll"] = True
|
||||
|
||||
if self.config["isAtAll"]:
|
||||
msg += "<at userid='all'>所有人</at>"
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
data = {
|
||||
"msg_type": "text",
|
||||
"content": {
|
||||
"text": msg
|
||||
}
|
||||
}
|
||||
status = False
|
||||
error = None
|
||||
try:
|
||||
def allowed_gai_family():
|
||||
family = socket.AF_INET
|
||||
return family
|
||||
allowed_gai_family_lib = urllib3_cn.allowed_gai_family
|
||||
urllib3_cn.allowed_gai_family = allowed_gai_family
|
||||
rdata = requests.post(
|
||||
url=self.config['url'],
|
||||
data=json.dumps(data),
|
||||
verify=False,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
).json()
|
||||
urllib3_cn.allowed_gai_family = allowed_gai_family_lib
|
||||
|
||||
if "StatusCode" in rdata and rdata["StatusCode"] == 0:
|
||||
status = True
|
||||
except:
|
||||
error = traceback.format_exc()
|
||||
|
||||
write_push_log("飞书", status, title)
|
||||
|
||||
return error
|
||||
|
||||
def test_send_msg(self) -> Optional[str]:
|
||||
test_msg = {
|
||||
"msg_list": ['>配置状态:<font color=#20a53a>成功</font>\n\n']
|
||||
}
|
||||
test_task = get_test_msg("消息通道配置提醒")
|
||||
res = self.send_msg(
|
||||
test_task.to_feishu_msg(test_msg, test_task.the_push_public_data()),
|
||||
"消息通道配置提醒"
|
||||
)
|
||||
if res is None:
|
||||
return None
|
||||
return res
|
||||
@@ -0,0 +1,135 @@
|
||||
#coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | 宝塔Linux面板
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2020 宝塔软件(http://www.bt.cn) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: 沐落 <cjx@bt.cn>
|
||||
# | Author: lx
|
||||
# | 消息通道邮箱模块
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
import smtplib
|
||||
import traceback
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import formataddr
|
||||
from typing import Tuple, Union, Optional
|
||||
|
||||
from mod.base.msg.util import write_push_log, write_mail_push_log, get_test_msg
|
||||
|
||||
|
||||
class MailMsg:
|
||||
|
||||
def __init__(self, mail_data):
|
||||
self.id = mail_data["id"]
|
||||
self.config = mail_data["data"]
|
||||
|
||||
@classmethod
|
||||
def check_args(cls, args: dict) -> Tuple[bool, Union[dict, str]]:
|
||||
if "send" not in args or "receive" not in args or len(args["receive"]) < 1:
|
||||
return False, "信息不完整,必须有发送方和至少一个接收方"
|
||||
|
||||
if "title" not in args:
|
||||
return False, "没有必要的备注信息"
|
||||
|
||||
title = args["title"]
|
||||
if len(title) > 15:
|
||||
return False, '备注名称不能超过15个字符'
|
||||
|
||||
send_data = args["send"]
|
||||
send = {}
|
||||
for i in ("qq_mail", "qq_stmp_pwd", "hosts", "port"):
|
||||
if i not in send_data:
|
||||
return False, "发送方配置信息不完整"
|
||||
send[i] = send_data[i].strip()
|
||||
|
||||
receive_data = args["receive"]
|
||||
if isinstance(receive_data, str):
|
||||
receive_list = [i.strip() for i in receive_data.split("\n") if i.strip()]
|
||||
else:
|
||||
receive_list = [i.strip() for i in receive_data if i.strip()]
|
||||
|
||||
data = {
|
||||
"send": send,
|
||||
"title": title,
|
||||
"receive": receive_list,
|
||||
}
|
||||
|
||||
test_obj = cls({"data": data, "id": None})
|
||||
test_msg = {
|
||||
"msg_list": ['>配置状态:成功<br>']
|
||||
}
|
||||
|
||||
test_task = get_test_msg("消息通道配置提醒")
|
||||
|
||||
res = test_obj.send_msg(
|
||||
test_task.to_mail_msg(test_msg, test_task.the_push_public_data()),
|
||||
"消息通道配置提醒"
|
||||
)
|
||||
if res is None or res.find("部分接收者时失败") != -1:
|
||||
return True, data
|
||||
|
||||
return False, res
|
||||
|
||||
def send_msg(self, msg: str, title: str):
|
||||
"""
|
||||
邮箱发送
|
||||
@msg 消息正文
|
||||
@title 消息标题
|
||||
"""
|
||||
if not self.config:
|
||||
return '未正确配置邮箱信息。'
|
||||
|
||||
if 'port' not in self.config['send']:
|
||||
self.config['send']['port'] = 465
|
||||
|
||||
receive_list = self.config['receive']
|
||||
|
||||
error_list, success_list = [], []
|
||||
error_msg_dict = {}
|
||||
for email in receive_list:
|
||||
if not email.strip():
|
||||
continue
|
||||
try:
|
||||
data = MIMEText(msg, 'html', 'utf-8')
|
||||
data['From'] = formataddr((self.config['send']['qq_mail'], self.config['send']['qq_mail']))
|
||||
data['To'] = formataddr((self.config['send']['qq_mail'], email.strip()))
|
||||
data['Subject'] = title
|
||||
if int(self.config['send']['port']) == 465:
|
||||
server = smtplib.SMTP_SSL(str(self.config['send']['hosts']), int(self.config['send']['port']))
|
||||
else:
|
||||
server = smtplib.SMTP(str(self.config['send']['hosts']), int(self.config['send']['port']))
|
||||
|
||||
server.login(self.config['send']['qq_mail'], self.config['send']['qq_stmp_pwd'])
|
||||
server.sendmail(self.config['send']['qq_mail'], [email.strip(), ], data.as_string())
|
||||
server.quit()
|
||||
success_list.append(email)
|
||||
except:
|
||||
error_list.append(email)
|
||||
error_msg_dict[email] = traceback.format_exc()
|
||||
|
||||
if not error_list and not success_list: # 没有接收者
|
||||
return "未配置接收邮箱"
|
||||
if not error_list:
|
||||
write_push_log("邮箱", True, title, success_list) # 没有失败
|
||||
return None
|
||||
if not success_list:
|
||||
write_push_log("邮箱", False, title, error_list) # 全都失败
|
||||
return "发送信息失败, 发送失败的接收人:{}".format(error_list)
|
||||
write_mail_push_log(title, error_list, success_list)
|
||||
|
||||
return "发送邮件到部分接收者时失败,包含:{}".format(error_list)
|
||||
|
||||
def test_send_msg(self) -> Optional[str]:
|
||||
test_msg = {
|
||||
"msg_list": ['>配置状态:<font color=#20a53a>成功</font>\n\n']
|
||||
}
|
||||
test_task = get_test_msg("消息通道配置提醒")
|
||||
res = self.send_msg(
|
||||
test_task.to_mail_msg(test_msg, test_task.the_push_public_data()),
|
||||
"消息通道配置提醒"
|
||||
)
|
||||
if res is None:
|
||||
return None
|
||||
return res
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from mod.base.push_mod import SenderConfig
|
||||
from .weixin_msg import WeiXinMsg
|
||||
from .mail_msg import MailMsg
|
||||
from .web_hook_msg import WebHookMsg
|
||||
from .feishu_msg import FeiShuMsg
|
||||
from .dingding_msg import DingDingMsg
|
||||
from .sms_msg import SMSMsg
|
||||
from .wx_account_msg import WeChatAccountMsg
|
||||
import json
|
||||
from mod.base import json_response
|
||||
from .util import write_file, read_file
|
||||
import sys,os
|
||||
sys.path.insert(0, "/www/server/panel/class/")
|
||||
import public
|
||||
|
||||
# 短信会自动添加到 sender 库中的第一个 且通过官方接口更新
|
||||
# 微信公众号信息通过官网接口更新, 不写入数据库,需要时由文件中读取并序列化
|
||||
# 其他告警通道本质都类似于web hook 在确认完数据信息无误后,都可以自行添加或启用
|
||||
class SenderManager:
|
||||
def __init__(self):
|
||||
self.custom_parameter_filename = "/www/server/panel/data/mod_push_data/custom_parameter.pl"
|
||||
|
||||
def set_sender_conf(self, get):
|
||||
sender_id = None
|
||||
try:
|
||||
if hasattr(get, "sender_id"):
|
||||
sender_id = get.sender_id.strip()
|
||||
if not sender_id:
|
||||
sender_id = None
|
||||
sender_type = get.sender_type.strip()
|
||||
args = json.loads(get.sender_data.strip())
|
||||
except (json.JSONDecoder, AttributeError, TypeError):
|
||||
return json_response(status=False, msg="参数错误")
|
||||
sender_config = SenderConfig()
|
||||
if sender_id is not None:
|
||||
tmp = sender_config.get_by_id(sender_id)
|
||||
if tmp is None:
|
||||
sender_id = None
|
||||
|
||||
if sender_type == "weixin":
|
||||
data = WeiXinMsg.check_args(args)
|
||||
if isinstance(data, str):
|
||||
return json_response(status=False, data=data, msg="测试发送失败")
|
||||
|
||||
elif sender_type == "mail":
|
||||
_, data = MailMsg.check_args(args)
|
||||
if isinstance(data, str):
|
||||
return json_response(status=False, data=data, msg="测试发送失败")
|
||||
|
||||
elif sender_type == "webhook":
|
||||
custom_parameter = args.get("custom_parameter", {})
|
||||
if custom_parameter:
|
||||
try:
|
||||
public.writeFile(self.custom_parameter_filename, json.dumps(custom_parameter))
|
||||
except:
|
||||
pass
|
||||
|
||||
# 检查参数
|
||||
data = WebHookMsg.check_args(args)
|
||||
if isinstance(data, str):
|
||||
return json_response(status=False, data=data, msg="测试发送失败")
|
||||
|
||||
# 从文件读取并删除文件
|
||||
try:
|
||||
if os.path.exists(self.custom_parameter_filename):
|
||||
custom_parameter = json.loads(public.readFile(self.custom_parameter_filename))
|
||||
data['custom_parameter'] = custom_parameter
|
||||
os.remove(self.custom_parameter_filename)
|
||||
except:
|
||||
pass
|
||||
|
||||
elif sender_type == "feishu":
|
||||
data = FeiShuMsg.check_args(args)
|
||||
if isinstance(data, str):
|
||||
return json_response(status=False, data=data, msg="测试发送失败")
|
||||
|
||||
elif sender_type == "dingding":
|
||||
data = DingDingMsg.check_args(args)
|
||||
if isinstance(data, str):
|
||||
return json_response(status=False, data=data, msg="测试发送失败")
|
||||
else:
|
||||
return json_response(status=False, msg="当前接口不适应的类型")
|
||||
# Check if the sender configuration already exists
|
||||
existing_sender = any(
|
||||
conf for conf in sender_config.config
|
||||
if conf['sender_type'] == sender_type and 'title' in conf['data'] and conf['data']['title'] == data['title'] and conf['id'] != sender_id
|
||||
)
|
||||
if existing_sender:
|
||||
return json_response(status=False, msg="同样的发送配置已存在,无法重复添加")
|
||||
now_sender_id = None
|
||||
if not sender_id:
|
||||
now_sender_id = sender_config.nwe_id()
|
||||
sender_config.config.append(
|
||||
{
|
||||
"id": now_sender_id,
|
||||
"sender_type": sender_type,
|
||||
"data": data,
|
||||
"used": True,
|
||||
})
|
||||
|
||||
else:
|
||||
now_sender_id = sender_id
|
||||
tmp = sender_config.get_by_id(sender_id)
|
||||
tmp["data"].update(data)
|
||||
|
||||
type_senders = [conf for conf in sender_config.config if conf['sender_type'] == sender_type]
|
||||
if len(type_senders) == 1:
|
||||
for conf in sender_config.config:
|
||||
conf["original"] = (conf['id'] == now_sender_id)
|
||||
|
||||
sender_config.save_config()
|
||||
if sender_type == "webhook":
|
||||
self.set_default_for_compatible(sender_config.get_by_id(now_sender_id))
|
||||
|
||||
return json_response(status=True, msg="保存成功")
|
||||
|
||||
@staticmethod
|
||||
def change_sendr_used(get):
|
||||
try:
|
||||
sender_id = get.sender_id.strip()
|
||||
except (AttributeError, TypeError):
|
||||
return json_response(status=False, msg="参数错误")
|
||||
|
||||
sender_config = SenderConfig()
|
||||
tmp = sender_config.get_by_id(sender_id)
|
||||
if tmp is None:
|
||||
return json_response(status=False, msg="未找到对应发送者")
|
||||
tmp["used"] = not tmp["used"]
|
||||
|
||||
sender_config.save_config()
|
||||
|
||||
return json_response(status=True, msg="保存成功")
|
||||
|
||||
@staticmethod
|
||||
def remove_sender(get):
|
||||
try:
|
||||
sender_id = get.sender_id.strip()
|
||||
except (AttributeError, TypeError):
|
||||
return json_response(status=False, msg="参数错误")
|
||||
|
||||
sender_config = SenderConfig()
|
||||
tmp = sender_config.get_by_id(sender_id)
|
||||
if tmp is None:
|
||||
return json_response(status=False, msg="未找到对应发送者")
|
||||
sender_config.config.remove(tmp)
|
||||
sender_config.save_config()
|
||||
|
||||
return json_response(status=True, msg="删除成功")
|
||||
|
||||
@staticmethod
|
||||
def get_sender_list(get):
|
||||
# 微信, 飞书, 钉钉, web-hook, 邮箱
|
||||
refresh = False
|
||||
try:
|
||||
if hasattr(get, 'refresh'):
|
||||
refresh = get.refresh.strip()
|
||||
if refresh in ("1", "true"):
|
||||
refresh = True
|
||||
except (AttributeError, TypeError):
|
||||
return json_response(status=False, msg="参数错误")
|
||||
|
||||
res = []
|
||||
WeChatAccountMsg.refresh_config(force=refresh)
|
||||
simple = ("weixin", "mail", "webhook", "feishu", "dingding")
|
||||
|
||||
for conf in SenderConfig().config:
|
||||
if conf["sender_type"] in simple or conf["sender_type"] == "wx_account":
|
||||
res.append(conf)
|
||||
elif conf["sender_type"] == "sms":
|
||||
conf["data"] = SMSMsg(conf).refresh_config(force=refresh)
|
||||
res.append(conf)
|
||||
res.sort(key=lambda x: x["sender_type"])
|
||||
return json_response(status=True, data=res)
|
||||
|
||||
@staticmethod
|
||||
def test_send_msg(get):
|
||||
try:
|
||||
sender_id = get.sender_id.strip()
|
||||
except (json.JSONDecoder, AttributeError, TypeError):
|
||||
return json_response(status=False, msg="参数错误")
|
||||
|
||||
sender_config = SenderConfig()
|
||||
tmp = sender_config.get_by_id(sender_id)
|
||||
if tmp is None:
|
||||
return json_response(status=False, msg="未找到对应发送者")
|
||||
|
||||
sender_type = tmp["sender_type"]
|
||||
|
||||
if sender_type == "weixin":
|
||||
sender_obj = WeiXinMsg(tmp)
|
||||
|
||||
elif sender_type == "mail":
|
||||
sender_obj = MailMsg(tmp)
|
||||
|
||||
elif sender_type == "webhook":
|
||||
sender_obj = WebHookMsg(tmp)
|
||||
|
||||
elif sender_type == "feishu":
|
||||
sender_obj = FeiShuMsg(tmp)
|
||||
|
||||
elif sender_type == "dingding":
|
||||
sender_obj = DingDingMsg(tmp)
|
||||
|
||||
elif sender_type == "wx_account":
|
||||
sender_obj = WeChatAccountMsg(tmp)
|
||||
else:
|
||||
return json_response(status=False, msg="当前接口不适应的类型")
|
||||
|
||||
res = sender_obj.test_send_msg()
|
||||
if isinstance(res, str):
|
||||
return json_response(status=False, data=res, msg="测试发送失败")
|
||||
return json_response(status=True, msg="发送成功")
|
||||
|
||||
@staticmethod
|
||||
def set_default_for_compatible(sender_data: dict):
|
||||
if sender_data["sender_type"] in ("sms", "wx_account"):
|
||||
return
|
||||
|
||||
panel_data = "/www/server/panel/data"
|
||||
if sender_data["sender_type"] == "weixin":
|
||||
weixin_file = "{}/weixin.json".format(panel_data)
|
||||
write_file(weixin_file, json.dumps({
|
||||
"state": 1,
|
||||
"weixin_url": sender_data["data"]["url"],
|
||||
"title": sender_data["data"]["title"],
|
||||
"list": {
|
||||
"default": {
|
||||
"data": sender_data["data"]["url"],
|
||||
"title": sender_data["data"]["title"],
|
||||
"status": 1,
|
||||
"addtime": int(time.time())
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
elif sender_data["sender_type"] == "mail":
|
||||
stmp_mail_file = "{}/stmp_mail.json".format(panel_data)
|
||||
mail_list_file = "{}/mail_list.json".format(panel_data)
|
||||
write_file(stmp_mail_file, json.dumps(sender_data["data"]["send"]))
|
||||
write_file(mail_list_file, json.dumps(sender_data["data"]["receive"]))
|
||||
|
||||
elif sender_data["sender_type"] == "feishu":
|
||||
feishu_file = "{}/feishu.json".format(panel_data)
|
||||
write_file(feishu_file, json.dumps({
|
||||
"feishu_url": sender_data["data"]["url"],
|
||||
"title": sender_data["data"]["title"],
|
||||
"isAtAll": True,
|
||||
"user": []
|
||||
}))
|
||||
|
||||
elif sender_data["sender_type"] == "dingding":
|
||||
dingding_file = "{}/dingding.json".format(panel_data)
|
||||
write_file(dingding_file, json.dumps({
|
||||
"dingding_url": sender_data["data"]["url"],
|
||||
"title": sender_data["data"]["title"],
|
||||
"isAtAll": True,
|
||||
"user": []
|
||||
}))
|
||||
|
||||
elif sender_data["sender_type"] == "webhook":
|
||||
webhook_file = "{}/hooks_msg.json".format(panel_data)
|
||||
try:
|
||||
webhook_data = json.loads(read_file(webhook_file))
|
||||
except:
|
||||
webhook_data =[]
|
||||
target_idx = -1
|
||||
for idx, i in enumerate(webhook_data):
|
||||
if i["name"] == sender_data["data"]["title"]:
|
||||
target_idx = idx
|
||||
break
|
||||
else:
|
||||
sender_data["data"]["name"] = sender_data["data"]["title"]
|
||||
webhook_data.append(sender_data["data"])
|
||||
if target_idx != -1:
|
||||
sender_data["data"]["name"] = sender_data["data"]["title"]
|
||||
webhook_data[target_idx] = sender_data["data"]
|
||||
write_file(webhook_file, json.dumps(webhook_data))
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
# coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | 宝塔Linux面板
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2020 宝塔软件(http://www.bt.cn) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: baozi
|
||||
# | 消息通道 短信模块(新)
|
||||
# +-------------------------------------------------------------------
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
from typing import Union, Optional
|
||||
from mod.base.push_mod import SenderConfig
|
||||
from .util import write_push_log, PANEL_PATH, write_file, read_file, public_http_post
|
||||
|
||||
|
||||
class SMSMsg:
|
||||
API_URL = 'http://www.bt.cn/api/wmsg'
|
||||
USER_PATH = '{}/data/userInfo.json'.format(PANEL_PATH)
|
||||
|
||||
# 构造方法
|
||||
def __init__(self, msm_data: dict):
|
||||
self.id = msm_data["id"]
|
||||
self.data = msm_data["data"]
|
||||
self.user_info = None
|
||||
try:
|
||||
self.user_info = json.loads(read_file(self.USER_PATH))
|
||||
except:
|
||||
self.user_info = None
|
||||
|
||||
self._PDATA = {
|
||||
"access_key": "" if self.user_info is None else self.user_info["access_key"],
|
||||
"data": {}
|
||||
}
|
||||
|
||||
def refresh_config(self, force=False):
|
||||
if "last_refresh_time" not in self.data:
|
||||
self.data["last_refresh_time"] = 0
|
||||
if self.data.get("last_refresh_time") + 60 * 60 * 24 < time.time() or force: # 一天最多更新一次
|
||||
result = self._request('get_user_sms')
|
||||
if not isinstance(result, dict) or ("status" in result and not result["status"]):
|
||||
return {
|
||||
"count": 0,
|
||||
"total": 0
|
||||
}
|
||||
sc = SenderConfig()
|
||||
tmp = sc.get_by_id(self.id)
|
||||
if tmp is not None:
|
||||
result["last_refresh_time"] = time.time()
|
||||
tmp["data"] = result
|
||||
sc.save_config()
|
||||
else:
|
||||
result = self.data
|
||||
return result
|
||||
|
||||
def send_msg(self, sm_type: str, sm_args: dict):
|
||||
"""
|
||||
@发送短信
|
||||
@sm_type 预警类型, ssl_end|宝塔SSL到期提醒
|
||||
@sm_args 预警参数
|
||||
"""
|
||||
if not self.user_info:
|
||||
return "未成功绑定官网账号,无法发送信息,请尝试重新绑定"
|
||||
tmp = sm_type.split('|')
|
||||
if "|" in sm_type and len(tmp) >= 2:
|
||||
s_type = tmp[0]
|
||||
title = tmp[1]
|
||||
else:
|
||||
s_type = sm_type
|
||||
title = '宝塔告警提醒'
|
||||
|
||||
sm_args = self.canonical_data(sm_args)
|
||||
self._PDATA['data']['sm_type'] = s_type
|
||||
self._PDATA['data']['sm_args'] = sm_args
|
||||
print(s_type)
|
||||
print(sm_args)
|
||||
result = self._request('send_msg')
|
||||
u_key = '{}****{}'.format(self.user_info['username'][:3], self.user_info['username'][-3:])
|
||||
print(result)
|
||||
if isinstance(result, str):
|
||||
write_push_log("短信", False, title, [u_key])
|
||||
return result
|
||||
|
||||
if result['status']:
|
||||
write_push_log("短信", True, title, [u_key])
|
||||
return None
|
||||
else:
|
||||
write_push_log("短信", False, title, [u_key])
|
||||
return result.get("msg", "发送错误")
|
||||
|
||||
@staticmethod
|
||||
def canonical_data(args):
|
||||
"""规范数据内容"""
|
||||
if not isinstance(args, dict):
|
||||
return args
|
||||
new_args = {}
|
||||
for param, value in args.items():
|
||||
if type(value) != str:
|
||||
new_str = str(value)
|
||||
else:
|
||||
new_str = value.replace(".", "_").replace("+", "+")
|
||||
new_args[param] = new_str
|
||||
return new_args
|
||||
|
||||
def push_data(self, data):
|
||||
return self.send_msg(data['sm_type'], data['sm_args'])
|
||||
|
||||
# 发送请求
|
||||
def _request(self, d_name: str) -> Union[dict, str]:
|
||||
pdata = {
|
||||
'access_key': self._PDATA['access_key'],
|
||||
'data': json.dumps(self._PDATA['data'])
|
||||
}
|
||||
try:
|
||||
result = public_http_post(self.API_URL + '/' + d_name, pdata)
|
||||
result = json.loads(result)
|
||||
return result
|
||||
except Exception:
|
||||
return traceback.format_exc()
|
||||
@@ -0,0 +1,105 @@
|
||||
[
|
||||
{
|
||||
"id": "f4e98e478b85e876",
|
||||
"used": true,
|
||||
"sender_type": "sms",
|
||||
"data": {}
|
||||
},
|
||||
{
|
||||
"id": "fb3e9e409b9d7c27",
|
||||
"sender_type": "mail",
|
||||
"data": {
|
||||
"send": {
|
||||
"qq_mail": "1191604998@qq.com",
|
||||
"qq_stmp_pwd": "alvonbfcwhlahbcg",
|
||||
"hosts": "smtp.qq.com",
|
||||
"port": "465"
|
||||
},
|
||||
"title": "test_mail",
|
||||
"receive": [
|
||||
"1191604998@qq.com",
|
||||
"225326944@qq.com"
|
||||
]
|
||||
},
|
||||
"used": true
|
||||
},
|
||||
{
|
||||
"id": "79900d4fb37fa83d",
|
||||
"sender_type": "feishu",
|
||||
"data": {
|
||||
"url": "https://open.feishu.cn/open-apis/bot/v2/hook/ba6a3f77-0349-4492-a8ad-0b4c99435bf1",
|
||||
"user": [],
|
||||
"title": "test_feishu",
|
||||
"isAtAll": true
|
||||
},
|
||||
"used": true
|
||||
},
|
||||
{
|
||||
"id": "8f70de4baa89133e",
|
||||
"sender_type": "webhook",
|
||||
"data": {
|
||||
"title": "webhook",
|
||||
"url": "http://192.168.69.172:11211",
|
||||
"query": {},
|
||||
"headers": {},
|
||||
"body_type": "json",
|
||||
"custom_parameter": {},
|
||||
"method": "POST",
|
||||
"ssl_verify": null,
|
||||
"status": true
|
||||
},
|
||||
"used": true
|
||||
},
|
||||
{
|
||||
"id": "10bcf5439299d9dd",
|
||||
"used": true,
|
||||
"sender_type": "wx_account",
|
||||
"data": {
|
||||
"id": "jsbRCBBinMmFjYjczNTQyYmUzxNDiWQw",
|
||||
"uid": 1228262,
|
||||
"is_subscribe": 1,
|
||||
"head_img": "https://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83epBUaqBcCkkxtKwuaOHLy1qjGeDvmf1hZsrkFGNrldyRgSuA3sYB1xlgKv1Z98PUciaxju71PUKchA/132",
|
||||
"nickname": "沈涛",
|
||||
"status": 1,
|
||||
"create_time": "2023-12-27 11:30:15",
|
||||
"update_time": "2023-12-27 11:30:15",
|
||||
"remaining": 98,
|
||||
"title": "沈涛"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "2c7c094eb23ddaae",
|
||||
"used": true,
|
||||
"sender_type": "webhook",
|
||||
"data": {
|
||||
"url": "http://192.168.69.159:8888/hook?access_key=IUSEViIMMhQio1WyP0ztCyoa8sIBjaWulihhcJX4rRJ4sW79",
|
||||
"query": {},
|
||||
"headers": {},
|
||||
"body_type": "json",
|
||||
"custom_parameter": {},
|
||||
"method": "GET",
|
||||
"ssl_verify": 1,
|
||||
"status": true,
|
||||
"name": "aaa",
|
||||
"title": "aaa"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "63c30845916fa722",
|
||||
"used": true,
|
||||
"sender_type": "feishu",
|
||||
"data": {
|
||||
"url": "https://open.feishu.cn/open-apis/bot/v2/hook/c6906d9f-01c5-4a74-80bd-3ccda33bf4ec",
|
||||
"title": "amber"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "6ccf834a95010bed",
|
||||
"used": true,
|
||||
"sender_type": "dingding",
|
||||
"data": {
|
||||
"url": "https://oapi.dingtalk.com/robot/send?access_token=00732dec605edc1c07f441eb9d470c8bdfa301c4ce89959916fe535d08c09043",
|
||||
"title": "dd"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,139 @@
|
||||
import sys
|
||||
from typing import Optional, List, Tuple
|
||||
from mod.base.push_mod import BaseTask, WxAccountMsgBase, WxAccountMsg, get_push_public_data
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
|
||||
import public
|
||||
|
||||
|
||||
PANEL_PATH = "/www/server/panel"
|
||||
public_http_post = public.httpPost
|
||||
|
||||
|
||||
def write_push_log(
|
||||
module_name: str,
|
||||
status: bool,
|
||||
title: str,
|
||||
user: Optional[List[str]] = None):
|
||||
"""
|
||||
记录 告警推送情况
|
||||
@param module_name: 通道方式
|
||||
@param status: 是否成功
|
||||
@param title: 标题
|
||||
@param user: 推送到的用户,可以为空,如:钉钉 不需要
|
||||
@return:
|
||||
"""
|
||||
if status:
|
||||
status_str = '<span style="color:#20a53a;">成功</span>'
|
||||
else:
|
||||
status_str = '<span style="color:red;">失败</span>'
|
||||
|
||||
if not user:
|
||||
user_str = '[ 默认 ]'
|
||||
else:
|
||||
user_str = '[ {} ]'.format(",".join(user))
|
||||
|
||||
log = '标题:【{}】,通知方式:【{}】,结果:【{}】,收件人:{}'.format(title, module_name, status_str, user_str)
|
||||
public.WriteLog('告警通知', log)
|
||||
return True
|
||||
|
||||
|
||||
def write_mail_push_log(
|
||||
title: str,
|
||||
error_user: List[str],
|
||||
success_user: List[str],
|
||||
):
|
||||
"""
|
||||
记录 告警推送情况
|
||||
@param title: 标题
|
||||
@param error_user: 失败的用户
|
||||
@param success_user: 成功的用户
|
||||
@return:
|
||||
"""
|
||||
e_fmt = '<span style="color:#20a53a;">{}</span>'
|
||||
s_fmt = '<span style="color:red;">{}</span>'
|
||||
error_user_msg = ",".join([e_fmt.format(i) for i in error_user])
|
||||
success_user = ",".join([s_fmt.format(i) for i in success_user])
|
||||
log = '标题:【{}】,通知方式:【邮箱】,发送失败的收件人:{},发送成功的收件人:{}'.format(
|
||||
title, error_user_msg, success_user
|
||||
)
|
||||
public.WriteLog('告警通知', log)
|
||||
return True
|
||||
|
||||
|
||||
def write_file(filename: str, s_body: str, mode='w+') -> bool:
|
||||
"""
|
||||
写入文件内容
|
||||
@filename 文件名
|
||||
@s_body 欲写入的内容
|
||||
return bool 若文件不存在则尝试自动创建
|
||||
"""
|
||||
try:
|
||||
fp = open(filename, mode=mode)
|
||||
fp.write(s_body)
|
||||
fp.close()
|
||||
return True
|
||||
except:
|
||||
try:
|
||||
fp = open(filename, mode=mode, encoding="utf-8")
|
||||
fp.write(s_body)
|
||||
fp.close()
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def read_file(filename, mode='r') -> Optional[str]:
|
||||
"""
|
||||
读取文件内容
|
||||
@filename 文件名
|
||||
return string(bin) 若文件不存在,则返回None
|
||||
"""
|
||||
import os
|
||||
if not os.path.exists(filename):
|
||||
return None
|
||||
fp = None
|
||||
try:
|
||||
fp = open(filename, mode=mode)
|
||||
f_body = fp.read()
|
||||
except:
|
||||
return None
|
||||
finally:
|
||||
if fp and not fp.closed:
|
||||
fp.close()
|
||||
return f_body
|
||||
|
||||
|
||||
class _TestMsgTask(BaseTask):
|
||||
"""
|
||||
用来测试的短息
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def the_push_public_data():
|
||||
return get_push_public_data()
|
||||
|
||||
def get_keywords(self, task_data: dict) -> str:
|
||||
pass
|
||||
|
||||
def to_sms_msg(self, push_data: dict, push_public_data: dict) -> Tuple[str, dict]:
|
||||
raise NotImplementedError()
|
||||
|
||||
def to_wx_account_msg(self, push_data: dict, push_public_data: dict) -> WxAccountMsg:
|
||||
msg = WxAccountMsg.new_msg()
|
||||
msg.thing_type = self.title
|
||||
msg.msg = "消息通道配置成功"
|
||||
return msg
|
||||
|
||||
|
||||
def get_test_msg(title: str, task_name="消息通道配置提醒") -> _TestMsgTask:
|
||||
"""
|
||||
用来测试的短息
|
||||
"""
|
||||
t = _TestMsgTask()
|
||||
|
||||
t.title = title
|
||||
t.template_name = task_name
|
||||
return t
|
||||
@@ -0,0 +1,221 @@
|
||||
# coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | 宝塔Linux面板
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2020 宝塔软件(http://www.bt.cn) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: baozi <baozi@bt.cn>
|
||||
# | 消息通道HOOK模块
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
|
||||
import requests
|
||||
from typing import Optional, Union
|
||||
from urllib3.util import parse_url
|
||||
|
||||
from .util import write_push_log, get_test_msg
|
||||
import json
|
||||
|
||||
# config = {
|
||||
# "name": "default",
|
||||
# "url": "https://www.bt.cn",
|
||||
# "query": {
|
||||
# "aaa": "111"
|
||||
# },
|
||||
# "header": {
|
||||
# "AAA": "BBBB",
|
||||
# },
|
||||
# "body_type": ["json", "form_data", "null"],
|
||||
# "custom_parameter": {
|
||||
# "rrr": "qqqq"
|
||||
# },
|
||||
# "method": ["GET", "POST", "PUT", "PATCH"],
|
||||
# "ssl_verify": [True, False]
|
||||
# }
|
||||
# #
|
||||
# # 1.自动解析Query参数,拼接并展示给用户 # 可不做
|
||||
# # 2.自定义Header头 # 必做
|
||||
# # 3.Body中的内容是: type:str="首页磁盘告警", time:int=168955427, data:str="xxxxxx" # ?
|
||||
# # 4.自定义参数: key=value 添加在Body中 # 可不做
|
||||
# # 5.请求类型自定义 # 必做
|
||||
# # 以上内容需要让用户可测试--!
|
||||
|
||||
|
||||
class WebHookMsg(object):
|
||||
DEFAULT_HEADERS = {
|
||||
"User-Agent": "BT-Panel",
|
||||
}
|
||||
|
||||
def __init__(self, hook_data: dict):
|
||||
self.id = hook_data["id"]
|
||||
self.config = hook_data["data"]
|
||||
|
||||
def _replace_and_parse(self, value, real_data):
|
||||
"""替换占位符并递归解析JSON字符串"""
|
||||
if isinstance(value, str):
|
||||
value = value.replace("$1", json.dumps(real_data, ensure_ascii=False))
|
||||
elif isinstance(value, dict):
|
||||
for k, v in value.items():
|
||||
value[k] = self._replace_and_parse(v, real_data)
|
||||
return value
|
||||
|
||||
def send_msg(self, msg: str, title:str, push_type:str) -> Optional[str]:
|
||||
the_url = parse_url(self.config['url'])
|
||||
|
||||
ssl_verify = self.config.get("ssl_verify", None)
|
||||
if ssl_verify is None:
|
||||
ssl_verify = the_url.scheme == "https"
|
||||
|
||||
real_data = {
|
||||
"title": title,
|
||||
"msg": msg,
|
||||
"type": push_type,
|
||||
}
|
||||
# 处理custom_parameter,将$1替换为real_data内容并递归解析
|
||||
custom_data = {}
|
||||
for k, v in self.config.get("custom_parameter", {}).items():
|
||||
custom_data[k] = self._replace_and_parse(v, real_data)
|
||||
|
||||
if custom_data:
|
||||
real_data = custom_data
|
||||
|
||||
|
||||
data = None
|
||||
json_data = None
|
||||
headers = self.DEFAULT_HEADERS.copy()
|
||||
if self.config["body_type"] == "json":
|
||||
json_data = real_data
|
||||
elif self.config["body_type"] == "form_data":
|
||||
data = real_data
|
||||
|
||||
for k, v in self.config.get("headers", {}).items():
|
||||
if not isinstance(v, str):
|
||||
v = str(v)
|
||||
headers[k] = v
|
||||
|
||||
status = False
|
||||
error = None
|
||||
timeout = 10
|
||||
if data:
|
||||
for k, v in data.items():
|
||||
if isinstance(v, str):
|
||||
continue
|
||||
else:
|
||||
data[k]=json.dumps(v)
|
||||
|
||||
for i in range(3):
|
||||
try:
|
||||
if json_data is not None:
|
||||
res = requests.request(
|
||||
method=self.config["method"],
|
||||
url=str(the_url),
|
||||
json=json_data,
|
||||
headers=headers,
|
||||
timeout=timeout,
|
||||
verify=ssl_verify,
|
||||
)
|
||||
else:
|
||||
res = requests.request(
|
||||
method=self.config["method"],
|
||||
url=str(the_url),
|
||||
data=data,
|
||||
headers=headers,
|
||||
timeout=timeout,
|
||||
verify=ssl_verify,
|
||||
)
|
||||
|
||||
if res.status_code == 200:
|
||||
status = True
|
||||
break
|
||||
else:
|
||||
status = False
|
||||
return res.text
|
||||
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError):
|
||||
timeout += 5
|
||||
continue
|
||||
except requests.exceptions.RequestException as e:
|
||||
error = str(e)
|
||||
break
|
||||
|
||||
write_push_log("Web Hook", status, title)
|
||||
return error
|
||||
|
||||
@classmethod
|
||||
def check_args(cls, args) -> Union[str, dict]:
|
||||
"""配置hook"""
|
||||
try:
|
||||
title = args['title']
|
||||
url = args["url"]
|
||||
query = args.get("query", {})
|
||||
headers = args.get("headers", {})
|
||||
body_type = args.get("body_type", "json")
|
||||
custom_parameter = args.get("custom_parameter", {})
|
||||
method = args.get("method", "POST")
|
||||
ssl_verify = args.get("ssl_verify", None) # null Ture
|
||||
except (ValueError, KeyError):
|
||||
return "参数错误"
|
||||
|
||||
the_url = parse_url(url)
|
||||
if the_url.scheme is None or the_url.host is None:
|
||||
return"url解析错误,这可能不是一个合法的url"
|
||||
|
||||
for i in (query, headers, custom_parameter):
|
||||
if not isinstance(i, dict):
|
||||
return "参数格式错误"
|
||||
|
||||
if body_type not in ('json', 'form_data', 'null'):
|
||||
return "body_type必须为json,form_data或者null"
|
||||
|
||||
if method not in ('GET', 'POST', 'PUT', 'PATCH'):
|
||||
return "发送方式选择错误"
|
||||
|
||||
if ssl_verify not in (True, False, None):
|
||||
return "是否验证ssl选项错误"
|
||||
|
||||
title = title.strip()
|
||||
if title == "":
|
||||
return"名称不能为空"
|
||||
|
||||
data = {
|
||||
"title": title,
|
||||
"url": url,
|
||||
"query": query,
|
||||
"headers": headers,
|
||||
"body_type": body_type,
|
||||
"custom_parameter": custom_parameter,
|
||||
"method": method,
|
||||
"ssl_verify": ssl_verify,
|
||||
"status": True
|
||||
}
|
||||
|
||||
test_obj = cls({"data": data, "id": None})
|
||||
test_msg = {
|
||||
"msg_list": ['>配置状态:成功\n\n']
|
||||
}
|
||||
|
||||
test_task = get_test_msg("消息通道配置提醒")
|
||||
|
||||
res = test_obj.send_msg(
|
||||
test_task.to_web_hook_msg(test_msg, test_task.the_push_public_data()),
|
||||
"消息通道配置提醒",
|
||||
"消息通道配置提醒"
|
||||
)
|
||||
if res is None:
|
||||
return data
|
||||
|
||||
return res
|
||||
|
||||
def test_send_msg(self) -> Optional[str]:
|
||||
test_msg = {
|
||||
"msg_list": ['>配置状态:<font color=#20a53a>成功</font>\n\n']
|
||||
}
|
||||
test_task = get_test_msg("消息通道配置提醒")
|
||||
res = self.send_msg(
|
||||
test_task.to_web_hook_msg(test_msg, test_task.the_push_public_data()),
|
||||
"消息通道配置提醒",
|
||||
"消息通道配置提醒"
|
||||
)
|
||||
if res is None:
|
||||
return None
|
||||
return res
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
# coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | 宝塔Linux面板
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2020 宝塔软件(http://www.bt.cn) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: baozi <baozi@bt.cn>
|
||||
# | 消息通道邮箱模块
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
import re
|
||||
import json
|
||||
import requests
|
||||
import traceback
|
||||
import socket
|
||||
|
||||
import requests.packages.urllib3.util.connection as urllib3_cn
|
||||
from requests.packages import urllib3
|
||||
from typing import Optional, Union
|
||||
|
||||
from .util import write_push_log, get_test_msg
|
||||
|
||||
# 关闭警告
|
||||
urllib3.disable_warnings()
|
||||
|
||||
|
||||
class WeiXinMsg:
|
||||
|
||||
def __init__(self, weixin_data):
|
||||
self.id = weixin_data["id"]
|
||||
self.config = weixin_data["data"]
|
||||
|
||||
@classmethod
|
||||
def check_args(cls, args: dict) -> Union[dict, str]:
|
||||
if "url" not in args or "title" not in args:
|
||||
return "信息不完整"
|
||||
|
||||
title = args["title"]
|
||||
if len(title) > 15:
|
||||
return '备注名称不能超过15个字符'
|
||||
|
||||
data = {
|
||||
"url": args["url"],
|
||||
"title": title,
|
||||
}
|
||||
|
||||
test_obj = cls({"data": data, "id": None})
|
||||
test_msg = {
|
||||
"msg_list": ['>配置状态:<font color=#20a53a>成功</font>\n']
|
||||
}
|
||||
|
||||
test_task = get_test_msg("消息通道配置提醒")
|
||||
|
||||
res = test_obj.send_msg(
|
||||
test_task.to_weixin_msg(test_msg, test_task.the_push_public_data()),
|
||||
"消息通道配置提醒"
|
||||
)
|
||||
if res is None:
|
||||
return data
|
||||
|
||||
return res
|
||||
|
||||
def send_msg(self, msg: str, title: str) -> Optional[str]:
|
||||
"""
|
||||
@name 微信发送信息
|
||||
@msg string 消息正文(正文内容,必须包含
|
||||
1、服务器名称
|
||||
2、IP地址
|
||||
3、发送时间
|
||||
)
|
||||
@to_user string 指定发送人
|
||||
"""
|
||||
if not self.config:
|
||||
return '未正确配置微信信息。'
|
||||
|
||||
reg = '<font.+>(.+)</font>'
|
||||
tmp = re.search(reg, msg)
|
||||
if tmp:
|
||||
tmp = tmp.groups()[0]
|
||||
msg = re.sub(reg, tmp, msg)
|
||||
|
||||
data = {
|
||||
"msgtype": "markdown",
|
||||
"markdown": {
|
||||
"content": msg
|
||||
}
|
||||
}
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
status = False
|
||||
error = None
|
||||
try:
|
||||
def allowed_gai_family():
|
||||
family = socket.AF_INET
|
||||
return family
|
||||
allowed_gai_family_lib = urllib3_cn.allowed_gai_family
|
||||
urllib3_cn.allowed_gai_family = allowed_gai_family
|
||||
response = requests.post(
|
||||
url=self.config["url"],
|
||||
data=json.dumps(data),
|
||||
verify=False,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
urllib3_cn.allowed_gai_family = allowed_gai_family_lib
|
||||
|
||||
if response.json()["errcode"] == 0:
|
||||
status = True
|
||||
except:
|
||||
error = traceback.format_exc()
|
||||
|
||||
write_push_log("企业微信", status, title)
|
||||
return error
|
||||
|
||||
def test_send_msg(self) -> Optional[str]:
|
||||
test_msg = {
|
||||
"msg_list": ['>配置状态:<font color=#20a53a>成功</font>\n\n']
|
||||
}
|
||||
test_task = get_test_msg("消息通道配置提醒")
|
||||
res = self.send_msg(
|
||||
test_task.to_weixin_msg(test_msg, test_task.the_push_public_data()),
|
||||
"消息通道配置提醒",
|
||||
)
|
||||
if res is None:
|
||||
return None
|
||||
return res
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,556 @@
|
||||
# coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | 宝塔Linux面板
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2020 宝塔软件(http://www.bt.cn) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: baozi <baozi@bt.cn>
|
||||
# | 消息通道微信公众号模块
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
import os, sys
|
||||
import time, base64
|
||||
|
||||
import re
|
||||
import json
|
||||
import requests
|
||||
import traceback
|
||||
import socket
|
||||
|
||||
import requests.packages.urllib3.util.connection as urllib3_cn
|
||||
from requests.packages import urllib3
|
||||
from typing import Optional, Union, List, Dict, Any
|
||||
|
||||
from .util import write_push_log, get_test_msg, read_file, public_http_post
|
||||
from mod.base.push_mod import WxAccountMsg, SenderConfig
|
||||
from mod.base import json_response
|
||||
|
||||
# 关闭警告
|
||||
urllib3.disable_warnings()
|
||||
|
||||
|
||||
class WeChatAccountMsg:
|
||||
USER_PATH = '/www/server/panel/data/userInfo.json'
|
||||
need_refresh_file = '/www/server/panel/data/mod_push_data/refresh_wechat_account.tip'
|
||||
refresh_time = '/www/server/panel/data/mod_push_data/refresh_wechat_account_time.pl'
|
||||
|
||||
def __init__(self, *config_data):
|
||||
if len(config_data) == 0:
|
||||
self.config = None
|
||||
elif len(config_data) == 1:
|
||||
self.config = config_data[0]["data"]
|
||||
else:
|
||||
self.config = config_data[0]["data"]
|
||||
self.config["users"] = [i["data"]['id'] for i in config_data]
|
||||
self.config["users_nickname"] = [i["data"]['nickname'] for i in config_data]
|
||||
try:
|
||||
self.user_info = json.loads(read_file(self.USER_PATH))
|
||||
except:
|
||||
self.user_info = None
|
||||
|
||||
@classmethod
|
||||
def get_user_info(cls) -> Optional[dict]:
|
||||
try:
|
||||
return json.loads(read_file(cls.USER_PATH))
|
||||
except:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def last_refresh(cls):
|
||||
tmp = read_file(cls.refresh_time)
|
||||
if not tmp:
|
||||
last_refresh_time = 0
|
||||
else:
|
||||
try:
|
||||
last_refresh_time = int(tmp)
|
||||
except:
|
||||
last_refresh_time = 0
|
||||
return last_refresh_time
|
||||
|
||||
@staticmethod
|
||||
def get_local_ip() -> str:
|
||||
"""获取内网IP"""
|
||||
import socket
|
||||
s = None
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(('8.8.8.8', 80))
|
||||
ip = s.getsockname()[0]
|
||||
return ip
|
||||
except:
|
||||
pass
|
||||
finally:
|
||||
if s is not None:
|
||||
s.close()
|
||||
return '127.0.0.1'
|
||||
|
||||
def send_msg(self, msg: WxAccountMsg) -> Optional[str]:
|
||||
if self.user_info is None:
|
||||
return '未获取到用户信息'
|
||||
|
||||
msg.set_ip_address(self.user_info["address"], self.get_local_ip())
|
||||
template_id, msg_data = msg.to_send_data()
|
||||
url = "https://www.bt.cn/api/v2/user/wx_web/send_template_msg_v3"
|
||||
wx_account_ids = self.config["users"] if "users" in self.config else [self.config["id"], ]
|
||||
data = {
|
||||
"uid": self.user_info["uid"],
|
||||
"access_key": self.user_info["access_key"],
|
||||
"data": base64.b64encode(json.dumps(msg_data).encode('utf-8')).decode('utf-8'),
|
||||
"wx_account_ids": base64.b64encode(json.dumps(wx_account_ids).encode('utf-8')).decode('utf-8'),
|
||||
}
|
||||
if template_id != "":
|
||||
data["template_id"] = template_id
|
||||
|
||||
status = False
|
||||
error = None
|
||||
user_name = self.config["users_nickname"] if "users_nickname" in self.config else [self.config["nickname"], ]
|
||||
try:
|
||||
|
||||
resp = public_http_post(url, data)
|
||||
x = json.loads(resp)
|
||||
if x["success"]:
|
||||
status = True
|
||||
else:
|
||||
status = False
|
||||
error = x["res"]
|
||||
except:
|
||||
error = traceback.format_exc()
|
||||
|
||||
write_push_log("微信公众号", status, msg.thing_type, user_name)
|
||||
|
||||
return error
|
||||
|
||||
@classmethod
|
||||
def refresh_config(cls, force: bool = False):
|
||||
if os.path.exists(cls.need_refresh_file):
|
||||
force = True
|
||||
os.remove(cls.need_refresh_file)
|
||||
if force or cls.last_refresh() + 60 * 10 < time.time():
|
||||
cls._get_by_web()
|
||||
|
||||
@classmethod
|
||||
def _get_by_web(cls) -> Optional[List]:
|
||||
user_info = cls.get_user_info()
|
||||
url = "https://www.bt.cn/api/v2/user/wx_web/bound_wx_accounts"
|
||||
data = {
|
||||
"uid": user_info["uid"],
|
||||
"access_key": user_info["access_key"],
|
||||
"serverid": user_info["serverid"]
|
||||
}
|
||||
try:
|
||||
data = json.loads(public_http_post(url, data))
|
||||
if not data["success"]:
|
||||
return None
|
||||
except:
|
||||
return None
|
||||
|
||||
cls._save_user_info(data["res"])
|
||||
return data["res"]
|
||||
|
||||
@staticmethod
|
||||
def _save_user_info(user_config_list: List[Dict[str, Any]]):
|
||||
print(user_config_list)
|
||||
user_config_dict = {i["hex"]: i for i in user_config_list}
|
||||
|
||||
remove_list = []
|
||||
sc = SenderConfig()
|
||||
for i in sc.config:
|
||||
if i['sender_type'] != "wx_account":
|
||||
continue
|
||||
if i['data'].get("hex", None) in user_config_dict:
|
||||
i['data'].update(user_config_dict[i['data']["hex"]])
|
||||
user_config_dict.pop(i['data']["hex"])
|
||||
else:
|
||||
remove_list.append(i)
|
||||
|
||||
for r in remove_list:
|
||||
sc.config.remove(r)
|
||||
|
||||
if user_config_dict: # 还有多的
|
||||
for v in user_config_dict.values():
|
||||
v["title"] = v["nickname"]
|
||||
sc.config.append({
|
||||
"id": sc.nwe_id(),
|
||||
"used": True,
|
||||
"sender_type": "wx_account",
|
||||
"data": v
|
||||
})
|
||||
sc.save_config()
|
||||
|
||||
@classmethod
|
||||
def unbind(cls, wx_account_uid: str):
|
||||
user_info = cls.get_user_info()
|
||||
if user_info is None:
|
||||
return json_response(status=True, msg='未获取到用户绑定的信息')
|
||||
url = "https://www.bt.cn/api/v2/user/wx_web/unbind_wx_accounts"
|
||||
data = {
|
||||
"uid": user_info["uid"],
|
||||
"access_key": user_info["access_key"],
|
||||
"serverid": user_info["serverid"],
|
||||
"ids": str(wx_account_uid)
|
||||
}
|
||||
try:
|
||||
datas = json.loads(public_http_post(url, data))
|
||||
if datas["success"]:
|
||||
return json_response(status=True, data=datas, msg="解绑成功")
|
||||
else:
|
||||
return json_response(status=False, data=datas, msg=datas["res"])
|
||||
except:
|
||||
return json_response(status=True, msg="链接云端失败")
|
||||
|
||||
@classmethod
|
||||
def get_auth_url(cls):
|
||||
user_info = cls.get_user_info()
|
||||
if user_info is None:
|
||||
return json_response(status=True, msg='未获取到用户绑定的信息')
|
||||
url = "https://www.bt.cn/api/v2/user/wx_web/get_auth_url"
|
||||
data = {
|
||||
"uid": user_info["uid"],
|
||||
"access_key": user_info["access_key"],
|
||||
"serverid": user_info["serverid"],
|
||||
}
|
||||
try:
|
||||
datas = json.loads(public_http_post(url, data))
|
||||
if datas["success"]:
|
||||
return json_response(status=True, data=datas)
|
||||
else:
|
||||
return json_response(status=False, data=datas, msg=datas["res"])
|
||||
except:
|
||||
return json_response(status=True, msg="链接云端失败")
|
||||
|
||||
def test_send_msg(self) -> Optional[str]:
|
||||
test_msg = {
|
||||
"msg_list": ['>配置状态:<font color=#20a53a>成功</font>\n\n']
|
||||
}
|
||||
test_task = get_test_msg("消息通道配置提醒")
|
||||
res = self.send_msg(
|
||||
test_task.to_wx_account_msg(test_msg, test_task.the_push_public_data()),
|
||||
)
|
||||
if res is None:
|
||||
return None
|
||||
return res
|
||||
|
||||
|
||||
# class wx_account_msg:
|
||||
# __module_name = None
|
||||
# __default_pl = "{}/data/default_msg_channel.pl".format(panelPath)
|
||||
# conf_path = '{}/data/wx_account_msg.json'.format(panelPath)
|
||||
# user_info = None
|
||||
#
|
||||
# def __init__(self):
|
||||
# try:
|
||||
# self.user_info = json.loads(public.ReadFile("{}/data/userInfo.json".format(public.get_panel_path())))
|
||||
# except:
|
||||
# self.user_info = None
|
||||
# self.__module_name = self.__class__.__name__.replace('_msg', '')
|
||||
#
|
||||
# def get_version_info(self, get):
|
||||
# """
|
||||
# 获取版本信息
|
||||
# """
|
||||
# data = {}
|
||||
# data['ps'] = '宝塔微信公众号,用于接收面板消息推送'
|
||||
# data['version'] = '1.0'
|
||||
# data['date'] = '2022-08-15'
|
||||
# data['author'] = '宝塔'
|
||||
# data['title'] = '微信公众号'
|
||||
# data['help'] = 'http://www.bt.cn'
|
||||
# return data
|
||||
#
|
||||
# def get_local_ip(self):
|
||||
# '''获取内网IP'''
|
||||
# import socket
|
||||
# try:
|
||||
# s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
# s.connect(('8.8.8.8', 80))
|
||||
# ip = s.getsockname()[0]
|
||||
# return ip
|
||||
# finally:
|
||||
# s.close()
|
||||
# return '127.0.0.1'
|
||||
#
|
||||
# def get_config(self, get):
|
||||
# """
|
||||
# 微信公众号配置
|
||||
# """
|
||||
# if os.path.exists(self.conf_path):
|
||||
# # 60S内不重复加载
|
||||
# start_time = int(time.time())
|
||||
# if os.path.exists("data/wx_account_msg.lock"):
|
||||
# lock_time = 0
|
||||
# try:
|
||||
# lock_time = int(public.ReadFile("data/wx_account_msg.lock"))
|
||||
# except:
|
||||
# pass
|
||||
# # 大于60S重新加载
|
||||
# if start_time - lock_time > 60:
|
||||
# public.run_thread(self.get_web_info2)
|
||||
# public.WriteFile("data/wx_account_msg.lock", str(start_time))
|
||||
# else:
|
||||
# public.WriteFile("data/wx_account_msg.lock", str(start_time))
|
||||
# public.run_thread(self.get_web_info2)
|
||||
# data = json.loads(public.ReadFile(self.conf_path))
|
||||
#
|
||||
# if not 'list' in data: data['list'] = {}
|
||||
#
|
||||
# title = '默认'
|
||||
# if 'res' in data and 'nickname' in data['res']: title = data['res']['nickname']
|
||||
#
|
||||
# data['list']['default'] = {'title': title, 'data': ''}
|
||||
#
|
||||
# data['default'] = self.__get_default_channel()
|
||||
# return data
|
||||
# else:
|
||||
# public.run_thread(self.get_web_info2)
|
||||
# return {"success": False, "res": "未获取到配置信息"}
|
||||
#
|
||||
# def set_config(self, get):
|
||||
# """
|
||||
# @设置默认值
|
||||
# """
|
||||
# if 'default' in get and get['default']:
|
||||
# public.writeFile(self.__default_pl, self.__module_name)
|
||||
#
|
||||
# return public.returnMsg(True, '设置成功')
|
||||
#
|
||||
# def get_web_info(self, get):
|
||||
# if self.user_info is None: return public.returnMsg(False, '未获取到用户绑定的信息')
|
||||
# url = "https://www.bt.cn/api/v2/user/wx_web/info"
|
||||
# data = {
|
||||
# "uid": self.user_info["uid"],
|
||||
# "access_key": self.user_info["access_key"],
|
||||
# "serverid": self.user_info["serverid"]
|
||||
# }
|
||||
# try:
|
||||
#
|
||||
# datas = json.loads(public.httpPost(url, data))
|
||||
#
|
||||
# if datas["success"]:
|
||||
# public.WriteFile(self.conf_path, json.dumps(datas))
|
||||
# return public.returnMsg(True, datas)
|
||||
# else:
|
||||
# public.WriteFile(self.conf_path, json.dumps(datas))
|
||||
# return public.returnMsg(False, datas)
|
||||
# except:
|
||||
# public.WriteFile(self.conf_path, json.dumps({"success": False, "res": "链接云端失败,请检查网络"}))
|
||||
# return public.returnMsg(False, "链接云端失败,请检查网络")
|
||||
#
|
||||
# def unbind(self):
|
||||
# if self.user_info is None:
|
||||
# return public.returnMsg(False, '未获取到用户绑定的信息')
|
||||
# url = "https://www.bt.cn/api/v2/user/wx_web/unbind"
|
||||
# data = {
|
||||
# "uid": self.user_info["uid"],
|
||||
# "access_key": self.user_info["access_key"],
|
||||
# "serverid": self.user_info["serverid"]
|
||||
# }
|
||||
# try:
|
||||
#
|
||||
# datas = json.loads(public.httpPost(url, data))
|
||||
#
|
||||
# if os.path.exists(self.conf_path):
|
||||
# os.remove(self.conf_path)
|
||||
#
|
||||
# if datas["success"]:
|
||||
# return public.returnMsg(True, datas)
|
||||
# else:
|
||||
# return public.returnMsg(False, datas)
|
||||
# except:
|
||||
# public.WriteFile(self.conf_path, json.dumps({"success": False, "res": "链接云端失败,请检查网络"}))
|
||||
# return public.returnMsg(False, "链接云端失败,请检查网络")
|
||||
#
|
||||
# def get_web_info2(self):
|
||||
# if self.user_info is None:
|
||||
# return public.returnMsg(False, '未获取到用户绑定的信息')
|
||||
# url = "https://www.bt.cn/api/v2/user/wx_web/info"
|
||||
# data = {
|
||||
# "uid": self.user_info["uid"],
|
||||
# "access_key": self.user_info["access_key"],
|
||||
# "serverid": self.user_info["serverid"]
|
||||
# }
|
||||
# try:
|
||||
# datas = json.loads(public.httpPost(url, data))
|
||||
# if datas["success"]:
|
||||
# public.WriteFile(self.conf_path, json.dumps(datas))
|
||||
# return public.returnMsg(True, datas)
|
||||
# else:
|
||||
# public.WriteFile(self.conf_path, json.dumps(datas))
|
||||
# return public.returnMsg(False, datas)
|
||||
# except:
|
||||
# public.WriteFile(self.conf_path, json.dumps({"success": False, "res": "链接云端失败"}))
|
||||
# return public.returnMsg(False, "链接云端失败")
|
||||
#
|
||||
# def get_send_msg(self, msg):
|
||||
# """
|
||||
# @name 处理md格式
|
||||
# """
|
||||
# try:
|
||||
# import re
|
||||
# title = '宝塔告警通知'
|
||||
# if msg.find("####") >= 0:
|
||||
# try:
|
||||
# title = re.search(r"####(.+)", msg).groups()[0]
|
||||
# except:
|
||||
# pass
|
||||
#
|
||||
# msg = msg.replace("####", ">").replace("\n\n", "\n").strip()
|
||||
# s_list = msg.split('\n')
|
||||
#
|
||||
# if len(s_list) > 3:
|
||||
# s_title = s_list[0].replace(" ", "")
|
||||
# s_list = s_list[3:]
|
||||
# s_list.insert(0, s_title)
|
||||
# msg = '\n'.join(s_list)
|
||||
#
|
||||
# s_list = []
|
||||
# for msg_info in msg.split('\n'):
|
||||
# reg = '<font.+>(.+)</font>'
|
||||
# tmp = re.search(reg, msg_info)
|
||||
# if tmp:
|
||||
# tmp = tmp.groups()[0]
|
||||
# msg_info = re.sub(reg, tmp, msg_info)
|
||||
# s_list.append(msg_info)
|
||||
# msg = '\n'.join(s_list)
|
||||
# except:
|
||||
# pass
|
||||
# return msg, title
|
||||
#
|
||||
# def send_msg(self, msg):
|
||||
# """
|
||||
# 微信发送信息
|
||||
# @msg 消息正文
|
||||
# """
|
||||
#
|
||||
# if self.user_info is None:
|
||||
# return public.returnMsg(False, '未获取到用户信息')
|
||||
#
|
||||
# if not isinstance(msg, str):
|
||||
# return self.send_msg_v2(msg)
|
||||
#
|
||||
# msg, title = self.get_send_msg(msg)
|
||||
# url = "https://www.bt.cn/api/v2/user/wx_web/send_template_msg_v2"
|
||||
# datassss = {
|
||||
# "first": {
|
||||
# "value": "堡塔主机告警",
|
||||
# },
|
||||
# "keyword1": {
|
||||
# "value": "内网IP " + self.get_local_ip() + "\n外网IP " + self.user_info[
|
||||
# "address"] + " \n服务器别名 " + public.GetConfigValue("title"),
|
||||
# },
|
||||
# "keyword2": {
|
||||
# "value": "堡塔主机告警",
|
||||
# },
|
||||
# "keyword3": {
|
||||
# "value": msg,
|
||||
# },
|
||||
# "remark": {
|
||||
# "value": "如有疑问,请联系宝塔客服",
|
||||
# },
|
||||
# }
|
||||
# data = {
|
||||
# "uid": self.user_info["uid"],
|
||||
# "access_key": self.user_info["access_key"],
|
||||
# "data": base64.b64encode(json.dumps(datassss).encode('utf-8')).decode('utf-8')
|
||||
# }
|
||||
#
|
||||
# try:
|
||||
# res = {}
|
||||
# error, success = 0, 0
|
||||
#
|
||||
# x = json.loads(public.httpPost(url, data))
|
||||
# conf = self.get_config(None)['list']
|
||||
#
|
||||
# # 立即刷新剩余次数
|
||||
# public.run_thread(self.get_web_info2)
|
||||
#
|
||||
# res[conf['default']['title']] = 0
|
||||
# if x['success']:
|
||||
# res[conf['default']['title']] = 1
|
||||
# success += 1
|
||||
# else:
|
||||
# error += 1
|
||||
#
|
||||
# try:
|
||||
# public.write_push_log(self.__module_name, title, res)
|
||||
# except:
|
||||
# pass
|
||||
#
|
||||
# result = public.returnMsg(True, '发送完成,发送成功{},发送失败{}.'.format(success, error))
|
||||
# result['success'] = success
|
||||
# result['error'] = error
|
||||
# return result
|
||||
#
|
||||
# except:
|
||||
# print(public.get_error_info())
|
||||
# return public.returnMsg(False, '微信消息发送失败。 --> {}'.format(public.get_error_info()))
|
||||
#
|
||||
# def push_data(self, data):
|
||||
# if isinstance(data, dict):
|
||||
# return self.send_msg(data['msg'])
|
||||
# else:
|
||||
# return self.send_msg_v2(data)
|
||||
#
|
||||
# def uninstall(self):
|
||||
# if os.path.exists(self.conf_path):
|
||||
# os.remove(self.conf_path)
|
||||
#
|
||||
# def send_msg_v2(self, msg):
|
||||
# from push.base_push import WxAccountMsgBase, WxAccountMsg
|
||||
# if self.user_info is None:
|
||||
# return public.returnMsg(False, '未获取到用户信息')
|
||||
#
|
||||
# if isinstance(msg, public.dict_obj):
|
||||
# msg = getattr(msg, "msg", "测试信息")
|
||||
# if len(msg) >= 20:
|
||||
# return self.send_msg(msg)
|
||||
#
|
||||
# if isinstance(msg, str):
|
||||
# the_msg = WxAccountMsg.new_msg()
|
||||
# the_msg.thing_type = msg
|
||||
# the_msg.msg = msg
|
||||
# msg = the_msg
|
||||
#
|
||||
# if not isinstance(msg, WxAccountMsgBase):
|
||||
# return public.returnMsg(False, '消息类型错误')
|
||||
#
|
||||
# msg.set_ip_address(self.user_info["address"], self.get_local_ip())
|
||||
#
|
||||
# template_id, msg_data = msg.to_send_data()
|
||||
# url = "https://www.bt.cn/api/v2/user/wx_web/send_template_msg_v2"
|
||||
# data = {
|
||||
# "uid": self.user_info["uid"],
|
||||
# "access_key": self.user_info["access_key"],
|
||||
# "data": base64.b64encode(json.dumps(msg_data).encode('utf-8')).decode('utf-8'),
|
||||
# }
|
||||
# if template_id != "":
|
||||
# data["template_id"] = template_id
|
||||
#
|
||||
# try:
|
||||
# error, success = 0, 0
|
||||
# resp = public.httpPost(url, data)
|
||||
# x = json.loads(resp)
|
||||
# conf = self.get_config(None)['list']
|
||||
#
|
||||
# # 立即刷新剩余次数
|
||||
# public.run_thread(self.get_web_info2)
|
||||
#
|
||||
# res = {
|
||||
# conf['default']['title']: 0
|
||||
# }
|
||||
# if x['success']:
|
||||
# res[conf['default']['title']] = 1
|
||||
# success += 1
|
||||
# else:
|
||||
# error += 1
|
||||
#
|
||||
# try:
|
||||
# public.write_push_log(self.__module_name, msg.thing_type, res)
|
||||
# except:
|
||||
# pass
|
||||
# result = public.returnMsg(True, '发送完成,发送成功{},发送失败{}.'.format(success, error))
|
||||
# result['success'] = success
|
||||
# result['error'] = error
|
||||
# return result
|
||||
#
|
||||
# except:
|
||||
# return public.returnMsg(False, '微信消息发送失败。 --> {}'.format(public.get_error_info()))
|
||||
Reference in New Issue
Block a user