RcsNumberApi.kt 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. package com.example.modifier.http.api
  2. import android.util.Log
  3. import com.example.modifier.baseTag
  4. import com.example.modifier.http.isTimeoutException
  5. import com.example.modifier.http.ktorClient
  6. import com.example.modifier.http.request.RcsNumberRequest
  7. import com.example.modifier.http.response.RcsNumberResponse
  8. import io.ktor.client.call.body
  9. import io.ktor.client.plugins.resources.get
  10. import io.ktor.client.plugins.resources.post
  11. import io.ktor.client.plugins.resources.put
  12. import io.ktor.client.plugins.retry
  13. import io.ktor.client.plugins.timeout
  14. import io.ktor.client.request.setBody
  15. import io.ktor.http.ContentType
  16. import io.ktor.http.contentType
  17. import io.ktor.resources.Resource
  18. import kotlinx.coroutines.CoroutineScope
  19. import kotlinx.coroutines.Dispatchers
  20. import kotlinx.coroutines.coroutineScope
  21. import kotlinx.coroutines.delay
  22. import kotlinx.coroutines.launch
  23. import kotlinx.coroutines.withTimeoutOrNull
  24. import kotlin.coroutines.cancellation.CancellationException
  25. import kotlin.coroutines.coroutineContext
  26. import kotlin.time.Duration.Companion.seconds
  27. @Resource("api/rcs-number")
  28. class RcsNumberApi() {
  29. @Resource("{id}")
  30. class Id(val parent: RcsNumberApi = RcsNumberApi(), val id: Int) {
  31. @Resource("delete")
  32. class Delete(val parent: Id)
  33. @Resource("otpState")
  34. class OtpState(val parent: Id)
  35. @Resource("configured")
  36. class Configured(val parent: Id)
  37. @Resource("wasted")
  38. class Wasted(val parent: Id)
  39. @Resource("stockFlag/{flag}")
  40. class UpdateStockFlag(val parent: Id, val flag: Int)
  41. }
  42. companion object {
  43. private const val TAG = "$baseTag/RcsNumberApi"
  44. suspend fun getRcsNumber(
  45. deviceId: String,
  46. taskId: Int? = null,
  47. pinCountry: String?,
  48. store: Boolean? = false
  49. ): RcsNumberResponse {
  50. val req = RcsNumberRequest(
  51. deviceId = deviceId,
  52. taskId = taskId,
  53. country = pinCountry,
  54. store = store
  55. )
  56. val response = ktorClient.put(RcsNumberApi()) {
  57. contentType(ContentType.Application.Json)
  58. setBody(req)
  59. timeout {
  60. requestTimeoutMillis = 60 * 1000
  61. socketTimeoutMillis = 60 * 1000
  62. }
  63. retry {
  64. maxRetries = 3600
  65. retryOnExceptionIf { _, cause ->
  66. when {
  67. cause.isTimeoutException() -> true
  68. cause is CancellationException -> false
  69. else -> true
  70. }
  71. }
  72. retryIf { _, response ->
  73. response.status.value.let {
  74. when (it) {
  75. 425, 429, 502, 503, 504 -> true
  76. else -> false
  77. }
  78. }
  79. }
  80. delayMillis { 1000 }
  81. }
  82. }
  83. return response.body<RcsNumberResponse>()
  84. }
  85. fun notifyOtpState(id: Int) {
  86. CoroutineScope(Dispatchers.IO).launch {
  87. runCatching {
  88. ktorClient.post(Id.OtpState(Id(RcsNumberApi(), id)))
  89. }.onFailure { e ->
  90. Log.e(TAG, "Send OtpState Error: ${e.message}", e)
  91. }
  92. }
  93. }
  94. fun notifyConfigured(id: Int) {
  95. CoroutineScope(Dispatchers.IO).launch {
  96. runCatching {
  97. ktorClient.post(Id.Configured(Id(RcsNumberApi(), id)))
  98. }.onFailure { e ->
  99. Log.e(TAG, "Send Configured Error: ${e.message}", e)
  100. }
  101. }
  102. }
  103. suspend fun notifyWasted(id: Int) {
  104. CoroutineScope(coroutineContext).launch {
  105. runCatching {
  106. ktorClient.post(Id.Wasted(Id(RcsNumberApi(), id)))
  107. }.onFailure { e ->
  108. Log.e(TAG, "Send Wasted Error: ${e.message}", e)
  109. }
  110. }
  111. }
  112. suspend fun waitForOtp(id: Int): String? {
  113. return withTimeoutOrNull(60.seconds) {
  114. while (true) {
  115. runCatching {
  116. val rcsNumber =
  117. ktorClient.get(Id(id = id))
  118. .body<RcsNumberResponse>()
  119. Log.i(TAG, "wait for otp response: $rcsNumber")
  120. if (rcsNumber.status == RcsNumberResponse.STATUS_SUCCESS) {
  121. val match = Regex("Your Messenger verification code is G-(\\d{6})")
  122. .find(rcsNumber.message!!)
  123. if (match != null) {
  124. val otp = match.groupValues[1]
  125. Log.i(TAG, "OTP: $otp")
  126. return@withTimeoutOrNull otp
  127. }
  128. } else if (rcsNumber.status == RcsNumberResponse.STATUS_EXPIRED) {
  129. return@withTimeoutOrNull null
  130. }
  131. }.onFailure { e ->
  132. Log.e(TAG, "wait for otp Error: ${e.stackTrace}")
  133. }
  134. delay(2.seconds)
  135. }
  136. null
  137. }
  138. }
  139. suspend fun updateStockFlag(id: Int, flag: Int) {
  140. try {
  141. ktorClient.put(Id.UpdateStockFlag(Id(RcsNumberApi(), id), flag))
  142. } catch (e: Exception) {
  143. Log.e(TAG, "Update Stock Flag Error: ${e.message}", e)
  144. }
  145. }
  146. }
  147. }