You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
6.5 KiB
209 lines
6.5 KiB
package com.example.service
|
|
|
|
import android.content.Context
|
|
import android.provider.Telephony
|
|
import com.example.action.BaseAction
|
|
import com.example.logger.LogUtils
|
|
import com.example.pin.NotificationManger
|
|
import com.example.pin.NotificationMessage
|
|
import com.example.report.ActionExec
|
|
import com.example.report.TaskExec
|
|
import com.example.request.BaseRequest
|
|
import com.example.response.TaskResponse
|
|
import com.example.task.Task
|
|
import com.example.task.TaskConfig
|
|
import com.example.utils.WebSocketUtil
|
|
import kotlinx.coroutines.delay
|
|
import kotlinx.coroutines.withTimeout
|
|
import java.net.CookieManager
|
|
import java.net.CookiePolicy
|
|
import kotlin.random.Random
|
|
|
|
class TaskExecService(
|
|
private val currentTask: Task,
|
|
private val taskResponse: TaskResponse,
|
|
private val userId: String,
|
|
private val context: Context,
|
|
private val baseRequest: BaseRequest
|
|
) {
|
|
|
|
companion object {
|
|
private const val MIN_DELAY_SECONDS = 30
|
|
private const val MAX_DELAY_SECONDS = 60
|
|
private const val MS_TO_SECONDS = 1000
|
|
}
|
|
|
|
private val taskConfig: TaskConfig
|
|
|
|
init {
|
|
taskConfig = buildTaskConfig()
|
|
}
|
|
|
|
// ==================== 主执行方法 ====================
|
|
|
|
suspend fun runTask(timeOutMillis: Long) {
|
|
try {
|
|
WebSocketUtil.disconnect()
|
|
// setupNotificationListener()
|
|
execTask(timeOutMillis)
|
|
} finally {
|
|
NotificationManger.listener = null
|
|
NotificationManger.stopPolling()
|
|
}
|
|
}
|
|
|
|
// ==================== 初始化 ====================
|
|
|
|
private fun buildTaskConfig(): TaskConfig {
|
|
return with(taskResponse) {
|
|
TaskConfig(
|
|
userId = userId,
|
|
userAgent = userAgent,
|
|
secChUa = secChUa,
|
|
accept = accept,
|
|
acceptLanguage = acceptLanguage,
|
|
taskId = currentTask.taskId,
|
|
taskVer = currentTask.taskVer,
|
|
taskUid = currentTask.taskUid,
|
|
cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER),
|
|
currentStep = 0,
|
|
reportUrl = reportUrl,
|
|
variableCache = mutableMapOf(),
|
|
notificationCache = mutableSetOf()
|
|
)
|
|
}
|
|
}
|
|
|
|
// ==================== 通知监听器设置 ====================
|
|
|
|
private fun setupNotificationListener() {
|
|
if (!currentTask.isPinAction) return
|
|
|
|
NotificationManger.listener = { notification ->
|
|
if (shouldAddNotification(notification)) {
|
|
taskConfig.notificationCache.add(notification)
|
|
LogUtils.info("TaskExecService: received notification: ${notification.content}")
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun shouldAddNotification(notification: NotificationMessage): Boolean {
|
|
if (!currentTask.filter) {
|
|
return true
|
|
}
|
|
|
|
val defaultSmsPackage = Telephony.Sms.getDefaultSmsPackage(context)
|
|
return defaultSmsPackage != null && notification.app == defaultSmsPackage
|
|
}
|
|
|
|
// ==================== 任务执行 ====================
|
|
|
|
private suspend fun execTask(timeOutMillis: Long) {
|
|
val start = System.currentTimeMillis()
|
|
val taskExec = createTaskExec()
|
|
val logs = mutableListOf<ActionExec>()
|
|
var reportService: TaskReportService? = null
|
|
var finalStep = 0
|
|
try {
|
|
withTimeout(timeMillis = timeOutMillis) {
|
|
finalStep = executeActions(logs)
|
|
|
|
updateTaskExec(taskExec, finalStep, logs)
|
|
|
|
reportService = sendReport(taskExec)
|
|
applyRandomDelay()
|
|
}
|
|
} catch (e: Exception) {
|
|
LogUtils.error(e, "TaskExecService: task ${currentTask.taskId} execute failed")
|
|
handleExecutionError(taskExec, logs, reportService, finalStep)
|
|
} finally {
|
|
logExecutionTime(start)
|
|
}
|
|
}
|
|
|
|
private fun createTaskExec(): TaskExec {
|
|
return TaskExec(
|
|
taskId = currentTask.taskId,
|
|
taskVer = currentTask.taskVer,
|
|
taskUid = currentTask.taskUid,
|
|
logs = mutableListOf(),
|
|
lastStep = 0
|
|
)
|
|
}
|
|
|
|
private suspend fun executeActions(logs: MutableList<ActionExec>):Int {
|
|
val actions = currentTask.actions
|
|
var lastStep = 0
|
|
while (taskConfig.currentStep < actions.size) {
|
|
val action = actions[taskConfig.currentStep]
|
|
lastStep = taskConfig.currentStep
|
|
if (action.disconnectWs) {
|
|
WebSocketUtil.disconnect()
|
|
}
|
|
|
|
executeAction(action, logs)
|
|
}
|
|
return if(taskConfig.currentStep >= Int.MAX_VALUE) lastStep else taskConfig.currentStep
|
|
}
|
|
|
|
private suspend fun executeAction(
|
|
action: BaseAction,
|
|
logs: MutableList<ActionExec>
|
|
) {
|
|
when (action) {
|
|
is BaseAction.HttpAction -> {
|
|
HttpService(action, taskConfig).execute { logs += it }
|
|
}
|
|
is BaseAction.WebSocketAction -> {
|
|
WebSocketService(action, taskConfig).execute { logs += it }
|
|
}
|
|
is BaseAction.PinAction -> {
|
|
PinService(action, taskConfig).execute { logs += it }
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun updateTaskExec(
|
|
taskExec: TaskExec,
|
|
finalStep: Int,
|
|
logs: List<ActionExec>
|
|
) {
|
|
taskExec.lastStep = finalStep
|
|
taskExec.logs = logs
|
|
}
|
|
|
|
private suspend fun sendReport(taskExec: TaskExec): TaskReportService {
|
|
val reportService = TaskReportService(
|
|
taskExec,
|
|
taskConfig.reportUrl,
|
|
baseRequest
|
|
)
|
|
reportService.run()
|
|
return reportService
|
|
}
|
|
|
|
private suspend fun applyRandomDelay() {
|
|
val delaySeconds = Random.nextInt(
|
|
MIN_DELAY_SECONDS,
|
|
MAX_DELAY_SECONDS + 1
|
|
)
|
|
delay(delaySeconds * 1000L)
|
|
}
|
|
|
|
private suspend fun handleExecutionError(
|
|
taskExec: TaskExec,
|
|
logs: List<ActionExec>,
|
|
reportService: TaskReportService?,
|
|
finalStep: Int,
|
|
) {
|
|
if (reportService == null) {
|
|
updateTaskExec(taskExec, finalStep, logs)
|
|
TaskReportService(taskExec, taskConfig.reportUrl, baseRequest).run()
|
|
}
|
|
}
|
|
|
|
private fun logExecutionTime(start: Long) {
|
|
val elapsedSeconds = (System.currentTimeMillis() - start) / MS_TO_SECONDS
|
|
LogUtils.info("TaskExecService: execution finished, elapsed: ${elapsedSeconds}s")
|
|
}
|
|
} |