package com.example.modifier.http.api import android.util.Log import com.example.modifier.baseTag import com.example.modifier.http.isTimeoutException import com.example.modifier.http.ktorClient import com.example.modifier.http.request.RcsNumberRequest import com.example.modifier.http.response.RcsNumberResponse import io.ktor.client.call.body import io.ktor.client.plugins.resources.get import io.ktor.client.plugins.resources.post import io.ktor.client.plugins.resources.put import io.ktor.client.plugins.retry import io.ktor.client.plugins.timeout import io.ktor.client.request.setBody import io.ktor.http.ContentType import io.ktor.http.contentType import io.ktor.resources.Resource import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeoutOrNull import kotlin.coroutines.cancellation.CancellationException import kotlin.coroutines.coroutineContext import kotlin.time.Duration.Companion.seconds @Resource("api/rcs-number") class RcsNumberApi() { @Resource("{id}") class Id(val parent: RcsNumberApi = RcsNumberApi(), val id: Int) { @Resource("delete") class Delete(val parent: Id) @Resource("otpState") class OtpState(val parent: Id) @Resource("configured") class Configured(val parent: Id) @Resource("wasted") class Wasted(val parent: Id) @Resource("stockFlag/{flag}") class UpdateStockFlag(val parent: Id, val flag: Int) } companion object { private const val TAG = "$baseTag/RcsNumberApi" suspend fun getRcsNumber( deviceId: String, taskId: Int? = null, pinCountry: String?, store: Boolean? = false ): RcsNumberResponse { val req = RcsNumberRequest( deviceId = deviceId, taskId = taskId, country = pinCountry, store = store ) val response = ktorClient.put(RcsNumberApi()) { contentType(ContentType.Application.Json) setBody(req) timeout { requestTimeoutMillis = 60 * 1000 socketTimeoutMillis = 60 * 1000 } retry { maxRetries = 3600 retryOnExceptionIf { _, cause -> when { cause.isTimeoutException() -> true cause is CancellationException -> false else -> true } } retryIf { _, response -> response.status.value.let { when (it) { 425, 429, 502, 503, 504 -> true else -> false } } } delayMillis { 1000 } } } return response.body() } fun notifyOtpState(id: Int) { CoroutineScope(Dispatchers.IO).launch { runCatching { ktorClient.post(Id.OtpState(Id(RcsNumberApi(), id))) }.onFailure { e -> Log.e(TAG, "Send OtpState Error: ${e.message}", e) } } } fun notifyConfigured(id: Int) { CoroutineScope(Dispatchers.IO).launch { runCatching { ktorClient.post(Id.Configured(Id(RcsNumberApi(), id))) }.onFailure { e -> Log.e(TAG, "Send Configured Error: ${e.message}", e) } } } suspend fun notifyWasted(id: Int) { CoroutineScope(coroutineContext).launch { runCatching { ktorClient.post(Id.Wasted(Id(RcsNumberApi(), id))) }.onFailure { e -> Log.e(TAG, "Send Wasted Error: ${e.message}", e) } } } suspend fun waitForOtp(id: Int): String? { return withTimeoutOrNull(60.seconds) { while (true) { runCatching { val rcsNumber = ktorClient.get(Id(id = id)) .body() Log.i(TAG, "wait for otp response: $rcsNumber") if (rcsNumber.status == RcsNumberResponse.STATUS_SUCCESS) { val match = Regex("Your Messenger verification code is G-(\\d{6})") .find(rcsNumber.message!!) if (match != null) { val otp = match.groupValues[1] Log.i(TAG, "OTP: $otp") return@withTimeoutOrNull otp } } else if (rcsNumber.status == RcsNumberResponse.STATUS_EXPIRED) { return@withTimeoutOrNull null } }.onFailure { e -> Log.e(TAG, "wait for otp Error: ${e.stackTrace}") } delay(2.seconds) } null } } suspend fun updateStockFlag(id: Int, flag: Int) { try { ktorClient.put(Id.UpdateStockFlag(Id(RcsNumberApi(), id), flag)) } catch (e: Exception) { Log.e(TAG, "Update Stock Flag Error: ${e.message}", e) } } } }