├── .gitignore ├── .gitmodules ├── README.md ├── config.py ├── everphoto.py ├── index.py ├── requirements.txt └── tools.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "push"] 2 | path = push 3 | url = https://github.com/arcturus-script/push.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 时光相册签到(云函数版) 2 | 3 | 注意需要把子模块一起下载了, 直接下载时 push 文件夹是空的 4 | 5 | ```bash 6 | git clone --recursive https://github.com/arcturus-script/everphoto.git 7 | ``` 8 | 9 | 配置 config.py , 执行入口改为 index.main 10 | 11 | ```python 12 | # config.py 13 | 14 | # 重复的推送服务可以写成一个 15 | push = { 16 | "type": "pushplus", 17 | "key": "069ac93da07...", 18 | } 19 | 20 | config = { 21 | "multi": [ 22 | { 23 | "account": "189...", 24 | "country": "+86", 25 | "password": "root123", 26 | "push": push, 27 | }, 28 | { 29 | "account": "186...", 30 | "password": "root123", 31 | "push": push, 32 | } 33 | ], 34 | } 35 | ``` 36 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | config = { 2 | "multi": [ 3 | { 4 | "account": "xxx", # 账号 5 | "country": "+86", # 区号, 默认 +86 6 | "password": "xxx", # 密码, 非 MD5 加密 7 | "tasks": True, # 是否需要完成每日任务, 默认 False 8 | # "push": { 9 | # "type": "pushplus", 10 | # "key": "xxx", 11 | # }, 12 | }, 13 | { 14 | "account": "xxx", 15 | "password": "xxx", 16 | # "push": [ 17 | # # 以数组的形式填写, 则会向多个服务推送消息 18 | # { 19 | # "type": "pushplus", 20 | # "key": "xxx", 21 | # }, 22 | # { 23 | # "type": "workWechat", 24 | # "key": { 25 | # "agentid": 1000002, 26 | # "corpSecret": "xxx", 27 | # "corpid": "xxx", 28 | # }, 29 | # }, 30 | # ], 31 | }, 32 | ], 33 | "push": { 34 | # 合并发送消息, 只合并未单独配置 push 的账号 35 | "type": "pushplus", 36 | "key": "xxx", 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /everphoto.py: -------------------------------------------------------------------------------- 1 | from tools import handler, dateTime_format 2 | import hashlib 3 | import requests as re 4 | 5 | 6 | # 登录地址 7 | LOGIN_URL = "https://web.everphoto.cn/api/auth" 8 | 9 | # 签到地址 10 | CHECKIN_URL = "https://openapi.everphoto.cn/sf/3/v4/PostCheckIn" 11 | 12 | # 每日奖励 13 | DAILY_REWARD = "https://openapi.everphoto.cn/sf/3/v4/MissionRewardClaim" 14 | 15 | # 备注, 收藏等任务共同的 api 16 | CMD = "https://openapi.everphoto.cn/sf/3/v4/PostSyncCommand" 17 | 18 | # 任务状态回调 19 | TASKREPORT = "https://openapi.everphoto.cn/sf/3/v4/MissionReport" 20 | 21 | 22 | class Everphoto: 23 | def __init__(self, **config) -> None: 24 | self.__account = config.get("account") 25 | self.__password = config.get("password") 26 | self.userInfo = {} 27 | self.country_code = config.get("country", "+86") 28 | self.cmd = 1 # task id 29 | self.needTask = config.get("tasks", False) 30 | self.headers = { 31 | "user-agent": "EverPhoto/4.5.0 (Android;4050002;MuMu;23;dev)", 32 | "application": "tc.everphoto", 33 | } 34 | 35 | # 获取 md5 加密后的密码 36 | def get_pwd_md5(self) -> str: 37 | salt = "tc.everphoto." 38 | pwd = salt + self.__password 39 | md5 = hashlib.md5(pwd.encode()) 40 | return md5.hexdigest() 41 | 42 | # 登陆 43 | def login(self): 44 | try: 45 | data = { 46 | "mobile": f"{self.country_code}{self.__account}", 47 | "password": self.get_pwd_md5(), 48 | } 49 | 50 | print(f"++ 开始登录账号 {self.__account} ++") 51 | 52 | res = re.post(LOGIN_URL, data=data, headers=self.headers).json() 53 | 54 | if res.get("code") == 0: 55 | print(f"++ 登录账号 {self.__account} 成功 ++") 56 | 57 | data = res.get("data") 58 | 59 | self.headers.update( 60 | { 61 | "authorization": f"Bearer {data['token']}", 62 | }, 63 | ) 64 | 65 | profile = data["user_profile"] 66 | 67 | self.userInfo.update( 68 | { # 账号 69 | "account": self.__account, 70 | # 用户名 71 | "name": profile["name"], 72 | # vip等级 73 | "vip": profile.get("vip_level"), 74 | # 创建时间 75 | "created": dateTime_format(profile["created_at"]), 76 | # 注册时长 77 | "day": profile["days_from_created"], 78 | }, 79 | ) 80 | 81 | return { 82 | "status": True, 83 | } 84 | else: 85 | raise Exception(res.get("message")) 86 | except Exception as e: 87 | print(f"[error] 登录账号 {self.__account} 时出现错误, 原因: {e}") 88 | 89 | return { 90 | "status": False, 91 | "message": e, 92 | } 93 | 94 | # 签到 95 | def checkin(self): 96 | try: 97 | headers = { 98 | "content-type": "application/json", 99 | "host": "openapi.everphoto.cn", 100 | "connection": "Keep-Alive", 101 | } 102 | 103 | headers.update(self.headers) 104 | 105 | print(f"++ 账号 {self.__account} 开始签到 ++") 106 | 107 | res = re.post(CHECKIN_URL, headers=headers).json() 108 | 109 | code = res.get("code") 110 | 111 | if code == 0: 112 | print(f"++ 账号 {self.__account} 签到成功 ++") 113 | 114 | data = res.get("data") 115 | 116 | if data.get("checkin_result") is True: 117 | rwd = data["reward"] / (1024 * 1024) # 今日获得 118 | msg = "签到成功" 119 | else: 120 | rwd = 0 121 | msg = "今日已签到" 122 | 123 | return { 124 | "status": True, 125 | "reward": rwd, 126 | "message": msg, 127 | # 连续签到天数 128 | "continuity": data.get("continuity"), 129 | # 总计获得 130 | "total": data.get("total_reward") / (1024 * 1024), 131 | # 明日可获得 132 | "tomorrow": data.get("tomorrow_reward") / (1024 * 1024), 133 | } 134 | elif code == 20104: 135 | # 未登录 136 | raise Exception(res.get("message")) 137 | elif code == 30001: 138 | # 服务器内部错误 139 | raise Exception(res.get("message")) 140 | else: 141 | raise Exception("其他错误") 142 | except Exception as e: 143 | print(f"[error] 账号 {self.__account} 签到时出现错误, 原因: {e}") 144 | 145 | return { 146 | "status": False, 147 | "message": f"签到失败, 原因: {e}", 148 | } 149 | 150 | # 获取任务奖励 151 | def reward(self): 152 | headers = { 153 | "content-type": "application/json", 154 | "host": "openapi.everphoto.cn", 155 | "connection": "Keep-Alive", 156 | } 157 | 158 | headers.update(self.headers) 159 | 160 | # 任务奖励列表 161 | tasks = { 162 | "收藏": {"mission_id": "star"}, 163 | "隐藏": {"mission_id": "hide"}, 164 | "相册": {"mission_id": "add_to_album"}, 165 | "备注": {"mission_id": "remark"}, 166 | } 167 | 168 | # 状态信息, 将会运用到消息推送 169 | codeMap = { 170 | 0: "获取奖励成功", 171 | 20128: "任务状态不正确", 172 | 30005: "系统内部错误", 173 | } 174 | 175 | print("+++++++ 开始完成每日任务 +++++++") 176 | 177 | for key, task in tasks.items(): 178 | resp = re.post(TASKREPORT, headers=headers, json=task).json() 179 | 180 | if resp.get("code") == 0: 181 | print(f"[success] {key}") 182 | else: 183 | print(f"[failed] {key} --> {resp.get('message')}") 184 | 185 | print("+++++++ 获取每日任务奖励 +++++++") 186 | 187 | res = {} 188 | 189 | for key, task in tasks.items(): 190 | resp = re.post(DAILY_REWARD, headers=headers, json=task).json() 191 | 192 | msg = codeMap.get(resp["code"], "error") 193 | 194 | print(f"[{msg}] {key}") 195 | 196 | res[key] = msg 197 | 198 | return res 199 | 200 | @handler 201 | def start(self): 202 | r = self.login() 203 | 204 | if r.get("status"): 205 | result = {} 206 | result.update(self.userInfo) 207 | 208 | res = self.checkin() # 签到 209 | 210 | result.update(res) 211 | 212 | # 每日任务 213 | if self.needTask: 214 | result.update(self.reward()) 215 | 216 | return result 217 | else: 218 | return { 219 | "status": False, 220 | "message": f"登录失败, 原因: {r['message']}", 221 | "account": self.__account, 222 | } 223 | -------------------------------------------------------------------------------- /index.py: -------------------------------------------------------------------------------- 1 | from everphoto import Everphoto 2 | from config import config 3 | from push import PushSender, parse 4 | 5 | 6 | def parse_message(message, push_type): 7 | if push_type == "pushplus": 8 | return parse(message, template="html") 9 | else: 10 | return parse(message, template="markdown") 11 | 12 | 13 | def pushMessage(message, config): 14 | if isinstance(config, list): 15 | for item in config: 16 | t = item.get("type") 17 | 18 | p = PushSender(t, item.get("key")) 19 | 20 | p.send(parse_message(message, t), title="时光相册") 21 | else: 22 | t = config.get("type") 23 | 24 | p = PushSender(config.get("type"), config.get("key")) 25 | 26 | p.send(parse_message(message, t), title="时光相册") 27 | 28 | 29 | def main(*args): 30 | accounts = config.get("multi") 31 | push_together = config.get("push") 32 | 33 | messages = [] 34 | 35 | for item in accounts: 36 | obj = Everphoto(**item) 37 | 38 | res = obj.start() 39 | 40 | push = item.get("push") 41 | 42 | if push is None: 43 | if push_together is not None: 44 | messages.extend(res) 45 | else: 46 | pushMessage(res, push) 47 | 48 | if len(messages) != 0 and push_together is not None: 49 | pushMessage(messages, push_together) 50 | 51 | 52 | if __name__ == "__main__": 53 | main() 54 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arcturus-script/everphoto/803afe46bb244fc90aa6372cc3a065b4c10a5ae8/requirements.txt -------------------------------------------------------------------------------- /tools.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | 4 | def handler(fn): 5 | def inner(*args, **kwargs): 6 | res = fn(*args, **kwargs) 7 | 8 | if res.get("status", False): 9 | result = [ 10 | { 11 | "h4": { 12 | "content": f"账号: {res['account']}", 13 | } 14 | }, 15 | { 16 | "h4": { 17 | "content": f"用户名: {res['name']}", 18 | } 19 | }, 20 | { 21 | "txt": { 22 | "content": res["message"], 23 | }, 24 | "table": { 25 | "contents": [ 26 | ("描述", "内容"), 27 | ("今日获得", f"{res['reward']}M"), 28 | ("明日获得", f"{res['tomorrow']}M"), 29 | ("总共获得", f"{res['total']}M"), 30 | ("连续签到", f"{res['continuity']}天"), 31 | ("注册时间", f"{res['created']}"), 32 | ("注册天数", f"{res['day']}天"), 33 | ] 34 | }, 35 | }, 36 | ] 37 | 38 | if res.get("收藏"): 39 | result.append( 40 | { 41 | "txt": { 42 | "content": "任务情况", 43 | }, 44 | "table": { 45 | "contents": [ 46 | ("任务", "执行结果"), 47 | ("收藏", f"{res['收藏']}"), 48 | ("隐藏", f"{res['隐藏']}"), 49 | ("相册", f"{res['相册']}"), 50 | ("备注", f"{res['备注']}"), 51 | ] 52 | }, 53 | } 54 | ) 55 | 56 | return result 57 | 58 | else: 59 | # 登录失败 or 签到失败 60 | return [ 61 | { 62 | "h4": { 63 | "content": f"账号: {res['account']}", 64 | }, 65 | "txt": { 66 | "content": res["message"], 67 | }, 68 | }, 69 | ] 70 | 71 | return inner 72 | 73 | 74 | # 日期字符串格式化 75 | def dateTime_format(dt: str) -> str: 76 | try: 77 | dl = datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S+08:00") 78 | 79 | return dl.strftime("%Y-%m-%d %H:%M:%S") 80 | except ValueError as e: 81 | print(f"Format failed, because: {e}") 82 | --------------------------------------------------------------------------------