Skip to content

Commit

Permalink
Add kTelegram for Kotlin Support (#53)
Browse files Browse the repository at this point in the history
* Add kTelegram as a Kotlin extension

* Add appropriate dependency scopes

* Add typealias for ease of use
  • Loading branch information
mkotb authored Aug 28, 2020
1 parent 1c8a087 commit d714010
Show file tree
Hide file tree
Showing 21 changed files with 359 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<TextMessageEvent> {
@Getter
private final TelegramBot bot;
private final List<CommandFilter> listeners = new ArrayList<>();

Expand Down Expand Up @@ -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?
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.jtelegram.api.events;

public interface EventHandler<E extends Event> {

void onEvent(E event);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Class<? extends Event>, List<EventHandler<? extends Event>>> handlers = new ConcurrentHashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import okhttp3.Response;

import java.io.IOException;
Expand All @@ -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<TelegramException> errorHandler;
@Setter
protected transient Consumer<TelegramException> errorHandler;

protected void handleError(TelegramException ex) {
if (errorHandler == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -14,7 +15,8 @@
*/
@EqualsAndHashCode(callSuper = true)
public abstract class QueryTelegramRequest<T> extends AbstractTelegramRequest {
private transient final Consumer<T> callback;
@Setter
private Consumer<T> callback;
private transient final Class<T> callbackType;

protected QueryTelegramRequest(String endPoint, Class<T> callbackType, Consumer<T> callback, Consumer<TelegramException> errorHandler) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,6 +15,7 @@
*
*/
public abstract class UpdateTelegramRequest extends AbstractTelegramRequest {
@Setter
protected transient Runnable callback;

protected UpdateTelegramRequest(String endPoint, Consumer<TelegramException> errorHandler, Runnable callback) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> extends QueryTelegramRequest<T> {
private final ChatId chatId;
@Setter
private ChatId chatId;

protected SendableChatRequest(String endPoint, Class<T> callbackType, Consumer<T> callback, Consumer<TelegramException> errorHandler, ChatId chatId) {
super(endPoint, callbackType, callback, errorHandler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> extends SendableChatRequest<T> {
private final Integer replyToMessageId;
@Setter
private Integer replyToMessageId;
private final Boolean disableNotification;
private final ReplyMarkup replyMarkup;

Expand All @@ -20,7 +22,6 @@ protected SendableMessageRequest(String endPoint, Class<T> callbackType, Consume
this.replyMarkup = replyMarkup;
}


@Override
protected boolean isValid() {
return super.isValid();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static TextBuilder create() {
return new TextBuilder();
}

private TextBuilder() {
protected TextBuilder() {
}

private String htmlEscaped(String text) {
Expand Down Expand Up @@ -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();
}
Expand Down
67 changes: 67 additions & 0 deletions ktelegrambotapi/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.jtelegram</groupId>
<artifactId>jtelegrambotapi</artifactId>
<version>4.0.10</version>
</parent>

<properties>
<kotlin.version>1.3.72</kotlin.version>
</properties>

<artifactId>ktelegrambotapi</artifactId>
<name>jTelegramBotAPI as Kotlin (AKA kTelegramBotAPI)</name>

<dependencies>
<dependency>
<groupId>com.jtelegram</groupId>
<artifactId>jtelegrambotapi-core</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.3.7</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>

<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
</plugins>
</build>


</project>
Original file line number Diff line number Diff line change
@@ -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 <T> sendText(chatId: ChatId<T>, 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 <ST, S: Message<ST>> Chat.sendMessage(request: SendableMessageRequest<S>): S {
return sendAction(request)
}

suspend fun <S> Chat.sendAction(request: SendableChatRequest<S>): 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 <ST, S: Message<ST>> User.sendMessage(request: SendableMessageRequest<S>): S {
return sendAction(request)
}

suspend fun <S> User.sendAction(request: SendableChatRequest<S>): S {
return bot.execute (
request.apply {
chatId = ChatId.of(id)
}
)
}
}
Original file line number Diff line number Diff line change
@@ -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())
}
Original file line number Diff line number Diff line change
@@ -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 <T> TelegramBot.execute(request: QueryTelegramRequest<T>): T = suspendCoroutine { cont ->
request.useContinuationForErrors(cont)

request.setCallback {
cont.resume(it)
}

perform(request)
}

suspend fun TelegramBot.execute(request: UpdateTelegramRequest) = suspendCoroutine<Unit?> { cont ->
request.useContinuationForErrors(cont)

request.setCallback {
cont.resume(null)
}

perform(request)
}

private fun <T> AbstractTelegramRequest.useContinuationForErrors(cont: Continuation<T>) {
setErrorHandler {
cont.resumeWithException(it)
}
}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit d714010

Please sign in to comment.