import { tl } from '../toast' ; import { $$ } from '../ui-object' ; import { Task } from './Task' ; interface History { current : number ; max : number ; } enum TaskStatus { initialized = 'initialized' , running = 'running' , failed = 'failed' , ended = 'ended' , suspend = 'suspend' } type CustomTaskSkip = ( task : Task ) => boolean ; interface WorkerConfig { name : string ; openApp : ( ) => void ; killApp : ( ) => void ; runTask : ( task : Task , index : number ) => void ; keepInTaskPage : ( ) => void ; queryTask ? : ( ) => Task [ ] ; taskSkip ? : CustomTaskSkip ; taskRetires ? : number ; workerRetires ? : number ; } class Worker { private historyMapping : { [ key ] : History ; } = { } ; private thread : threads . Thread | null = null ; private volumeThread : threads . Thread ; private innerStatus = TaskStatus . initialized ; private currentIndex = 0 ; private workerRetires : number ; private em = events . emitter ( ) ; public constructor ( private config ) { this . workerRetires = config . workerRetires ?? 3 ; this . volumeThread = this . volumeListen ( ) ; } public start ( ) { if ( this . taskStatus === TaskStatus . running ) { tl ( '任务正在执行中, 不做任何操作' , this . config . name ) ; return ; } if ( this . taskStatus !== TaskStatus . suspend && this . currentIndex > this . workerRetires ) { tl ( '已经达到最大次数, 销毁程序' , this . config . name ) ; this . destroy ( ) ; return ; } if ( this . taskStatus === TaskStatus . suspend ) { tl ( `执行任务 ${ this . config . name }` ) ; } else { this . currentIndex = this . currentIndex + 1 ; this . historyMapping = { } ; tl ( `尝试第 ${ this . currentIndex } 次执行` , this . config . name ) ; if ( this . taskStatus !== TaskStatus . initialized ) { sleep ( 1000 ) ; this . config . killApp ( ) ; sleep ( 1000 ) ; } tl ( `打开 [${ this . config . name }] 应用中` ) ; this . config . openApp ( ) ; sleep ( 1000 ) ; } this . updateEventEmitter ( ) ; this . closeThread ( ) ; this . thread = threads . start ( ( ) => { try { this . taskStatus = TaskStatus . running ; this . run ( ) ; this . taskStatus = TaskStatus . ended ; } catch ( e ) { console . warn ( e ) ; this . taskStatus = TaskStatus . failed ; } } ) ; } public suspend ( ) { this . closeThread ( ) ; this . taskStatus = TaskStatus . suspend ; } public destroy ( ) { this . closeThread ( ) ; this . volumeThread . interrupt ( ) ; this . taskStatus = TaskStatus . ended ; } private updateEventEmitter ( ) { this . em . removeAllListeners ( ) ; this . em . on ( 'status' , ( state ) => { console . log ( '状态变更: ' , state ) ; const status = state . after ; if ( status === TaskStatus . ended || status === TaskStatus . failed ) { this . start ( ) ; } } ) ; } private closeThread ( ) { if ( this . thread ) { this . thread . interrupt ( ) ; this . thread = null ; } } private get taskStatus ( ) : TaskStatus { return this . innerStatus ; } private set taskStatus ( status ) { const state = { before : this . innerStatus , after : status } ; this . innerStatus = status ; this . em ?. emit ( 'status' , state ) ; } private run ( ) { this . config . keepInTaskPage ( ) ; while ( true ) { let tasks ; if ( this . config . queryTask ) { tasks = this . config . queryTask ( ) ; } else { tasks = $$ ( /.*\(\d+\/\d+.*/ ) . map ( ( ele ) => { const container = ele . parent ( ) ; return new Task ( { container } ) ; } ) ; } const task = this . getValidTask ( tasks ) ; if ( ! task ) { tl ( '没有任务可以执行' ) ; break ; } const history = this . getHistory ( task ) ; this . config . runTask ( task , history ?. current ?? 1 ) ; this . config . keepInTaskPage ( ) ; } } private getValidTask ( tasks ) { const { taskSkip } = this . config ; let taskResult ; while ( true ) { const task = tasks . shift ( ) ; if ( ! task ) { break ; } const history = this . getHistory ( task ) ; if ( history && history . current >= history . max ) { if ( history . current === history . max ) { tl ( `重试次数达到上限, 跳过任务 [${ task . title }]  ${ history . current }` ) ; this . updateHistory ( task , { current : history . max + 1 , } ) ; } continue ; } const taskCount = task . taskCount ; if ( ! taskCount ) { continue ; } if ( taskCount . left === 0 ) { continue ; } if ( ! taskSkip ) { taskResult = task ; break ; } const isSkip = taskSkip ( task ) ; if ( isSkip ) { continue ; } taskResult = task ; break ; } if ( ! taskResult ) { return null ; } const history = this . getHistory ( taskResult ) ; this . updateHistory ( taskResult , { current : ( history ?. current ?? 0 ) + 1 , } ) ; return taskResult ; } private getHistoryTitle ( task ) { let title ; if ( typeof task === 'string' ) { title = task ; } else { title = task . title ; } return title ; } private getHistory ( task ) { return this . historyMapping [ this . getHistoryTitle ( task ) ] ; } private updateHistory ( task , value ) { const title = this . getHistoryTitle ( task ) ; const old = this . getHistory ( title ) ; this . historyMapping [ title ] = { max : this . config . taskRetires ?? 3 , current : 0 , ... old , ... value , } ; } private volumeListen ( ) { let lastTime = Date . now ( ) ; return threads . start ( ( ) => { events . setKeyInterceptionEnabled ( 'volume_up' , true ) ; events . observeKey ( ) ; events . onKeyDown ( 'volume_up' , ( ) => { if ( Date . now ( ) - lastTime < 1000 ) { tl ( '退出整个 App' ) ; this . destroy ( ) ; exit ( ) ; } else if ( this . taskStatus === TaskStatus . running ) { tl ( `暂停执行脚本 [${ this . config . name }]` ) ; this . suspend ( ) ; } else if ( this . taskStatus === TaskStatus . ended ) { tl ( `脚本执行完成 [${ this . config . name }], 不做操作` ) ; } else { tl ( `重新执行脚本 [${ this . config . name }]` ) ; this . start ( ) ; } lastTime = Date . now ( ) ; } ) ; } ) ; } } export { Worker } ;