|
|
|
@@ -4,10 +4,10 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
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.TelegramBot;
|
|
|
|
import hdvtdev.telegram.core.UpdateConsumer;
|
|
|
|
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.TelegramApiException;
|
|
|
|
import hdvtdev.telegram.core.exceptions.TelegramApiNetworkException;
|
|
|
|
import hdvtdev.telegram.core.exceptions.TelegramApiNetworkException;
|
|
|
|
import hdvtdev.telegram.core.exceptions.TelegramMethodParsingException;
|
|
|
|
import hdvtdev.telegram.core.exceptions.TelegramMethodParsingException;
|
|
|
|
@@ -22,14 +22,10 @@ import okhttp3.*;
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
|
|
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.Files;
|
|
|
|
import java.nio.file.Path;
|
|
|
|
import java.nio.file.Path;
|
|
|
|
import java.nio.file.StandardCopyOption;
|
|
|
|
import java.nio.file.StandardCopyOption;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Objects;
|
|
|
|
|
|
|
|
import java.util.concurrent.*;
|
|
|
|
import java.util.concurrent.*;
|
|
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
|
|
|
|
|
|
|
|
|
|
@@ -40,24 +36,10 @@ public class OkHttpTelegramBot implements TelegramBot {
|
|
|
|
private final String TELEGRAM_FILE_API_URL;
|
|
|
|
private final String TELEGRAM_FILE_API_URL;
|
|
|
|
private final ObjectMapper json;
|
|
|
|
private final ObjectMapper json;
|
|
|
|
|
|
|
|
|
|
|
|
static {
|
|
|
|
private ScheduledExecutorService scheduler;
|
|
|
|
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();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (responseCode != 200) {
|
|
|
|
private final UpdateExecutor updateExecutor;
|
|
|
|
throw new TelegramApiNetworkException("Telegram API is unreachable. Response code: " + responseCode);
|
|
|
|
private final AtomicLong lastUpdateId = new AtomicLong(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
|
|
throw new TelegramApiNetworkException("Error checking Telegram API connectivity.", e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ExecutorService thread;
|
|
|
|
|
|
|
|
private AtomicLong lastUpdateId;
|
|
|
|
|
|
|
|
private int updateLimit = 10;
|
|
|
|
private int updateLimit = 10;
|
|
|
|
private int updateTimeout = 25;
|
|
|
|
private int updateTimeout = 25;
|
|
|
|
private final OkHttpClient client = buildOkHttpClient();
|
|
|
|
private final OkHttpClient client = buildOkHttpClient();
|
|
|
|
@@ -68,6 +50,7 @@ public class OkHttpTelegramBot implements TelegramBot {
|
|
|
|
dispatcher.setMaxRequestsPerHost(100);
|
|
|
|
dispatcher.setMaxRequestsPerHost(100);
|
|
|
|
|
|
|
|
|
|
|
|
return new OkHttpClient.Builder()
|
|
|
|
return new OkHttpClient.Builder()
|
|
|
|
|
|
|
|
|
|
|
|
.dispatcher(dispatcher)
|
|
|
|
.dispatcher(dispatcher)
|
|
|
|
.connectionPool(new ConnectionPool(
|
|
|
|
.connectionPool(new ConnectionPool(
|
|
|
|
100,
|
|
|
|
100,
|
|
|
|
@@ -78,14 +61,37 @@ public class OkHttpTelegramBot implements TelegramBot {
|
|
|
|
.writeTimeout(updateTimeout, TimeUnit.SECONDS)
|
|
|
|
.writeTimeout(updateTimeout, TimeUnit.SECONDS)
|
|
|
|
.connectTimeout(updateTimeout, TimeUnit.SECONDS)
|
|
|
|
.connectTimeout(updateTimeout, TimeUnit.SECONDS)
|
|
|
|
.retryOnConnectionFailure(true)
|
|
|
|
.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();
|
|
|
|
.build();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private UpdateConsumer updateConsumer;
|
|
|
|
private UpdateConsumer updateConsumer;
|
|
|
|
private boolean enableHandlers = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public OkHttpTelegramBot(String token) {
|
|
|
|
public OkHttpTelegramBot(String token) {
|
|
|
|
this.json = new ObjectMapper();
|
|
|
|
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_API_URL = "https://api.telegram.org/bot" + token + "/";
|
|
|
|
this.TELEGRAM_FILE_API_URL = "https://api.telegram.org/file/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) {
|
|
|
|
private OkHttpTelegramBot(Builder builder) {
|
|
|
|
updateLimit = builder.updateLimit;
|
|
|
|
updateLimit = builder.updateLimit;
|
|
|
|
updateTimeout = builder.updateTimeout;
|
|
|
|
updateTimeout = builder.updateTimeout;
|
|
|
|
enableHandlers = builder.enableHandlers;
|
|
|
|
|
|
|
|
json = builder.objectMapper == null ? new ObjectMapper() : builder.objectMapper;
|
|
|
|
json = builder.objectMapper == null ? new ObjectMapper() : builder.objectMapper;
|
|
|
|
/*
|
|
|
|
ExecutorService pool = builder.pool;
|
|
|
|
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));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
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_API_URL = "https://api.telegram.org/bot" + builder.token + "/";
|
|
|
|
this.TELEGRAM_FILE_API_URL = "https://api.telegram.org/file/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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@Override
|
|
|
|
* Enables a long polling update consumer. If {@link #enableHandlers} is {@code true},
|
|
|
|
public void start(UpdateConsumer updateConsumer) throws IllegalStateException {
|
|
|
|
* the specified handlers will be invoked for each received update.
|
|
|
|
if (scheduler != null && !scheduler.isShutdown()) {
|
|
|
|
*
|
|
|
|
throw new IllegalStateException("Long polling is already running. You must stop it first.");
|
|
|
|
* @param updateConsumer class that implements {@code UpdateConsumer}
|
|
|
|
}
|
|
|
|
* @throws IllegalStateException if an {@code UpdateConsumer} is already defined
|
|
|
|
|
|
|
|
* @see #enableHandlers
|
|
|
|
|
|
|
|
* @since 0.0.1
|
|
|
|
|
|
|
|
*/
|
|
|
|
boolean moduleEnabled = true;
|
|
|
|
private void setUpdateConsumer(UpdateConsumer updateConsumer) throws IllegalStateException {
|
|
|
|
|
|
|
|
if (thread != null) throw new IllegalStateException("Update Consumer is already defined. You must first stop the previous");
|
|
|
|
try {
|
|
|
|
this.updateConsumer = updateConsumer;
|
|
|
|
Class<?> handlersModule = Class.forName("Handlers");
|
|
|
|
this.lastUpdateId = new AtomicLong(0);
|
|
|
|
HandlersModule module = (HandlersModule) handlersModule.getDeclaredConstructor().newInstance();
|
|
|
|
thread = Executors.newSingleThreadExecutor();
|
|
|
|
//this.updateConsumer = module.enable(updateConsumer);
|
|
|
|
thread.execute(this::getUpdates);
|
|
|
|
} 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() {
|
|
|
|
private void getUpdates() {
|
|
|
|
|
|
|
|
|
|
|
|
List<Update> updates = List.of(awaitExecute(new GetUpdates(lastUpdateId.get() + 1, updateLimit, updateTimeout)));
|
|
|
|
List<Update> updates = List.of(awaitExecute(new GetUpdates(lastUpdateId.get() + 1, updateLimit, updateTimeout)));
|
|
|
|
try {
|
|
|
|
|
|
|
|
if (!updates.isEmpty()) {
|
|
|
|
if (!updates.isEmpty()) {
|
|
|
|
if (updateConsumer != null) CompletableFuture.runAsync(() -> updateConsumer.onUpdates(updates));
|
|
|
|
for (Update update : updates) {
|
|
|
|
lastUpdateId.set(updates.getLast().updateId());
|
|
|
|
updateExecutor.execute(update);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
lastUpdateId.set(updates.getLast().updateId());
|
|
|
|
if (!thread.isShutdown()) getUpdates();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
public void shutdown() {
|
|
|
|
public void shutdown() {
|
|
|
|
this.thread.close();
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
@@ -151,7 +172,7 @@ public class OkHttpTelegramBot implements TelegramBot {
|
|
|
|
Request.Builder request = new Request.Builder()
|
|
|
|
Request.Builder request = new Request.Builder()
|
|
|
|
.url(TELEGRAM_API_URL + telegramApiMethod.getMethodName());
|
|
|
|
.url(TELEGRAM_API_URL + telegramApiMethod.getMethodName());
|
|
|
|
if (body == null) {
|
|
|
|
if (body == null) {
|
|
|
|
if (telegramApiMethod.getClass().isAnnotationPresent(Jsonable.class)) {
|
|
|
|
if (telegramApiMethod.isJsonable()) {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
request.post(RequestBody.create(json.writeValueAsString(telegramApiMethod), MediaType.get("application/json; charset=utf-8")));
|
|
|
|
request.post(RequestBody.create(json.writeValueAsString(telegramApiMethod), MediaType.get("application/json; charset=utf-8")));
|
|
|
|
} catch (JsonProcessingException e) {
|
|
|
|
} catch (JsonProcessingException e) {
|
|
|
|
@@ -168,24 +189,24 @@ public class OkHttpTelegramBot implements TelegramBot {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try (Response response = client.newCall(request.build()).execute()) {
|
|
|
|
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()) {
|
|
|
|
if (response.isSuccessful()) {
|
|
|
|
JsonNode rootNode = json.readTree(responseBody);
|
|
|
|
JsonNode rootNode = json.readTree(responseBodyString);
|
|
|
|
JsonNode resultNode = rootNode.path("result");
|
|
|
|
JsonNode resultNode = rootNode.path("result");
|
|
|
|
return json.treeToValue(resultNode, telegramApiMethod.getResponseClass());
|
|
|
|
return json.treeToValue(resultNode, telegramApiMethod.getResponseClass());
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
throw new TelegramApiException(json.readValue(responseBody, TelegramApiException.ErrorResponse.class));
|
|
|
|
throw new TelegramApiException(json.readValue(responseBodyString, TelegramApiException.ErrorResponse.class));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new TelegramApiNetworkException(e);
|
|
|
|
throw new TelegramApiNetworkException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private File getFile(TelegramFile telegramFile, Path targetDirectory) {
|
|
|
|
private File getFile(TelegramFile telegramFile, Path targetDirectory) {
|
|
|
|
try (Response response = client.newCall(new Request.Builder().url(TELEGRAM_FILE_API_URL + telegramFile.filePath()).build()).execute()) {
|
|
|
|
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())
|
|
|
|
if (!response.isSuccessful())
|
|
|
|
throw new TelegramApiException(json.readValue(responseBody.string(), TelegramApiException.ErrorResponse.class));
|
|
|
|
throw new TelegramApiException(json.readValue(responseBody.string(), TelegramApiException.ErrorResponse.class));
|
|
|
|
Path filePath = Files.isDirectory(targetDirectory) ? targetDirectory.resolve(Path.of(telegramFile.filePath()).getFileName()) : targetDirectory;
|
|
|
|
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 {
|
|
|
|
public static final class Builder {
|
|
|
|
private int updateLimit = 10;
|
|
|
|
private int updateLimit = 10;
|
|
|
|
private int updateTimeout = 25;
|
|
|
|
private int updateTimeout = 25;
|
|
|
|
private boolean enableHandlers = false;
|
|
|
|
|
|
|
|
private boolean enableScan = false;
|
|
|
|
|
|
|
|
private final String token;
|
|
|
|
private final String token;
|
|
|
|
private UpdateConsumer updateConsumer;
|
|
|
|
private UpdateConsumer updateConsumer;
|
|
|
|
private ObjectMapper objectMapper;
|
|
|
|
private ObjectMapper objectMapper;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ExecutorService pool;
|
|
|
|
|
|
|
|
|
|
|
|
public Builder(String token) {
|
|
|
|
public Builder(String token) {
|
|
|
|
this.token = token;
|
|
|
|
this.token = token;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Builder threadPool(ExecutorService pool) {
|
|
|
|
|
|
|
|
this.pool = pool;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
|
|
|
|
}
|
|
|
|
public Builder objectMapper(ObjectMapper objectMapper) {
|
|
|
|
public Builder objectMapper(ObjectMapper objectMapper) {
|
|
|
|
this.objectMapper = objectMapper;
|
|
|
|
this.objectMapper = objectMapper;
|
|
|
|
return this;
|
|
|
|
return this;
|
|
|
|
@@ -233,17 +259,6 @@ public class OkHttpTelegramBot implements TelegramBot {
|
|
|
|
return this;
|
|
|
|
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() {
|
|
|
|
public OkHttpTelegramBot build() {
|
|
|
|
return new OkHttpTelegramBot(this);
|
|
|
|
return new OkHttpTelegramBot(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|