Task: Completed telegram and clean functions and add Twilio SMS sender provider and add provider for message sender and fixed config and add twilio config too for global envs and props
This commit is contained in:
parent
122db91ecb
commit
9617e34f73
@ -6,7 +6,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "com.cubetiqs"
|
group = "com.cubetiqs"
|
||||||
version = "0.0.1-SNAPSHOT"
|
version = "0.0.1"
|
||||||
java.sourceCompatibility = JavaVersion.VERSION_1_8
|
java.sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@ -14,17 +14,17 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
|
||||||
|
|
||||||
implementation("com.squareup.okhttp3:okhttp:4.9.0")
|
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
|
|
||||||
// logback driver and slf4j logging
|
// http client
|
||||||
implementation("ch.qos.logback:logback-core:1.3.0-alpha5")
|
implementation("com.squareup.okhttp3:okhttp:4.9.0")
|
||||||
implementation("org.slf4j:slf4j-api:2.0.0-alpha1")
|
|
||||||
|
|
||||||
|
// logback driver and slf4j logging
|
||||||
|
implementation("org.slf4j:slf4j-api:1.7.30")
|
||||||
|
implementation("org.slf4j:slf4j-simple:1.7.30")
|
||||||
|
|
||||||
|
// test framework
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter:5.7.0")
|
testImplementation("org.junit.jupiter:junit-jupiter:5.7.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.cubetiqs.messaging.client.email
|
||||||
|
|
||||||
|
import com.cubetiqs.messaging.client.provider.MessageProvider
|
||||||
|
|
||||||
|
interface IEmailProvider : MessageProvider
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.cubetiqs.messaging.client.provider
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message Sender
|
||||||
|
*
|
||||||
|
* @author sombochea
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class MessageSender (provider: MessageProvider? = null) {
|
||||||
|
private var provider: MessageProvider? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.provider = provider
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setProvider(provider: MessageProvider) = apply {
|
||||||
|
this.provider = provider
|
||||||
|
}
|
||||||
|
|
||||||
|
fun send() = provider?.send()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun send(provider: MessageProvider): Any? {
|
||||||
|
return MessageSender(provider).send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.cubetiqs.messaging.client.sms
|
||||||
|
|
||||||
|
import com.cubetiqs.messaging.client.provider.MessageProvider
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sms Provider
|
||||||
|
*
|
||||||
|
* @author sombochea
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface ISmsProvider : MessageProvider
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.cubetiqs.messaging.client.sms
|
||||||
|
|
||||||
|
data class SmsMessage (
|
||||||
|
var to: String,
|
||||||
|
var sender: String,
|
||||||
|
var text: String,
|
||||||
|
// if this true, meant the sender is phone number (from)
|
||||||
|
// else is the message service id for send the message
|
||||||
|
var isSenderNumber: Boolean = false,
|
||||||
|
) {
|
||||||
|
class SmsMessageBuilder {
|
||||||
|
private var to: String? = null
|
||||||
|
private var sender: String? = null
|
||||||
|
private var text: String? = null
|
||||||
|
private var isSenderNumber: Boolean? = null
|
||||||
|
|
||||||
|
fun setTo(to: String) = apply { this.to = to }
|
||||||
|
fun setSender(sender: String) = apply { this.sender = sender }
|
||||||
|
fun setText(text: String) = apply { this.text = text }
|
||||||
|
fun isSenderNumber(isSenderNumber: Boolean) = apply { this.isSenderNumber = isSenderNumber }
|
||||||
|
|
||||||
|
fun build(): SmsMessage {
|
||||||
|
return SmsMessage(
|
||||||
|
to = to ?: throw IllegalArgumentException("Receiver is required!"),
|
||||||
|
sender = sender ?: throw IllegalArgumentException("Sender is required!"),
|
||||||
|
text = text ?: throw IllegalArgumentException("Message is required!"),
|
||||||
|
isSenderNumber = isSenderNumber ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun builder() = SmsMessageBuilder()
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,23 @@
|
|||||||
package com.cubetiqs.messaging.client.sms
|
package com.cubetiqs.messaging.client.sms
|
||||||
|
|
||||||
import com.cubetiqs.messaging.client.provider.MessageProvider
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sms Provider
|
* SMS Provider
|
||||||
*
|
*
|
||||||
* @author sombochea
|
* @author sombochea
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
interface SmsProvider : MessageProvider {
|
abstract class SmsProvider : ISmsProvider {
|
||||||
|
private var to: String? = null
|
||||||
|
private var text: String? = null
|
||||||
|
|
||||||
|
fun getToNumber(): String = to?.trim() ?: throw IllegalArgumentException("Sms receiver is required!")
|
||||||
|
fun getText(): String = text ?: throw IllegalArgumentException("Sms content is required!")
|
||||||
|
|
||||||
|
fun setText(text: String?) = apply {
|
||||||
|
this.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setToNumber(toNumber: String?) = apply {
|
||||||
|
this.to = toNumber
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.cubetiqs.messaging.client.sms
|
||||||
|
|
||||||
|
open class SmsSendException : RuntimeException {
|
||||||
|
constructor(message: String? = "Sms send occurred!") : super(message)
|
||||||
|
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||||
|
constructor(cause: Throwable?) : super(cause)
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.cubetiqs.messaging.client.sms
|
||||||
|
|
||||||
|
class SmsSimulatorProvider : SmsProvider() {
|
||||||
|
private fun getFormatMessage() = """
|
||||||
|
To Number: ${getToNumber()}
|
||||||
|
Message: ${getText()}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
override fun send(): Any {
|
||||||
|
return getFormatMessage()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.cubetiqs.messaging.client.sms.twlio
|
||||||
|
|
||||||
|
import com.cubetiqs.messaging.client.util.ConfigUtils
|
||||||
|
|
||||||
|
object TwilioConfig {
|
||||||
|
const val ENDPOINT = "https://api.twilio.com/2010-04-01/Accounts"
|
||||||
|
private const val CUBETIQ_TWILIO_TOKEN = "CUBETIQ_TWILIO_TOKEN"
|
||||||
|
private const val CUBETIQ_TWILIO_ID = "CUBETIQ_TWILIO_ID"
|
||||||
|
private const val CUBETIQ_TWILIO_SENDER = "CUBETIQ_TWILIO_SENDER"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getAccountToken(): String {
|
||||||
|
return ConfigUtils.getEnv(CUBETIQ_TWILIO_TOKEN, ConfigUtils.getProperty(CUBETIQ_TWILIO_TOKEN)) ?: throw NullPointerException("CUBETIQ_TWILIO_TOKEN is required!")
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getAccountId(): String {
|
||||||
|
return ConfigUtils.getEnv(CUBETIQ_TWILIO_ID, ConfigUtils.getProperty(CUBETIQ_TWILIO_ID)) ?: throw NullPointerException("CUBETIQ_TWILIO_ID is required!")
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getAccountSender(): String {
|
||||||
|
return ConfigUtils.getEnv(CUBETIQ_TWILIO_SENDER, ConfigUtils.getProperty(CUBETIQ_TWILIO_SENDER)) ?: throw NullPointerException("CUBETIQ_TWILIO_SENDER is required for SMS Sender!")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.cubetiqs.messaging.client.sms.twlio
|
||||||
|
|
||||||
|
import com.cubetiqs.messaging.client.sms.SmsMessage
|
||||||
|
import com.cubetiqs.messaging.client.sms.SmsProvider
|
||||||
|
|
||||||
|
open class TwilioProvider (private val message: SmsMessage) : SmsProvider() {
|
||||||
|
override fun send(): Any? {
|
||||||
|
return TwilioUtils.sendMessage(message)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package com.cubetiqs.messaging.client.sms.twlio
|
||||||
|
|
||||||
|
import com.cubetiqs.messaging.client.sms.SmsMessage
|
||||||
|
import com.cubetiqs.messaging.client.sms.SmsSendException
|
||||||
|
import com.cubetiqs.messaging.client.sms.twlio.TwilioConfig.ENDPOINT
|
||||||
|
import com.cubetiqs.messaging.client.util.Loggable
|
||||||
|
import com.cubetiqs.messaging.client.webclient.WebClientUtils
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
object TwilioUtils : Loggable {
|
||||||
|
private val limiters = mutableMapOf<String, AtomicInteger>()
|
||||||
|
// able to send the sms
|
||||||
|
private var capacity = 3
|
||||||
|
|
||||||
|
private fun increaseSentCount(key: String): Int {
|
||||||
|
return if (limiters.containsKey(key)) {
|
||||||
|
limiters[key]!!.incrementAndGet()
|
||||||
|
} else {
|
||||||
|
limiters[key] = AtomicInteger(1)
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
// reset statistic for key of sms
|
||||||
|
fun resetCounter(key: String) {
|
||||||
|
if (limiters.containsKey(key)) {
|
||||||
|
limiters[key]!!.setRelease(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
// reset all statistic
|
||||||
|
fun releaseAllCounter() = limiters.clear()
|
||||||
|
|
||||||
|
private var accountId: String? = null
|
||||||
|
private var accountToken: String? = null
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun init(accountId: String, accountToken: String, capacity: Int = 3) {
|
||||||
|
log.info("Twilio initializing Account ID: {} and Account Token: {}", accountId, accountToken)
|
||||||
|
|
||||||
|
TwilioUtils.accountId = accountId
|
||||||
|
TwilioUtils.accountToken = accountToken
|
||||||
|
TwilioUtils.capacity = capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAccountId(): String {
|
||||||
|
return this.accountId ?: TwilioConfig.getAccountId()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAccountToken(): String {
|
||||||
|
return this.accountToken ?: TwilioConfig.getAccountToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun capacity(capacity: Int) = apply { TwilioUtils.capacity = capacity }
|
||||||
|
|
||||||
|
private fun getEndpointMessage(): String {
|
||||||
|
if (getAccountId().isEmpty() && getAccountToken().isEmpty()) throw IllegalArgumentException("account id and token must be not empty!")
|
||||||
|
return "$ENDPOINT/$accountId/Messages.json"
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun sendMessage(message: SmsMessage): Any {
|
||||||
|
if (message.to.isEmpty() || message.to.isBlank()) throw IllegalArgumentException("message send to must be not empty or blank!")
|
||||||
|
if (message.sender.isEmpty() || message.sender.isBlank()) throw IllegalArgumentException("message sender must be not empty or blank!")
|
||||||
|
if (message.text.isEmpty() || message.text.isBlank()) throw IllegalArgumentException("message must be not empty or blank!")
|
||||||
|
|
||||||
|
if (increaseSentCount(message.to) > capacity) {
|
||||||
|
throw SmsSendException("send capacity out of bound!")
|
||||||
|
}
|
||||||
|
|
||||||
|
val body: MutableMap<String, String> = mutableMapOf()
|
||||||
|
body["To"] = message.to
|
||||||
|
body["Body"] = message.text
|
||||||
|
|
||||||
|
if (message.isSenderNumber) {
|
||||||
|
body["From"] = message.sender
|
||||||
|
} else {
|
||||||
|
body["MessagingServiceSid"] = message.sender
|
||||||
|
}
|
||||||
|
|
||||||
|
val url = getEndpointMessage()
|
||||||
|
val result = WebClientUtils.postRequest(url, body)
|
||||||
|
|
||||||
|
log.info("Twilio Complete sent message via: {}", message)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun sendMessage(sendTo: String, message: String): Any {
|
||||||
|
val request = SmsMessage.builder()
|
||||||
|
.setText(message)
|
||||||
|
.setSender(TwilioConfig.getAccountSender())
|
||||||
|
.setTo(sendTo)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return sendMessage(request)
|
||||||
|
}
|
||||||
|
}
|
@ -15,13 +15,13 @@ object TelegramBotUtils : Loggable {
|
|||||||
private fun makeRequest(
|
private fun makeRequest(
|
||||||
request: Request,
|
request: Request,
|
||||||
): Response? {
|
): Response? {
|
||||||
log.debug("Start send message via telegram bot...")
|
log.info("Start send message via telegram bot...")
|
||||||
return try {
|
return try {
|
||||||
WebClientUtils.makeRequest(request)
|
WebClientUtils.makeRequest(request)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
log.error("Telegram make request error {}", ex.message)
|
log.error("Telegram make request error {}", ex.message)
|
||||||
null
|
throw TelegramSendException(ex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ object TelegramBotUtils : Loggable {
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
val result = makeRequest(request)
|
val result = makeRequest(request)
|
||||||
log.debug("Telegram complete sent message to {}", chatId)
|
log.info("Telegram complete sent message to {}", chatId)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ object TelegramBotUtils : Loggable {
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
val result = makeRequest(request)
|
val result = makeRequest(request)
|
||||||
log.debug("Telegram complete sent message to {}", chatId)
|
log.info("Telegram complete sent message to {}", chatId)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.cubetiqs.messaging.client.telegram
|
||||||
|
|
||||||
|
open class TelegramSendException : RuntimeException {
|
||||||
|
constructor(message: String = "Telegram send occurred!") : super(message)
|
||||||
|
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||||
|
constructor(cause: Throwable?) : super(cause)
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package com.cubetiqs.messaging.client.webclient
|
package com.cubetiqs.messaging.client.webclient
|
||||||
|
|
||||||
import com.cubetiqs.messaging.client.util.Loggable
|
import com.cubetiqs.messaging.client.util.Loggable
|
||||||
|
import okhttp3.MultipartBody
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
@ -18,7 +19,7 @@ object WebClientUtils : Loggable {
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun makeRequest(request: Request): Response {
|
fun makeRequest(request: Request): Response {
|
||||||
log.debug("Web is make request to: {} with method: {}", request.url, request.method)
|
log.info("Web is make request to: {} with method: {}", request.url, request.method)
|
||||||
val call = getClient().newCall(request)
|
val call = getClient().newCall(request)
|
||||||
var response: Response? = null
|
var response: Response? = null
|
||||||
return try {
|
return try {
|
||||||
@ -30,4 +31,20 @@ object WebClientUtils : Loggable {
|
|||||||
response?.close()
|
response?.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun postRequest(url: String, params: Map<String, String>): Response {
|
||||||
|
val requestBody = MultipartBody.Builder()
|
||||||
|
.setType(MultipartBody.FORM)
|
||||||
|
params.forEach {
|
||||||
|
requestBody.addFormDataPart(it.key, it.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.post(requestBody.build())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return makeRequest(request)
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user