migration

This commit is contained in:
hdvt
2025-11-03 21:18:28 +03:00
6 changed files with 123 additions and 87 deletions

View File

@@ -1,5 +1,6 @@
plugins {
id 'java'
id 'application'
}
group = 'com.github.hdvtdev'
@@ -11,7 +12,16 @@ repositories {
dependencies {
implementation(project(":core"))
annotationProcessor(project(":annotation-processor"))
implementation(project(":longpolling-okhttp"))
implementation(project(":event-handlers"))
implementation(project(":event-handlers-annotations"))
}
application {
mainClass = "Main"
}
tasks.register('fat', Jar) {

View File

@@ -11,6 +11,12 @@ repositories {
}
dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.3'
implementation platform('com.fasterxml.jackson:jackson-bom:2.18.3')
implementation 'com.fasterxml.jackson.core:jackson-core'
implementation 'com.fasterxml.jackson.core:jackson-annotations'
implementation 'com.fasterxml.jackson.core:jackson-databind'
implementation 'org.jetbrains:annotations:26.0.2-1'
}

View File

@@ -1,8 +1,9 @@
module core {
requires com.fasterxml.jackson.databind;
requires org.jetbrains.annotations;
exports hdvtdev.telegram.core.exceptions;
exports hdvtdev.telegram.core.objects.command;
exports hdvtdev.telegram.core.annotaions;
exports hdvtdev.telegram.core.methods;
exports hdvtdev.telegram.core;
exports hdvtdev.telegram.core.objects;

View File

@@ -10,15 +10,14 @@ repositories {
}
dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.3'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation platform('com.fasterxml.jackson:jackson-bom:2.18.3')
implementation 'com.fasterxml.jackson.core:jackson-core'
implementation 'com.fasterxml.jackson.core:jackson-databind'
//implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.squareup.okhttp3:okhttp:5.2.1'
implementation(project(":core"))
}
tasks.register('fat') {
jar {
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
}

View File

@@ -4,10 +4,10 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import hdvtdev.telegram.core.InvokeMethod;
import hdvtdev.telegram.core.HandlersModule;
import hdvtdev.telegram.core.TelegramBot;
import hdvtdev.telegram.core.UpdateConsumer;
import hdvtdev.telegram.core.annotaions.Jsonable;
import hdvtdev.telegram.core.UpdateExecutor;
import hdvtdev.telegram.core.exceptions.TelegramApiException;
import hdvtdev.telegram.core.exceptions.TelegramApiNetworkException;
import hdvtdev.telegram.core.exceptions.TelegramMethodParsingException;
@@ -22,14 +22,10 @@ import okhttp3.*;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
@@ -40,24 +36,10 @@ public class OkHttpTelegramBot implements TelegramBot {
private final String TELEGRAM_FILE_API_URL;
private final ObjectMapper json;
static {
try {
HttpURLConnection connection = (HttpURLConnection) URI.create("https://api.telegram.org").toURL().openConnection();
connection.setRequestMethod("HEAD");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
int responseCode = connection.getResponseCode();
private ScheduledExecutorService scheduler;
if (responseCode != 200) {
throw new TelegramApiNetworkException("Telegram API is unreachable. Response code: " + responseCode);
}
} catch (IOException e) {
throw new TelegramApiNetworkException("Error checking Telegram API connectivity.", e);
}
}
private ExecutorService thread;
private AtomicLong lastUpdateId;
private final UpdateExecutor updateExecutor;
private final AtomicLong lastUpdateId = new AtomicLong(0);
private int updateLimit = 10;
private int updateTimeout = 25;
private final OkHttpClient client = buildOkHttpClient();
@@ -68,6 +50,7 @@ public class OkHttpTelegramBot implements TelegramBot {
dispatcher.setMaxRequestsPerHost(100);
return new OkHttpClient.Builder()
.dispatcher(dispatcher)
.connectionPool(new ConnectionPool(
100,
@@ -78,14 +61,37 @@ public class OkHttpTelegramBot implements TelegramBot {
.writeTimeout(updateTimeout, TimeUnit.SECONDS)
.connectTimeout(updateTimeout, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.addInterceptor(chain -> {
Request request = chain.request();
int retryDelay = 1000;
int maxRetries = 5;
for (int attempt = 1; attempt <= maxRetries; attempt++) {
try {
return chain.proceed(request);
} catch (IOException e) {
if (attempt == maxRetries) throw e;
try {
TimeUnit.MILLISECONDS.sleep(retryDelay);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw e;
}
retryDelay *= 2;
}
}
throw new TelegramApiNetworkException("Network is unreachable");
})
.build();
}
private UpdateConsumer updateConsumer;
private boolean enableHandlers = false;
public OkHttpTelegramBot(String token) {
this.json = new ObjectMapper();
this.updateExecutor = (Update update) -> CompletableFuture.runAsync(() -> updateConsumer.onUpdate(update));
this.TELEGRAM_API_URL = "https://api.telegram.org/bot" + token + "/";
this.TELEGRAM_FILE_API_URL = "https://api.telegram.org/file/bot" + token + "/";
}
@@ -93,54 +99,69 @@ public class OkHttpTelegramBot implements TelegramBot {
private OkHttpTelegramBot(Builder builder) {
updateLimit = builder.updateLimit;
updateTimeout = builder.updateTimeout;
enableHandlers = builder.enableHandlers;
json = builder.objectMapper == null ? new ObjectMapper() : builder.objectMapper;
/*
if (false) {
Class<? extends UpdateConsumer> updateConsumerClass = builder.updateConsumer == null ? UpdateConsumer.class : builder.updateConsumer.getClass();
Map<Class<?>, Map<String, InvokeMethod>> handlers = builder.enableScan ? ClassFinder.getClasses() : ClassFinder.localScan(updateConsumerClass);
this.messageHandlers = Collections.unmodifiableMap(handlers.get(TextMessageHandler.class));
this.callbackQueryHandlers = Collections.unmodifiableMap(handlers.get(CallbackQueryHandler.class));
}
*/
ExecutorService pool = builder.pool;
this.updateExecutor = pool == null ? (Update update) -> CompletableFuture.runAsync(() -> updateConsumer.onUpdate(update))
: (Update update) -> pool.execute(() -> updateConsumer.onUpdate(update));
this.TELEGRAM_API_URL = "https://api.telegram.org/bot" + builder.token + "/";
this.TELEGRAM_FILE_API_URL = "https://api.telegram.org/file/bot" + builder.token + "/";
if (builder.updateConsumer != null) setUpdateConsumer(builder.updateConsumer);
if (builder.updateConsumer != null) start(builder.updateConsumer);
}
/**
* Enables a long polling update consumer. If {@link #enableHandlers} is {@code true},
* the specified handlers will be invoked for each received update.
*
* @param updateConsumer class that implements {@code UpdateConsumer}
* @throws IllegalStateException if an {@code UpdateConsumer} is already defined
* @see #enableHandlers
* @since 0.0.1
*/
private void setUpdateConsumer(UpdateConsumer updateConsumer) throws IllegalStateException {
if (thread != null) throw new IllegalStateException("Update Consumer is already defined. You must first stop the previous");
this.updateConsumer = updateConsumer;
this.lastUpdateId = new AtomicLong(0);
thread = Executors.newSingleThreadExecutor();
thread.execute(this::getUpdates);
@Override
public void start(UpdateConsumer updateConsumer) throws IllegalStateException {
if (scheduler != null && !scheduler.isShutdown()) {
throw new IllegalStateException("Long polling is already running. You must stop it first.");
}
boolean moduleEnabled = true;
try {
Class<?> handlersModule = Class.forName("Handlers");
HandlersModule module = (HandlersModule) handlersModule.getDeclaredConstructor().newInstance();
//this.updateConsumer = module.enable(updateConsumer);
} catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException | IllegalArgumentException | NoSuchMethodException | InstantiationException e) {
moduleEnabled = false;
}
if (!moduleEnabled) this.updateConsumer = updateConsumer;
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleWithFixedDelay(this::getUpdates, 0, 1, TimeUnit.SECONDS);
}
public void stop() {
if (scheduler == null || scheduler.isShutdown()) {
return;
}
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
Thread.currentThread().interrupt();
}
}
private void getUpdates() {
List<Update> updates = List.of(awaitExecute(new GetUpdates(lastUpdateId.get() + 1, updateLimit, updateTimeout)));
try {
if (!updates.isEmpty()) {
if (updateConsumer != null) CompletableFuture.runAsync(() -> updateConsumer.onUpdates(updates));
lastUpdateId.set(updates.getLast().updateId());
if (!updates.isEmpty()) {
for (Update update : updates) {
updateExecutor.execute(update);
}
} finally {
if (!thread.isShutdown()) getUpdates();
lastUpdateId.set(updates.getLast().updateId());
}
}
@Override
public void shutdown() {
this.thread.close();
stop();
}
@Override
@@ -151,7 +172,7 @@ public class OkHttpTelegramBot implements TelegramBot {
Request.Builder request = new Request.Builder()
.url(TELEGRAM_API_URL + telegramApiMethod.getMethodName());
if (body == null) {
if (telegramApiMethod.getClass().isAnnotationPresent(Jsonable.class)) {
if (telegramApiMethod.isJsonable()) {
try {
request.post(RequestBody.create(json.writeValueAsString(telegramApiMethod), MediaType.get("application/json; charset=utf-8")));
} catch (JsonProcessingException e) {
@@ -168,24 +189,24 @@ public class OkHttpTelegramBot implements TelegramBot {
}
try (Response response = client.newCall(request.build()).execute()) {
String responseBody = Objects.requireNonNull(response.body()).string();
ResponseBody responseBody = response.body();
String responseBodyString = responseBody.string();
if (response.isSuccessful()) {
JsonNode rootNode = json.readTree(responseBody);
JsonNode rootNode = json.readTree(responseBodyString);
JsonNode resultNode = rootNode.path("result");
return json.treeToValue(resultNode, telegramApiMethod.getResponseClass());
} else {
throw new TelegramApiException(json.readValue(responseBody, TelegramApiException.ErrorResponse.class));
throw new TelegramApiException(json.readValue(responseBodyString, TelegramApiException.ErrorResponse.class));
}
} catch (IOException e) {
throw new TelegramApiNetworkException(e);
}
}
private File getFile(TelegramFile telegramFile, Path targetDirectory) {
try (Response response = client.newCall(new Request.Builder().url(TELEGRAM_FILE_API_URL + telegramFile.filePath()).build()).execute()) {
ResponseBody responseBody = Objects.requireNonNull(response.body());
ResponseBody responseBody = response.body();
if (!response.isSuccessful())
throw new TelegramApiException(json.readValue(responseBody.string(), TelegramApiException.ErrorResponse.class));
Path filePath = Files.isDirectory(targetDirectory) ? targetDirectory.resolve(Path.of(telegramFile.filePath()).getFileName()) : targetDirectory;
@@ -203,16 +224,21 @@ public class OkHttpTelegramBot implements TelegramBot {
public static final class Builder {
private int updateLimit = 10;
private int updateTimeout = 25;
private boolean enableHandlers = false;
private boolean enableScan = false;
private final String token;
private UpdateConsumer updateConsumer;
private ObjectMapper objectMapper;
private ExecutorService pool;
public Builder(String token) {
this.token = token;
}
public Builder threadPool(ExecutorService pool) {
this.pool = pool;
return this;
}
public Builder objectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
return this;
@@ -233,17 +259,6 @@ public class OkHttpTelegramBot implements TelegramBot {
return this;
}
public Builder enableHandlers() {
this.enableHandlers = true;
return this;
}
public Builder enableHandlers(boolean enableScan) {
this.enableHandlers = true;
this.enableScan = enableScan;
return this;
}
public OkHttpTelegramBot build() {
return new OkHttpTelegramBot(this);
}

View File

@@ -2,3 +2,8 @@ rootProject.name = 'TeleJ'
include 'core'
include 'longpolling-okhttp'
include 'test'
include 'event-handlers'
include 'annotation-processor'
include 'event-handlers-annotations'