上传文件至「py」

This commit is contained in:
2026-05-15 21:26:06 +08:00
parent 04d66b765a
commit f284a47a0e
4 changed files with 2572 additions and 0 deletions
+645
View File
@@ -0,0 +1,645 @@
# [author: authook]
# [title: jd_账密登陆]
# [create_at: 2023-09-19 15:29:17]
# [version: 2.3]
# [pin: true]
# [price: 0]
# [rule: ^账密登录$]
# [rule: ^账密登陆$]
# [rule: ^账密刷新$]
# [rule: ^账密强制刷新$]
# [rule: ^账密管理$]
# [priority: 6666666]优先级
# [admin: false]
# [service: authook]
# [icon: https://bbs.autman.cn/assets/files/2024-05-16/1715842374-826456-2584e965be6ed12773865dbb8e29a449.png]
# [description: jd_账密登陆插件 兼容兔子,pro等框架</br>指令:账密登录,账密登录,账密管理 </br> 配置:在插件配参中配置,</br>权限:需开启qlsjdNotify权限</br>注意⚠️账密刷新请勿手动发送,否则会将ck绑定至管理员账号上!!!(插件已自动内置定时刷新账密)]
# [param: {"required":true,"key":"jd.rabbit_host","placeholder":"http://192.168.1.1:81","name":"【rabbit】登陆地址:","desc":"登陆地址,例:http://192.168.3.22"}]
# [param: {"required":true,"key":"jd.pro_host","placeholder":"http://192.168.1.1:81","name":"【pro】登陆地址:","desc":"pro登陆地址"}]
# [param: {"required":true,"key":"jd.login_ql_name","placeholder":"ql容器名称","name":"【ql】容器名称","desc":"ql容器名称"}]
# [param: {"required":true,"key":"jd.login_type","placeholder":"登陆框架","name":"登陆框架","desc":"pro框架请输入pro rabbit兔子框架请输入rabbit"}]
# =======================
# 项目:jd_账密登陆.py
# 构建时间:2025-02-10 20:52:29
import datetime
from email import message
import json
import logging
import math
import os
import random
import re
import time
import traceback
import inspect
from base64 import b64encode
from urllib.parse import quote, urlparse
import colorlog
import middleware
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
class TOOL:
def __init__(self):
self.plugin_pre = "rabbit_zm"
self.remark = ""
self.platform_arr = self.platformArr()
# 将日期和时间格式化为字符串
self.cut_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
self.logger = logging.getLogger()
self.color_formatter = colorlog.ColoredFormatter(
'%(log_color)s%(levelname)s: %(message)s',
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white',
}
)
self.logger = self._configure_logger()
def _configure_logger(self):
"""配置并返回一个带颜色的 logger 对象"""
logger = logging.getLogger(self.plugin_pre)
logger.setLevel(logging.DEBUG) # 设置日志默认最低级别
# 检查是否已添加过控制台处理器,避免重复添加
if not any(isinstance(handler, logging.StreamHandler) for handler in logger.handlers):
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG) # 控制台日志最低级别
console_handler.setFormatter(self.color_formatter)
logger.addHandler(console_handler)
return logger
def log(self, level, message):
"""通用日志方法"""
if level == logging.INFO:
self.logger.info(message)
elif level == logging.ERROR:
self.logger.error(message)
elif level == logging.WARNING:
self.logger.warning(message)
elif level == logging.DEBUG:
self.logger.debug(message)
elif level == logging.CRITICAL:
self.logger.critical(message)
def log_info(self, message):
self.log(logging.INFO, message)
def log_err(self, message):
self.log(logging.ERROR, message)
def log_warn(self, message):
self.log(logging.WARNING, message)
def platformArr(self):
plat_arr = []
for p in ["qq", "wx", "wb", "qb", "bt", "tg"]:
t = {"platform": f"{p}", "imType": p}
plat_arr.append(t)
return plat_arr
def pushMsg(self, userId, chatId, imType, title, content):
# 获取当前日期和时间
now = datetime.datetime.now()
# 将日期和时间格式化为字符串
formatted_datetime = now.strftime('%Y-%m-%d %H:%M:%S')
self.remark = ""
return middleware.push(imType, chatId, userId, title, f"{formatted_datetime}\n{content}")
def pushGroup(self, imtype, groupCode, content):
self.pushMsg(0, groupCode, imtype, "", content)
def autman_host(self):
file_path = inspect.getframeinfo(inspect.currentframe()).filename
dir_path = os.path.dirname(os.path.abspath(file_path))
setConfPath = dir_path.split("develop/scripts")[0] + "/middleware.py"
try:
os.setuid(0) # 强制转为root 前提是autman账户以root用户启用
with open(setConfPath, "r", encoding="utf-8") as f:
data = f.read()
lines = data.splitlines()
for line in lines:
if "url =" in line:
res = line.split('"')
url = res[1][0:-5]
self.log_info(url)
return url
except Exception as e:
self.log_info(f"❌autman_host error {e}")
def replyMsg(self, content):
return sender.reply(content)
def pushMaster(self, content):
arr = ["qq", "wx", "tg", "qb", 'wb']
for p in arr:
masters = sender.bucketGet(p, "masters")
if masters:
masters_arr = masters.split(",") if "," in masters else masters.split("&")
for m in masters_arr:
middleware.push(p, "", m, "", content)
def qls(self):
qls = []
ql_id_arr = sender.bucketAllKeys("qls")
if not ql_id_arr:
return []
for ql_id in ql_id_arr:
value = sender.bucketGet("qls", ql_id)
if value:
qls.append(json.loads(value))
return qls
def autman_host(self):
file_path = inspect.getframeinfo(inspect.currentframe()).filename
dir_path = os.path.dirname(os.path.abspath(file_path))
setConfPath = dir_path.split("develop/scripts")[0] + "/middleware.py"
try:
os.setuid(0) # 强制转为root 前提是autman账户以root用户启用
with open(setConfPath, "r", encoding="utf-8") as f:
data = f.read()
lines = data.splitlines()
for line in lines:
if "url =" in line:
res = line.split('"')
url = res[1][0:-5]
self.log_info(url)
return url
except Exception as e:
self.log_info(f"❌autman_host error {e}")
class QL:
def __init__(self, ql):
self.host = ql["host"]
self.client_id = ql["client_id"]
self.client_secret = ql["client_secret"]
self.token = self.get_token()
self.name = ql['name']
self.content = ""
self.version = self.system()
def gettimestamp(self):
return str(int(time.time() * 1000))
def get_token(self):
url = f"{self.host}/open/auth/token?client_id={self.client_id}&client_secret={self.client_secret}"
print(url)
headers = {
'Content-Type': 'application/json',
'Accept': '*/*',
'Cache-Control': 'no-cache',
'Host': "",
'Connection': 'keep-alive'
}
host = urlparse(url).netloc
if ":" in host:
host = host.split(":")[0]
headers['Host'] = host
try:
response = requests.get(url, timeout=10, headers=headers)
res = response.json()
if response.status_code == 200 and res["code"] == 200:
return res["data"]["token"]
else:
return None
except Exception as e:
return None
def Api(self, api, apd, method, payload):
url = f"{self.host}/open/{api}/{apd}"
headers = {
"Content-Type": "application/json;charset=UTF-8",
"Authorization": f"Bearer {self.token}",
}
try:
response = requests.request(method, url, headers=headers, data=payload, timeout=5)
return response.json()
except Exception as e:
return None
# 环境变量
def envGet(self, keyword):
return self.Api('envs', f"?searchValue={keyword}&t={self.gettimestamp()}", 'GET', {})
def envSet(self, env):
return self.Api('envs', f"?t={self.gettimestamp()}", 'POST', env)
def envUpdate(self, env):
return self.Api('envs', f"?t={self.gettimestamp()}", 'PUT', env)
def envEnable(self, ids):
return self.Api('envs', f"/enable?t={self.gettimestamp()}", 'PUT', json.dumps(ids))
def envDisable(self, ids):
return self.Api('envs', f"/disable?t={self.gettimestamp()}", 'PUT', json.dumps(ids))
def envDel(self, ids):
return self.Api('envs', f"?t={self.gettimestamp()}", 'delete', json.dumps(ids))
def envMove(self, id, data):
return self.Api('envs', f"/{id}/move?t={self.gettimestamp()}", 'PUT', json.dumps(data))
# 定时任务
def cronGet(self, keyword):
return self.Api('crons', f"?searchValue={keyword}&t={self.gettimestamp()}", 'GET', None)
def cronRun(self, ids):
return self.Api('crons', f"run?t={self.gettimestamp()}", 'PUT', ids)
def cronStop(self, ids):
return self.Api('crons', f"stop?t={self.gettimestamp()}", 'PUT', ids)
def cronEnable(self, ids):
return self.Api('crons', f"enable?t={self.gettimestamp()}", 'PUT', ids)
def cronDisable(self, ids):
return self.Api('crons', f"disable?t={self.gettimestamp()}", 'PUT', ids)
def cronDetail(self, cronId):
return self.Api('crons', f"{cronId}?t={self.gettimestamp()}", 'GET', None)
def cronLogs(self, cronId):
return self.Api('crons', f"{cronId}/log?t={self.gettimestamp()}", 'GET', None)
def cronLog(self, cronId):
return self.Api('crons', f"{cronId}/log?t={self.gettimestamp()}", 'GET', None)
# 配置文件
def configsGet(self, name):
if self.version < "2.17":
return self.Api('configs', f"{name}?t={self.gettimestamp()}", 'GET', None)
else:
return self.Api('configs/detail', f"?path={name}&t={self.gettimestamp()}", 'GET', None)
def configsSet(self, data):
return self.Api('configs', f"save?t={self.gettimestamp()}", 'POST', data)
# 订阅任务
def subsGet(self, keyword):
return self.Api('subscriptions', f"?searchValue={keyword}&t={self.gettimestamp()}", 'GET', None)
def subsSet(self, data):
return self.Api('subscriptions', f"save?t={self.gettimestamp()}", 'PUT', data)
def subsRun(self, ids):
return self.Api('subscriptions', f"run?t={self.gettimestamp()}", 'PUT', ids)
def subsEnable(self, ids):
return self.Api('subscriptions', f"enable?t={self.gettimestamp()}", 'PUT', ids)
def subsDisable(self, ids):
return self.Api('subscriptions', f"disable?t={self.gettimestamp()}", 'PUT', ids)
def subsLog(self, subId):
return self.Api('subscriptions', f"{subId}/log?t={self.gettimestamp()}", 'GET', None)
def system(self):
url = f"{self.host}/open/system?t={self.gettimestamp()}"
headers = {
"Content-Type": "application/json;charset=UTF-8",
}
try:
res = requests.get(url)
if res.status_code == 200:
rj = res.json()
return rj['data']['version']
except Exception as e:
return 2.16
class ZM_RABBIT():
def __init__(self):
self.pins = self.get_pins()
self.conf = self.get_conf()
self.init()
self.s = requests.session()
pass
def hide_phone(self, text):
if not text:
return text
if len(text) != 11:
return text
return re.sub(r'(\d{3})\d{4}(\d{4})', r'\1****\2', text)
def check_ck_status(self, ck):
headers = {
"Cookie": ck,
"referer": "https://h5.m.jd.com/",
"User-Agent": "jdapp;iPhone;10.1.2;15.0;network/wifi;Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1",
}
response = requests.get('https://plogin.m.jd.com/cgi-bin/ml/islogin', headers=headers)
data = response.json()
return data['islogin'] == "1" if 'islogin' in data else False
def en_pwd(self, pwd):
key = b"wn:_/imCQF;rBNS{V=|kML[(+$%Tjgy)"
iv = b"zA#f.x>jN!h-0[{$"
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(pad(pwd.encode(), AES.block_size))
return b64encode(encrypted).decode()
def get_conf(self):
self.conf = {
"paid": "n",
"fee": 0,
"qr_code": "",
"host": "",
"ql_name": None
}
if middleware.bucketGet("otto", "rabbit_pro_paid"):
self.conf["paid"] = middleware.bucketGet("otto", "rabbit_pro_paid")
if middleware.bucketGet("otto", "rabbit_pro_fee"):
self.conf['fee'] = middleware.bucketGet("otto", "rabbit_pro_fee")
if middleware.bucketGet("otto", "rabbit_pro_qr_code"):
self.conf['qr_code'] = middleware.bucketGet("otto", "rabbit_pro_qr_code")
if not middleware.bucketGet("otto", "rabbit_pro_host"):
tool.pushMaster("未配置rabbit登录地址,请先去【配参】配置")
exit()
self.conf['host'] = middleware.bucketGet("otto", "rabbit_pro_host")
if not middleware.bucketGet("otto", "rabbit_ql_name"):
tool.pushMaster("未配置rabbit登陆容器名称,请先去【配参】配置")
exit()
self.conf['ql_name'] = middleware.bucketGet("otto", "rabbit_ql_name")
return self.conf
def init(self):
res = requests.get(self.conf['host'] + "/api/Config")
tool.log_info(res.text)
if res.status_code == 200:
rj = res.json()
if rj['success']:
self.conf['container_id'] = rj['data']['list'][0]['container_id']
self.conf['container_name'] = rj['data']['list'][0]['container_name']
self.conf['autocount'] = rj['data']['autocount']
return self.conf
tool.replyMsg(rj['message'] + ",请完善rabbitPro配置")
exit()
def get_pins(self):
return sender.bucketKeys("pin" + imType.upper(), userId)
def account_list(self):
acc_list = []
acc_key = set()
if self.pins and len(self.pins) > 0:
for pin in self.pins:
acc_str = sender.bucketGet("", pin)
if acc_str:
acc = {
"username": acc_str.split("#")[0],
"pwd": acc_str.split("#")[1],
"ck": acc_str.split("#")[2],
}
if acc_str.split("#")[0] not in acc_key:
acc_list.append(acc)
acc_key.add(acc_str.split("#")[0])
return acc_list
def login_init(self, mobile):
headers = {
"Content-Type": "application/json",
"User_Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1"
}
data = {"account": mobile, "container_id": self.conf['container_id']}
url = self.conf['host'] + "/pwd/init"
res = self.s.post(url, headers=headers, data=json.dumps(data))
tool.log_info(f"{mobile}: login_init res {res.text}")
if res.status_code == 200:
rj = res.json()
if not rj['success']:
tool.replyMsg(f"[{self.hide_phone(mobile)}]登陆初始化失败:{rj['message']}")
return
if rj['data']['status'] == 505:
return True
else:
tool.replyMsg(f"f{rj['message']}")
return False
pass
def login(self, mobile, pwd):
headers = {
"Content-Type": "application/json",
"User_Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1"
}
data = {"account": mobile, "pwd": self.en_pwd(pwd), "container_id": self.conf['container_id']}
res = self.s.post(f"{self.conf['host']}/pwd/login", headers=headers, data=json.dumps(data))
tool.log_info(f"{mobile}: login res {res.text}")
if res.status_code == 200:
rj = res.json()
if rj['code'] == 601:
tool.replyMsg(f"[{self.hide_phone(mobile)}]账号需要点击验证之后,重新登陆\n{rj['RiskUrl']}")
return False, None
if rj['code'] == 200:
tool.replyMsg(f"【登陆账户】:{self.hide_phone(mobile)}\n【JD账户】:{rj['pin']}\n【登陆结果】:{rj['message']}")
value_json = {
"mobile": mobile,
"pwd": pwd
}
sender.bucketSet("rabbit_zm_acc", rj['pin'], json.dumps(value_json))
return True, rj['pin']
elif rj['code'] == 503:
tool.replyMsg(f"[{self.hide_phone(mobile)}]{rj['message']}")
return False, None
elif rj['code'] == 555:
# 需要验证
riskUrl = rj['RiskUrl']
tool.replyMsg(f"[{self.hide_phone(mobile)}]请手动点击验证链接,然后再次返回登陆:{riskUrl}")
return False, None
else:
tool.replyMsg(f"[{self.hide_phone(mobile)}]登陆异常:{rj['message']}")
return False, None
def after_login(self, pin):
time.sleep(random.randint(3,5))
sender.bucketSet('pin' + imType.upper(), pin, userId)
qls = tool.qls()
if not qls or len(qls) == 0:
tool.pushMaster("未配置青龙容器,无法同步更新容器ck")
exit()
default_ql = list(filter(lambda item: self.conf['ql_name'] in item['name'],qls))
if not default_ql:
tool.pushMaster(f"容器【{self.conf['ql_name']}】,不存在,请重新配置")
return
tql = QL(default_ql[0])
if not tql.token:
return
res = tql.envGet("JD_COOKIE")
if res['code'] != 200 or len(res['data']) == 0:
return
if len(res['data']) <= 0:
return
cookie = list(filter(lambda item: pin in item['value'], res['data']))
if not cookie:
return
if cookie[0]['status'] == 1:
return
if not self.check_ck_status(cookie[0]['value']):
return
ck = cookie[0]['value']
pt_key = re.findall(r"pt_key=(.*?);", ck)[0]
val = {
"ID": pin,
"Pet": False,
"Fruit": False,
"DreamFactory": False,
"Note": "",
"PtKey": pt_key,
"AssetCron": "",
"PushPlus": "",
"LoginedAt": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
"ClientID": tql.client_id
}
sender.bucketSet('jdNotify', pin, json.dumps(val))
for ql in qls:
if self.conf['ql_name'] in ql['name']:
continue
tql = QL(ql)
if not tql.token:
continue
res = tql.envGet("JD_COOKIE")
if res['code'] != 200 or len(res['data']) == 0:
continue
if len(res['data']) <= 0:
continue
cookie = list(filter(lambda item: pin in item['value'], res['data']))
if not cookie:
continue
ck = cookie[0]['value']
id = cookie[0].get('id', cookie[0].get("_id"))
remarks = cookie[0].get('remarks', pin)
if ck:
updateEnv = {
"name": "JD_COOKIE",
"value": ck,
"remarks": remarks,
"id": id,
}
res = tql.envUpdate(json.dumps(updateEnv))
tool.log_info(f"update res {res}")
res = tql.envEnable([id])
tool.log_info(f"envEnable res {res}")
def refresh_account(self):
need = 0
success = 0
failed = 0
failed_user = []
tool.log_info(f"[rabbit]账密刷新开始......")
acc_map = sender.bucketAll("rabbit_zm_acc")
if not acc_map:
sender.reply("暂无账密数据,已退出")
# 查询pin对应的ck是否有效
for pin, accStr in acc_map.items():
jdStr = sender.bucketGet("jdNotify", pin)
if not jdStr:
continue
jd_json = json.loads(jdStr)
ck = f"pt_pin={pin}; pt_key={jd_json['PtKey']}"
if self.check_ck_status(ck):
tool.log_info(f"账号【{pin}】有效✅,跳过刷新")
continue
need += 1
acc_json = json.loads(accStr)
refresh_res = self.run_login(acc_json['mobile'], acc_json['pwd'])
if not refresh_res:
failed += 1
failed_user.append(f"{quote(pin)}[{self.hide_phone(acc_json['mobile'])}]")
else:
success += 1
content = f"[rabbit]账密刷新结束"
if need > 0:
content += f"\n本次需要刷新:{need}"
content += f"\n本次刷新成功:{success}个🎉"
if failed_user:
content += f"\n本次刷新失败:{failed}个😭"
s = "\n".join(failed_user)
content += f"\n刷新失败用户:{s}"
else:
content += f"\n本次所有账户有效,无需刷新🎉"
tool.pushMaster(content)
def account_manage(self):
acc_map = sender.bucketAll("rabbit_zm_acc")
if not acc_map:
tool.replyMsg("暂无账密记录,请先登录")
exit()
content = f"账密列表如下:"
no = 0
for pin,accStr in acc_map.items():
acc_json = json.loads(accStr)
no +=1
content += f"\n{no}{quote(pin)}[{self.hide_phone(acc_json['mobile'])}]"
tool.replyMsg(content)
def run_login(self, mobile, pwd):
if not self.login_init(mobile):
return False
login_res, pin = self.login(mobile, pwd)
if not login_res:
return False
self.after_login(pin)
return True
if __name__ == "__main__":
sender = middleware.Sender(middleware.getSenderID())
plugin_ver = sender.getPluginVersion()
plugin_name = sender.getPluginName()
msg = sender.getMessage().strip('"')
imType = "fake" if sender.getImtype() == "cron" else sender.getImtype()
chatId = sender.getChatID()
userId = sender.getUserID()
isAdmin = sender.isAdmin()
username = sender.getUserName()
tool = TOOL()
#### 定时任务 #####
cron = middleware.Cron(None)
crons = list(filter(lambda item: "兔子账密刷新" in item['cmd'],cron.getCrons()))
if not crons:
tool.log_info(f"【兔子账密刷新】定时任务不存在,重新添加")
id = cron.addCron("0 0,3,5 * * *","兔子账密刷新",True,False,"兔子账密刷新","","","")
tool.log_info(f"add cron id = {id}")
#### 定时任务 ####
tz = ZM_RABBIT()
if "登录" in msg or "登陆" in msg:
sms_content = "请在60秒内输入手机号(q:退出)"
tool.replyMsg(sms_content)
mobile = sender.listen(60000)
if not mobile or mobile == "q" or mobile == "error":
tool.replyMsg("已退出!")
exit()
pattern = r'^1[3-9]\d{9}$'
if not re.match(pattern, mobile):
tool.replyMsg("手机号格式错误,已退出!")
exit()
tool.replyMsg("请在60秒内输入登录密码(q:退出)")
pwd = sender.listen(60000)
if not pwd or pwd == "q" or pwd == "error":
tool.replyMsg("已退出!")
exit()
if len(pwd) < 8:
tool.replyMsg("密码格式错误,已退出!")
exit()
tz.run_login(mobile, pwd)
exit()
if "刷新" in msg:
tz.refresh_account()
exit()
if "管理" in msg:
tz.account_manage()
+296
View File
@@ -0,0 +1,296 @@
#[title: mi10rb-aut]
#[language: python]
#[class: 工具类]
#[service: 10010] ADB设备重启管理
#[disable: false]
#[admin: false]
#[rule: ^重启Mi10$]
#[priority: 999]
#[platform: qq,qb,wx,tb,tg,web,wxmp]
#[open_source: false]
#[icon: 图标url]
#[version: 1.0.0]
#[public: false]
#[price: 0.00]
#[description: 通过ADB(TCP)连接到设备并执行重启;发送"重启Mi10"执行。<br>]
# [param: {"required":true,"key":"mi10rb.address","placeholder":"127.0.0.1:5555","name":"ADB地址:","desc":"设备的 ADB 地址,如 192.168.1.100:5555"}]
# [param: {"required":false,"key":"mi10rb.adb_path","placeholder":"adb","name":"adb路径:","desc":"adb 可执行文件路径,默认使用系统 adb"}]
# [param: {"required":false,"key":"mi10rb.timeout","placeholder":"20","name":"超时:","desc":"单次命令超时时间(秒),默认20"}]
import os
import sys
import shlex
import logging
import subprocess
import time
import colorlog
import middleware
import sender
from typing import Tuple, Optional
# 读取插件参数
CONF_ADDRESS = middleware.bucketGet('mi10rb', 'address')
CONF_ADB_PATH = middleware.bucketGet('mi10rb', 'adb_path') or 'adb'
CONF_TIMEOUT = int(middleware.bucketGet('mi10rb', 'timeout') or '20')
sender = middleware.Sender(middleware.getSenderID())
class TOOL:
def __init__(self):
self.plugin_pre = "mi10rb"
self.color_formatter = colorlog.ColoredFormatter(
'%(log_color)s%(levelname)s: %(message)s',
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white',
}
)
self.logger = self._configure_logger()
def _configure_logger(self):
logger_obj = logging.getLogger(self.plugin_pre)
logger_obj.setLevel(logging.DEBUG)
if not any(isinstance(h, logging.StreamHandler) for h in logger_obj.handlers):
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(self.color_formatter)
logger_obj.addHandler(console_handler)
return logger_obj
def log(self, level, message):
self.logger.log(level, message)
def reply(self, content):
return sender.reply(content)
def run_command(command: str, timeout_seconds: int = 20) -> Tuple[int, str, str]:
"""
以非交互方式执行命令,返回 (exit_code, stdout, stderr)
"""
try:
process = subprocess.Popen(
shlex.split(command),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
stdout, stderr = process.communicate(timeout=timeout_seconds)
return process.returncode, (stdout or '').strip(), (stderr or '').strip()
except subprocess.TimeoutExpired:
try:
process.kill()
except Exception:
pass
return 124, "", f"Command timed out after {timeout_seconds}s: {command}"
except FileNotFoundError:
return 127, "", f"Command not found: {command.split()[0]}"
class ADBRebootClient:
def __init__(self, address: str, adb_path: str = 'adb', timeout: int = 20, tool: Optional[TOOL] = None):
self.address = address
self.adb = adb_path or 'adb'
self.timeout = timeout
self.tool = tool
def _log(self, level, message: str):
if self.tool:
self.tool.log(level, message)
def disconnect(self) -> None:
# 断开指定地址,忽略错误
run_command(f"{self.adb} disconnect {self.address}", timeout_seconds=min(self.timeout, 5))
def kill_server(self) -> None:
# 尝试重启 adb 服务端,忽略错误
run_command(f"{self.adb} kill-server", timeout_seconds=min(self.timeout, 5))
def ensure_adb(self) -> bool:
code, out, err = run_command(f"{self.adb} version", timeout_seconds=self.timeout)
if code != 0:
self._log(logging.ERROR, "未找到可用的 adb,请安装或在参数中指定 adb 路径")
if err:
self._log(logging.ERROR, err)
return False
self._log(logging.INFO, (out or 'adb 可用'))
return True
def connect(self) -> bool:
# 先尝试断开可能残留的连接
self.disconnect()
# 首次连接
code, out, err = run_command(f"{self.adb} connect {self.address}", timeout_seconds=self.timeout)
msg = ((out or '') + ("\n" + err if err else '')).strip()
if code != 0:
self._log(logging.WARNING, f"adb connect 首次失败(code={code}),尝试重启 adb 并重试...\n{msg}")
# 重启 adb 后重试一次
self.kill_server()
self.disconnect()
code, out, err = run_command(f"{self.adb} connect {self.address}", timeout_seconds=self.timeout)
msg = ((out or '') + ("\n" + err if err else '')).strip()
if code != 0:
self._log(logging.ERROR, f"adb connect 重试失败(code={code}):\n{msg}")
return False
low = msg.lower()
if ("connected to" in low) or ("already connected to" in low):
self._log(logging.INFO, "✓ ADB 连接成功")
else:
self._log(logging.WARNING, "adb connect 返回0,但输出未包含已连接提示")
if msg:
self._log(logging.INFO, msg)
# 连接校验:确认设备在线
ok, detail = self.get_state()
if not ok:
self._log(logging.ERROR, f"连接校验失败:{detail}")
return False
self._log(logging.INFO, f"设备状态:{detail}")
return True
def get_state(self) -> Tuple[bool, str]:
code, out, err = run_command(f"{self.adb} -s {self.address} get-state", timeout_seconds=min(self.timeout, 10))
text = (out or err or '').strip()
if code == 0 and (out or '').strip() == 'device':
return True, 'device'
# 某些 ROM 会返回 unknown/unauthorized,这里给出更友好的提示
return False, (text or f"get-state 失败(code={code})")
def reboot(self) -> bool:
def looks_like_reboot_sent(message: str) -> bool:
low = (message or '').lower()
# 设备重启会导致连接被关闭,adb 可能返回如下错误,但指令已下发
hints = [
'closed',
'device offline',
'no devices/emulators found',
'cannot connect to daemon',
'daemon not running',
'failed to connect to',
'connection refused',
'connection reset',
'transport endpoint is not connected',
'broken pipe',
'read failed',
'i/o error',
'more than one device/emulator',
]
return any(h in low for h in hints)
def observe_post_reboot_disconnect(max_attempts: int = 8, interval_seconds: float = 0.5) -> bool:
# 在短时间内观察设备从在线到离线/不可达的过渡,以此判断重启已生效
for _ in range(max_attempts):
code_s, out_s, err_s = run_command(
f"{self.adb} -s {self.address} get-state",
timeout_seconds=min(self.timeout, 5)
)
text_s = ((out_s or '') + ('\n' + err_s if err_s else '')).strip().lower()
if code_s != 0:
# 命令失败,多半是设备掉线,视为重启进行中
return True
if (out_s or '').strip() != 'device':
# 非 device 状态(例如 offline/unknown),也视为重启进行中
return True
# 仍为在线,继续等待
time.sleep(interval_seconds)
return False
code, out, err = run_command(f"{self.adb} -s {self.address} reboot", timeout_seconds=self.timeout)
combined = ((out or '') + ("\n" + err if err else '')).strip()
if code == 0:
self._log(logging.INFO, "✓ 已发送重启指令")
return True
# 非零返回,但疑似已下发
if looks_like_reboot_sent(combined):
self._log(logging.WARNING, f"adb reboot 返回非零(code={code}),但检测到重启迹象,视为成功:\n{combined}")
return True
# 回退方案:走 shell reboot
self._log(logging.WARNING, f"adb reboot 失败(code={code}),尝试使用 shell reboot 回退...\n{combined}")
code2, out2, err2 = run_command(f"{self.adb} -s {self.address} shell reboot", timeout_seconds=self.timeout)
combined2 = ((out2 or '') + ("\n" + err2 if err2 else '')).strip()
if code2 == 0 or looks_like_reboot_sent(combined2):
self._log(logging.INFO, "✓ 已通过 shell reboot 发送重启指令")
return True
# 最终失败前,观察设备是否已开始断连(重启进行中)
if observe_post_reboot_disconnect():
self._log(logging.WARNING, "检测到重启后的断连迹象,尽管命令返回失败,仍视为已下发成功")
return True
self._log(logging.ERROR, f"shell reboot 仍失败(code={code2}):\n{combined2}")
return False
def run_reboot_flow(tool: TOOL):
"""执行 ADB 设备重启流程"""
tool.reply("开始执行 Mi10 ADB 重启流程...")
# 校验参数
if not CONF_ADDRESS:
tool.reply("❌ 配置参数不完整,请设置 mi10rb.address (如 192.168.1.100:5555)")
return
tool.log(logging.INFO, "配置信息:")
tool.log(logging.INFO, f" ADB地址: {CONF_ADDRESS}")
tool.log(logging.INFO, f" adb路径: {CONF_ADB_PATH}")
tool.log(logging.INFO, f" 超时: {CONF_TIMEOUT}")
client = ADBRebootClient(address=CONF_ADDRESS, adb_path=CONF_ADB_PATH, timeout=CONF_TIMEOUT, tool=tool)
try:
if not client.ensure_adb():
tool.reply("❌ 未检测到 adb,请安装或指定有效的 adb 路径")
return
tool.log(logging.INFO, "🚀 正在连接设备...")
if not client.connect():
tool.reply("❌ ADB 连接失败,请检查地址与网络")
return
tool.log(logging.INFO, "🔁 正在发送重启指令...")
if client.reboot():
tool.reply("✅ 重启指令已发送!设备即将重启")
else:
tool.reply("❌ 重启指令发送失败")
except Exception as exc:
tool.log(logging.ERROR, f"程序执行出错: {exc}")
tool.reply("❌ 程序执行出错,请检查配置与网络")
def exit_with_msg(msg: str):
if msg:
sender.reply(msg)
sender.reply("已退出!")
sys.exit(0)
def _safe_get_message():
try:
return sender.getMessage().strip('"')
except Exception:
return ""
if __name__ == "__main__":
tool = TOOL()
msg = _safe_get_message()
if "重启Mi10" in msg:
run_reboot_flow(tool)
sys.exit(0)
sender.reply("指令不支持,请发送:重启Mi10")
+459
View File
@@ -0,0 +1,459 @@
#[title: nazha]
#[language: python]
#[service: 10086] 售后联系方式
#[disable: false] 禁用开关,true表示禁用,false表示可用
#[admin: true] 是否为管理员指令
#[rule: ^哪吒查询$]
#[rule: ^哪吒管理$]
#[cron: 0 7 * * *]
#[priority: 300] 优先级,数字越大表示优先级越高
#[platform: qq,wx,tg,tb] 适用的平台
#[open_source: true]是否开源
#[icon: 图标url]图标链接地址,请使用48像素的正方形图标,支持http和https
#[version: 1.0.0]版本号
#[public: false] 是否发布?值为true或false,不设置则上传aut云时会自动设置为true,false时上传后不显示在市场中,但是搜索能搜索到,方便开发者测试
#[price: 0.01] 上架价格
# [description: 【简介】:哪吒监控查询 支持多账号管理,可查询服务器状态、服务状态等]
# [icon: https://cdn.jsdelivr.net/gh/naiba/nezha@master/resource/static/img/nezha.png]
# =======================
import importlib
import inspect
import json
import logging
import math
from math import fabs
import os
import random
import subprocess
import sys
import time
from datetime import datetime, timedelta
from multiprocessing import Pool
from multiprocessing.pool import ThreadPool
from urllib.parse import urlencode
from wsgiref import headers
import requests
import aiohttp
import asyncio
import middleware
plugin_pre = 'vhook_nezha_'
class TOOL:
def __init__(self):
self.plat = ["qq", "wx", "wb", "qb", "bt", "tg"]
self.platform_arr = self.platformArr()
self.sender = middleware.Sender(middleware.getSenderID())
self.plugin_title = self.sender.getPluginName()
self.plugin_ver = self.sender.getPluginVersion()
self.plugin_name = self.sender.getPluginName()
self.msg = self.sender.getMessage().strip('"')
self.imType = "fake" if self.sender.getImtype() == "cron" else self.sender.getImtype()
self.chatId = self.sender.getChatID()
self.userId = self.sender.getUserID()
self.isAdmin = self.sender.isAdmin()
self.remark = ""
self.timeStr = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 安装必须依赖
self.dependencies = ['colorlog', "requests", "aiohttp"]
self.install_dependencies()
self.logger = logging.getLogger()
self.color_formatter = sys.modules['colorlog'].ColoredFormatter(
'%(log_color)s%(levelname)s: %(message)s',
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white',
}
)
def install_dependencies(self):
for dependency in self.dependencies:
try:
importlib.import_module(dependency)
except ImportError as e:
self.pushErr2Master(f"【错误】:缺少【{dependency}】模块, 正在自动安装,请稍后....")
if self.check_pip_version("pip3"):
cmd = "pip3"
else:
cmd = "pip"
result = subprocess.run([cmd, 'install', dependency], capture_output=True, text=True)
if result.returncode == 0:
self.pushErr2Master(f"依赖【{dependency}】安装完成,请重新发送指令,运行插件")
def check_pip_version(self, pip_command):
try:
result = subprocess.run([pip_command, '--version'], capture_output=True, text=True)
if result.returncode == 0:
return True
except FileNotFoundError:
pass
return False
def get_log(self, level=logging.INFO):
self.logger.setLevel(level)
console_handler = logging.StreamHandler()
console_handler.setLevel(level)
console_handler.setFormatter(self.color_formatter)
for handler in self.logger.handlers:
self.logger.removeHandler(handler)
self.logger.addHandler(console_handler)
def log_info(self, msg):
self.get_log(logging.INFO)
return self.logger.info(f"{msg}")
def log_err(self, msg):
self.get_log(logging.ERROR)
return self.logger.error(f"{msg}")
def log_warn(self, msg):
self.get_log(logging.WARN)
return self.logger.warning(f"{msg}")
def platformArr(self):
plat_arr = []
for item in self.plat:
t = {"platform": f"{plugin_pre}{item}", "imType": item}
plat_arr.append(t)
return plat_arr
def pushMsg(self, userId, chatId, imType, title, content):
now = datetime.now()
formatted_datetime = now.strftime('%Y-%m-%d %H:%M:%S')
self.remark = ""
msg = f"*********{tool.plugin_title}*********"
msg += f'\n【任务时间】:{formatted_datetime}'
msg += f"\n{content}"
middleware.push(imType, chatId, userId, title, msg)
return
def pushGroup(self, imtype, groupCode, content):
self.pushMsg(0, groupCode, imtype, "", content)
def pushErr2Master(self, content):
middleware.notifyMasters(
f"【插件】:{self.plugin_title}\n {content}",
['qq', 'tg', 'wx', 'wb', ])
def replyMsg(self, content):
return self.sender.reply(content)
class ACCOUNT:
def __init__(self):
self.method = "account"
self.plugin_title = "哪吒监控"
self.attr_arr = [
{
"title": "备注",
"key": "name",
"timeOut": 60000
}, {
"title": "面板地址",
"key": "dashboard_url",
"timeOut": 60000
}, {
"title": "用户名",
"key": "username",
"timeOut": 60000
}, {
"title": "密码",
"key": "password",
"timeOut": 60000
}
]
def setVal(self, item, title, key, timeOut):
tool.log_info(item)
tool.replyMsg(f"请输入{title}")
value = tool.sender.listen(timeOut)
tool.log_info(f"{key}==={value}")
if value == "q" or value == "error":
tool.replyMsg("输入有误/超时,已退出!")
return False
item[key] = value
return True
def addCount(self):
tool.replyMsg("哪吒监控配置说明:\n1. 备注:用于区分不同账号\n2. 面板地址:哪吒面板的完整URL\n3. 用户名:面板登录用户名\n4. 密码:面板登录密码")
item = {}
go = True
for attr in self.attr_arr:
if not self.setVal(item, attr['title'], attr['key'], attr['timeOut']):
go = False
break
if not go:
tool.log_info("数据配置有误,已退出!")
return tool.replyMsg("数据配置有误,已退出!")
item_arr = []
item_str = middleware.bucketGet(f"{plugin_pre}{tool.imType}", tool.userId)
if item_str:
item_arr = json.loads(item_str)
item_arr.append(item)
middleware.bucketSet(f"{plugin_pre}{tool.imType}", tool.userId, json.dumps(item_arr))
tool.replyMsg("数据已配置完成,开始自动任务")
item['push_user_id'] = tool.userId
item['push_im_type'] = tool.imType
item['index'] = 1
item['total'] = 1
READ(item).run()
def editCount(self, item_arr, no):
item = item_arr[no]
content = f"请在【2分钟】内输入 序号,编辑对应的属性(q:退出)"
content += f"\n--------------------"
content += f"\n输入数字:0 删除此账号!"
content += f"\n--------------------"
for index, attr in enumerate(self.attr_arr):
if attr['key'] not in item:
item[attr['key']] = ""
content += f"\n{index + 1}.【{attr['title']}】:{item[attr['key']]}"
tool.replyMsg(content)
value = tool.sender.listen(120000)
if value == "q" or value == "error":
middleware.bucketSet(f"{plugin_pre}{tool.imType}", tool.userId, json.dumps(item_arr))
return tool.replyMsg("已退出!")
if not value.isdigit():
return self.editCount(item_arr, no)
if 0 < int(value) <= len(self.attr_arr):
attr = self.attr_arr[int(value) - 1]
if self.setVal(item, attr['title'], attr['key'], 12000):
item_arr[no] = item
self.editCount(item_arr, no)
elif value == "0":
item_arr.pop(no)
if len(item_arr) == 0:
middleware.bucketDel(f"{plugin_pre}{tool.imType}", tool.userId)
else:
middleware.bucketSet(f"{plugin_pre}{tool.imType}", tool.userId, json.dumps(item_arr))
return tool.replyMsg(f"已删除第{no + 1}个账号信息!请重新发送:哪吒管理 ")
else:
return self.editCount(item_arr, no)
def accoount_manager(self):
item_str = middleware.bucketGet(f"{plugin_pre}{tool.imType}", tool.userId)
if not item_str or item_str == "":
tool.replyMsg(f"未配置账号,请发送:哪吒查询")
exit(1)
item_arr = json.loads(item_str)
content = f"请选择要账号查看详情:(0增加, q退出)\n"
for index, item in enumerate(item_arr):
if not hasattr(item, ""):
item['disable'] = True
disable = item['disable']
status = '已启用' if disable else '已禁用'
content = "".join([content, f"\n{index + 1}{item['name']}"])
tool.replyMsg(content)
value = tool.sender.listen(60000)
if value == "q" or value == "error":
tool.replyMsg("输入有误,已退出!")
elif value == "0":
self.addCount()
elif value.isdigit() and 0 < int(value) <= len(item_arr):
self.editCount(item_arr, int(value) - 1)
else:
tool.replyMsg(f"输入有误,请重新发送:哪吒管理,并输入正确的序号!")
def cron_account_arr(self):
account_arr = []
for plat in tool.platformArr():
p = plat["platform"]
user_id_arr = middleware.bucketAllKeys(p)
if not user_id_arr:
continue
for index, user_id in enumerate(user_id_arr):
user_data_str = middleware.bucketGet(p, user_id)
if not user_data_str or user_id == '':
continue
user_data_arr_temp = json.loads(user_data_str)
for n, account_data in enumerate(user_data_arr_temp):
account_data["push_user_id"] = user_id
account_data["push_im_type"] = plat["imType"]
account_data['index'] = n + 1
account_data['total'] = len(user_data_arr)
account_arr.append(account_data)
return account_arr
def account_task(self, item, no):
read = READ(item)
read.run()
class READ:
def __init__(self, account):
self.name = account['name']
self.dashboard_url = account['dashboard_url']
self.username = account['username']
self.password = account['password']
self.index = account['index']
self.total = account['total']
self.push_user_id = account['push_user_id']
self.push_im_type = account['push_im_type']
self.msg = ''
self.dateStr = datetime.now().strftime('%Y-%m-%d')
self.timeStr = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
self.servers = [] # 初始化servers属性
self.chat_id = None # 初始化chat_id属性
def pushMsg(self, msg):
content = f"【账号备注】:{self.name}"
content += f'{msg}'
tool.pushMsg(self.push_user_id, None, self.push_im_type, "", content)
def format_bytes(self, size_in_bytes):
if size_in_bytes == 0:
return "0B"
units = ['B', 'KB', 'MB', 'GB', 'TB']
power = int(math.floor(math.log(size_in_bytes, 1024)))
power = min(power, len(units) - 1)
size = size_in_bytes / (1024 ** power)
formatted_size = f"{size:.2f}{units[power]}"
return formatted_size
async def get_server_info(self):
try:
async with aiohttp.ClientSession() as session:
# 登录获取token
login_url = f"{self.dashboard_url.rstrip('/')}/api/v1/login"
login_data = {
"username": self.username,
"password": self.password
}
async with session.post(login_url, json=login_data) as resp:
login_result = await resp.json()
if not login_result.get('success'):
return False, "登录失败,请检查用户名和密码"
token = login_result['data']['token']
# 获取服务器信息
headers = {"Authorization": f"Bearer {token}"}
server_url = f"{self.dashboard_url.rstrip('/')}/api/v1/server"
async with session.get(server_url, headers=headers) as resp:
server_result = await resp.json()
if not server_result.get('success'):
return False, "获取服务器信息失败"
self.servers = server_result['data'] # 填充servers属性
# 计算总体统计信息
online_count = sum(1 for server in self.servers if server.get('state', {}).get('last_active'))
total_mem = sum(server.get('host', {}).get('mem_total', 0) for server in self.servers)
used_mem = sum(server.get('state', {}).get('mem_used', 0) for server in self.servers)
total_disk = sum(server.get('host', {}).get('disk_total', 0) for server in self.servers)
used_disk = sum(server.get('state', {}).get('disk_used', 0) for server in self.servers)
total_swap = sum(server.get('host', {}).get('swap_total', 0) for server in self.servers)
used_swap = sum(server.get('state', {}).get('swap_used', 0) for server in self.servers)
total_net_in = sum(server.get('state', {}).get('net_in_transfer', 0) for server in self.servers)
total_net_out = sum(server.get('state', {}).get('net_out_transfer', 0) for server in self.servers)
# 返回总体统计信息
self.msg += f"\n【服务器数量】:{len(self.servers)}"
self.msg += f"\n【在线服务器】:{online_count}"
self.msg += f"\n【内存使用】:{self.format_bytes(used_mem)}/{self.format_bytes(total_mem)}"
self.msg += f"\n【磁盘使用】:{self.format_bytes(used_disk)}/{self.format_bytes(total_disk)}"
self.msg += f"\n【交换使用】:{self.format_bytes(used_swap)}/{self.format_bytes(total_swap)}"
self.msg += f"\n【总流量】:↓{self.format_bytes(total_net_in)}{self.format_bytes(total_net_out)}"
return True, "获取成功"
except Exception as e:
return False, f"获取信息失败:{str(e)}"
def run(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
success, message = loop.run_until_complete(self.get_server_info())
loop.close()
if not success:
self.msg += f"\n【错误】:{message}"
self.pushMsg(self.msg)
return
self.display_menu()
def display_menu(self):
# 只返回总体数据和选单
self.msg = f"\n【服务器数量】:{len(self.servers)}"
self.msg += f"\n【在线服务器】:{sum(1 for server in self.servers if server.get('state', {}).get('last_active'))}"
self.msg += f"\n【内存使用】:{self.format_bytes(sum(server.get('state', {}).get('mem_used', 0) for server in self.servers))}/{self.format_bytes(sum(server.get('host', {}).get('mem_total', 0) for server in self.servers))}"
self.msg += f"\n【磁盘使用】:{self.format_bytes(sum(server.get('state', {}).get('disk_used', 0) for server in self.servers))}/{self.format_bytes(sum(server.get('host', {}).get('disk_total', 0) for server in self.servers))}"
self.msg += f"\n【交换使用】:{self.format_bytes(sum(server.get('state', {}).get('swap_used', 0) for server in self.servers))}/{self.format_bytes(sum(server.get('host', {}).get('swap_total', 0) for server in self.servers))}"
self.msg += f"\n【总流量】:↓{self.format_bytes(sum(server.get('state', {}).get('net_in_transfer', 0) for server in self.servers))}{self.format_bytes(sum(server.get('state', {}).get('net_out_transfer', 0) for server in self.servers))}"
self.msg += "\n\n【服务器编号选单】:"
for index, server in enumerate(self.servers):
server_name = server.get('name', '未知')
self.msg += f"\n{index + 1}. {server_name}"
self.msg += "\n0. 查询所有服务器信息"
self.pushMsg(self.msg)
# 等待用户输入
user_input = tool.sender.listen(30000) # 30秒超时
if user_input.lower() == 'q':
tool.replyMsg("对话已结束。")
elif user_input.isdigit():
index = int(user_input) - 1
if index == -1:
# 查询所有服务器信息
all_details = ""
for i in range(len(self.servers)):
all_details += self.query_server_by_index(i) + "\n"
tool.replyMsg(all_details)
#tool.replyMsg("查询结束。")
elif 0 <= index < len(self.servers):
details = self.query_server_by_index(index)
tool.replyMsg(details)
self.display_menu() # 返回到二级菜单
else:
tool.replyMsg("无效的编号,请重新输入。")
self.display_menu()
else:
tool.replyMsg("超时或无效输入,已自动退出。")
def query_server_by_index(self, index):
if index < 0 or index >= len(self.servers):
return "无效的服务器编号。"
server = self.servers[index]
server_name = server.get('name', '未知')
status = "在线" if server.get('state', {}).get('last_active') else ""
details = f"\n{server_name}{status}"
details += f"\nCPU使用率:{server.get('state', {}).get('cpu', 0):.2f}%"
details += f"\n内存使用:{self.format_bytes(server.get('state', {}).get('mem_used', 0))}/{self.format_bytes(server.get('host', {}).get('mem_total', 0))}"
details += f"\n磁盘使用:{self.format_bytes(server.get('state', {}).get('disk_used', 0))}/{self.format_bytes(server.get('host', {}).get('disk_total', 0))}"
details += f"\n流量统计:↓{self.format_bytes(server.get('state', {}).get('net_in_transfer', 0))}{self.format_bytes(server.get('state', {}).get('net_out_transfer', 0))}"
details += f"\n当前下载速率:↓{self.format_bytes(server.get('state', {}).get('net_in_speed', 0))}/s"
details += f"\n当前上传速率:↑{self.format_bytes(server.get('state', {}).get('net_out_speed', 0))}/s"
return details
if __name__ == "__main__":
tool = TOOL()
account = ACCOUNT()
if tool.msg == '哪吒管理':
account.accoount_manager()
exit(1)
if tool.imType == 'fake':
user_data_arr = account.cron_account_arr()
else:
user_str = middleware.bucketGet(f"{plugin_pre}{tool.imType}", tool.userId)
if not user_str or user_str == "":
account.addCount()
exit(1)
user_data_arr = json.loads(user_str)
for i, user_data in enumerate(user_data_arr):
user_data["push_user_id"] = tool.userId
user_data["push_im_type"] = tool.imType
user_data['index'] = i + 1
user_data['total'] = len(user_data_arr)
if tool.sender.param(1):
user_data_arr = [user_data_arr[int(tool.sender.param(1))-1]]
for account_data in user_data_arr:
account.account_task(account_data, account_data['index'])
exit(1)
+1172
View File
File diff suppressed because it is too large Load Diff