1- ( async function ( ) {
2-
1+ import { ocrUid } from "./utils/uid.js" ;
2+ import { toMainUi , throwError } from "./utils/tool.js" ;
3+
4+ const config = {
5+ tryRe : {
6+ max : 3 ,
7+ count : 0
8+ } ,
9+ user : {
10+ uid : undefined ,
11+ insufficient_exchange : false , //兑换不足
12+ } ,
13+ send_notification : false
14+ }
15+
316/**
417 * 判断任务是否已刷新
518 * @param {string } filePath - 存储最后完成时间的文件路径
@@ -23,26 +36,52 @@ async function isTaskRefreshed(filePath, options = {}) {
2336 monthlyDay = 1 , // 每月刷新默认第1天
2437 monthlyHour = 4 // 每月刷新默认凌晨4点
2538 } = options ;
26-
39+ if ( config . user . insufficient_exchange ) {
40+ throwError ( "兑换不足,请手动兑换" , config . send_notification )
41+ }
42+ config . tryRe . count ++
43+ const retry = config . tryRe ;
44+ const try_count_max = retry . max
45+ const try_count = retry . count
46+ if ( try_count_max < try_count ) {
47+ throw new Error ( "已重试" + ( try_count - 1 ) + "次数,超出最大重试" + try_count_max + "次数" ) ;
48+ }
49+ if ( ! config . user . uid ) {
50+ const resolvedUid = await ocrUid ( ) ;
51+ if ( ! Number . isInteger ( resolvedUid ) || resolvedUid <= 0 ) {
52+ throw new Error ( `UID 识别失败: ${ resolvedUid } ` ) ;
53+ }
54+ config . user . uid = resolvedUid ;
55+ }
56+ const uid = config . user . uid ;
57+ const current = { uid : uid , time : undefined }
58+ // 读取文件内容
59+ let contentList = [ ] ;
60+ try {
61+ contentList = JSON . parse ( file . readTextSync ( filePath ) )
62+ } catch ( e ) {
63+ log . debug ( "warn:" + e . message )
64+ }
65+ const last = contentList . find ( item => item . uid === uid )
66+ const lastTimeNumber = last ?. time
67+ const lastTime = lastTimeNumber ? new Date ( lastTimeNumber ) : new Date ( 0 ) ;
2768 try {
28- // 读取文件内容
29- let content = await file . readText ( filePath ) ;
30- const lastTime = new Date ( content ) ;
3169 const nowTime = new Date ( ) ;
32-
70+ current . time = nowTime . getTime ( ) ;
71+
3372 let shouldRefresh = false ;
34-
73+
3574
3675 switch ( refreshType ) {
3776 case 'hourly' : // 每小时刷新
3877 shouldRefresh = ( nowTime - lastTime ) >= 3600 * 1000 ;
3978 break ;
40-
79+
4180 case 'daily' : // 每天固定时间刷新
4281 // 检查是否已经过了当天的刷新时间
4382 const todayRefresh = new Date ( nowTime ) ;
4483 todayRefresh . setHours ( dailyHour , 0 , 0 , 0 ) ;
45-
84+
4685 // 如果当前时间已经过了今天的刷新时间,检查上次完成时间是否在今天刷新之前
4786 if ( nowTime >= todayRefresh ) {
4887 shouldRefresh = lastTime < todayRefresh ;
@@ -53,15 +92,15 @@ async function isTaskRefreshed(filePath, options = {}) {
5392 shouldRefresh = lastTime < yesterdayRefresh ;
5493 }
5594 break ;
56-
95+
5796 case 'weekly' : // 每周固定时间刷新
5897 // 获取本周的刷新时间
5998 const thisWeekRefresh = new Date ( nowTime ) ;
6099 // 计算与本周指定星期几的差值
61100 const dayDiff = ( thisWeekRefresh . getDay ( ) - weeklyDay + 7 ) % 7 ;
62101 thisWeekRefresh . setDate ( thisWeekRefresh . getDate ( ) - dayDiff ) ;
63102 thisWeekRefresh . setHours ( weeklyHour , 0 , 0 , 0 ) ;
64-
103+
65104 // 如果当前时间已经过了本周的刷新时间
66105 if ( nowTime >= thisWeekRefresh ) {
67106 shouldRefresh = lastTime < thisWeekRefresh ;
@@ -72,14 +111,14 @@ async function isTaskRefreshed(filePath, options = {}) {
72111 shouldRefresh = lastTime < lastWeekRefresh ;
73112 }
74113 break ;
75-
114+
76115 case 'monthly' : // 每月固定时间刷新
77116 // 获取本月的刷新时间
78117 const thisMonthRefresh = new Date ( nowTime ) ;
79118 // 设置为本月指定日期的凌晨
80119 thisMonthRefresh . setDate ( monthlyDay ) ;
81120 thisMonthRefresh . setHours ( monthlyHour , 0 , 0 , 0 ) ;
82-
121+
83122 // 如果当前时间已经过了本月的刷新时间
84123 if ( nowTime >= thisMonthRefresh ) {
85124 shouldRefresh = lastTime < thisMonthRefresh ;
@@ -94,39 +133,60 @@ async function isTaskRefreshed(filePath, options = {}) {
94133 case 'custom' : // 自定义小时数刷新
95134 shouldRefresh = ( nowTime - lastTime ) >= customHours * 3600 * 1000 ;
96135 break ;
97-
136+
98137 default :
99138 throw new Error ( `未知的刷新类型: ${ refreshType } ` ) ;
100139 }
101-
102- // 如果文件内容无效或不存在,视为需要刷新
103- if ( ! content || isNaN ( lastTime . getTime ( ) ) ) {
104- await file . writeText ( filePath , nowTime . toISOString ( ) ) ;
105- shouldRefresh = true ;
106- }
107-
108- if ( shouldRefresh ) {
109- notification . send ( `任务已刷新,执行每月兑换抽卡资源` ) ;
110- await exchangeGoods ( ) ;
111- // 更新最后完成时间
112- await file . writeText ( filePath , nowTime . toISOString ( ) ) ;
113- return true ;
114- } else {
115- notification . send ( `任务未刷新,跳过每月兑换抽卡资源` ) ;
116- return false ;
140+
141+ // // 如果文件内容无效或不存在,视为需要刷新
142+ // if (!contentList || isNaN(lastTime.getTime())) {
143+ // //todo:写入也要改 contentList.put(uid, nowTime.toISOString())
144+ // // await file.writeText(filePath, JSON.stringify(contentList));
145+ // shouldRefresh = true;
146+ // }
147+ try {
148+ if ( shouldRefresh ) {
149+ const message = `任务已刷新,执行每月兑换抽卡资源` ;
150+ log . info ( message )
151+ if ( config . send_notification ) {
152+ notification . send ( message ) ;
153+ }
154+ await exchangeGoods ( ) ;
155+
156+ if ( contentList . some ( item => item . uid === current . uid ) ) {
157+ contentList . forEach ( item => {
158+ if ( item . uid === current . uid ) {
159+ item . time = current . time ;
160+ }
161+ } )
162+ } else {
163+ contentList . push ( current ) ;
164+ }
165+
166+ // 更新最后完成时间
167+ await file . writeText ( filePath , JSON . stringify ( contentList ) ) ;
168+ return true ;
169+ } else {
170+ const message = `任务未刷新,跳过每月兑换抽卡资源` ;
171+ log . info ( message )
172+ if ( config . send_notification ) {
173+ notification . send ( message ) ;
174+ }
175+ return false ;
176+ }
177+ } finally {
178+ log . debug ( "contentList:" , JSON . stringify ( contentList ) )
117179 }
118-
119180 } catch ( error ) {
120- // 如果文件不存在,创建新文件并返回true(视为需要刷新)
121- const createResult = await file . writeText ( filePath , '' ) ;
181+ log . error ( `刷新任务失败: ${ error } ` ) ;
182+ const createResult = await file . writeText ( filePath , JSON . stringify ( contentList ) ) ;
122183 if ( createResult ) {
123- log . info ( "创建新文件成功" ) ;
124- await isTaskRefreshed ( filePath , options = { } ) ;
184+ log . debug ( "创建新文件成功" ) ;
185+ await isTaskRefreshed ( filePath , options = { } ) ;
125186 }
126187 }
127188}
128189
129-
130190//检查是否为正整数
131191function positiveIntegerJudgment ( testNumber ) {
132192 // 如果输入是字符串,尝试转换为数字
@@ -135,76 +195,149 @@ function positiveIntegerJudgment(testNumber) {
135195 const cleaned = testNumber . replace ( / [ ^ \d ] / g, '' ) ;
136196 testNumber = parseInt ( cleaned , 10 ) ;
137197 }
138-
198+
139199 // 检查是否为有效的数字
140200 if ( typeof testNumber !== 'number' || isNaN ( testNumber ) ) {
141201 throw new Error ( `无效的值: ${ testNumber } (必须为数字)` ) ;
142202 }
143-
203+
144204 // 检查是否为整数
145205 if ( ! Number . isInteger ( testNumber ) ) {
146206 throw new Error ( `必须为整数: ${ testNumber } ` ) ;
147207 }
148-
208+
149209 return testNumber ;
150210}
151211
152-
153212async function exchangeGoods ( ) {
154-
155- await genshin . returnMainUi ( ) ; await sleep ( 1000 ) ;
156- keyPress ( "ESCAPE" ) ; await sleep ( 2000 ) ; //呼叫派蒙
157- click ( 198 , 416 ) ; await sleep ( 2000 ) ; //点击商城
158- click ( 127 , 434 ) ; await sleep ( 1000 ) ; //尘辉兑换
159- click ( 998 , 125 ) ; await sleep ( 1000 ) ; //星辰兑换
213+ await toMainUi ( ) ;
214+ await sleep ( 1000 ) ;
215+ keyPress ( "ESCAPE" ) ;
216+ await sleep ( 2000 ) ; //呼叫派蒙
217+ click ( 198 , 416 ) ;
218+ await sleep ( 2000 ) ; //点击商城
219+ click ( 127 , 434 ) ;
220+ await sleep ( 1000 ) ; //尘辉兑换
221+ click ( 998 , 125 ) ;
222+ await sleep ( 1000 ) ; //星辰兑换
223+ let materialQuantity = "" ;
160224 //检查星辰的数量
161225 const region = RecognitionObject . ocr ( 1400 , 31 , 150 , 50 ) ; // 星辰数量区域
162226 let capture = captureGameRegion ( ) ;
163- let res = capture . find ( region ) ;
164- capture . dispose ( ) ;
165- let materialQuantity = res . text ;
166- let validatedMaterialQuantity = positiveIntegerJudgment ( materialQuantity ) ;
167- if ( validatedMaterialQuantity < 750 ) {
168- notification . send ( `星尘数量为: ${ validatedMaterialQuantity } ,无法全部兑换` ) ;
169- throw new Error ( `星尘数量为: ${ validatedMaterialQuantity } ,不能完全兑换` ) ;
227+ try {
228+ let res = capture . find ( region ) ;
229+ materialQuantity = res . text ;
230+ } finally {
231+ if ( capture ) {
232+ capture . dispose ( ) ;
233+ }
170234 }
171- log . info ( `星尘数量为: ${ validatedMaterialQuantity } ,数量充足,可以全部兑换` ) ;
235+
172236 const pinkBallRo = RecognitionObject . TemplateMatch ( file . ReadImageMatSync ( "assets/pinkBall.png" ) ) ;
173237 let ro1 = captureGameRegion ( ) ;
174- let pinkBall = ro1 . find ( pinkBallRo ) ;
175- ro1 . dispose ( ) ;
176- if ( pinkBall . isExist ( ) ) {
177- pinkBall . click ( ) ; await sleep ( 1000 ) ;
178- click ( 1290 , 604 ) ; await sleep ( 500 ) ; //增加
179- click ( 1290 , 604 ) ; await sleep ( 500 ) ; //增加
180- click ( 1290 , 604 ) ; await sleep ( 500 ) ; //增加
181- click ( 1290 , 604 ) ; await sleep ( 500 ) ; //增加
182- click ( 1164 , 782 ) ; await sleep ( 500 ) ; //确认兑换
183- click ( 960 , 754 ) ; await sleep ( 1000 ) ; //点击空白处继续
238+ let pinkBallExist = false
239+ let pinkBall
240+ try {
241+ let pinkBallFind = ro1 . find ( pinkBallRo ) ;
242+ pinkBallExist = pinkBallFind . isExist ( ) ;
243+ pinkBall = {
244+ x : pinkBallFind . x ,
245+ y : pinkBallFind . y
246+ }
247+ } finally {
248+ if ( ro1 ) {
249+ ro1 . dispose ( ) ;
250+ }
184251 }
252+
185253 const blueBallRo = RecognitionObject . TemplateMatch ( file . ReadImageMatSync ( "assets/blueBall.png" ) ) ;
186254 let ro2 = captureGameRegion ( ) ;
187- let blueBall = ro2 . find ( blueBallRo ) ;
188- ro2 . dispose ( ) ;
189- if ( blueBall . isExist ( ) ) {
190- blueBall . click ( ) ; await sleep ( 1000 ) ;
191- click ( 1290 , 604 ) ; await sleep ( 500 ) ; //增加
192- click ( 1290 , 604 ) ; await sleep ( 500 ) ; //增加
193- click ( 1290 , 604 ) ; await sleep ( 500 ) ; //增加
194- click ( 1290 , 604 ) ; await sleep ( 500 ) ; //增加
195- click ( 1164 , 782 ) ; await sleep ( 500 ) ; //确认兑换
196- click ( 960 , 754 ) ; await sleep ( 1000 ) ; //点击空白处继续
197- }
198- notification . send ( `商城抽卡资源兑换完成` ) ;
199- }
255+ let blueBallExist = false
256+ let blueBall
257+ try {
258+ let blueBallFind = ro2 . find ( blueBallRo ) ;
259+ blueBallExist = blueBallFind . isExist ( ) ;
260+ blueBall = {
261+ x : blueBallFind . x ,
262+ y : blueBallFind . y
263+ }
264+ } finally {
265+ if ( ro2 ) {
266+ ro2 . dispose ( ) ;
267+ }
268+ }
200269
270+ if ( ! pinkBallExist && ! blueBallExist ) {
271+ log . info ( `没有粉球和蓝球,跳过兑换` ) ;
272+ return
273+ }
274+
275+ let validatedMaterialQuantity = positiveIntegerJudgment ( materialQuantity ) ;
276+ if ( validatedMaterialQuantity < 750 ) {
277+ config . user . insufficient_exchange = true
278+ throwError ( `星尘数量为:${ validatedMaterialQuantity } ,数量不足,无法全部兑换` , config . send_notification )
279+ // notification.send(`星尘数量为:${validatedMaterialQuantity},无法全部兑换`);
280+ // throw new Error(`星尘数量为:${validatedMaterialQuantity},不能完全兑换`);
281+ }
282+ log . info ( `星尘数量为:${ validatedMaterialQuantity } ,数量充足,可以全部兑换` ) ;
201283
202-
203- await isTaskRefreshed ( "assets/monthly.txt" , {
204- refreshType : 'monthly' ,
205- monthlyDay : 1 , // 每月第1天(默认值,可省略)
206- monthlyHour : 4 // 凌晨4点(默认值,可省略)
207- } ) ;
284+ if ( pinkBallExist && pinkBall ) {
285+ // pinkBall.click();
286+ click ( pinkBall . x , pinkBall . y )
287+ await sleep ( 1000 ) ;
288+ click ( 1290 , 604 ) ;
289+ await sleep ( 500 ) ; //增加
290+ click ( 1290 , 604 ) ;
291+ await sleep ( 500 ) ; //增加
292+ click ( 1290 , 604 ) ;
293+ await sleep ( 500 ) ; //增加
294+ click ( 1290 , 604 ) ;
295+ await sleep ( 500 ) ; //增加
296+ click ( 1164 , 782 ) ;
297+ await sleep ( 500 ) ; //确认兑换
298+ click ( 960 , 754 ) ;
299+ await sleep ( 1000 ) ; //点击空白处继续
300+ }
208301
302+ if ( blueBallExist && blueBall ) {
303+ // blueBall.click();
304+ click ( blueBall . x , blueBall . y )
305+ await sleep ( 1000 ) ;
306+ click ( 1290 , 604 ) ;
307+ await sleep ( 500 ) ; //增加
308+ click ( 1290 , 604 ) ;
309+ await sleep ( 500 ) ; //增加
310+ click ( 1290 , 604 ) ;
311+ await sleep ( 500 ) ; //增加
312+ click ( 1290 , 604 ) ;
313+ await sleep ( 500 ) ; //增加
314+ click ( 1164 , 782 ) ;
315+ await sleep ( 500 ) ; //确认兑换
316+ click ( 960 , 754 ) ;
317+ await sleep ( 1000 ) ; //点击空白处继续
318+ }
319+ const message = `商城抽卡资源兑换完成` ;
320+ log . info ( message )
321+ if ( config . send_notification ) {
322+ notification . send ( message ) ;
323+ }
324+ }
325+
326+ async function main ( ) {
327+ try {
328+ config . tryRe . max = parseInt ( settings . try_count_max + "" ) || config . tryRe . max
329+ config . send_notification = settings . send_notification
330+ } catch ( e ) {
331+ }
332+ try {
333+ await isTaskRefreshed ( "assets/monthly.txt" , {
334+ refreshType : 'monthly' ,
335+ monthlyDay : 1 , // 每月第1天(默认值,可省略)
336+ monthlyHour : 4 // 凌晨4点(默认值,可省略)
337+ } ) ;
338+ } finally {
339+ await toMainUi ( )
340+ }
341+ }
209342
210- } ) ( ) ;
343+ await main ( ) ;
0 commit comments