1+ #!/usr/bin/env python3
2+ # -*- coding: utf-8 -*-
3+ """
4+ new Env('上海云媒体积分任务');
5+ cron: 10 8 * * *
6+
7+ 【使用说明】
8+ 1. 环境变量名: SHYP_ACCOUNTS
9+ 2. 格式要求:
10+ - 多个账号之间用 & 或者 换行 分隔。
11+ - 账号内部参数用 # 分隔,顺序为:token#device_id#备注名
12+
13+ 例如:
14+ export SHYP_ACCOUNTS="token1#device1#张三 & token2#device2#李四"
15+ """
16+
17+ import os
18+ import re
19+ import time
20+ import random
21+ import logging
22+ import requests
23+ from typing import Dict , Any , Optional
24+
25+ # ==================== 配置与日志 ====================
26+ logging .basicConfig (level = logging .INFO , format = '%(asctime)s - %(levelname)s - %(message)s' )
27+ logger = logging .getLogger ("SHYP" )
28+
29+ # 随机延迟设置
30+ DELAY_ACCOUNTS = (5 , 12 )
31+ DELAY_TASKS = (3 , 6 )
32+ COMMENT_POOL = ["👍" , "支持" , "不错" , "很好" , "赞" , "学习了" ]
33+
34+ # ==================== API 核心类 ====================
35+ class ShypAPI :
36+ def __init__ (self , token : str , device_id : str , name : str ):
37+ self .base_url = "https://app.ypmedia.cn"
38+ self .token = token
39+ self .device_id = device_id
40+ self .name = name
41+ self .site_id = "310110"
42+ self .headers = {
43+ "User-Agent" : "okhttp/4.10.0" ,
44+ "deviceid" : self .device_id ,
45+ "siteid" : self .site_id ,
46+ "token" : self .token ,
47+ "Content-Type" : "application/json; charset=UTF-8"
48+ }
49+
50+ def _post (self , endpoint : str , data : Dict = None ) -> Optional [Dict ]:
51+ try :
52+ res = requests .post (f"{ self .base_url } { endpoint } " , json = data , headers = self .headers , timeout = 15 )
53+ return res .json ()
54+ except Exception as e :
55+ logger .error (f"请求失败: { e } " )
56+ return None
57+
58+ def get_info (self ):
59+ return self ._post ("/media-basic-port/api/app/personal/score/info" ,
60+ {"orderBy" : "release_desc" , "requestType" : "2" , "siteId" : self .site_id })
61+
62+ def get_articles (self , channel_id : str , size : int ):
63+ return self ._post ("/media-basic-port/api/app/news/content/list" ,
64+ {"channel" : {"id" : channel_id }, "pageNo" : 1 , "pageSize" : size ,
65+ "orderBy" : "release_desc" , "requestType" : "1" , "siteId" : self .site_id })
66+
67+ # ==================== 任务执行器 ====================
68+ class ShypRunner :
69+ def __init__ (self , api : ShypAPI ):
70+ self .api = api
71+
72+ def start (self ):
73+ logger .info (f"🚀 开始处理账号: { self .api .name } " )
74+ info = self .api .get_info ()
75+ if not info or info .get ("code" ) != 0 :
76+ logger .error (f"❌ { self .api .name } Token无效或获取失败" )
77+ return
78+
79+ data = info .get ("data" , {})
80+ logger .info (f"📊 当前积分: { data .get ('totalScore' )} | 今日已获: { data .get ('todayPoint' )} " )
81+
82+ for job in data .get ("jobs" , []):
83+ if job .get ("status" ) == "1" : continue
84+
85+ title = job .get ("title" )
86+ need = job .get ("totalProgress" , 0 ) - job .get ("progress" , 0 )
87+ if need <= 0 : continue
88+
89+ if "阅读" in title : self .do_read (need )
90+ elif "视频" in title : self .do_video (need )
91+ elif "收藏" in title : self .do_favor (need )
92+ elif "评论" in title : self .do_comment (need )
93+ elif "分享" in title : self .do_share (need )
94+
95+ time .sleep (random .uniform (* DELAY_TASKS ))
96+
97+ def do_read (self , count ):
98+ logger .info (f"📖 准备阅读 { count } 篇文章" )
99+ res = self .api .get_articles ("a978f44b3e284e5e86777f9d4e3be7bb" , count )
100+ for a in res .get ("data" , {}).get ("records" , [])[:count ]:
101+ self .api ._post ("/media-basic-port/api/app/common/count/usage/inc" ,
102+ {"countType" : "contentRead" , "id" : a ['id' ], "requestType" : "1" , "siteId" : self .api .site_id })
103+ self .api ._post ("/media-basic-port/api/app/points/read/add" , {"requestType" : "1" , "siteId" : self .api .site_id })
104+ logger .info (f"✅ 已阅: { a ['title' ][:12 ]} " )
105+ time .sleep (random .uniform (2 , 4 ))
106+
107+ def do_video (self , count ):
108+ logger .info (f"📺 准备观看 { count } 个视频" )
109+ res = self .api .get_articles ("d7036c2839e047b48fe64bc36987650c" , count )
110+ for v in res .get ("data" , {}).get ("records" , [])[:count ]:
111+ self .api ._post ("/media-basic-port/api/app/points/video/add" , {"requestType" : "1" , "siteId" : self .api .site_id })
112+ logger .info (f"✅ 已看: { v ['title' ][:12 ]} " )
113+ time .sleep (random .uniform (5 , 8 ))
114+
115+ def do_favor (self , count ):
116+ res = self .api .get_articles ("a978f44b3e284e5e86777f9d4e3be7bb" , count )
117+ for a in res .get ("data" , {}).get ("records" , [])[:count ]:
118+ self .api ._post ("/media-basic-port/api/app/news/content/favor" , {"id" : a ['id' ]})
119+ logger .info (f"✅ 已收藏: { a ['title' ][:12 ]} " )
120+ time .sleep (2 )
121+
122+ def do_comment (self , count ):
123+ res = self .api .get_articles ("a978f44b3e284e5e86777f9d4e3be7bb" , count )
124+ for a in res .get ("data" , {}).get ("records" , [])[:count ]:
125+ self .api ._post ("/media-basic-port/api/app/common/comment/add" ,
126+ {"content" : random .choice (COMMENT_POOL ), "targetType" : "content" , "targetId" : a ['id' ]})
127+ logger .info (f"✅ 已评论: { a ['title' ][:12 ]} " )
128+ time .sleep (4 )
129+
130+ def do_share (self , count ):
131+ for _ in range (count ):
132+ self .api ._post ("/media-basic-port/api/app/points/share/add" , {"requestType" : "1" , "siteId" : self .api .site_id })
133+ logger .info ("✅ 已分享任务" )
134+ time .sleep (2 )
135+
136+ # ==================== 主程序入口 ====================
137+ def main ():
138+ conf = os .getenv ("SHYP_ACCOUNTS" , "" )
139+ if not conf :
140+ logger .error ("❌ 未找到环境变量 SHYP_ACCOUNTS" )
141+ return
142+
143+ # 1. 按照 & 或者 换行符 分割出多个账号
144+ account_blocks = re .split (r'[&\n]+' , conf .strip ())
145+
146+ for idx , block in enumerate (account_blocks ):
147+ block = block .strip ()
148+ if not block : continue
149+
150+ # 2. 按照 # 分割内部参数 (token#device_id#name)
151+ parts = block .split ('#' )
152+
153+ if len (parts ) < 2 :
154+ logger .warning (f"⚠️ 第 { idx + 1 } 个账号格式错误,需至少包含 token#device_id" )
155+ continue
156+
157+ token = parts [0 ].strip ()
158+ device_id = parts [1 ].strip ()
159+ name = parts [2 ].strip () if len (parts ) > 2 else f"账号{ idx + 1 } "
160+
161+ api = ShypAPI (token , device_id , name )
162+ runner = ShypRunner (api )
163+ try :
164+ runner .start ()
165+ except Exception as e :
166+ logger .error (f"💥 运行异常: { e } " )
167+
168+ if idx < len (account_blocks ) - 1 :
169+ wait_time = random .uniform (* DELAY_ACCOUNTS )
170+ logger .info (f"💤 等待 { wait_time :.1f} 秒处理下一个账号..." )
171+ time .sleep (wait_time )
172+
173+ if __name__ == "__main__" :
174+ main ()
0 commit comments