From 9f464ec33679d8baacef052aac604dcca43ce86a Mon Sep 17 00:00:00 2001 From: wd210010 <76995206+wd210010@users.noreply.github.com> Date: Sat, 14 Jun 2025 14:55:59 +0800 Subject: [PATCH] Update Misport.py --- Misport.py | 472 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 356 insertions(+), 116 deletions(-) diff --git a/Misport.py b/Misport.py index 3a81aa5..c758392 100644 --- a/Misport.py +++ b/Misport.py @@ -22,147 +22,387 @@ # "max_step": 22000 # } # ] - +# -*- coding: utf-8 -*- +import os +import json import random -import re -import time,os +import time +import logging +import hmac +import hashlib +import base64 +from datetime import datetime +from urllib.parse import urlencode, unquote, quote_plus +from functools import wraps -import requests +# --- 导入 requests 库,如果不存在则提示安装 --- +try: + import requests +except ImportError: + print("错误: 'requests' 库未安装。请运行 'pip install requests' 进行安装。") + exit(1) + +# --- 日志配置 --- +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' +) + +# --- 全局常量 --- +MAX_RETRIES = 3 +RETRY_DELAY = 2 # 初始延迟时间(秒) +RETRY_MULTIPLIER = 1.5 # 延迟时间乘数 -class MiMotion: - def __init__(self, check_items): - self.check_items = check_items - self.headers = { - "User-Agent": "Dalvik/2.1.0 (Linux; U; Android 9; MI 6 MIUI/20.6.18)" +# --- 重试装饰器 --- +def with_retry(operation_name="请求"): + """ + 一个装饰器,为函数添加重试逻辑。 + """ + + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + delay = RETRY_DELAY + for i in range(MAX_RETRIES): + try: + return func(*args, **kwargs) + except Exception as e: + logging.warning(f"{operation_name}失败: {e}。将在 {int(delay)} 秒后重试 ({i + 1}/{MAX_RETRIES})...") + if i < MAX_RETRIES - 1: + time.sleep(delay) + delay *= RETRY_MULTIPLIER + else: + logging.error(f"{operation_name}失败,已达到最大重试次数。") + raise # 重新抛出最终的异常 + + return wrapper + + return decorator + + +# --- 通知模块 --- +class Notifier: + """ + 处理所有通知服务的类。 + """ + + def __init__(self): + self.serverchan_key = os.getenv("SERVERCHAN_KEY") + self.pushplus_token = os.getenv("PUSHPLUS_TOKEN") + self.bark_key = os.getenv("BARK_KEY") + self.tg_bot_token = os.getenv("TG_BOT_TOKEN") + self.tg_chat_id = os.getenv("TG_CHAT_ID") + self.dingtalk_webhook = os.getenv("DINGTALK_WEBHOOK") + self.dingtalk_secret = os.getenv("DINGTALK_SECRET") + self.wecom_key = os.getenv("WECOM_KEY") + + def send(self, title, content): + """ + 发送通知到所有已配置的平台。 + """ + logging.info("准备发送通知...") + if self.serverchan_key: + self._send_serverchan(title, content) + if self.pushplus_token: + self._send_pushplus(title, content) + if self.bark_key: + self._send_bark(title, content) + if self.tg_bot_token and self.tg_chat_id: + self._send_telegram(title, content) + if self.dingtalk_webhook: + self._send_dingtalk(title, content) + if self.wecom_key: + self._send_wecom(title, content) + + def _safe_request(self, method, url, platform, **kwargs): + try: + response = requests.request(method, url, timeout=10, **kwargs) + if response.status_code == 200: + logging.info(f"✅ {platform} 通知发送成功。响应: {response.text}") + else: + logging.error(f"❌ {platform} 通知发送失败。状态码: {response.status_code}, 响应: {response.text}") + except Exception as e: + logging.error(f"❌ 发送 {platform} 通知时出错: {e}") + + def _send_serverchan(self, title, content): + url = f"https://sctapi.ftqq.com/{self.serverchan_key}.send" + data = {"title": title, "desp": content} + self._safe_request("POST", url, "Server酱", data=data) + + def _send_pushplus(self, title, content): + url = "http://www.pushplus.plus/send" + data = {"token": self.pushplus_token, "title": title, "content": content, "template": "markdown"} + self._safe_request("POST", url, "PushPlus", json=data) + + def _send_bark(self, title, content): + if self.bark_key.startswith("http"): + url = f"{self.bark_key}/{quote_plus(title)}/{quote_plus(content)}" + else: + url = f"https://api.day.app/{self.bark_key}/{quote_plus(title)}/{quote_plus(content)}" + self._safe_request("GET", url, "Bark") + + def _send_telegram(self, title, content): + url = f"https://api.telegram.org/bot{self.tg_bot_token}/sendMessage" + data = {"chat_id": self.tg_chat_id, "text": f"*{title}*\n\n{content}", "parse_mode": "Markdown"} + self._safe_request("POST", url, "Telegram", data=data) + + def _send_dingtalk(self, title, content): + url = self.dingtalk_webhook + if self.dingtalk_secret: + timestamp = str(round(time.time() * 1000)) + secret_enc = self.dingtalk_secret.encode('utf-8') + string_to_sign = f'{timestamp}\n{self.dingtalk_secret}' + hmac_code = hmac.new(secret_enc, string_to_sign.encode('utf-8'), digestmod=hashlib.sha256).digest() + sign = quote_plus(base64.b64encode(hmac_code)) + url = f"{url}×tamp={timestamp}&sign={sign}" + + data = { + "msgtype": "markdown", + "markdown": {"title": title, "text": f"## {title}\n\n{content}"} } + self._safe_request("POST", url, "钉钉", json=data) - def get_time(self): - url = "http://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp" - res = requests.get(url, headers=self.headers).json() - return res["data"]["t"] + # def _send_wecom(self, title, content): + # url = f"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={self.wecom_key}" + # data = { + # "msgtype": "markdown", + # "markdown": {"content": f"## {title}\n\n{content.replace('\n', '\n\n')}"} + # } + # self._safe_request("POST", url, "企业微信", json=data) - def get_app_token(self, login_token): - url = ( - f"https://account-cn.huami.com/v1/client/app_tokens?" - f"app_name=com.xiaomi.hm.health&" - f"dn=api-user.huami.com%2Capi-mifit.huami.com%2Capp-analytics.huami.com&" - f"login_token={login_token}" - ) - res = requests.get(url, headers=self.headers).json() - return res["token_info"]["app_token"] - @staticmethod - def login(phone, password): - url1 = f"https://api-user.huami.com/registrations/{phone}/tokens" - headers = { - "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", - "User-Agent": "MiFit/4.6.0 (iPhone; iOS 14.0.1; Scale/2.00)", - } - data1 = { +# --- 核心业务逻辑 --- +class MiFit: + """ + 封装与小米运动 API 交互的核心逻辑。 + """ + + def __init__(self, user_config, data_template): + self.phone = user_config["phone"] + self.password = user_config["password"] + self.min_step = user_config.get("min_step", 18000) + self.max_step = user_config.get("max_step", 25000) + self.data_template = data_template + self.session = requests.Session() + self.session.headers.update({ + 'User-Agent': 'MiFit/4.6.0 (iPhone; iOS 14.0.1; Scale/2.00)' + }) + self.user_id = None + self.app_token = None + + @with_retry("获取登录 Code") + def _get_code(self): + """第一步:使用手机号和密码登录,获取重定向URL中的 access code。""" + url = f"https://api-user.huami.com/registrations/+86{self.phone}/tokens" + data = { "client_id": "HuaMi", - "country_code": "CN", - "password": f"{password}", + "password": self.password, "redirect_uri": "https://s3-us-west-2.amazonaws.com/hm-registration/successsignin.html", - "state": "REDIRECTION", "token": "access", } - r1 = requests.post(url1, data1, headers=headers, allow_redirects=False) - location = r1.headers["Location"] - try: - code_pattern = re.compile("(?<=access=).*?(?=&)") - code = code_pattern.findall(location)[0] - except Exception as e: - print(e) - return 0, 0 - url2 = "https://account.huami.com/v2/client/login" - third_name = "phone" if phone.find("@") < 0 else "email" - data2 = { + headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'} + + response = self.session.post( + url, data=urlencode(data), headers=headers, allow_redirects=False + ) + response.raise_for_status() # 确保请求成功 + + location = response.headers.get("Location") + if not location or "access=" not in location: + raise Exception("获取登录 Code 失败,响应中未找到 Location 或 access code。") + + code = location.split("access=")[1].split("&")[0] + logging.info(f"✅ 账号 {self.phone[-4:]}: 获取登录 Code 成功。") + return code + + @with_retry("获取 Login Token") + def _get_login_token(self, code): + """第二步:使用 code 换取 login_token 和 user_id。""" + url = "https://account.huami.com/v2/client/login" + data = { "app_name": "com.xiaomi.hm.health", - "app_version": "6.3.5", - "code": f"{code}", + "app_version": "4.6.0", + "code": code, "country_code": "CN", - "device_id": "10E2A98F-D36F-4DF1-A7B9-3FBD8FBEB800", + "device_id": "2C8B4939-0CCD-4E94-8CBA-CB8EA6E613A1", "device_model": "phone", "grant_type": "access_token", - "source": "com.xiaomi.hm.health", - "third_name": f"{third_name}", + "third_name": "huami_phone", } - r2 = requests.post(url2, data2, headers=headers).json() - login_token = r2["token_info"]["login_token"] - user_id = r2["token_info"]["user_id"] - return login_token, user_id + headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'} - def main(self): - msg_all = "" - for check_item in self.check_items: - phone = str(check_item.get("phone")) - if phone.find("@") < 0: - phone = f"+86{phone}" - password = str(check_item.get("password")) + response = self.session.post(url, data=urlencode(data), headers=headers) + response_json = response.json() - try: - min_step = int(check_item.get("min_step", 10000)) - except Exception as e: - print("初始化步数失败: 已将最小值设置为 19999", e) - min_step = 10000 - try: - max_step = int(check_item.get("max_step", 19999)) - except Exception as e: - print("初始化步数失败: 已将最大值设置为 19999", e) - max_step = 19999 + if "token_info" not in response_json: + raise Exception(f"获取 Login Token 失败: {response_json.get('message', '未知错误')}") - step = str(random.randint(min_step, max_step)) - login_token, user_id = self.login(phone, password) + self.user_id = response_json["token_info"]["user_id"] + login_token = response_json["token_info"]["login_token"] + logging.info(f"✅ 账号 {self.phone[-4:]}: 获取 Login Token 和 UserID 成功。") + return login_token - if login_token == 0: - msg = f"帐号信息: *******{phone[-4:]}\n修改状态: 登陆失败" + @with_retry("获取 App Token") + def _get_app_token(self, login_token): + """第三步:使用 login_token 换取 app_token,这是与数据API交互的最终凭证。""" + url = "https://account.huami.com/v1/client/app_tokens" + params = { + "app_name": "com.xiaomi.hm.health", + "dn": "api-user.huami.com,api-mifit.huami.com,app-analytics.huami.com", + "login_token": login_token, + } - else: - t = self.get_time() - app_token = self.get_app_token(login_token) - today = time.strftime("%F") - data_json = "%5B%7B%22data_hr%22%3A%22%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9L%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FVv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0v%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9e%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0n%5C%2Fa%5C%2F%5C%2F%5C%2FS%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0b%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F1FK%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FR%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9PTFFpaf9L%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FR%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0j%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9K%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FOv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fzf%5C%2F%5C%2F%5C%2F86%5C%2Fzr%5C%2FOv88%5C%2Fzf%5C%2FPf%5C%2F%5C%2F%5C%2F0v%5C%2FS%5C%2F8%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FSf%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fz3%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0r%5C%2FOv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FS%5C%2F9L%5C%2Fzb%5C%2FSf9K%5C%2F0v%5C%2FRf9H%5C%2Fzj%5C%2FSf9K%5C%2F0%5C%2F%5C%2FN%5C%2F%5C%2F%5C%2F%5C%2F0D%5C%2FSf83%5C%2Fzr%5C%2FPf9M%5C%2F0v%5C%2FOv9e%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FS%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fzv%5C%2F%5C%2Fz7%5C%2FO%5C%2F83%5C%2Fzv%5C%2FN%5C%2F83%5C%2Fzr%5C%2FN%5C%2F86%5C%2Fz%5C%2F%5C%2FNv83%5C%2Fzn%5C%2FXv84%5C%2Fzr%5C%2FPP84%5C%2Fzj%5C%2FN%5C%2F9e%5C%2Fzr%5C%2FN%5C%2F89%5C%2F03%5C%2FP%5C%2F89%5C%2Fz3%5C%2FQ%5C%2F9N%5C%2F0v%5C%2FTv9C%5C%2F0H%5C%2FOf9D%5C%2Fzz%5C%2FOf88%5C%2Fz%5C%2F%5C%2FPP9A%5C%2Fzr%5C%2FN%5C%2F86%5C%2Fzz%5C%2FNv87%5C%2F0D%5C%2FOv84%5C%2F0v%5C%2FO%5C%2F84%5C%2Fzf%5C%2FMP83%5C%2FzH%5C%2FNv83%5C%2Fzf%5C%2FN%5C%2F84%5C%2Fzf%5C%2FOf82%5C%2Fzf%5C%2FOP83%5C%2Fzb%5C%2FMv81%5C%2FzX%5C%2FR%5C%2F9L%5C%2F0v%5C%2FO%5C%2F9I%5C%2F0T%5C%2FS%5C%2F9A%5C%2Fzn%5C%2FPf89%5C%2Fzn%5C%2FNf9K%5C%2F07%5C%2FN%5C%2F83%5C%2Fzn%5C%2FNv83%5C%2Fzv%5C%2FO%5C%2F9A%5C%2F0H%5C%2FOf8%5C%2F%5C%2Fzj%5C%2FPP83%5C%2Fzj%5C%2FS%5C%2F87%5C%2Fzj%5C%2FNv84%5C%2Fzf%5C%2FOf83%5C%2Fzf%5C%2FOf83%5C%2Fzb%5C%2FNv9L%5C%2Fzj%5C%2FNv82%5C%2Fzb%5C%2FN%5C%2F85%5C%2Fzf%5C%2FN%5C%2F9J%5C%2Fzf%5C%2FNv83%5C%2Fzj%5C%2FNv84%5C%2F0r%5C%2FSv83%5C%2Fzf%5C%2FMP%5C%2F%5C%2F%5C%2Fzb%5C%2FMv82%5C%2Fzb%5C%2FOf85%5C%2Fz7%5C%2FNv8%5C%2F%5C%2F0r%5C%2FS%5C%2F85%5C%2F0H%5C%2FQP9B%5C%2F0D%5C%2FNf89%5C%2Fzj%5C%2FOv83%5C%2Fzv%5C%2FNv8%5C%2F%5C%2F0f%5C%2FSv9O%5C%2F0ZeXv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F1X%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9B%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FTP%5C%2F%5C%2F%5C%2F1b%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9N%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%22%2C%22date%22%3A%222021-08-07%22%2C%22data%22%3A%5B%7B%22start%22%3A0%2C%22stop%22%3A1439%2C%22value%22%3A%22UA8AUBQAUAwAUBoAUAEAYCcAUBkAUB4AUBgAUCAAUAEAUBkAUAwAYAsAYB8AYB0AYBgAYCoAYBgAYB4AUCcAUBsAUB8AUBwAUBIAYBkAYB8AUBoAUBMAUCEAUCIAYBYAUBwAUCAAUBgAUCAAUBcAYBsAYCUAATIPYD0KECQAYDMAYB0AYAsAYCAAYDwAYCIAYB0AYBcAYCQAYB0AYBAAYCMAYAoAYCIAYCEAYCYAYBsAYBUAYAYAYCIAYCMAUB0AUCAAUBYAUCoAUBEAUC8AUB0AUBYAUDMAUDoAUBkAUC0AUBQAUBwAUA0AUBsAUAoAUCEAUBYAUAwAUB4AUAwAUCcAUCYAUCwKYDUAAUUlEC8IYEMAYEgAYDoAYBAAUAMAUBkAWgAAWgAAWgAAWgAAWgAAUAgAWgAAUBAAUAQAUA4AUA8AUAkAUAIAUAYAUAcAUAIAWgAAUAQAUAkAUAEAUBkAUCUAWgAAUAYAUBEAWgAAUBYAWgAAUAYAWgAAWgAAWgAAWgAAUBcAUAcAWgAAUBUAUAoAUAIAWgAAUAQAUAYAUCgAWgAAUAgAWgAAWgAAUAwAWwAAXCMAUBQAWwAAUAIAWgAAWgAAWgAAWgAAWgAAWgAAWgAAWgAAWREAWQIAUAMAWSEAUDoAUDIAUB8AUCEAUC4AXB4AUA4AWgAAUBIAUA8AUBAAUCUAUCIAUAMAUAEAUAsAUAMAUCwAUBYAWgAAWgAAWgAAWgAAWgAAWgAAUAYAWgAAWgAAWgAAUAYAWwAAWgAAUAYAXAQAUAMAUBsAUBcAUCAAWwAAWgAAWgAAWgAAWgAAUBgAUB4AWgAAUAcAUAwAWQIAWQkAUAEAUAIAWgAAUAoAWgAAUAYAUB0AWgAAWgAAUAkAWgAAWSwAUBIAWgAAUC4AWSYAWgAAUAYAUAoAUAkAUAIAUAcAWgAAUAEAUBEAUBgAUBcAWRYAUA0AWSgAUB4AUDQAUBoAXA4AUA8AUBwAUA8AUA4AUA4AWgAAUAIAUCMAWgAAUCwAUBgAUAYAUAAAUAAAUAAAUAAAUAAAUAAAUAAAUAAAUAAAWwAAUAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAeSEAeQ8AcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBcAcAAAcAAAcCYOcBUAUAAAUAAAUAAAUAAAUAUAUAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcCgAeQAAcAAAcAAAcAAAcAAAcAAAcAYAcAAAcBgAeQAAcAAAcAAAegAAegAAcAAAcAcAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcCkAeQAAcAcAcAAAcAAAcAwAcAAAcAAAcAIAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcCIAeQAAcAAAcAAAcAAAcAAAcAAAeRwAeQAAWgAAUAAAUAAAUAAAUAAAUAAAcAAAcAAAcBoAeScAeQAAegAAcBkAeQAAUAAAUAAAUAAAUAAAUAAAUAAAcAAAcAAAcAAAcAAAcAAAcAAAegAAegAAcAAAcAAAcBgAeQAAcAAAcAAAcAAAcAAAcAAAcAkAegAAegAAcAcAcAAAcAcAcAAAcAAAcAAAcAAAcA8AeQAAcAAAcAAAeRQAcAwAUAAAUAAAUAAAUAAAUAAAUAAAcAAAcBEAcA0AcAAAWQsAUAAAUAAAUAAAUAAAUAAAcAAAcAoAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAYAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBYAegAAcAAAcAAAegAAcAcAcAAAcAAAcAAAcAAAcAAAeRkAegAAegAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAEAcAAAcAAAcAAAcAUAcAQAcAAAcBIAeQAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBsAcAAAcAAAcBcAeQAAUAAAUAAAUAAAUAAAUAAAUBQAcBYAUAAAUAAAUAoAWRYAWTQAWQAAUAAAUAAAUAAAcAAAcAAAcAAAcAAAcAAAcAMAcAAAcAQAcAAAcAAAcAAAcDMAeSIAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBQAeQwAcAAAcAAAcAAAcAMAcAAAeSoAcA8AcDMAcAYAeQoAcAwAcFQAcEMAeVIAaTYAbBcNYAsAYBIAYAIAYAIAYBUAYCwAYBMAYDYAYCkAYDcAUCoAUCcAUAUAUBAAWgAAYBoAYBcAYCgAUAMAUAYAUBYAUA4AUBgAUAgAUAgAUAsAUAsAUA4AUAMAUAYAUAQAUBIAASsSUDAAUDAAUBAAYAYAUBAAUAUAUCAAUBoAUCAAUBAAUAoAYAIAUAQAUAgAUCcAUAsAUCIAUCUAUAoAUA4AUB8AUBkAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAA%22%2C%22tz%22%3A32%2C%22did%22%3A%22DA932FFFFE8816E7%22%2C%22src%22%3A24%7D%5D%2C%22summary%22%3A%22%7B%5C%22v%5C%22%3A6%2C%5C%22slp%5C%22%3A%7B%5C%22st%5C%22%3A1628296479%2C%5C%22ed%5C%22%3A1628296479%2C%5C%22dp%5C%22%3A0%2C%5C%22lt%5C%22%3A0%2C%5C%22wk%5C%22%3A0%2C%5C%22usrSt%5C%22%3A-1440%2C%5C%22usrEd%5C%22%3A-1440%2C%5C%22wc%5C%22%3A0%2C%5C%22is%5C%22%3A0%2C%5C%22lb%5C%22%3A0%2C%5C%22to%5C%22%3A0%2C%5C%22dt%5C%22%3A0%2C%5C%22rhr%5C%22%3A0%2C%5C%22ss%5C%22%3A0%7D%2C%5C%22stp%5C%22%3A%7B%5C%22ttl%5C%22%3A18272%2C%5C%22dis%5C%22%3A10627%2C%5C%22cal%5C%22%3A510%2C%5C%22wk%5C%22%3A41%2C%5C%22rn%5C%22%3A50%2C%5C%22runDist%5C%22%3A7654%2C%5C%22runCal%5C%22%3A397%2C%5C%22stage%5C%22%3A%5B%7B%5C%22start%5C%22%3A327%2C%5C%22stop%5C%22%3A341%2C%5C%22mode%5C%22%3A1%2C%5C%22dis%5C%22%3A481%2C%5C%22cal%5C%22%3A13%2C%5C%22step%5C%22%3A680%7D%2C%7B%5C%22start%5C%22%3A342%2C%5C%22stop%5C%22%3A367%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A2295%2C%5C%22cal%5C%22%3A95%2C%5C%22step%5C%22%3A2874%7D%2C%7B%5C%22start%5C%22%3A368%2C%5C%22stop%5C%22%3A377%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1592%2C%5C%22cal%5C%22%3A88%2C%5C%22step%5C%22%3A1664%7D%2C%7B%5C%22start%5C%22%3A378%2C%5C%22stop%5C%22%3A386%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1072%2C%5C%22cal%5C%22%3A51%2C%5C%22step%5C%22%3A1245%7D%2C%7B%5C%22start%5C%22%3A387%2C%5C%22stop%5C%22%3A393%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1036%2C%5C%22cal%5C%22%3A57%2C%5C%22step%5C%22%3A1124%7D%2C%7B%5C%22start%5C%22%3A394%2C%5C%22stop%5C%22%3A398%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A488%2C%5C%22cal%5C%22%3A19%2C%5C%22step%5C%22%3A607%7D%2C%7B%5C%22start%5C%22%3A399%2C%5C%22stop%5C%22%3A414%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A2220%2C%5C%22cal%5C%22%3A120%2C%5C%22step%5C%22%3A2371%7D%2C%7B%5C%22start%5C%22%3A415%2C%5C%22stop%5C%22%3A427%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1268%2C%5C%22cal%5C%22%3A59%2C%5C%22step%5C%22%3A1489%7D%2C%7B%5C%22start%5C%22%3A428%2C%5C%22stop%5C%22%3A433%2C%5C%22mode%5C%22%3A1%2C%5C%22dis%5C%22%3A152%2C%5C%22cal%5C%22%3A4%2C%5C%22step%5C%22%3A238%7D%2C%7B%5C%22start%5C%22%3A434%2C%5C%22stop%5C%22%3A444%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A2295%2C%5C%22cal%5C%22%3A95%2C%5C%22step%5C%22%3A2874%7D%2C%7B%5C%22start%5C%22%3A445%2C%5C%22stop%5C%22%3A455%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1592%2C%5C%22cal%5C%22%3A88%2C%5C%22step%5C%22%3A1664%7D%2C%7B%5C%22start%5C%22%3A456%2C%5C%22stop%5C%22%3A466%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1072%2C%5C%22cal%5C%22%3A51%2C%5C%22step%5C%22%3A1245%7D%2C%7B%5C%22start%5C%22%3A467%2C%5C%22stop%5C%22%3A477%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1036%2C%5C%22cal%5C%22%3A57%2C%5C%22step%5C%22%3A1124%7D%2C%7B%5C%22start%5C%22%3A478%2C%5C%22stop%5C%22%3A488%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A488%2C%5C%22cal%5C%22%3A19%2C%5C%22step%5C%22%3A607%7D%2C%7B%5C%22start%5C%22%3A489%2C%5C%22stop%5C%22%3A499%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A2220%2C%5C%22cal%5C%22%3A120%2C%5C%22step%5C%22%3A2371%7D%2C%7B%5C%22start%5C%22%3A500%2C%5C%22stop%5C%22%3A511%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1268%2C%5C%22cal%5C%22%3A59%2C%5C%22step%5C%22%3A1489%7D%2C%7B%5C%22start%5C%22%3A512%2C%5C%22stop%5C%22%3A522%2C%5C%22mode%5C%22%3A1%2C%5C%22dis%5C%22%3A152%2C%5C%22cal%5C%22%3A4%2C%5C%22step%5C%22%3A238%7D%5D%7D%2C%5C%22goal%5C%22%3A8000%2C%5C%22tz%5C%22%3A%5C%2228800%5C%22%7D%22%2C%22source%22%3A24%2C%22type%22%3A0%7D%5D" - find_date = re.compile(r".*?date%22%3A%22(.*?)%22%2C%22data.*?") - find_step = re.compile(r".*?ttl%5C%22%3A(.*?)%2C%5C%22dis.*?") - data_json = re.sub(find_date.findall(data_json)[0], today, data_json) - data_json = re.sub( - find_step.findall(data_json)[0], step, str(data_json) - ) - url = f"https://api-mifit-cn.huami.com/v1/data/band_data.json?&t={t}" - data = ( - f"userid={user_id}&last_sync_data_time=1628256960&" - f"device_type=0&last_deviceid=DA932FFFFE8816E7&data_json={data_json}" - ) - headers = { - "apptoken": app_token, - "Content-Type": "application/x-www-form-urlencoded", - } - res = requests.post(url, data, headers=headers).json() - msg = f"帐号信息: *******{phone[-4:]}\n修改状态: {res['message']}\n修改步数: {step}" - msg_all += msg + "\n\n" - return msg_all + response = self.session.get(url, params=params) + response_json = response.json() -def string_to_dict(s): - parts = s.split('-') - result_dict = {} - result_dict['phone'] = parts[0] - result_dict['password'] = parts[1] - result_dict['min_step'] = int(parts[2]) - result_dict['max_step'] = int(parts[3]) - return result_dict + if "token_info" not in response_json: + raise Exception(f"获取 App Token 失败: {response_json.get('message', '未知错误')}") -def start(): - s = os.getenv("misport").split('#') - print(f'共{len(s)}个账号') - for i in range(len(s)): - item =s[i] - _check_items = [string_to_dict(item)] - result = MiMotion(check_items=_check_items).main() - print(result) - print('*'*15) + self.app_token = response_json["token_info"]["app_token"] + logging.info(f"✅ 账号 {self.phone[-4:]}: 获取 App Token 成功。") + def _process_data(self, step): + """ + 处理数据模板,更新日期和步数。 + 这是一个更稳健的实现,通过解析JSON来修改。 + """ + logging.info("📦 正在处理数据模板...") + # 1. URL解码 + decoded_template = unquote(self.data_template) + # 2. 解析为Python对象 (List) + data_list = json.loads(decoded_template) + + # 3. 修改日期 + today_str = datetime.now().strftime("%Y-%m-%d") + data_list[0]['date'] = today_str + logging.info(f"📅 日期已更新为: {today_str}") + + # 4. 修改步数 + # `summary` 是一个字符串形式的JSON,需要再次解析 + summary_dict = json.loads(data_list[0]['summary']) + summary_dict['stp']['ttl'] = step + logging.info(f"👣 步数已更新为: {step}") + + # 5. 将修改后的 summary 写回 + # 使用 separators 去除空格,使其与原始格式更接近 + data_list[0]['summary'] = json.dumps(summary_dict, separators=(',', ':')) + + # 6. 将整个对象转回为JSON字符串 + return json.dumps(data_list) + + @with_retry("发送步数数据") + def _send_data(self, data_json): + """第四步:将处理好的数据发送到服务器。""" + url = "https://api-mifit-cn2.huami.com/v1/data/band_data.json" + data = { + "userid": self.user_id, + "last_sync_data_time": "1597306380", + "device_type": "0", + "last_deviceid": "DA932FFFFE8816E7", + "data_json": data_json, + } + headers = { + 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', + 'apptoken': self.app_token, + } + + response = self.session.post(url, data=urlencode(data), headers=headers) + response_json = response.json() + + if response_json.get("message") == "success": + logging.info(f"✅ 账号 {self.phone[-4:]}: 数据发送成功!服务器响应: {response_json}") + else: + raise Exception(f"数据发送失败,服务器响应: {response_json}") + + def run(self): + """ + 执行修改步数的完整流程。 + """ + # 随机生成步数 + if self.max_step < self.min_step: + self.min_step, self.max_step = self.max_step, self.min_step + + target_step = random.randint(self.min_step, self.max_step) + logging.info(f"--- 正在为账号 {self.phone[-4:]} 执行任务,目标步数: {target_step} ---") + + # 1. 获取 Code + code = self._get_code() + + # 2. 获取 Token + login_token = self._get_login_token(code) + + # 3. 获取 App Token + self._get_app_token(login_token) + + # 4. 处理数据 + processed_json = self._process_data(target_step) + + # 5. 发送数据 + self._send_data(processed_json) + + logging.info(f"🎉 账号 {self.phone[-4:]} 任务成功完成!") + return f"账号 {self.phone[-4:]} 成功: 步数修改为 {target_step}" + + +# --- 主函数 --- +def main(): + """ + 主执行函数,负责读取配置并为每个用户运行任务。 + """ + users_json = os.getenv("MI_USERS") + data_json_template = os.getenv("MI_DATA_JSON") + + if not users_json or not data_json_template: + logging.error("❌ 环境变量 'MI_USERS' 或 'MI_DATA_JSON' 未设置,脚本无法运行。") + return + + try: + users = json.loads(users_json) + if not isinstance(users, list) or not users: + raise ValueError("MI_USERS 格式错误,应为一个非空的JSON数组。") + except (json.JSONDecodeError, ValueError) as e: + logging.error(f"❌ 解析 MI_USERS 环境变量失败: {e}") + return + + notifier = Notifier() + success_count = 0 + failure_count = 0 + + logging.info(f"共找到 {len(users)} 个账户,开始执行任务...") + + for i, user_config in enumerate(users): + start_time = time.time() + try: + if "phone" not in user_config or "password" not in user_config: + logging.error(f"第 {i + 1} 个用户配置缺少 'phone' 或 'password',已跳过。") + failure_count += 1 + continue + + mifit_runner = MiFit(user_config, data_json_template) + result_message = mifit_runner.run() + success_count += 1 + except Exception as e: + failure_count += 1 + phone = user_config.get("phone", "未知") + error_message = f"❌ 账号 {phone[-4:]} 任务失败: {e}" + logging.error(error_message) + # 发送失败通知 + notifier.send( + title="小米运动步数修改失败通知", + content=error_message + ) + finally: + end_time = time.time() + logging.info(f"账号 {user_config.get('phone', '未知')[-4:]} 本次任务耗时: {end_time - start_time:.2f} 秒\n") + + logging.info("=" * 40) + logging.info(f"所有任务执行完毕。成功: {success_count}, 失败: {failure_count}") + logging.info("=" * 40) if __name__ == "__main__": - start() + main() + +