├── .gitignore ├── OnePlus.py ├── README.md ├── SunLogin.py ├── fun ├── __init__.py ├── baidu.py ├── code.py └── com.py ├── image ├── 1.png └── 3.png ├── notify.py └── wpspc.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /OnePlus.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | File: OnePlus.py(OnePlus签到) 6 | Author: ytt447735 7 | cron: 0 8 * * * 8 | new Env('OnePlus签到'); 9 | Update: 2024/10/19 10 | """ 11 | import os,notify 12 | import ujson 13 | import requests 14 | import re 15 | import time 16 | from fun import com 17 | 18 | class oneplus: 19 | def __init__(self): 20 | self.ck = '' 21 | self.Log = "" 22 | self.UA = "Mozilla/5.0 (Linux; Android 14; LE2120 Build/UKQ1.230924.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/128.0.6613.146 Mobile Safari/537.36 oppostore/403201 ColorOS/V14.0.0 brand/OnePlus model/LE2120;kyc/h5face;kyc/2.0;netType:NETWORK_WIFI;appVersion:403201;packageName:com.oppo.store" 23 | self.activityId_activityInfo = "" 24 | self.activityId_taskActivityInfo = "" 25 | 26 | # 获取签到标识 27 | def get_activityId(self): 28 | url = "https://hd.opposhop.cn/bp/b371ce270f7509f0?nightModelEnable=true&us=wode&um=qiandaobanner" 29 | payload = {} 30 | headers = { 31 | 'Cookie': self.ck, 32 | 'User-Agent': self.UA 33 | } 34 | response = requests.request("GET", url, headers=headers, data=payload) 35 | # print(response.text) 36 | # "activityInfo":{"activityId":"1838147945355288576", 37 | # "taskActivityInfo":{"activityId":"1838149802563739648", 38 | match = re.search(r'"activityInfo":{"activityId":"(\d+)"', response.text) 39 | if match: 40 | # return self.shopping_signIn(match.group(1)) 41 | self.activityId_activityInfo = match.group(1) 42 | print(f"activactivityId_activityInfoityId={self.activityId_activityInfo}") 43 | else: 44 | print("签到标识获取失败") 45 | self.Log = self.Log + f"📝签到失败,签到标识获取失败!\n" 46 | 47 | match = re.search(r'"taskActivityInfo":{"activityId":"(\d+)"', response.text) 48 | if match: 49 | self.activityId_taskActivityInfo = match.group(1) 50 | print(f"activityId_taskActivityInfo={self.activityId_taskActivityInfo}") 51 | else: 52 | print("任务标识获取失败") 53 | self.Log = self.Log + f"📝签到失败,任务标识获取失败!\n" 54 | 55 | 56 | 57 | # 商城签到 58 | def shopping_signIn(self): 59 | if self.activityId_activityInfo =="": 60 | return 61 | url = "https://hd.opposhop.cn/api/cn/oapi/marketing/cumulativeSignIn/signIn" 62 | payload = ujson.dumps({ 63 | "activityId": self.activityId_activityInfo 64 | }) 65 | headers = { 66 | 'Cookie': self.ck, 67 | 'User-Agent': self.UA, 68 | 'Content-Type': 'application/json' 69 | } 70 | response = requests.request("POST", url, headers=headers, data=payload) 71 | print("shopping_signIn",response.text) 72 | j = ujson.loads(response.text) 73 | if j["code"] == 200: 74 | awardType = j['data']['awardType'] 75 | awardValue = j['data']['awardType'] 76 | if awardType == 1: 77 | self.Log = self.Log + f"📝签到成功,奖励{ awardValue } 积分\n" 78 | else: 79 | message = j['message'] 80 | self.Log = self.Log + f"📝签到失败,{ message }\n" 81 | 82 | 83 | # 积分额度查询 84 | def integral_query(self): 85 | url = "https://msec.opposhop.cn/users/web/memberCenter/assets?couponStatus=1&couponType=0" 86 | payload = {} 87 | headers = { 88 | 'Cookie': self.ck, 89 | 'User-Agent': self.UA 90 | } 91 | response = requests.request("GET", url, headers=headers, data=payload) 92 | print("integral_query",response.text) 93 | j = ujson.loads(response.text) 94 | if j["code"] == 200: 95 | self.Log = self.Log + "💰当前余额:\n" 96 | for i, element in enumerate(j["data"]): 97 | title = element["title"] 98 | text = element["text"] 99 | Type = element["type"] 100 | if Type == "coupon" or Type == "credit" or Type == "growing": 101 | self.Log = self.Log + f"👛{ title }:{ text }\n" 102 | 103 | 104 | # 会员等级 105 | def membership_grade(self): 106 | url = "https://msec.opposhop.cn/users/web/memberCenter/getMemberExpDetail" 107 | payload = {} 108 | headers = { 109 | 'Cookie': self.ck, 110 | 'User-Agent': self.UA 111 | } 112 | response = requests.request("GET", url, headers=headers, data=payload) 113 | print("membership_grade",response.text) 114 | j = ujson.loads(response.text) 115 | if j["code"] == 200: 116 | gradeName = j['data']['gradeName'] 117 | des = j['data']['des'] 118 | self.Log = self.Log + f"🎖️会员等级:{ gradeName }({ des })\n" 119 | 120 | 121 | # 获取任务列表 122 | def get_task(self): 123 | if self.activityId_taskActivityInfo=="": 124 | return 125 | url = f"https://hd.opposhop.cn/api/cn/oapi/marketing/task/queryTaskList?activityId={ self.activityId_taskActivityInfo }&source=c" 126 | payload = {} 127 | headers = { 128 | 'Cookie': self.ck, 129 | 'Accept': 'application/json, text/plain, */*', 130 | 'User-Agent': self.UA 131 | } 132 | response = requests.request("GET", url, headers=headers, data=payload) 133 | print("get_task",response.text) 134 | j = ujson.loads(response.text) 135 | if j["code"] == 200: 136 | for i, element in enumerate(j["data"]["taskDTOList"]): 137 | taskName = element['taskName'] 138 | taskId = element['taskId'] 139 | activityId = element['activityId'] 140 | taskType = element['taskType'] # 1=浏览,4=预约, 6=开卡/购买 141 | taskStatus = element['taskStatus'] # 是否完成 142 | attachConfigTwo_link = element['attachConfigTwo']['link'] 143 | skuId = '' 144 | match = re.search(r'skuId=(\d+)', attachConfigTwo_link) 145 | if match: 146 | skuId = match.group(1) 147 | 148 | 149 | tt = self.button_text_status(element) 150 | if tt == 2: 151 | self.task_signInOrShareTask(taskName, taskId, activityId) 152 | elif tt==3: 153 | print(f"skuId={skuId}") 154 | self.subscribes(skuId,taskName, taskId, activityId) 155 | else: 156 | self.Log = self.Log + f"❌{ taskName } 任务执行失败,{ tt }\n" 157 | time.sleep(3) 158 | 159 | # 提交任务 160 | def task_signInOrShareTask(self, taskName, taskId, activityId): 161 | url = f"https://hd.opposhop.cn/api/cn/oapi/marketing/taskReport/signInOrShareTask?taskId={ taskId }&activityId={ activityId }&taskType=1" 162 | payload = {} 163 | headers = { 164 | 'Cookie': self.ck, 165 | 'Accept': 'application/json, text/plain, */*', 166 | 'User-Agent': self.UA 167 | } 168 | response = requests.request("GET", url, headers=headers, data=payload) 169 | print("task_signInOrShareTask",response.text) 170 | j = ujson.loads(response.text) 171 | if j["code"] == 200: 172 | self.task_receiveAward(taskName, taskId, activityId) 173 | else: 174 | message = j['message'] 175 | self.Log = self.Log + f"❌{ taskName } 任务提交失败,{ message }\n" 176 | 177 | 178 | 179 | # 领取任务奖励 180 | def task_receiveAward(self, taskName, taskId, activityId): 181 | url = f"https://hd.opposhop.cn/api/cn/oapi/marketing/task/receiveAward?taskId={ taskId }&activityId={ activityId }" 182 | payload = {} 183 | headers = { 184 | 'Cookie': self.ck, 185 | 'Accept': 'application/json, text/plain, */*', 186 | 'User-Agent': self.UA 187 | } 188 | response = requests.request("GET", url, headers=headers, data=payload) 189 | print("task_receiveAward",response.text) 190 | j = ujson.loads(response.text) 191 | if j["code"] == 200: 192 | awardType = j['data']['awardType'] 193 | awardValue = j['data']['awardType'] 194 | if awardType == 1: 195 | self.Log = self.Log + f"✅{taskName} 任务完成,奖励{ awardValue } 积分\n" 196 | else: 197 | message = j['message'] 198 | self.Log = self.Log + f"❌{ taskName } 任务失败,{ message }\n" 199 | 200 | # 预约任务 201 | def subscribes(self, skuId,taskName, taskId, activityId): 202 | url = "https://msec.opposhop.cn/goods/web/subscribes/goodsSubscribeV1" 203 | payload = f"type=1&skuId={ skuId }" 204 | headers = { 205 | 'Cookie': self.ck, 206 | 'Content-Type': 'application/x-www-form-urlencoded', 207 | 'User-Agent': 'okhttp/4.9.3.6' 208 | } 209 | response = requests.post(url, data=payload, headers=headers) 210 | # response = requests.request("POST", url, headers=headers, data=payload) 211 | print("subscribes",response.text) 212 | j = ujson.loads(response.text) 213 | if j["code"] == 200: 214 | self.task_receiveAward(taskName, taskId, activityId) 215 | else: 216 | message = j['errorMessage'] 217 | self.Log = self.Log + f"❌{ taskName } 预约失败,{ message }\n" 218 | 219 | # 新增日志 220 | def set_log(self,text): 221 | self.Log = self.Log + text 222 | 223 | 224 | # 获取日志 225 | def get_log(self): 226 | # return self.Log.replace("\n","\r\n") 227 | return self.Log 228 | 229 | 230 | 231 | def button_text_status(self,t): 232 | TASK_STATUS = { 233 | 'PREPARE_FINISH': 1, 234 | 'GO_AWARD': 2, 235 | 'FINISHED': 3, 236 | 'NOT_REMAINING_NUMBER': 6 237 | } 238 | task_type_texts = [ 239 | 1,# "立即签到", 240 | 2,# "去看看", 241 | 4,# "去分享", 242 | 2,# "去逛逛", 243 | 3,# "去预约", 244 | 3,# "去预约", 245 | 3,# "去预约", 246 | 5,# "去购买", 247 | 6,# "去组队", 248 | 2,# "去看看", 249 | 3,# "去预约", 250 | 7,# "去完成", 251 | 8,# "去添加", 252 | 9,# "去认证", 253 | 10,# "去关注", 254 | 11,# "去填写", 255 | 2,# "去逛逛", 256 | 2,# "去看看" 257 | ] 258 | 259 | if t['taskStatus'] == TASK_STATUS['PREPARE_FINISH']: 260 | # return task_type_texts.get(t['taskType'], "已结束") 261 | return task_type_texts[t['taskType']] 262 | elif t['taskStatus'] == TASK_STATUS['GO_AWARD']: 263 | return "领奖励" 264 | elif t['taskStatus'] == TASK_STATUS['NOT_REMAINING_NUMBER']: 265 | return "领光了" 266 | elif t['taskStatus'] == TASK_STATUS['FINISHED']: 267 | return "已完成" 268 | else: 269 | return "已结束" 270 | 271 | 272 | # 钱包签到 273 | def continueSign(self): 274 | url = "https://hwallet.finzfin.com/act/usersign/v1/continueSign" 275 | 276 | payload = ujson.dumps({ 277 | "actId": "AID202207111442220933", 278 | "funcId": "CONTINUEV2202209161649037309" 279 | }) 280 | 281 | headers = { 282 | 'User-Agent': "Mozilla/5.0 (Linux; Android 14; LE2120 Build/UKQ1.230924.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/128.0.6613.146 Mobile Safari/537.36;webank/h5face;webank/2.0 JSBridge/1 wallet/5.31.0_befb176_240927 FinshellWebSDK/3.0.2.74", 283 | 'Accept': "application/json;charset=UTF-8", 284 | 'Content-Type': "application/json", 285 | 'x-token': "TOKEN_eyJhbGciOiJFQ0RTQSIsInYiOiIxIn0.eyJleHAiOjE3MzE4Mzc3NzYyMTgsImlkIjoiNjk5ODkzMDU5IiwiaWRjIjoic2hvdW1pbmciLCJ0aWQiOiJYR2VCNUN4SkRyM25MN0lna2R5aHZ0RlFIczNWdXF1d3hNdTNBWFM4UGZPMHdwcXh0WmtXTkVWWGJ0cTJNTEZOS1dYK2Rpa0xmakZnZ2luNEtxK0JpZm0rTEdVeUtJNWdvUDlqbG9RVlpmST0ifQ.MEUCIQDTYNCBx3iliVXlR79AUkyZdPRfoCzePXtw2mY2eDIyuAIgF6hirnqnJunzzQpr1yq86QLQEWwaPGXOIlPV_GU6UBo" 286 | } 287 | 288 | response = requests.post(url, data=payload, headers=headers) 289 | # response = requests.request("POST", url, headers=headers, data=payload) 290 | print("continueSign", response.text) 291 | j = ujson.loads(response.text) 292 | if j["code"] == 200: 293 | print(1) 294 | else: 295 | message = j['msg'] 296 | self.Log = self.Log + f"❌签到失败,{ message }\n" 297 | 298 | 299 | # 一加论坛签到 300 | def bbsSign(self): 301 | url = "https://bbs-api-cn.oneplus.com/task/api/sign/v1/create" 302 | params = { 303 | 'ver': "bbs42703", 304 | 'timestamp': com.get_time(), 305 | # 'sign': "" 306 | } 307 | headers = { 308 | 'User-Agent': "bbs/android/42703", 309 | 'Accept-Encoding': "gzip", 310 | 'model': "LE2120", 311 | 'osver': "android14", 312 | 'romv': "LE2120_14.0.0.720(CN01)", 313 | 'lang': "zh-CN", 314 | 'token': com.GetIntermediateText(self.ck,"TOKENSID=",";"), 315 | 'tz': "Asia/Shanghai" 316 | } 317 | response = requests.post(url, params=params, headers=headers) 318 | print(response.text) 319 | j = ujson.loads(response.text) 320 | if j["code"] == 200: 321 | # if j['data']['todaySigned']==True: 322 | # self.Log = self.Log +'今天已经签到过啦!\n' 323 | # else: 324 | # self.Log = self.Log +'今天签到成功!\n' 325 | # self.Log = self.Log + '累计签到:'+str(j['data']['signDays'])+'天\n' 326 | # self.Log = self.Log + '连续签到:'+str(j['data']['continuousSignDays'])+'天\n' 327 | # self.Log = self.Log + '再连续签到'+str(j['data']['extSignDays'])+'天,可额外获得'+str(j['data']['extCredit'])+'积分\n' 328 | self.Log = self.Log +'今天签到成功!\n' 329 | else: 330 | self.Log = self.Log +"一加论坛签到失败,"+j['msg']+"\n" 331 | 332 | 333 | def run(self): 334 | OnePlus_COOKIE = os.getenv("OnePlus_COOKIE") 335 | if not OnePlus_COOKIE: 336 | notify.send("OnePlus_COOKIE",'🙃OnePlus_COOKIE 变量未设置') 337 | print('🙃OnePlus_COOKIE 变量未设置') 338 | exit() 339 | ck_list = OnePlus_COOKIE.split('&') 340 | print("-------------------总共" + str(int(len(ck_list))) + "🙃OnePlus_COOKIE CK-------------------") 341 | for mt_token in ck_list: 342 | # try: 343 | self.ck = mt_token 344 | self.set_log("\n--------一加论坛签到--------\n") 345 | r.bbsSign() 346 | self.set_log("\n--------OPPO商城任务--------\n") 347 | t = self.get_activityId() 348 | self.shopping_signIn() 349 | self.get_task() 350 | self.membership_grade() 351 | self.integral_query() 352 | # self.continueSign() 353 | print(self.get_log()) 354 | notify.send("OnePlus", self.get_log()) 355 | # except Exception as e: 356 | # print("出错了!详细错误👇错误CK👉" + mt_token) 357 | # print(e) 358 | # notify.send("OnePlus", "出错了!详细错误👇错误CK👉" + mt_token +"\n错误内容:" + str(e)) 359 | 360 | 361 | if __name__ == '__main__': 362 | r = oneplus() 363 | r.run() 364 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### 功能 2 | | 名称 | 功能 | 变量获取 | 变量名 | 变量模版 | 3 | |:-------|:------------------------------------------------------------------------|:------------------------------|:------------------------------|:------------------------------| 4 | | wps pc | pc端签到领取兑换vip时间
云空间签到领取容量
| [获取](https://vip.wps.cn/home) | wps_pc | wpsua=***;wps_sid=***;day=1 | 5 | | OPPO积分 | OPPO商城签到、每日任务、一加论坛签到 | Reqable抓包获取CK | OnePlus_COOKIE | TOKENSID=***;apkPkg=*** | 6 | | 贝锐 | 阳光小店每日签到、每日任务 | [获取](https://www.oray.com/) | BR_COOKIE | _s_id_=*** | 7 | 8 | --- 9 | #### 使用 10 |
青龙面板 11 | 12 | ##### 拉库 13 | ``` 14 | ql repo https://github.com/ytt447735/automation.git fun|notify.py fun main py 15 | ``` 16 | ##### 环境变量 17 | PC(day等于每日签到时自动兑换天数,可不设): 18 | ``` 19 | wps_pc 20 | wpsua=***;wps_sid=***;day=1 21 | ``` 22 | ##### 依赖 23 | ``` 24 | ujson 25 | requests 26 | ``` 27 | #### 验证码识别配置 28 | 之前版本采用的是百度的手写文字识别功能,[获取](https://console.bce.baidu.com/ai/?_=1722298138766#/ai/ocr/overview/index) 29 | 在"/fun/baidu.py"文件内修改"API_KEY"、"SECRET_KEY"的值
30 | 百度识别准确率低,已替换,原代码保留着,可自行替换
31 | 新代码已采用YOLOv8模型识别,准确率98%左右,识别模型代码暂不开放,识别接口暂免费提供
32 | 33 | #### CK失效 34 | 目前测试发现CK好像不会过期,一直有效 35 | 36 |
37 | 38 |
WPS签到打码部署 39 | 40 | 犹豫各位需求有点多,我服务器压力有点大,各位自行部署吧
41 | 部署完毕后 http://IP:8080/ 打开能看见“Hello, World!”说明部署成功了
42 | 最后替换对应的fun/code.py中的链接
43 | ``` 44 | docker run -d \ 45 | --name wps_verification \ 46 | -p 8080:8080 \ 47 | --restart unless-stopped \ 48 | ytt4477/wps_verification:latest \ 49 | /bin/bash -c /apps/run.sh 50 | ``` 51 | 52 |
53 | 54 | --- 55 | #### 预览 56 |
WPS 57 | 58 | ![WPS](image/3.png) 59 |
60 | -------------------------------------------------------------------------------- /SunLogin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | File: SunLogin.py(贝锐签到) 6 | Author: ytt447735 7 | cron: 2 8 * * * 8 | new Env('贝锐签到'); 9 | Update: 2024/10/19 10 | """ 11 | import ujson 12 | import requests 13 | import base64 14 | import hashlib 15 | import time 16 | import os 17 | import notify 18 | 19 | class sunlogin: 20 | def __init__(self): 21 | self.ck = '' 22 | self.Log = "" 23 | 24 | 25 | # 阳光小店-每日签到 26 | def income(self): 27 | url = "https://store-api.oray.com/point/1/income" 28 | headers = { 29 | 'User-Agent': "SLCC/15.3.1.66811 (Android)", 30 | 'Content-Type': "application/x-www-form-urlencoded;charset=utf-8", 31 | 'Referer': "https://sunlight.oray.com/", 32 | 'Cookie': self.ck 33 | } 34 | response = requests.post(url, headers=headers) 35 | print(response.text) 36 | if "code" in response.text: 37 | j = ujson.loads(response.text) 38 | self.Log = self.Log + j['message'] +'\n' 39 | return 40 | if "userid" in response.text: 41 | j = ujson.loads(response.text) 42 | self.Log = self.Log + "签到成功,获得🌞" + str(j['pointtotal']) + '阳光值\n' 43 | return 44 | self.Log = self.Log + "签到失败,未知错误\n" 45 | 46 | 47 | def sign(self): 48 | url = "https://store-api.oray.com/points/sign" 49 | headers = { 50 | 'User-Agent': "SLCC/15.3.1.66811 (Android)", 51 | 'Content-Type': "application/x-www-form-urlencoded;charset=utf-8", 52 | 'Referer': "https://sunlight.oray.com/", 53 | 'Cookie': self.ck 54 | } 55 | response = requests.get(url, headers=headers) 56 | print(response.text) 57 | if '签到成功' in response.text: 58 | j = ujson.loads(response.text) 59 | self.Log = self.Log + j['arguments']['dialogtitle'] + j['arguments']['dialogdesc']+"\n" 60 | else: 61 | self.Log = self.Log + "签到失败"+"\n" 62 | 63 | 64 | #收集阳光 65 | def production(self, pointdailyid): 66 | url = f"https://store-api.oray.com/points/{pointdailyid}/daily" 67 | headers = { 68 | 'User-Agent': "SLCC/15.3.1.66811 (Android)", 69 | 'Accept': "application/json, text/plain, */*", 70 | 'Accept-Encoding': "gzip, deflate, br, zstd", 71 | 'Content-Type': "application/x-www-form-urlencoded;charset=utf-8", 72 | 'Referer': "https://sunlight.oray.com/", 73 | 'Accept-Language': "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", 74 | 'Cookie': self.ck 75 | } 76 | response = requests.post(url, headers=headers) 77 | print(response.text) 78 | if "code" in response.text: 79 | j = ujson.loads(response.text) 80 | self.Log = self.Log +j["message"]+"\n" 81 | else: 82 | self.Log = self.Log + "☀️x1 收集成功\n" 83 | 84 | 85 | # 获取阳光列表 86 | def getDailys(self): 87 | url = f"https://store-api.oray.com/points/daily" 88 | headers = { 89 | # 'User-Agent': "SLCC/15.3.1.66811 (Android)", 90 | # 'Accept': "application/json, text/plain, */*", 91 | # 'Accept-Encoding': "gzip, deflate, br, zstd", 92 | 'Content-Type': "application/x-www-form-urlencoded;charset=utf-8", 93 | 'Referer': "https://sunlight.oray.com/", 94 | # 'Accept-Language': "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", 95 | 'Cookie': self.ck 96 | } 97 | response = requests.get(url, headers=headers) 98 | print("getDailys:"+response.text + '🔚') 99 | j = ujson.loads(response.text) 100 | for i, element in enumerate(j): 101 | id = element['pointdailyid'] 102 | userid = element['userid'] 103 | print(id) 104 | self.production(id) 105 | time.sleep(2) 106 | 107 | # 获取阳光余额 108 | def getPlants(self): 109 | url = "https://store-api.oray.com/point/plants" 110 | headers = { 111 | # 'User-Agent': "SLCC/15.3.1.66811 (Android)", 112 | # 'Accept': "application/json, text/plain, */*", 113 | # 'Accept-Encoding': "gzip, deflate, br, zstd", 114 | 'Content-Type': "application/x-www-form-urlencoded;charset=utf-8", 115 | 'Referer': "https://sunlight.oray.com/", 116 | # 'Accept-Language': "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", 117 | 'Cookie': self.ck 118 | } 119 | response = requests.get(url, headers=headers) 120 | print("getPlants:"+response.text) 121 | if '' == response.text: 122 | return False 123 | j = ujson.loads(response.text) 124 | if "pointtotal" in response.text: 125 | self.Log = self.Log +"☀️余额:"+str(j['pointtotal'])+"\n" 126 | return True 127 | else: 128 | self.Log = self.Log +"☀️余额:未知\n" 129 | return False 130 | 131 | # 提交任务 132 | def setIncome(self,key): 133 | url = "https://store-api.oray.com/point/0/income" 134 | k = key.replace('=','%3D') 135 | payload = f"point_key={k}" 136 | headers = { 137 | # 'User-Agent': "SLCC/15.3.1.66811 (Android)", 138 | # 'Accept': "application/json, text/plain, */*", 139 | # 'Accept-Encoding': "gzip, deflate, br, zstd", 140 | 'Content-Type': "application/x-www-form-urlencoded;charset=utf-8", 141 | 'Referer': "https://sunlight.oray.com/", 142 | # 'Accept-Language': "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", 143 | 'Cookie': self.ck 144 | } 145 | response = requests.post(url, data=payload, headers=headers) 146 | print("setIncome:"+response.text) 147 | message = '' 148 | if 'userid' in response.text: 149 | return True, message 150 | if 'message' in response.text: 151 | j = ujson.loads(response.text) 152 | message = j['message'] 153 | return False, message 154 | 155 | 156 | # 获取任务列表 157 | def getPoints(self,brand): 158 | url = f'https://store-api.oray.com/points?brand={brand}' 159 | headers = { 160 | # 'User-Agent': "SLCC/15.3.1.66811 (Android)", 161 | # 'Accept': "application/json, text/plain, */*", 162 | # 'Accept-Encoding': "gzip, deflate, br, zstd", 163 | 'Content-Type': "application/x-www-form-urlencoded;charset=utf-8", 164 | 'Referer': "https://sunlight.oray.com/", 165 | # 'Accept-Language': "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", 166 | 'Cookie': self.ck 167 | } 168 | response = requests.get(url, headers=headers) 169 | print("getDailys:"+response.text) 170 | j = ujson.loads(response.text) 171 | for i, element in enumerate(j): 172 | name = element['name'] 173 | count = element['condition']['count'] 174 | used = element['used'] 175 | print(count,used) 176 | if used == count: 177 | self.Log = self.Log + name +f"({str(count)}/{str(count)}) ✅已完成\n" 178 | continue 179 | acc = used 180 | for i in range(count-used): 181 | pointid = element['pointid'] 182 | print(pointid,count,used) 183 | pointid = self.pointjm(str(pointid)) 184 | message = '' 185 | isOK, message = self.setIncome(pointid) 186 | if isOK: 187 | acc = acc + 1 188 | message = '✅已完成' 189 | else: 190 | message = "❌失败,"+message 191 | time.sleep(10) 192 | # break 193 | self.Log = self.Log + name +f"({str(acc)}/{str(count)}) {message}\n" 194 | time.sleep(3) 195 | # break 196 | 197 | 198 | # 加密 199 | def pointjm(self, t): 200 | from Crypto.Cipher import AES 201 | from Crypto.Util.Padding import pad 202 | key = "Nx4xnfHmGzz4t1rH" 203 | l = hashlib.md5(key.encode()).hexdigest() 204 | c = l[:16] 205 | d = l.encode('utf-8') 206 | h = c.encode('utf-8') 207 | cipher = AES.new(d, AES.MODE_CBC, h) 208 | e = t.encode('utf-8') 209 | n = cipher.encrypt(pad(e, AES.block_size)) 210 | return base64.b64encode(n).decode('utf-8') 211 | # Example usage 212 | # t = "3" 213 | # result = encrypt(t) 214 | # print(result) # Output should be "flEnHDBJMbe1mlNaEFKtzw==" 215 | 216 | # flEnHDBJMbe1mlNaEFKtzw== 217 | 218 | 219 | # 新增日志 220 | def set_log(self,text): 221 | self.Log = self.Log + text 222 | 223 | 224 | # 获取日志 225 | def get_log(self): 226 | # return self.Log.replace("\n","\r\n") 227 | return self.Log 228 | 229 | # 执行 230 | def run(self): 231 | task_name = '贝锐' 232 | ck_value = 'BR_COOKIE' 233 | CKS = os.getenv(ck_value) 234 | if not CKS: 235 | notify.send(task_name,f'🙃{ck_value} 变量未设置') 236 | print(f'🙃{ck_value} 变量未设置') 237 | exit() 238 | CKS_list = CKS.split('&') 239 | print("-------------------总共" + str(int(len(CKS_list))) + f"个{ck_value} CK-------------------") 240 | for mt_token in CKS_list: 241 | # try: 242 | self.ck = mt_token 243 | if self.getPlants() == False: 244 | self.set_log('⚠️ '+mt_token+ ' CK失效了') 245 | continue 246 | self.set_log("\n--------阳光小店签到--------\n") 247 | self.income() 248 | self.getDailys() # 收集阳光 249 | self.set_log("\n--------阳光任务--------\n") 250 | self.getPoints(3) 251 | self.getPoints(2) 252 | self.getPoints(0) 253 | self.getPlants() #最终额度 254 | # except Exception as e: 255 | # print("出错了!详细错误👇错误CK👉" + mt_token) 256 | # print(e) 257 | # notify.send(task_name, "出错了!详细错误👇错误CK👉" + mt_token +"\n错误内容:" + str(e)) 258 | print(self.get_log()) 259 | notify.send(task_name, self.get_log()) 260 | 261 | 262 | if __name__ == '__main__': 263 | w = sunlogin() 264 | w.run() -------------------------------------------------------------------------------- /fun/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | # 获取当前文件的目录 5 | current_dir = os.path.dirname(os.path.abspath(__file__)) 6 | 7 | # 将 fun 文件夹添加到 sys.path 8 | sys.path.append(current_dir) 9 | -------------------------------------------------------------------------------- /fun/baidu.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import urllib 3 | import requests 4 | import ujson 5 | 6 | # 百度手写识别key 7 | API_KEY = "" 8 | SECRET_KEY = "" 9 | 10 | 11 | def get_manage(base64): 12 | url = "https://aip.baidubce.com/rest/2.0/ocr/v1/handwriting?access_token=" + get_access_token() 13 | 14 | # image 可以通过 get_file_content_as_base64("C:\fakepath\segment_1.png",True) 方法获取 15 | payload = { 16 | "image": base64, 17 | "detect_direction": "false", 18 | "probability": "false", 19 | "detect_alteration": "false" 20 | } 21 | # payload = 'image=iVBORw0KGgoAAAANSUhEUgAAAEgAAABYCAIAAADZWSaLAAAIt0lEQVR4nO1bf0iTzx%2B%2FfcdYIiIjZISYyLAVIhEjRCxkmVhIiZnIEBkyRCRKJMWGxBgyRCKGyIgYYiEmERLDxggRMREbMkS0Yo0xZMiQKRJD1hC5zx%2F3%2Fd7n7T3PfjzPZrkve%2F2159537%2Ff7dfc8d%2Fd%2B302CMUb%2Fj%2FjP33bgtJAjlm3IEcs25IhlG3LEsg05YtmGHLFsQ47YH8fW1lZ%2Ff%2F%2Fly5dv3bolpj0%2BY3A6ncXFxYyTDQ0NQvVkgJjH40lfCcGjR48yNQDpEqOuHB0dpaNneno68Zul0%2BkEKUyLWDQapYYHBwfFKXG73RqNJukn4%2Ff7BalNi1goFKKGFQrF%2Fv6%2BUA1v377lcpBKpU6nE59MxgjVnBaxQCAAba%2BtrXErtLe3I4Tq6%2BsPDg6gyO%2F35%2BXlcVnV19d7vV6MscfjoYWFhYXRaPSvEdvc3ITS8fFxKDWbzVTkcrm4lFQqVSgUonWqq6up6N69e0J9S%2Fcbg73u8%2FlI%2Bfz8PNfvvr4%2BIp2bm%2BNKLRbL8fEx1by0tASli4uLf5QYxlipVFLzLpdrc3MT9jTEysoKxvj169dMuVwu%2F%2FbtG6O2tLQU1hHhWLrESkpKqPmamhqpVMqlpNfryRcSjUaLioqgaHBwMBaLMTqZGWVqauovEGtoaEAIyWQy3rVVq9W6XC5YH46w1WpVq9UIoa6urhM%2BAVy9elWcY2kROz4%2B7uzsbG1t9fv9Xq%2BXYbW0tMRt4na7idRsNsPZxWQyMRUIRHxdBOKJDQ8PI4Sampr%2B1fU%2F9Pb2pqJhdHQUctjY2MAYP3%2F%2BHBaKdk9My%2B3t7Zs3bxLDAwMDpJDMhBqNZnZ2NkU9a2trkMPTp0%2Fxyfewo6NDhHsEgolNTU1B27W1tRsbGz09PXl5eaOjo0K1wfHJz8%2FHnKMfnU4XDAaFqsWCiAWDwWvXrqH4WFhYEGr%2B8PAQanA6nZnSnCoxn8%2FHrC0Iod7eXolEQh%2B5rY6Pj%2Bfm5paXlxNolslkVENTUxOzX6H49etX5oltb28zZiQSicPhwCffnPX1daahTqcjosLCQrfbzavcarVCJRjjcDjc2NjIWKysrMwwMa1Wy9hobW39tz0AoUoQiUTOnz%2FPNAwEAlz9kUiEIUYQDAabmpqgaH5%2BPjPEgsHg9evXGefogoMx9vl8UEQ3wSsrK4gPWq2W3wmE6A7mxYsXjIhCIpGkHpUlIlZeXs54ZjQaYYX19XVuZ%2Fv9fl5WBHD%2FTnB0dGQwGBYWFoi2srIyGIy%2FfPkSNq%2BqqkqX2MePHxmfZmZmuD5xiV24cCEBsbGxMa6toaGhlpYWjLHD4WhubmaGpa2tDWrY2dlJixgM%2B%2BN9uGazmdbRaDTBYFChUEAnJBKJ1%2BttaWlJ3OXLy8u0X0Kh0MjICOslgFqtjkQi4olhjA8PD9vb200mUzxFVVVVkFh9fT30oLq6mlRjwjNeVdXV1T9%2B%2FKA0VldXodRut0MNVqs1LWJJwf0IISsYzEMRDCgpZmdntVotmffKysqKi4uZ7S9M%2BCiVylMkBt9VLsLh8AkzALzx1cbGBpGura1ZLBbyG37V5HVNPOyZIQbdvXLlCrT6%2BfNnWofsGOrq6qiUN60bjUYrKytJBchhenqaaw4hlHQDKZ7Y5uYmNZOfn09%2Fz83N4ZOTqsPhmJiYSNrfcCqCMBqNZAEgUSnB0NDQaREzmUwIodLS0p2dndraWmJveHgYY8zQqKioYN5bXoXv37%2FnJYYQUqvVHo%2Fn8ePHtEShUJwWMYPBgBBaXV2lO3Sr1RoIBPR6PeMWiTthCe9unQnPYDaFt%2BS0iEHtCCGtVkvGkEFVVRWZHsvKymhhZ2cnV%2BH%2B%2Fj5M9djtdmZ%2FzOBUiO3u7hLtMzMzRqMxnu3JyUmMcSQSmZiYgGu3TCbjVUveAoJXr17hOKlVhFB3d%2FepEGtubiYGysvL4%2B2hPnz4QCqTTFYq%2FW2z2WiFuro6Uri4uMhtzmSdM0MsFosVFhYylmDutq%2Bvj25W4uVPadoYghmf7e1tUj47OwvLmQggY8TgRA9HoKCgAIGVx%2B1206WJCzJ%2FMmDiIJjAC4fDNpstXrSaGWK9vb1cR0k6pKenh9RhZnyEkEqlgut4vEwoHGFxOWDxxKC7TExht9sxX8iDEPJ6vcxKxat8cHCQVtDr9X%2BOGJ0PCcbHx2mOESGk0%2Bm6u7u5rOhuPSkxuJEvKCgQcZgokhjzjkWj0cnJSS4TBjTPAwt5c%2BDMARJJD4uA4Hse7969o7%2B7urrOnTuXyj2MGzdufP%2F%2BnSn88uULt%2BalS5fg4%2B%2Ffv4V6%2BF8I7QnYllyEgCfREDabDe5FlEplOBwmJ7cEjY2NvCZUKhWt84cOJeBUIZfLScgYDochn7t378IQE06hFRUVDoeDPpaUlPBagUlFOs2eIjFmkaFnykw6ta2tjWkol8upFG5TiouLeY%2FMyTkOgVQqPXViTCbshBYAlUrFNGSSWRA0zwHB7KFEsMKCiEFjFoslngghxD1THhkZ4SUWL7mbPrFUZ8WvX7%2FCxzt37sBHGPkjhLa2tpjmz5496%2Bzs5Krd29vjNQcPQN68eZOikyeQYgfAnQ43SQSnBIQQNzHIHYfENWEvaDSa1AeKIqUR%2B%2FTp0%2BrqKn188uQJU%2BH%2B%2FftDQ0P08eLFi7x6uDn9nz9%2F8ta8ffs2%2FQ2v6AhAYt6xWIx7LhGvMgndZTJZgiz0wMAAVFVTU8NbjVkbUxwliCRtxsbGGFZkmxsPXq93d3c3sU44kdBglMczgKRhJU%2FzxOKOjg6GGA3%2B0gHpL%2BZ6BwOa%2BUIIGQwGoSaSEGMOTpNmGjIIuEyLeBuTNDg4OKAH6lKpNJVjjkyBycYJbZ68Abl%2Bk5eXl3pYnilAYkKv5p65W9wQ8M4gycaljrN77x4h9ODBA%2FobxoGp4EwT6%2B%2Fvp%2FdIHj58KKitBJ%2FtP3jv7e0VFRWJcPKsExONM%2F0qpoMcsWxDjli2IUcs25Ajlm3IEcs25IhlG%2F4BarRllGnmfvIAAAAASUVORK5CYII%3D&detect_direction=false&probability=false&detect_alteration=false' 22 | headers = { 23 | 'Content-Type': 'application/x-www-form-urlencoded', 24 | 'Accept': 'application/json' 25 | } 26 | response = requests.request("POST", url, headers=headers, data=payload) 27 | print(response.text) 28 | j = ujson.loads(response.text) 29 | return j['words_result_num'] 30 | 31 | 32 | def get_file_content_as_base64(path, urlencoded=False): 33 | """ 34 | 获取文件base64编码 35 | :param path: 文件路径 36 | :param urlencoded: 是否对结果进行urlencoded 37 | :return: base64编码信息 38 | """ 39 | with open(path, "rb") as f: 40 | content = base64.b64encode(f.read()).decode("utf8") 41 | if urlencoded: 42 | content = urllib.parse.quote_plus(content) 43 | return content 44 | 45 | 46 | def get_access_token(): 47 | """ 48 | 使用 AK,SK 生成鉴权签名(Access Token) 49 | :return: access_token,或是None(如果错误) 50 | """ 51 | url = "https://aip.baidubce.com/oauth/2.0/token" 52 | params = {"grant_type": "client_credentials", "client_id": API_KEY, "client_secret": SECRET_KEY} 53 | return str(requests.post(url, params=params).json().get("access_token")) 54 | -------------------------------------------------------------------------------- /fun/code.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import re 4 | import base64 5 | 6 | # 识别验证码 7 | def identify(mo,code,model): 8 | url = f"http://cn-hk-bgp-4.ofalias.net:50818/inference_wps_{mo}" 9 | payload = code 10 | headers = { 11 | 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', 12 | 'Content-Type': 'text/plain', 13 | 'source': '1', 14 | 'model': model 15 | } 16 | response = requests.request("POST", url, headers=headers, data=payload) 17 | j = response.text.replace(' ', '').replace('\n', '').replace('\t', '') 18 | pattern = re.compile(r'\[\[(.*?)\]\]') 19 | matches = pattern.findall(j) 20 | print(matches) 21 | # 38%2C43%7C105%2C50%7C174%2C30%7C245%2C50%7C314%2C34 22 | # 38,43|105,50|174,30|245,50|314,34 23 | # 174,40|314,41 24 | matches = matches[0].replace('],[', '%7C').replace(',', '%2C') 25 | return matches 26 | -------------------------------------------------------------------------------- /fun/com.py: -------------------------------------------------------------------------------- 1 | import time 2 | import re 3 | 4 | # 获取时间戳 5 | def get_time(): 6 | return int(round(time.time() * 1000)) 7 | 8 | # 取中间文本 9 | def GetIntermediateText(text, start, end): 10 | print(text) 11 | pattern = re.escape(start) + '(.*?)' + re.escape(end) 12 | match = re.search(pattern, text) 13 | if match: 14 | return match.group(1) 15 | return '' 16 | -------------------------------------------------------------------------------- /image/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytt447735/automation/df1a196c201249464f874c5caad750325896e87e/image/1.png -------------------------------------------------------------------------------- /image/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytt447735/automation/df1a196c201249464f874c5caad750325896e87e/image/3.png -------------------------------------------------------------------------------- /notify.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # _*_ coding:utf-8 _*_ 3 | import base64 4 | import hashlib 5 | import hmac 6 | import json 7 | import os 8 | import re 9 | import threading 10 | import time 11 | import urllib.parse 12 | import smtplib 13 | from email.mime.text import MIMEText 14 | from email.header import Header 15 | from email.utils import formataddr 16 | 17 | import requests 18 | 19 | # 原先的 print 函数和主线程的锁 20 | _print = print 21 | mutex = threading.Lock() 22 | 23 | 24 | # 定义新的 print 函数 25 | def print(text, *args, **kw): 26 | """ 27 | 使输出有序进行,不出现多线程同一时间输出导致错乱的问题。 28 | """ 29 | with mutex: 30 | _print(text, *args, **kw) 31 | 32 | 33 | # 通知服务 34 | # fmt: off 35 | push_config = { 36 | 'HITOKOTO': False, # 启用一言(随机句子) 37 | 38 | 'BARK_PUSH': '', # bark IP 或设备码,例:https://api.day.app/DxHcxxxxxRxxxxxxcm/ 39 | 'BARK_ARCHIVE': '', # bark 推送是否存档 40 | 'BARK_GROUP': '', # bark 推送分组 41 | 'BARK_SOUND': '', # bark 推送声音 42 | 'BARK_ICON': '', # bark 推送图标 43 | 'BARK_LEVEL': '', # bark 推送时效性 44 | 'BARK_URL': '', # bark 推送跳转URL 45 | 46 | 'CONSOLE': True, # 控制台输出 47 | 48 | 'DD_BOT_SECRET': '', # 钉钉机器人的 DD_BOT_SECRET 49 | 'DD_BOT_TOKEN': '', # 钉钉机器人的 DD_BOT_TOKEN 50 | 51 | 'FSKEY': '', # 飞书机器人的 FSKEY 52 | 53 | 'GOBOT_URL': '', # go-cqhttp 54 | # 推送到个人QQ:http://127.0.0.1/send_private_msg 55 | # 群:http://127.0.0.1/send_group_msg 56 | 'GOBOT_QQ': '', # go-cqhttp 的推送群或用户 57 | # GOBOT_URL 设置 /send_private_msg 时填入 user_id=个人QQ 58 | # /send_group_msg 时填入 group_id=QQ群 59 | 'GOBOT_TOKEN': '', # go-cqhttp 的 access_token 60 | 61 | 'GOTIFY_URL': '', # gotify地址,如https://push.example.de:8080 62 | 'GOTIFY_TOKEN': '', # gotify的消息应用token 63 | 'GOTIFY_PRIORITY': 0, # 推送消息优先级,默认为0 64 | 65 | 'IGOT_PUSH_KEY': '', # iGot 聚合推送的 IGOT_PUSH_KEY 66 | 67 | 'PUSH_KEY': '', # server 酱的 PUSH_KEY,兼容旧版与 Turbo 版 68 | 69 | 'DEER_KEY': '', # PushDeer 的 PUSHDEER_KEY 70 | 'DEER_URL': '', # PushDeer 的 PUSHDEER_URL 71 | 72 | 'CHAT_URL': '', # synology chat url 73 | 'CHAT_TOKEN': '', # synology chat token 74 | 75 | 'PUSH_PLUS_TOKEN': '', # push+ 微信推送的用户令牌 76 | 'PUSH_PLUS_USER': '', # push+ 微信推送的群组编码 77 | 78 | 'QMSG_KEY': '', # qmsg 酱的 QMSG_KEY 79 | 'QMSG_TYPE': '', # qmsg 酱的 QMSG_TYPE 80 | 81 | 'QYWX_ORIGIN': '', # 企业微信代理地址 82 | 83 | 'QYWX_AM': '', # 企业微信应用 84 | 85 | 'QYWX_KEY': '', # 企业微信机器人 86 | 87 | 'TG_BOT_TOKEN': '', # tg 机器人的 TG_BOT_TOKEN,例:1407203283:AAG9rt-6RDaaX0HBLZQq0laNOh898iFYaRQ 88 | 'TG_USER_ID': '', # tg 机器人的 TG_USER_ID,例:1434078534 89 | 'TG_API_HOST': '', # tg 代理 api 90 | 'TG_PROXY_AUTH': '', # tg 代理认证参数 91 | 'TG_PROXY_HOST': '', # tg 机器人的 TG_PROXY_HOST 92 | 'TG_PROXY_PORT': '', # tg 机器人的 TG_PROXY_PORT 93 | 94 | 'AIBOTK_KEY': '', # 智能微秘书 个人中心的apikey 文档地址:http://wechat.aibotk.com/docs/about 95 | 'AIBOTK_TYPE': '', # 智能微秘书 发送目标 room 或 contact 96 | 'AIBOTK_NAME': '', # 智能微秘书 发送群名 或者好友昵称和type要对应好 97 | 98 | 'SMTP_SERVER': '', # SMTP 发送邮件服务器,形如 smtp.exmail.qq.com:465 99 | 'SMTP_SSL': 'false', # SMTP 发送邮件服务器是否使用 SSL,填写 true 或 false 100 | 'SMTP_EMAIL': '', # SMTP 收发件邮箱,通知将会由自己发给自己 101 | 'SMTP_PASSWORD': '', # SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定 102 | 'SMTP_NAME': '', # SMTP 收发件人姓名,可随意填写 103 | 104 | 'PUSHME_KEY': '', # PushMe 酱的 PUSHME_KEY 105 | 106 | 'CHRONOCAT_QQ': '', # qq号 107 | 'CHRONOCAT_TOKEN': '', # CHRONOCAT 的token 108 | 'CHRONOCAT_URL': '', # CHRONOCAT的url地址 109 | 110 | 'WEBHOOK_URL': '', # 自定义通知 请求地址 111 | 'WEBHOOK_BODY': '', # 自定义通知 请求体 112 | 'WEBHOOK_HEADERS': '', # 自定义通知 请求头 113 | 'WEBHOOK_METHOD': '', # 自定义通知 请求方法 114 | 'WEBHOOK_CONTENT_TYPE': '', # 自定义通知 content-type 115 | 116 | 'SYNOLOG_CHAT24_URL':'', 117 | } 118 | notify_function = [] 119 | # fmt: on 120 | 121 | # 首先读取 面板变量 或者 github action 运行变量 122 | for k in push_config: 123 | if os.getenv(k): 124 | v = os.getenv(k) 125 | push_config[k] = v 126 | 127 | 128 | def bark(title: str, content: str) -> None: 129 | """ 130 | 使用 bark 推送消息。 131 | """ 132 | if not push_config.get("BARK_PUSH"): 133 | print("bark 服务的 BARK_PUSH 未设置!!\n取消推送") 134 | return 135 | print("bark 服务启动") 136 | 137 | if push_config.get("BARK_PUSH").startswith("http"): 138 | url = f'{push_config.get("BARK_PUSH")}/{urllib.parse.quote_plus(title)}/{urllib.parse.quote_plus(content)}' 139 | else: 140 | url = f'https://api.day.app/{push_config.get("BARK_PUSH")}/{urllib.parse.quote_plus(title)}/{urllib.parse.quote_plus(content)}' 141 | 142 | bark_params = { 143 | "BARK_ARCHIVE": "isArchive", 144 | "BARK_GROUP": "group", 145 | "BARK_SOUND": "sound", 146 | "BARK_ICON": "icon", 147 | "BARK_LEVEL": "level", 148 | "BARK_URL": "url", 149 | } 150 | params = "" 151 | for pair in filter( 152 | lambda pairs: pairs[0].startswith("BARK_") 153 | and pairs[0] != "BARK_PUSH" 154 | and pairs[1] 155 | and bark_params.get(pairs[0]), 156 | push_config.items(), 157 | ): 158 | params += f"{bark_params.get(pair[0])}={pair[1]}&" 159 | if params: 160 | url = url + "?" + params.rstrip("&") 161 | response = requests.get(url).json() 162 | 163 | if response["code"] == 200: 164 | print("bark 推送成功!") 165 | else: 166 | print("bark 推送失败!") 167 | 168 | 169 | def console(title: str, content: str) -> None: 170 | """ 171 | 使用 控制台 推送消息。 172 | """ 173 | print(f"{title}\n\n{content}") 174 | 175 | 176 | def dingding_bot(title: str, content: str) -> None: 177 | """ 178 | 使用 钉钉机器人 推送消息。 179 | """ 180 | if not push_config.get("DD_BOT_SECRET") or not push_config.get("DD_BOT_TOKEN"): 181 | print("钉钉机器人 服务的 DD_BOT_SECRET 或者 DD_BOT_TOKEN 未设置!!\n取消推送") 182 | return 183 | print("钉钉机器人 服务启动") 184 | 185 | timestamp = str(round(time.time() * 1000)) 186 | secret_enc = push_config.get("DD_BOT_SECRET").encode("utf-8") 187 | string_to_sign = "{}\n{}".format(timestamp, push_config.get("DD_BOT_SECRET")) 188 | string_to_sign_enc = string_to_sign.encode("utf-8") 189 | hmac_code = hmac.new( 190 | secret_enc, string_to_sign_enc, digestmod=hashlib.sha256 191 | ).digest() 192 | sign = urllib.parse.quote_plus(base64.b64encode(hmac_code)) 193 | url = f'https://oapi.dingtalk.com/robot/send?access_token={push_config.get("DD_BOT_TOKEN")}×tamp={timestamp}&sign={sign}' 194 | headers = {"Content-Type": "application/json;charset=utf-8"} 195 | data = {"msgtype": "text", "text": {"content": f"{title}\n\n{content}"}} 196 | response = requests.post( 197 | url=url, data=json.dumps(data), headers=headers, timeout=15 198 | ).json() 199 | 200 | if not response["errcode"]: 201 | print("钉钉机器人 推送成功!") 202 | else: 203 | print("钉钉机器人 推送失败!") 204 | 205 | 206 | def feishu_bot(title: str, content: str) -> None: 207 | """ 208 | 使用 飞书机器人 推送消息。 209 | """ 210 | if not push_config.get("FSKEY"): 211 | print("飞书 服务的 FSKEY 未设置!!\n取消推送") 212 | return 213 | print("飞书 服务启动") 214 | 215 | url = f'https://open.feishu.cn/open-apis/bot/v2/hook/{push_config.get("FSKEY")}' 216 | data = {"msg_type": "text", "content": {"text": f"{title}\n\n{content}"}} 217 | response = requests.post(url, data=json.dumps(data)).json() 218 | 219 | if response.get("StatusCode") == 0: 220 | print("飞书 推送成功!") 221 | else: 222 | print("飞书 推送失败!错误信息如下:\n", response) 223 | 224 | 225 | def go_cqhttp(title: str, content: str) -> None: 226 | """ 227 | 使用 go_cqhttp 推送消息。 228 | """ 229 | if not push_config.get("GOBOT_URL") or not push_config.get("GOBOT_QQ"): 230 | print("go-cqhttp 服务的 GOBOT_URL 或 GOBOT_QQ 未设置!!\n取消推送") 231 | return 232 | print("go-cqhttp 服务启动") 233 | 234 | url = f'{push_config.get("GOBOT_URL")}?access_token={push_config.get("GOBOT_TOKEN")}&{push_config.get("GOBOT_QQ")}&message=标题:{title}\n内容:{content}' 235 | response = requests.get(url).json() 236 | 237 | if response["status"] == "ok": 238 | print("go-cqhttp 推送成功!") 239 | else: 240 | print("go-cqhttp 推送失败!") 241 | 242 | 243 | def gotify(title: str, content: str) -> None: 244 | """ 245 | 使用 gotify 推送消息。 246 | """ 247 | if not push_config.get("GOTIFY_URL") or not push_config.get("GOTIFY_TOKEN"): 248 | print("gotify 服务的 GOTIFY_URL 或 GOTIFY_TOKEN 未设置!!\n取消推送") 249 | return 250 | print("gotify 服务启动") 251 | 252 | url = f'{push_config.get("GOTIFY_URL")}/message?token={push_config.get("GOTIFY_TOKEN")}' 253 | data = { 254 | "title": title, 255 | "message": content, 256 | "priority": push_config.get("GOTIFY_PRIORITY"), 257 | } 258 | response = requests.post(url, data=data).json() 259 | 260 | if response.get("id"): 261 | print("gotify 推送成功!") 262 | else: 263 | print("gotify 推送失败!") 264 | 265 | 266 | def iGot(title: str, content: str) -> None: 267 | """ 268 | 使用 iGot 推送消息。 269 | """ 270 | if not push_config.get("IGOT_PUSH_KEY"): 271 | print("iGot 服务的 IGOT_PUSH_KEY 未设置!!\n取消推送") 272 | return 273 | print("iGot 服务启动") 274 | 275 | url = f'https://push.hellyw.com/{push_config.get("IGOT_PUSH_KEY")}' 276 | data = {"title": title, "content": content} 277 | headers = {"Content-Type": "application/x-www-form-urlencoded"} 278 | response = requests.post(url, data=data, headers=headers).json() 279 | 280 | if response["ret"] == 0: 281 | print("iGot 推送成功!") 282 | else: 283 | print(f'iGot 推送失败!{response["errMsg"]}') 284 | 285 | 286 | def serverJ(title: str, content: str) -> None: 287 | """ 288 | 通过 serverJ 推送消息。 289 | """ 290 | if not push_config.get("PUSH_KEY"): 291 | print("serverJ 服务的 PUSH_KEY 未设置!!\n取消推送") 292 | return 293 | print("serverJ 服务启动") 294 | 295 | data = {"text": title, "desp": content.replace("\n", "\n\n")} 296 | if push_config.get("PUSH_KEY").find("SCT") != -1: 297 | url = f'https://sctapi.ftqq.com/{push_config.get("PUSH_KEY")}.send' 298 | else: 299 | url = f'https://sc.ftqq.com/{push_config.get("PUSH_KEY")}.send' 300 | response = requests.post(url, data=data).json() 301 | 302 | if response.get("errno") == 0 or response.get("code") == 0: 303 | print("serverJ 推送成功!") 304 | else: 305 | print(f'serverJ 推送失败!错误码:{response["message"]}') 306 | 307 | 308 | def pushdeer(title: str, content: str) -> None: 309 | """ 310 | 通过PushDeer 推送消息 311 | """ 312 | if not push_config.get("DEER_KEY"): 313 | print("PushDeer 服务的 DEER_KEY 未设置!!\n取消推送") 314 | return 315 | print("PushDeer 服务启动") 316 | data = { 317 | "text": title, 318 | "desp": content, 319 | "type": "markdown", 320 | "pushkey": push_config.get("DEER_KEY"), 321 | } 322 | url = "https://api2.pushdeer.com/message/push" 323 | if push_config.get("DEER_URL"): 324 | url = push_config.get("DEER_URL") 325 | 326 | response = requests.post(url, data=data).json() 327 | 328 | if len(response.get("content").get("result")) > 0: 329 | print("PushDeer 推送成功!") 330 | else: 331 | print("PushDeer 推送失败!错误信息:", response) 332 | 333 | 334 | def chat(title: str, content: str) -> None: 335 | """ 336 | 通过Chat 推送消息 337 | """ 338 | if not push_config.get("CHAT_URL") or not push_config.get("CHAT_TOKEN"): 339 | print("chat 服务的 CHAT_URL或CHAT_TOKEN 未设置!!\n取消推送") 340 | return 341 | print("chat 服务启动") 342 | data = "payload=" + json.dumps({"text": title + "\n" + content}) 343 | url = push_config.get("CHAT_URL") + push_config.get("CHAT_TOKEN") 344 | response = requests.post(url, data=data) 345 | 346 | if response.status_code == 200: 347 | print("Chat 推送成功!") 348 | else: 349 | print("Chat 推送失败!错误信息:", response) 350 | 351 | 352 | def pushplus_bot(title: str, content: str) -> None: 353 | """ 354 | 通过 push+ 推送消息。 355 | """ 356 | if not push_config.get("PUSH_PLUS_TOKEN"): 357 | print("PUSHPLUS 服务的 PUSH_PLUS_TOKEN 未设置!!\n取消推送") 358 | return 359 | print("PUSHPLUS 服务启动") 360 | 361 | url = "http://www.pushplus.plus/send" 362 | data = { 363 | "token": push_config.get("PUSH_PLUS_TOKEN"), 364 | "title": title, 365 | "content": content, 366 | "topic": push_config.get("PUSH_PLUS_USER"), 367 | } 368 | body = json.dumps(data).encode(encoding="utf-8") 369 | headers = {"Content-Type": "application/json"} 370 | response = requests.post(url=url, data=body, headers=headers).json() 371 | 372 | if response["code"] == 200: 373 | print("PUSHPLUS 推送成功!") 374 | 375 | else: 376 | url_old = "http://pushplus.hxtrip.com/send" 377 | headers["Accept"] = "application/json" 378 | response = requests.post(url=url_old, data=body, headers=headers).json() 379 | 380 | if response["code"] == 200: 381 | print("PUSHPLUS(hxtrip) 推送成功!") 382 | 383 | else: 384 | print("PUSHPLUS 推送失败!") 385 | 386 | 387 | def qmsg_bot(title: str, content: str) -> None: 388 | """ 389 | 使用 qmsg 推送消息。 390 | """ 391 | if not push_config.get("QMSG_KEY") or not push_config.get("QMSG_TYPE"): 392 | print("qmsg 的 QMSG_KEY 或者 QMSG_TYPE 未设置!!\n取消推送") 393 | return 394 | print("qmsg 服务启动") 395 | 396 | url = f'https://qmsg.zendee.cn/{push_config.get("QMSG_TYPE")}/{push_config.get("QMSG_KEY")}' 397 | payload = {"msg": f'{title}\n\n{content.replace("----", "-")}'.encode("utf-8")} 398 | response = requests.post(url=url, params=payload).json() 399 | 400 | if response["code"] == 0: 401 | print("qmsg 推送成功!") 402 | else: 403 | print(f'qmsg 推送失败!{response["reason"]}') 404 | 405 | 406 | def wecom_app(title: str, content: str) -> None: 407 | """ 408 | 通过 企业微信 APP 推送消息。 409 | """ 410 | if not push_config.get("QYWX_AM"): 411 | print("QYWX_AM 未设置!!\n取消推送") 412 | return 413 | QYWX_AM_AY = re.split(",", push_config.get("QYWX_AM")) 414 | if 4 < len(QYWX_AM_AY) > 5: 415 | print("QYWX_AM 设置错误!!\n取消推送") 416 | return 417 | print("企业微信 APP 服务启动") 418 | 419 | corpid = QYWX_AM_AY[0] 420 | corpsecret = QYWX_AM_AY[1] 421 | touser = QYWX_AM_AY[2] 422 | agentid = QYWX_AM_AY[3] 423 | try: 424 | media_id = QYWX_AM_AY[4] 425 | except IndexError: 426 | media_id = "" 427 | wx = WeCom(corpid, corpsecret, agentid) 428 | # 如果没有配置 media_id 默认就以 text 方式发送 429 | if not media_id: 430 | message = title + "\n\n" + content 431 | response = wx.send_text(message, touser) 432 | else: 433 | response = wx.send_mpnews(title, content, media_id, touser) 434 | 435 | if response == "ok": 436 | print("企业微信推送成功!") 437 | else: 438 | print("企业微信推送失败!错误信息如下:\n", response) 439 | 440 | 441 | class WeCom: 442 | def __init__(self, corpid, corpsecret, agentid): 443 | self.CORPID = corpid 444 | self.CORPSECRET = corpsecret 445 | self.AGENTID = agentid 446 | self.ORIGIN = "https://qyapi.weixin.qq.com" 447 | if push_config.get("QYWX_ORIGIN"): 448 | self.ORIGIN = push_config.get("QYWX_ORIGIN") 449 | 450 | def get_access_token(self): 451 | url = f"{self.ORIGIN}/cgi-bin/gettoken" 452 | values = { 453 | "corpid": self.CORPID, 454 | "corpsecret": self.CORPSECRET, 455 | } 456 | req = requests.post(url, params=values) 457 | data = json.loads(req.text) 458 | return data["access_token"] 459 | 460 | def send_text(self, message, touser="@all"): 461 | send_url = ( 462 | f"{self.ORIGIN}/cgi-bin/message/send?access_token={self.get_access_token()}" 463 | ) 464 | send_values = { 465 | "touser": touser, 466 | "msgtype": "text", 467 | "agentid": self.AGENTID, 468 | "text": {"content": message}, 469 | "safe": "0", 470 | } 471 | send_msges = bytes(json.dumps(send_values), "utf-8") 472 | respone = requests.post(send_url, send_msges) 473 | respone = respone.json() 474 | return respone["errmsg"] 475 | 476 | def send_mpnews(self, title, message, media_id, touser="@all"): 477 | send_url = ( 478 | f"{self.ORIGIN}/cgi-bin/message/send?access_token={self.get_access_token()}" 479 | ) 480 | send_values = { 481 | "touser": touser, 482 | "msgtype": "mpnews", 483 | "agentid": self.AGENTID, 484 | "mpnews": { 485 | "articles": [ 486 | { 487 | "title": title, 488 | "thumb_media_id": media_id, 489 | "author": "Author", 490 | "content_source_url": "", 491 | "content": message.replace("\n", "
"), 492 | "digest": message, 493 | } 494 | ] 495 | }, 496 | } 497 | send_msges = bytes(json.dumps(send_values), "utf-8") 498 | respone = requests.post(send_url, send_msges) 499 | respone = respone.json() 500 | return respone["errmsg"] 501 | 502 | 503 | def wecom_bot(title: str, content: str) -> None: 504 | """ 505 | 通过 企业微信机器人 推送消息。 506 | """ 507 | if not push_config.get("QYWX_KEY"): 508 | print("企业微信机器人 服务的 QYWX_KEY 未设置!!\n取消推送") 509 | return 510 | print("企业微信机器人服务启动") 511 | 512 | origin = "https://qyapi.weixin.qq.com" 513 | if push_config.get("QYWX_ORIGIN"): 514 | origin = push_config.get("QYWX_ORIGIN") 515 | 516 | url = f"{origin}/cgi-bin/webhook/send?key={push_config.get('QYWX_KEY')}" 517 | headers = {"Content-Type": "application/json;charset=utf-8"} 518 | data = {"msgtype": "text", "text": {"content": f"{title}\n\n{content}"}} 519 | response = requests.post( 520 | url=url, data=json.dumps(data), headers=headers, timeout=15 521 | ).json() 522 | 523 | if response["errcode"] == 0: 524 | print("企业微信机器人推送成功!") 525 | else: 526 | print("企业微信机器人推送失败!") 527 | 528 | 529 | def telegram_bot(title: str, content: str) -> None: 530 | """ 531 | 使用 telegram 机器人 推送消息。 532 | """ 533 | if not push_config.get("TG_BOT_TOKEN") or not push_config.get("TG_USER_ID"): 534 | print("tg 服务的 bot_token 或者 user_id 未设置!!\n取消推送") 535 | return 536 | print("tg 服务启动") 537 | 538 | if push_config.get("TG_API_HOST"): 539 | url = f"https://{push_config.get('TG_API_HOST')}/bot{push_config.get('TG_BOT_TOKEN')}/sendMessage" 540 | else: 541 | url = ( 542 | f"https://api.telegram.org/bot{push_config.get('TG_BOT_TOKEN')}/sendMessage" 543 | ) 544 | headers = {"Content-Type": "application/x-www-form-urlencoded"} 545 | payload = { 546 | "chat_id": str(push_config.get("TG_USER_ID")), 547 | "text": f"{title}\n\n{content}", 548 | "disable_web_page_preview": "true", 549 | } 550 | proxies = None 551 | if push_config.get("TG_PROXY_HOST") and push_config.get("TG_PROXY_PORT"): 552 | if push_config.get("TG_PROXY_AUTH") is not None and "@" not in push_config.get( 553 | "TG_PROXY_HOST" 554 | ): 555 | push_config["TG_PROXY_HOST"] = ( 556 | push_config.get("TG_PROXY_AUTH") 557 | + "@" 558 | + push_config.get("TG_PROXY_HOST") 559 | ) 560 | proxyStr = "http://{}:{}".format( 561 | push_config.get("TG_PROXY_HOST"), push_config.get("TG_PROXY_PORT") 562 | ) 563 | proxies = {"http": proxyStr, "https": proxyStr} 564 | response = requests.post( 565 | url=url, headers=headers, params=payload, proxies=proxies 566 | ).json() 567 | 568 | if response["ok"]: 569 | print("tg 推送成功!") 570 | else: 571 | print("tg 推送失败!") 572 | 573 | 574 | def aibotk(title: str, content: str) -> None: 575 | """ 576 | 使用 智能微秘书 推送消息。 577 | """ 578 | if ( 579 | not push_config.get("AIBOTK_KEY") 580 | or not push_config.get("AIBOTK_TYPE") 581 | or not push_config.get("AIBOTK_NAME") 582 | ): 583 | print("智能微秘书 的 AIBOTK_KEY 或者 AIBOTK_TYPE 或者 AIBOTK_NAME 未设置!!\n取消推送") 584 | return 585 | print("智能微秘书 服务启动") 586 | 587 | if push_config.get("AIBOTK_TYPE") == "room": 588 | url = "https://api-bot.aibotk.com/openapi/v1/chat/room" 589 | data = { 590 | "apiKey": push_config.get("AIBOTK_KEY"), 591 | "roomName": push_config.get("AIBOTK_NAME"), 592 | "message": {"type": 1, "content": f"【青龙快讯】\n\n{title}\n{content}"}, 593 | } 594 | else: 595 | url = "https://api-bot.aibotk.com/openapi/v1/chat/contact" 596 | data = { 597 | "apiKey": push_config.get("AIBOTK_KEY"), 598 | "name": push_config.get("AIBOTK_NAME"), 599 | "message": {"type": 1, "content": f"【青龙快讯】\n\n{title}\n{content}"}, 600 | } 601 | body = json.dumps(data).encode(encoding="utf-8") 602 | headers = {"Content-Type": "application/json"} 603 | response = requests.post(url=url, data=body, headers=headers).json() 604 | print(response) 605 | if response["code"] == 0: 606 | print("智能微秘书 推送成功!") 607 | else: 608 | print(f'智能微秘书 推送失败!{response["error"]}') 609 | 610 | 611 | def smtp(title: str, content: str) -> None: 612 | """ 613 | 使用 SMTP 邮件 推送消息。 614 | """ 615 | if ( 616 | not push_config.get("SMTP_SERVER") 617 | or not push_config.get("SMTP_SSL") 618 | or not push_config.get("SMTP_EMAIL") 619 | or not push_config.get("SMTP_PASSWORD") 620 | or not push_config.get("SMTP_NAME") 621 | ): 622 | print( 623 | "SMTP 邮件 的 SMTP_SERVER 或者 SMTP_SSL 或者 SMTP_EMAIL 或者 SMTP_PASSWORD 或者 SMTP_NAME 未设置!!\n取消推送" 624 | ) 625 | return 626 | print("SMTP 邮件 服务启动") 627 | 628 | message = MIMEText(content, "plain", "utf-8") 629 | message["From"] = formataddr( 630 | ( 631 | Header(push_config.get("SMTP_NAME"), "utf-8").encode(), 632 | push_config.get("SMTP_EMAIL"), 633 | ) 634 | ) 635 | message["To"] = formataddr( 636 | ( 637 | Header(push_config.get("SMTP_NAME"), "utf-8").encode(), 638 | push_config.get("SMTP_EMAIL"), 639 | ) 640 | ) 641 | message["Subject"] = Header(title, "utf-8") 642 | 643 | try: 644 | smtp_server = ( 645 | smtplib.SMTP_SSL(push_config.get("SMTP_SERVER")) 646 | if push_config.get("SMTP_SSL") == "true" 647 | else smtplib.SMTP(push_config.get("SMTP_SERVER")) 648 | ) 649 | smtp_server.login( 650 | push_config.get("SMTP_EMAIL"), push_config.get("SMTP_PASSWORD") 651 | ) 652 | smtp_server.sendmail( 653 | push_config.get("SMTP_EMAIL"), 654 | push_config.get("SMTP_EMAIL"), 655 | message.as_bytes(), 656 | ) 657 | smtp_server.close() 658 | print("SMTP 邮件 推送成功!") 659 | except Exception as e: 660 | print(f"SMTP 邮件 推送失败!{e}") 661 | 662 | 663 | def pushme(title: str, content: str) -> None: 664 | """ 665 | 使用 PushMe 推送消息。 666 | """ 667 | if not push_config.get("PUSHME_KEY"): 668 | print("PushMe 服务的 PUSHME_KEY 未设置!!\n取消推送") 669 | return 670 | print("PushMe 服务启动") 671 | 672 | url = f'https://push.i-i.me/?push_key={push_config.get("PUSHME_KEY")}' 673 | data = { 674 | "title": title, 675 | "content": content, 676 | } 677 | response = requests.post(url, data=data) 678 | 679 | if response.status_code == 200 and response.text == "success": 680 | print("PushMe 推送成功!") 681 | else: 682 | print(f"PushMe 推送失败!{response.status_code} {response.text}") 683 | 684 | 685 | def chronocat(title: str, content: str) -> None: 686 | """ 687 | 使用 CHRONOCAT 推送消息。 688 | """ 689 | if ( 690 | not push_config.get("CHRONOCAT_URL") 691 | or not push_config.get("CHRONOCAT_QQ") 692 | or not push_config.get("CHRONOCAT_TOKEN") 693 | ): 694 | print("CHRONOCAT 服务的 CHRONOCAT_URL 或 CHRONOCAT_QQ 未设置!!\n取消推送") 695 | return 696 | 697 | print("CHRONOCAT 服务启动") 698 | 699 | user_ids = re.findall(r"user_id=(\d+)", push_config.get("CHRONOCAT_QQ")) 700 | group_ids = re.findall(r"group_id=(\d+)", push_config.get("CHRONOCAT_QQ")) 701 | 702 | url = f'{push_config.get("CHRONOCAT_URL")}/api/message/send' 703 | headers = { 704 | "Content-Type": "application/json", 705 | "Authorization": f'Bearer {push_config.get("CHRONOCAT_TOKEN")}', 706 | } 707 | 708 | for chat_type, ids in [(1, user_ids), (2, group_ids)]: 709 | if not ids: 710 | continue 711 | for chat_id in ids: 712 | data = { 713 | "peer": {"chatType": chat_type, "peerUin": chat_id}, 714 | "elements": [ 715 | { 716 | "elementType": 1, 717 | "textElement": {"content": f"{title}\n\n{content}"}, 718 | } 719 | ], 720 | } 721 | response = requests.post(url, headers=headers, data=json.dumps(data)) 722 | if response.status_code == 200: 723 | if chat_type == 1: 724 | print(f"QQ个人消息:{ids}推送成功!") 725 | else: 726 | print(f"QQ群消息:{ids}推送成功!") 727 | else: 728 | if chat_type == 1: 729 | print(f"QQ个人消息:{ids}推送失败!") 730 | else: 731 | print(f"QQ群消息:{ids}推送失败!") 732 | 733 | 734 | def parse_headers(headers): 735 | if not headers: 736 | return {} 737 | 738 | parsed = {} 739 | lines = headers.split("\n") 740 | 741 | for line in lines: 742 | i = line.find(":") 743 | if i == -1: 744 | continue 745 | 746 | key = line[:i].strip().lower() 747 | val = line[i + 1 :].strip() 748 | parsed[key] = parsed.get(key, "") + ", " + val if key in parsed else val 749 | 750 | return parsed 751 | 752 | 753 | def parse_body(body, content_type): 754 | if not body: 755 | return "" 756 | 757 | parsed = {} 758 | lines = body.split("\n") 759 | 760 | for line in lines: 761 | i = line.find(":") 762 | if i == -1: 763 | continue 764 | 765 | key = line[:i].strip().lower() 766 | val = line[i + 1 :].strip() 767 | 768 | if not key or key in parsed: 769 | continue 770 | 771 | try: 772 | json_value = json.loads(val) 773 | parsed[key] = json_value 774 | except: 775 | parsed[key] = val 776 | 777 | if content_type == "application/x-www-form-urlencoded": 778 | data = urlencode(parsed, doseq=True) 779 | return data 780 | 781 | if content_type == "application/json": 782 | data = json.dumps(parsed) 783 | return data 784 | 785 | return parsed 786 | 787 | 788 | def format_notify_content(url, body, title, content): 789 | if "$title" not in url and "$title" not in body: 790 | return {} 791 | 792 | formatted_url = url.replace("$title", urllib.parse.quote_plus(title)).replace( 793 | "$content", urllib.parse.quote_plus(content) 794 | ) 795 | formatted_body = body.replace("$title", title).replace("$content", content) 796 | 797 | return formatted_url, formatted_body 798 | 799 | 800 | def custom_notify(title: str, content: str) -> None: 801 | """ 802 | 通过 自定义通知 推送消息。 803 | """ 804 | if not push_config.get("WEBHOOK_URL") or not push_config.get("WEBHOOK_METHOD"): 805 | print("自定义通知的 WEBHOOK_URL 或 WEBHOOK_METHOD 未设置!!\n取消推送") 806 | return 807 | 808 | print("自定义通知服务启动") 809 | 810 | WEBHOOK_URL = push_config.get("WEBHOOK_URL") 811 | WEBHOOK_METHOD = push_config.get("WEBHOOK_METHOD") 812 | WEBHOOK_CONTENT_TYPE = push_config.get("WEBHOOK_CONTENT_TYPE") 813 | WEBHOOK_BODY = push_config.get("WEBHOOK_BODY") 814 | WEBHOOK_HEADERS = push_config.get("WEBHOOK_HEADERS") 815 | 816 | formatUrl, formatBody = format_notify_content( 817 | WEBHOOK_URL, WEBHOOK_BODY, title, content 818 | ) 819 | 820 | if not formatUrl and not formatBody: 821 | print("请求头或者请求体中必须包含 $title 和 $content") 822 | return 823 | 824 | headers = parse_headers(WEBHOOK_HEADERS) 825 | body = parse_body(formatBody, WEBHOOK_CONTENT_TYPE) 826 | response = requests.request( 827 | method=WEBHOOK_METHOD, url=formatUrl, headers=headers, timeout=15, data=body 828 | ) 829 | 830 | if response.status_code == 200: 831 | print("自定义通知推送成功!") 832 | else: 833 | print(f"自定义通知推送失败!{response.status_code} {response.text}") 834 | 835 | def synolog_chat24(title: str, content: str) -> None: 836 | """ 837 | 通过 群晖chat2.4 推送消息。 838 | """ 839 | if not push_config.get("SYNOLOG_CHAT24_URL"): 840 | print("自定义通知的 SYNOLOG_CHAT24_URL 未设置!!\n取消推送") 841 | return 842 | 843 | print("自定义通知服务启动") 844 | data = '{"text": "'+content+'"}' 845 | SYNOLOG_CHAT24_URL = push_config.get("SYNOLOG_CHAT24_URL") 846 | body = { 847 | "payload":data.encode('utf-8') 848 | } 849 | response = requests.request( 850 | method='POST', url=SYNOLOG_CHAT24_URL, timeout=15, data=body 851 | ) 852 | # print(body) 853 | # print(response.text) 854 | if response.status_code == 200: 855 | print("自定义通知推送成功!") 856 | else: 857 | print(f"自定义通知推送失败!{response.status_code} {response.text}") 858 | 859 | def one() -> str: 860 | """ 861 | 获取一条一言。 862 | :return: 863 | """ 864 | url = "https://v1.hitokoto.cn/" 865 | res = requests.get(url).json() 866 | return res["hitokoto"] + " ----" + res["from"] 867 | 868 | 869 | if push_config.get("BARK_PUSH"): 870 | notify_function.append(bark) 871 | if push_config.get("CONSOLE"): 872 | notify_function.append(console) 873 | if push_config.get("DD_BOT_TOKEN") and push_config.get("DD_BOT_SECRET"): 874 | notify_function.append(dingding_bot) 875 | if push_config.get("FSKEY"): 876 | notify_function.append(feishu_bot) 877 | if push_config.get("GOBOT_URL") and push_config.get("GOBOT_QQ"): 878 | notify_function.append(go_cqhttp) 879 | if push_config.get("GOTIFY_URL") and push_config.get("GOTIFY_TOKEN"): 880 | notify_function.append(gotify) 881 | if push_config.get("IGOT_PUSH_KEY"): 882 | notify_function.append(iGot) 883 | if push_config.get("PUSH_KEY"): 884 | notify_function.append(serverJ) 885 | if push_config.get("DEER_KEY"): 886 | notify_function.append(pushdeer) 887 | if push_config.get("CHAT_URL") and push_config.get("CHAT_TOKEN"): 888 | notify_function.append(chat) 889 | if push_config.get("PUSH_PLUS_TOKEN"): 890 | notify_function.append(pushplus_bot) 891 | if push_config.get("QMSG_KEY") and push_config.get("QMSG_TYPE"): 892 | notify_function.append(qmsg_bot) 893 | if push_config.get("QYWX_AM"): 894 | notify_function.append(wecom_app) 895 | if push_config.get("QYWX_KEY"): 896 | notify_function.append(wecom_bot) 897 | if push_config.get("TG_BOT_TOKEN") and push_config.get("TG_USER_ID"): 898 | notify_function.append(telegram_bot) 899 | if ( 900 | push_config.get("AIBOTK_KEY") 901 | and push_config.get("AIBOTK_TYPE") 902 | and push_config.get("AIBOTK_NAME") 903 | ): 904 | notify_function.append(aibotk) 905 | if ( 906 | push_config.get("SMTP_SERVER") 907 | and push_config.get("SMTP_SSL") 908 | and push_config.get("SMTP_EMAIL") 909 | and push_config.get("SMTP_PASSWORD") 910 | and push_config.get("SMTP_NAME") 911 | ): 912 | notify_function.append(smtp) 913 | if push_config.get("PUSHME_KEY"): 914 | notify_function.append(pushme) 915 | if ( 916 | push_config.get("CHRONOCAT_URL") 917 | and push_config.get("CHRONOCAT_QQ") 918 | and push_config.get("CHRONOCAT_TOKEN") 919 | ): 920 | notify_function.append(chronocat) 921 | if push_config.get("WEBHOOK_URL") and push_config.get("WEBHOOK_METHOD"): 922 | notify_function.append(custom_notify) 923 | 924 | if push_config.get("SYNOLOG_CHAT24_URL"): 925 | notify_function.append(synolog_chat24) 926 | 927 | def send(title: str, content: str) -> None: 928 | if not content: 929 | print(f"{title} 推送内容为空!") 930 | return 931 | 932 | # 根据标题跳过一些消息推送,环境变量:SKIP_PUSH_TITLE 用回车分隔 933 | skipTitle = os.getenv("SKIP_PUSH_TITLE") 934 | if skipTitle: 935 | if title in re.split("\n", skipTitle): 936 | print(f"{title} 在SKIP_PUSH_TITLE环境变量内,跳过推送!") 937 | return 938 | 939 | hitokoto = push_config.get("HITOKOTO") 940 | 941 | text = one() if hitokoto else "" 942 | content += "\n\n" + text 943 | 944 | ts = [ 945 | threading.Thread(target=mode, args=(title, content), name=mode.__name__) 946 | for mode in notify_function 947 | ] 948 | [t.start() for t in ts] 949 | [t.join() for t in ts] 950 | 951 | 952 | def main(): 953 | send("title", "content") 954 | 955 | 956 | if __name__ == "__main__": 957 | main() 958 | -------------------------------------------------------------------------------- /wpspc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | File: main.py(wps签到) 6 | Author: ytt447735 7 | cron: 0 8 * * * 8 | new Env('wps签到'); 9 | Update: 2024/10/19 10 | """ 11 | import os, notify 12 | import time 13 | import ujson 14 | import requests 15 | from io import BytesIO 16 | from fun import baidu, code 17 | import base64 18 | from io import BytesIO 19 | 20 | class wps: 21 | def __init__(self): 22 | self.Position = ["38%2C43", "105%2C50", "174%2C30", "245%2C50", "314%2C34"] # 位置信息 23 | self.ck = '' 24 | self.Referer = 'https://vip.wps.cn/spa/2021/wps-sign/?position=2020_vip_massing&client_pay_version=202301' 25 | self.Origin = 'https://vip.wps.cn' 26 | self.Log = "" 27 | self.code_fail=0 28 | self.userid = '' 29 | 30 | # 获取奖励信息 31 | def get_reward(self): 32 | url = "https://personal-act.wps.cn/wps_clock/v2" 33 | 34 | headers = { 35 | 'Origin': 'https://vip.wps.cn', 36 | 'Cookie': self.ck 37 | } 38 | response = requests.request("GET", url, headers=headers) 39 | # print(response.text) 40 | j = ujson.loads(response.text) 41 | if j["result"] == "ok": 42 | self.Log = self.Log + "📝签到日志:\n" 43 | for i, element in enumerate(j["data"]["list"]): 44 | if element["status"] == 1: 45 | status = "已领取" 46 | else: 47 | status = "未领取" 48 | jj = ujson.loads(element["ext"]) 49 | # print(jj[0]) 50 | self.Log = self.Log + f"⌚️第{i + 1}天🎁奖励{jj[0]['hour']}小时会员🎊{status}\n" 51 | 52 | # 获取用户信息 53 | def get_check(self): 54 | url = "https://account.wps.cn/p/auth/check" 55 | 56 | payload = {} 57 | headers = { 58 | 'Origin': 'https://vip.wps.cn', 59 | 'Cookie': self.ck, 60 | } 61 | response = requests.request("POST", url, headers=headers, data=payload) 62 | print(response.text) 63 | if "userid" in response.text: 64 | j = ujson.loads(response.text) 65 | if j["result"] == "ok": 66 | self.Log = self.Log + f"👤用户信息:{j['nickname']}\n" 67 | self.userid = j['userid'] 68 | return True 69 | self.Log = self.Log + f"👤用户信息:获取失败\n" 70 | return False 71 | 72 | # 获取时间戳 73 | def get_time(self): 74 | return int(round(time.time() * 1000)) 75 | 76 | # 处理验证码 77 | def code_processing(self): 78 | if self.userid == "": 79 | return False 80 | url = f"https://personal-act.wps.cn/vas_risk_system/v1/captcha/image?service_id=wps_clock&t={self.get_time()}&request_id=wps_clock_{self.userid}" 81 | 82 | # 构造请求头,包含Cookie信息 83 | headers = {'Cookie': self.ck} 84 | 85 | # 发送带有Cookie的HTTP请求获取图片 86 | response = requests.get(url, headers=headers) 87 | if response.status_code == 200: 88 | # 将图片内容转换为base64 89 | image_base64 = base64.b64encode(response.content).decode('utf-8') 90 | # 处理验证码 91 | if self.code_fail<=3: 92 | co = code.identify('pc', image_base64, '0') 93 | if self.code_fail>=3: 94 | co = code.identify('pc', image_base64, '1') 95 | if co == None: 96 | return False 97 | # return code 98 | return self.submit_code(co) 99 | else: 100 | self.code_fail = self.code_fail + 1 101 | return False 102 | 103 | 104 | # 提交验证码 105 | def submit_code(self, c): 106 | url = "https://personal-act.wps.cn/wps_clock/v2" 107 | 108 | payload = { 109 | 'double': '0', 110 | 'v': '11.1.0.10314', 111 | 'c': c 112 | } 113 | headers = { 114 | 'Cookie': self.ck 115 | } 116 | response = requests.request("POST", url, headers=headers, data=payload) 117 | print("submit_code:" + response.text) 118 | if 'ClockAgent' in response.text: 119 | self.Log = self.Log + "🙅你今日已经签到过了!\n" 120 | return True 121 | j = ujson.loads(response.text) 122 | if j["result"] == "ok": 123 | self.Log = self.Log + f"🎉今日签到成功,获得{j['data']['member']['hour']}小时会员\n" 124 | return True 125 | else: 126 | self.Log = self.Log + f"🥀今日签到失败,{j['msg']}\n" 127 | return False 128 | 129 | # 签到兑换 130 | def exchange(self, day): 131 | url = f"https://vipapi.wps.cn/wps_clock/v2/exchange?day={day}" 132 | 133 | headers = { 134 | 'Cookie': self.ck, 135 | 'Origin': self.Origin, 136 | 'Referer': self.Referer 137 | } 138 | 139 | response = requests.request("POST", url, headers=headers) 140 | print(response.text) 141 | j = ujson.loads(response.text) 142 | if j["result"] == "ok": 143 | self.Log = self.Log + f"🎉兑换成功,获得{day}天会员\n" 144 | return True 145 | else: 146 | self.Log = self.Log + f"🥀兑换失败,{j['msg']}\n" 147 | return False 148 | 149 | # 获取余额 150 | def get_balance(self): 151 | url = "https://vipapi.wps.cn/wps_clock/v2/user" 152 | 153 | payload = {} 154 | headers = { 155 | 'Referer': self.Referer, 156 | 'Origin': self.Origin, 157 | 'Cookie': self.ck 158 | } 159 | response = requests.request("GET", url, headers=headers, data=payload) 160 | print(response.text) 161 | j = ujson.loads(response.text) 162 | if j["result"] == "ok": 163 | total = j['data']['total'] // 3600 164 | cost = j['data']['cost'] // 3600 165 | self.Log = self.Log + f"🏦已使用额度:{ cost }小时({ cost // 24}天)\n" 166 | self.Log = self.Log + f"💰剩余额度:{total}小时({total // 24}天)\n" 167 | return j 168 | 169 | # 空间额度查询 170 | def get_space_quota(self): 171 | url = "https://vip.wps.cn/sign/mobile/v3/get_data" 172 | payload={} 173 | headers = { 174 | 'Referer': 'https://zt.wps.cn/spa/2019/vip_mobile_sign_v2/?csource=pc_cloud_personalpanel&position=pc_cloud_sign', 175 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36', 176 | 'Cookie': self.ck 177 | } 178 | response = requests.request("GET", url, headers=headers, data=payload) 179 | print(response.text) 180 | j = ujson.loads(response.text) 181 | if j["result"] == "ok": 182 | used = j['data']['spaces_info']['used'] 183 | total = j['data']['spaces_info']['total'] 184 | unit = j['data']['spaces_info']['unit'] 185 | series_signed = j['data']['sign_status']['series_signed'] 186 | total_signed = j['data']['sign_status']['total_signed'] 187 | not_sign = j['data']['sign_status']['not_sign'] 188 | self.Log = self.Log + f"☁️云空间:{ used }{ unit }/{ total }{ unit }\n" 189 | self.Log = self.Log + f"🪷连续签到:{ series_signed }天\n" 190 | self.Log = self.Log + f"⚡️累计签到:{ total_signed }天\n" 191 | self.Log = self.Log + "📝签到日志:\n" 192 | normal_list = j['data']["reward_list"]["space"]["normal"] 193 | # 循环输出normal数组,带循环序号 194 | for index, value in enumerate(normal_list, start=1): 195 | self.Log = self.Log + f"⌚️第{index}天🎁奖励{ value }M\n" 196 | 197 | 198 | 199 | # 空间验证码处理 200 | def space_code_processing(self): 201 | url = f"https://vip.wps.cn/checkcode/signin/captcha.png?platform=8&encode=0&img_witdh=336&img_height=84.48&v={self.get_time()}" 202 | payload={} 203 | headers = { 204 | 'Referer': 'https://zt.wps.cn/spa/2019/vip_mobile_sign_v2/?csource=pc_cloud_personalpanel&position=pc_cloud_sign', 205 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36', 206 | 'Cookie': self.ck 207 | } 208 | response = requests.request("GET", url, headers=headers, data=payload) 209 | # print(response.text) 210 | if response.status_code == 200: 211 | # 将图片内容转换为base64 212 | image_base64 = base64.b64encode(response.content).decode('utf-8') 213 | # 处理验证码 214 | co = code.identify('space',image_base64, '0') 215 | # return 216 | return self.submit_space(co) 217 | else: 218 | return None 219 | 220 | 221 | # 空间签到 222 | def submit_space(self, c): 223 | url = f"https://vip.wps.cn/sign/v2?platform=8&captcha_pos={c}&img_witdh=336&img_height=84.48" 224 | payload={} 225 | headers = { 226 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36', 227 | 'Referer': 'https://zt.wps.cn/spa/2019/vip_mobile_sign_v2/?csource=pc_cloud_personalpanel&position=pc_cloud_sign', 228 | 'Cookie': self.ck 229 | } 230 | response = requests.request("POST", url, headers=headers, data=payload) 231 | print(response.text) 232 | if "10003" in response.text: 233 | self.Log = self.Log + f"🙅你今日已经空间已经签到过了!\n" 234 | return True 235 | j = ujson.loads(response.text) 236 | if j["result"] == "ok": 237 | self.Log = self.Log + f"🎉今日空间签到成功!\n" 238 | return True 239 | else: 240 | self.Log = self.Log + f"🥀今日空间签到失败,{j['msg']}\n" 241 | return False 242 | 243 | 244 | # 新增日志 245 | def set_log(self,text): 246 | self.Log = self.Log + text 247 | 248 | 249 | # 获取日志 250 | def get_log(self): 251 | # return self.Log.replace("\n","\r\n") 252 | return self.Log 253 | 254 | 255 | def run(self): 256 | wps_pc = os.getenv("wps_pc") 257 | if not wps_pc: 258 | notify.send("WPS_PC",'🙃wps PC CK 变量未设置') 259 | print('🙃wps PC CK 变量未设置') 260 | exit() 261 | wps_pc_list = wps_pc.split('&') 262 | print("-------------------总共" + str(int(len(wps_pc_list))) + "个wps_PC CK-------------------") 263 | for mt_token in wps_pc_list: 264 | # try: 265 | self.ck = mt_token 266 | self.set_log("\n--------PC打卡--------\n") 267 | if not self.get_check(): 268 | self.set_log(mt_token+" CK失效了\n") 269 | continue 270 | i = 0 271 | while True: 272 | i = i + 1 273 | if self.code_processing(): 274 | print("第" + str(i + 1) + "次尝试签到成功") 275 | break 276 | else: 277 | print("第" + str(i + 1) + "次尝试签到失败") 278 | time.sleep(2) 279 | self.get_reward() # 获取奖励信息 280 | self.get_balance() # 获取余额 281 | # 开始空间处理 282 | self.set_log("\n--------云空间--------\n") 283 | i = 0 284 | while True: 285 | i = i + 1 286 | if self.space_code_processing(): 287 | print("第" + str(i + 1) + "次尝试空间签到成功") 288 | break 289 | else: 290 | print("第" + str(i + 1) + "次尝试空间签到失败") 291 | time.sleep(2) 292 | self.get_space_quota() #获取空间额度 293 | print("📝签到日志:") 294 | print(self.get_log()) 295 | notify.send("WPS_PC", self.get_log()) 296 | # except Exception as e: 297 | # print("出错了!详细错误👇错误CK👉" + mt_token) 298 | # print(e) 299 | # notify.send("WPS_PC", "出错了!详细错误👇错误CK👉" + mt_token +"\n错误内容:" + str(e)) 300 | 301 | 302 | if __name__ == '__main__': 303 | r = wps() 304 | r.run() 305 | --------------------------------------------------------------------------------