diff --git a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/TelegramBot.java b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/TelegramBot.java index 39c76973e..8e4409879 100644 --- a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/TelegramBot.java +++ b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/TelegramBot.java @@ -27,7 +27,7 @@ public class TelegramBot { @Setter private User botInfo; - TelegramBot(TelegramBotRegistry registry, String apiKey) { + protected TelegramBot(TelegramBotRegistry registry, String apiKey) { this.registry = registry; this.apiKey = apiKey; this.eventRegistry = new EventRegistry(this); diff --git a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/commands/CommandRegistry.java b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/commands/CommandRegistry.java index 4029a3613..16fcee01d 100644 --- a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/commands/CommandRegistry.java +++ b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/commands/CommandRegistry.java @@ -9,11 +9,13 @@ import com.jtelegram.api.message.entity.MessageEntity; import com.jtelegram.api.message.entity.MessageEntityType; import com.jtelegram.api.message.impl.TextMessage; +import lombok.Getter; import java.util.*; import java.util.stream.Collectors; public class CommandRegistry implements EventHandler { + @Getter private final TelegramBot bot; private final List listeners = new ArrayList<>(); @@ -61,9 +63,7 @@ public void onEvent(TextMessageEvent event) { ); Command command = new Command(baseCommand, mentioned, argsList, message); - long handled = listeners.stream() - .filter(e -> e.test(event, command)) - .count(); + listeners.forEach(e -> e.test(event, command)); // Log number of handlers that used the command? } } diff --git a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/events/EventHandler.java b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/events/EventHandler.java index 8b2c3c21a..e53f6c223 100644 --- a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/events/EventHandler.java +++ b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/events/EventHandler.java @@ -1,6 +1,5 @@ package com.jtelegram.api.events; public interface EventHandler { - void onEvent(E event); } diff --git a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/events/EventRegistry.java b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/events/EventRegistry.java index 499c4945c..0d298a5c1 100644 --- a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/events/EventRegistry.java +++ b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/events/EventRegistry.java @@ -4,6 +4,7 @@ import com.jtelegram.api.events.inline.ChosenInlineResultEvent; import com.jtelegram.api.requests.inline.AnswerInlineQuery; import com.jtelegram.api.util.ExceptionThreadFactory; +import lombok.Getter; import java.util.ArrayList; import java.util.List; @@ -20,7 +21,9 @@ */ public class EventRegistry { private final ThreadFactory factory = new ExceptionThreadFactory(); + @Getter private final TelegramBot bot; + @Getter private final ExecutorService threadPool; private Map, List>> handlers = new ConcurrentHashMap<>(); diff --git a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/framework/AbstractTelegramRequest.java b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/framework/AbstractTelegramRequest.java index a8bd1df3f..4e69f9c09 100644 --- a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/framework/AbstractTelegramRequest.java +++ b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/framework/AbstractTelegramRequest.java @@ -9,6 +9,7 @@ import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; +import lombok.Setter; import okhttp3.Response; import java.io.IOException; @@ -20,7 +21,8 @@ public abstract class AbstractTelegramRequest implements TelegramRequest { // utility field protected transient static Gson gson = TelegramBotRegistry.GSON; private transient final String endPoint; - protected transient final Consumer errorHandler; + @Setter + protected transient Consumer errorHandler; protected void handleError(TelegramException ex) { if (errorHandler == null) { diff --git a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/framework/QueryTelegramRequest.java b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/framework/QueryTelegramRequest.java index 5107fb173..dd4c54417 100644 --- a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/framework/QueryTelegramRequest.java +++ b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/framework/QueryTelegramRequest.java @@ -3,6 +3,7 @@ import com.google.gson.JsonElement; import com.jtelegram.api.ex.TelegramException; import lombok.EqualsAndHashCode; +import lombok.Setter; import okhttp3.Response; import java.io.IOException; @@ -14,7 +15,8 @@ */ @EqualsAndHashCode(callSuper = true) public abstract class QueryTelegramRequest extends AbstractTelegramRequest { - private transient final Consumer callback; + @Setter + private Consumer callback; private transient final Class callbackType; protected QueryTelegramRequest(String endPoint, Class callbackType, Consumer callback, Consumer errorHandler) { diff --git a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/framework/UpdateTelegramRequest.java b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/framework/UpdateTelegramRequest.java index d28b72016..554de25f9 100644 --- a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/framework/UpdateTelegramRequest.java +++ b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/framework/UpdateTelegramRequest.java @@ -1,6 +1,7 @@ package com.jtelegram.api.requests.framework; import com.jtelegram.api.ex.TelegramException; +import lombok.Setter; import okhttp3.Response; import java.io.IOException; @@ -14,6 +15,7 @@ * */ public abstract class UpdateTelegramRequest extends AbstractTelegramRequest { + @Setter protected transient Runnable callback; protected UpdateTelegramRequest(String endPoint, Consumer errorHandler, Runnable callback) { diff --git a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/message/framework/req/SendableChatRequest.java b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/message/framework/req/SendableChatRequest.java index 19c402787..acb8545da 100644 --- a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/message/framework/req/SendableChatRequest.java +++ b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/message/framework/req/SendableChatRequest.java @@ -5,13 +5,15 @@ import com.jtelegram.api.requests.framework.QueryTelegramRequest; import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.Setter; import java.util.function.Consumer; @Getter @EqualsAndHashCode(callSuper = true) public class SendableChatRequest extends QueryTelegramRequest { - private final ChatId chatId; + @Setter + private ChatId chatId; protected SendableChatRequest(String endPoint, Class callbackType, Consumer callback, Consumer errorHandler, ChatId chatId) { super(endPoint, callbackType, callback, errorHandler); diff --git a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/message/framework/req/SendableMessageRequest.java b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/message/framework/req/SendableMessageRequest.java index 0dfcdb326..72171bf69 100644 --- a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/message/framework/req/SendableMessageRequest.java +++ b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/requests/message/framework/req/SendableMessageRequest.java @@ -4,12 +4,14 @@ import com.jtelegram.api.ex.TelegramException; import com.jtelegram.api.requests.message.framework.ReplyMarkup; import lombok.Getter; +import lombok.Setter; import java.util.function.Consumer; @Getter public abstract class SendableMessageRequest extends SendableChatRequest { - private final Integer replyToMessageId; + @Setter + private Integer replyToMessageId; private final Boolean disableNotification; private final ReplyMarkup replyMarkup; @@ -20,7 +22,6 @@ protected SendableMessageRequest(String endPoint, Class callbackType, Consume this.replyMarkup = replyMarkup; } - @Override protected boolean isValid() { return super.isValid(); diff --git a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/util/TextBuilder.java b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/util/TextBuilder.java index 89f6d9728..a4d1774b1 100644 --- a/jtelegrambotapi-core/src/main/java/com/jtelegram/api/util/TextBuilder.java +++ b/jtelegrambotapi-core/src/main/java/com/jtelegram/api/util/TextBuilder.java @@ -28,7 +28,7 @@ public static TextBuilder create() { return new TextBuilder(); } - private TextBuilder() { + protected TextBuilder() { } private String htmlEscaped(String text) { @@ -85,6 +85,14 @@ public TextBuilder newLine() { return this; } + public TextBuilder newLines(int n) { + for (int i = 0; i < n; i++) { + newLine(); + } + + return this; + } + public TextBuilder nextLine() { return newLine(); } diff --git a/ktelegrambotapi/pom.xml b/ktelegrambotapi/pom.xml new file mode 100644 index 000000000..2c638f099 --- /dev/null +++ b/ktelegrambotapi/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + + com.jtelegram + jtelegrambotapi + 4.0.10 + + + + 1.3.72 + + + ktelegrambotapi + jTelegramBotAPI as Kotlin (AKA kTelegramBotAPI) + + + + com.jtelegram + jtelegrambotapi-core + ${project.version} + compile + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + provided + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + 1.3.7 + provided + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + + 1.8 + + + + + + + diff --git a/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/BotContext.kt b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/BotContext.kt new file mode 100644 index 000000000..750c4422f --- /dev/null +++ b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/BotContext.kt @@ -0,0 +1,57 @@ +package com.jtelegram.api.kotlin + +import com.jtelegram.api.chat.Chat +import com.jtelegram.api.chat.id.ChatId +import com.jtelegram.api.message.Message +import com.jtelegram.api.message.impl.TextMessage +import com.jtelegram.api.requests.message.framework.ParseMode +import com.jtelegram.api.requests.message.framework.req.SendableChatRequest +import com.jtelegram.api.requests.message.framework.req.SendableMessageRequest +import com.jtelegram.api.requests.message.send.SendText +import com.jtelegram.api.user.User + +class BotContext(val bot: KTelegramBot) { + private suspend fun sendText(chatId: ChatId, text: String, parseMode: ParseMode): TextMessage { + return bot.execute ( + SendText.builder() + .chatId(chatId) + .text(text) + .parseMode(parseMode) + .build() + ) + } + + suspend fun Chat.sendText(text: String, parseMode: ParseMode = ParseMode.MARKDOWN): TextMessage { + return sendText(chatId, text, parseMode) + } + + suspend fun > Chat.sendMessage(request: SendableMessageRequest): S { + return sendAction(request) + } + + suspend fun Chat.sendAction(request: SendableChatRequest): S { + val chatIdO = chatId + + return bot.execute ( + request.apply { + chatId = chatIdO + } + ) + } + + suspend fun User.sendText(text: String, parseMode: ParseMode = ParseMode.MARKDOWN): TextMessage { + return sendText(ChatId.of(id), text, parseMode) + } + + suspend fun > User.sendMessage(request: SendableMessageRequest): S { + return sendAction(request) + } + + suspend fun User.sendAction(request: SendableChatRequest): S { + return bot.execute ( + request.apply { + chatId = ChatId.of(id) + } + ) + } +} \ No newline at end of file diff --git a/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/KTelegramBot.kt b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/KTelegramBot.kt new file mode 100644 index 000000000..e00fbdad2 --- /dev/null +++ b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/KTelegramBot.kt @@ -0,0 +1,10 @@ +package com.jtelegram.api.kotlin + +import com.jtelegram.api.TelegramBot +import com.jtelegram.api.TelegramBotRegistry +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.asCoroutineDispatcher + +class KTelegramBot constructor(registry: TelegramBotRegistry?, apiKey: String?) : TelegramBot(registry, apiKey) { + val coroutineScope = CoroutineScope(eventRegistry.threadPool.asCoroutineDispatcher()) +} \ No newline at end of file diff --git a/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/TelegramBot.kt b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/TelegramBot.kt new file mode 100644 index 000000000..f7fe096ce --- /dev/null +++ b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/TelegramBot.kt @@ -0,0 +1,36 @@ +package com.jtelegram.api.kotlin + +import com.jtelegram.api.TelegramBot +import com.jtelegram.api.requests.framework.AbstractTelegramRequest +import com.jtelegram.api.requests.framework.QueryTelegramRequest +import com.jtelegram.api.requests.framework.UpdateTelegramRequest +import kotlin.coroutines.Continuation +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +suspend fun TelegramBot.execute(request: QueryTelegramRequest): T = suspendCoroutine { cont -> + request.useContinuationForErrors(cont) + + request.setCallback { + cont.resume(it) + } + + perform(request) +} + +suspend fun TelegramBot.execute(request: UpdateTelegramRequest) = suspendCoroutine { cont -> + request.useContinuationForErrors(cont) + + request.setCallback { + cont.resume(null) + } + + perform(request) +} + +private fun AbstractTelegramRequest.useContinuationForErrors(cont: Continuation) { + setErrorHandler { + cont.resumeWithException(it) + } +} \ No newline at end of file diff --git a/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/TelegramBotRegistry.kt b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/TelegramBotRegistry.kt new file mode 100644 index 000000000..c6b054b93 --- /dev/null +++ b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/TelegramBotRegistry.kt @@ -0,0 +1,15 @@ +package com.jtelegram.api.kotlin + +import com.jtelegram.api.TelegramBotRegistry +import com.jtelegram.api.requests.GetMe + +suspend fun TelegramBotRegistry.registerBot(key: String): KTelegramBot { + val bot = KTelegramBot(this, key) + + bot.botInfo = bot.execute(GetMe.builder().build()) + + bots.add(bot) + updateProvider.listenFor(bot) + + return bot +} diff --git a/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/commands/CommandRegistry.kt b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/commands/CommandRegistry.kt new file mode 100644 index 000000000..14de06461 --- /dev/null +++ b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/commands/CommandRegistry.kt @@ -0,0 +1,22 @@ +package com.jtelegram.api.kotlin.commands + +import com.jtelegram.api.commands.Command +import com.jtelegram.api.commands.filters.CommandFilter +import com.jtelegram.api.events.message.TextMessageEvent +import com.jtelegram.api.kotlin.BotContext +import com.jtelegram.api.kotlin.KTelegramBot +import kotlinx.coroutines.launch + +fun suspendCommand(filter: suspend BotContext.(TextMessageEvent, Command) -> Unit) = CommandFilter { event, command -> + val bot = event.bot + + if (bot !is KTelegramBot) { + throw IllegalStateException("Suspending command filters can only be used with KTelegramBots!") + } + + bot.coroutineScope.launch { + filter.invoke(BotContext(bot), event, command) + } + + true +} diff --git a/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/events/EventRegistry.kt b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/events/EventRegistry.kt new file mode 100644 index 000000000..66c5bfe52 --- /dev/null +++ b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/events/EventRegistry.kt @@ -0,0 +1,24 @@ +package com.jtelegram.api.kotlin.events + +import com.jtelegram.api.events.Event +import com.jtelegram.api.events.EventRegistry +import com.jtelegram.api.kotlin.BotContext +import com.jtelegram.api.kotlin.KTelegramBot +import kotlinx.coroutines.launch +import kotlin.reflect.KClass + +typealias KEventListener = suspend BotContext.(E) -> Unit + +fun EventRegistry.on(eventType: KClass, listener: KEventListener) { + val bot = this.bot + + if (bot !is KTelegramBot) { + throw IllegalStateException("Suspending listeners can only be used with KTelegramBots!") + } + + registerEvent(eventType.java) { event -> + bot.coroutineScope.launch { + listener.invoke(BotContext(bot), event) + } + } +} diff --git a/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/events/inline/InlineQueryEvent.kt b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/events/inline/InlineQueryEvent.kt new file mode 100644 index 000000000..be39a9168 --- /dev/null +++ b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/events/inline/InlineQueryEvent.kt @@ -0,0 +1,27 @@ +package com.jtelegram.api.kotlin.events.inline + +import com.jtelegram.api.events.inline.InlineQueryEvent +import com.jtelegram.api.inline.result.framework.InlineResult +import com.jtelegram.api.kotlin.execute +import com.jtelegram.api.requests.inline.AnswerInlineQuery + +suspend fun InlineQueryEvent.answer ( + results: List, + cacheTime: Int? = null, + isPersonal: Boolean? = null, + nextOffset: String? = null, + switchPmText: String? = null, + switchPmParameter: String? = null +) { + bot.execute ( + AnswerInlineQuery.builder() + .queryId(query.id) + .results(results) + .cacheTime(cacheTime) + .isPersonal(isPersonal) + .nextOffset(nextOffset) + .switchPmText(switchPmText) + .switchPmParameter(switchPmParameter) + .build() + ) +} \ No newline at end of file diff --git a/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/events/message/MessageEvent.kt b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/events/message/MessageEvent.kt new file mode 100644 index 000000000..9eef91070 --- /dev/null +++ b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/events/message/MessageEvent.kt @@ -0,0 +1,55 @@ +package com.jtelegram.api.kotlin.events.message + +import com.jtelegram.api.events.message.MessageEvent +import com.jtelegram.api.kotlin.execute +import com.jtelegram.api.message.Message +import com.jtelegram.api.message.impl.TextMessage +import com.jtelegram.api.requests.message.framework.ParseMode +import com.jtelegram.api.requests.message.framework.req.SendableChatRequest +import com.jtelegram.api.requests.message.framework.req.SendableMessageRequest +import com.jtelegram.api.requests.message.send.SendText +import com.jtelegram.api.util.TextBuilder + +suspend fun > MessageEvent.replyWith( + text: String, + replyToMessage: Boolean = false, + parseMode: ParseMode = ParseMode.MARKDOWN +): TextMessage { + return replyWithMessage ( + SendText.builder() + .text(text) + .parseMode(parseMode) + .build(), + replyToMessage + ) +} + +suspend fun > MessageEvent.replyWith( + text: TextBuilder, + replyToMessage: Boolean = false +): TextMessage { + return replyWith(text.toHtml(), replyToMessage, ParseMode.HTML) +} + +suspend fun , ST, S: Message> MessageEvent.replyWithMessage( + request: SendableMessageRequest, + replyToMessage: Boolean = false +): S { + return replyWithAction ( + request.apply { + if (replyToMessage) { + replyToMessageId = message.messageId + } + } + ) +} + +suspend fun , S> MessageEvent.replyWithAction( + request: SendableChatRequest +): S { + return bot.execute ( + request.apply { + chatId = message.chat.chatId + } + ) +} diff --git a/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/util/TextBuilder.kt b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/util/TextBuilder.kt new file mode 100644 index 000000000..5600f8b8a --- /dev/null +++ b/ktelegrambotapi/src/main/kotlin/com/jtelegram/api/kotlin/util/TextBuilder.kt @@ -0,0 +1,15 @@ +package com.jtelegram.api.kotlin.util + +import com.jtelegram.api.util.TextBuilder + +fun textBuilder(init: KTextBuilder.() -> Unit): KTextBuilder { + val builder = KTextBuilder() + builder.init() + return builder +} + +class KTextBuilder: TextBuilder() { + operator fun String.unaryPlus() { + plain(this) + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 19501598f..3abdd34ea 100644 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,7 @@ jtelegrambotapi-menus jtelegrambotapi-test jtelegrambotapi-webhooks + ktelegrambotapi