dfkndsfjkdsjjdssdf
This commit is contained in:
24
longpolling-okhttp/build.gradle
Normal file
24
longpolling-okhttp/build.gradle
Normal file
@@ -0,0 +1,24 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group = 'com.github.hdvtdev'
|
||||
version = '1.0.0'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.3'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||
implementation(project(":core"))
|
||||
}
|
||||
|
||||
tasks.register('fat') {
|
||||
jar {
|
||||
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
package hdvtdev.telegram.longpolling;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import hdvtdev.telegram.annotations.handlers.CallbackQueryHandler;
|
||||
import hdvtdev.telegram.annotations.handlers.TextMessageHandler;
|
||||
import hdvtdev.telegram.annotations.util.Jsonable;
|
||||
import hdvtdev.telegram.core.TelegramBot;
|
||||
import hdvtdev.telegram.exceptions.TelegramApiException;
|
||||
import hdvtdev.telegram.exceptions.TelegramApiNetworkException;
|
||||
import hdvtdev.telegram.exceptions.TelegramMethodParsingException;
|
||||
import hdvtdev.telegram.methods.GetUpdates;
|
||||
import hdvtdev.telegram.methods.TelegramApiMethod;
|
||||
import hdvtdev.telegram.objects.CallbackQuery;
|
||||
import hdvtdev.telegram.objects.Message;
|
||||
import hdvtdev.telegram.objects.TelegramFile;
|
||||
import hdvtdev.telegram.objects.Update;
|
||||
import hdvtdev.telegram.util.ClassFinder;
|
||||
import hdvtdev.telegram.util.InvokeMethod;
|
||||
|
||||
import okhttp3.*;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
|
||||
public class OkHttpTelegramBot implements TelegramBot {
|
||||
|
||||
private final String TELEGRAM_API_URL;
|
||||
private final String TELEGRAM_FILE_API_URL;
|
||||
private final ObjectMapper json = new ObjectMapper();
|
||||
|
||||
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();
|
||||
|
||||
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 int updateLimit = 10;
|
||||
private int updateTimeout = 25;
|
||||
private final OkHttpClient client = buildOkHttpClient();
|
||||
|
||||
private OkHttpClient buildOkHttpClient() {
|
||||
Dispatcher dispatcher = new Dispatcher();
|
||||
dispatcher.setMaxRequests(100);
|
||||
dispatcher.setMaxRequestsPerHost(100);
|
||||
|
||||
return new OkHttpClient.Builder()
|
||||
.dispatcher(dispatcher)
|
||||
.connectionPool(new ConnectionPool(
|
||||
100,
|
||||
75,
|
||||
TimeUnit.SECONDS
|
||||
))
|
||||
.readTimeout(updateTimeout + 10, TimeUnit.SECONDS)
|
||||
.writeTimeout(updateTimeout, TimeUnit.SECONDS)
|
||||
.connectTimeout(updateTimeout, TimeUnit.SECONDS)
|
||||
.retryOnConnectionFailure(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
private UpdateConsumer updateConsumer;
|
||||
|
||||
private boolean enableHandlers = false;
|
||||
private Map<String, InvokeMethod> messageHandlers;
|
||||
private Map<String, InvokeMethod> callbackQueryHandlers;
|
||||
|
||||
private UserState userState;
|
||||
private Map<String, UserState> userStateStorage;
|
||||
|
||||
public UserState getUserState(String id) {
|
||||
return userStateStorage.getOrDefault(id, userState);
|
||||
}
|
||||
|
||||
public void setUserState(String id, UserState userState) {
|
||||
userStateStorage.put(id, userState);
|
||||
}
|
||||
|
||||
public boolean compareUserState(String id, UserState userState) {
|
||||
return getUserState(id).equals(userState);
|
||||
}
|
||||
|
||||
public OkHttpTelegramBot(String token) {
|
||||
this.TELEGRAM_API_URL = "https://api.telegram.org/bot" + token + "/";
|
||||
this.TELEGRAM_FILE_API_URL = "https://api.telegram.org/file/bot" + token + "/";
|
||||
}
|
||||
|
||||
private OkHttpTelegramBot(Builder builder) {
|
||||
updateLimit = builder.updateLimit;
|
||||
updateTimeout = builder.updateTimeout;
|
||||
enableHandlers = builder.enableHandlers;
|
||||
userStateStorage = builder.userStateStorage;
|
||||
userState = builder.userState;
|
||||
if (builder.updateConsumer != null) setUpdateConsumer(builder.updateConsumer);
|
||||
if (enableHandlers) {
|
||||
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.TELEGRAM_API_URL = "https://api.telegram.org/bot" + builder.token + "/";
|
||||
this.TELEGRAM_FILE_API_URL = "https://api.telegram.org/file/bot" + builder.token + "/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the default long polling update consumer for handlers.
|
||||
* <p>
|
||||
* This method is effective only if {@link #enableHandlers} is{@code true},
|
||||
* otherwise, it just marks updates as read/processed without invoking any handlers.
|
||||
* <p>
|
||||
* <pre><code>
|
||||
* Equivalent to: setUpdateConsumer(null);
|
||||
* </code></pre>
|
||||
* @throws IllegalStateException if an {@code UpdateConsumer} is already defined
|
||||
* @see #setUpdateConsumer(UpdateConsumer)
|
||||
* @see #enableHandlers
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public void enableDefaultUpdateConsumer() throws IllegalStateException {
|
||||
setUpdateConsumer(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 #enableDefaultUpdateConsumer()
|
||||
* @see #enableHandlers
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public 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);
|
||||
}
|
||||
|
||||
private void getUpdates() {
|
||||
List<Update> updates = List.of(awaitExecute(new GetUpdates(lastUpdateId.get() + 1, updateLimit, updateTimeout)));
|
||||
System.out.println("UPDATE");
|
||||
if (!updates.isEmpty()) {
|
||||
try {
|
||||
if (enableHandlers) {
|
||||
for (Update update : updates) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
if (update.hasMessage()) {
|
||||
Message message = update.message();
|
||||
if (message.hasText() && !messageHandlers.isEmpty()) {
|
||||
String key = message.text();
|
||||
invokeAnnotatedMethod(messageHandlers.containsKey("*") ? messageHandlers.get("*") : messageHandlers.get(key), update);
|
||||
}
|
||||
}
|
||||
if (update.hasCallbackQuery()) {
|
||||
CallbackQuery callbackQuery = update.callbackQuery();
|
||||
if (callbackQuery.hasMessage()) {
|
||||
Message message = (Message) callbackQuery.message();
|
||||
if (message.hasText() && !callbackQueryHandlers.isEmpty()) {
|
||||
invokeAnnotatedMethod(callbackQueryHandlers.containsKey("*") ? callbackQueryHandlers.get("*") : callbackQueryHandlers.get(message.text()), update);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (updateConsumer != null) CompletableFuture.runAsync(() -> updateConsumer.getUpdates(updates));
|
||||
} finally {
|
||||
lastUpdateId.set(updates.getLast().updateId());
|
||||
if (!thread.isShutdown()) getUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void invokeAnnotatedMethod(InvokeMethod invokeMethod, Update update) {
|
||||
|
||||
if (invokeMethod != null) {
|
||||
Method method = invokeMethod.method();
|
||||
method.setAccessible(true);
|
||||
try {
|
||||
switch (invokeMethod.parameterType().getSimpleName()) {
|
||||
case "Message" -> method.invoke(null, update.message());
|
||||
case "Update" -> method.invoke(null, update);
|
||||
case "CallbackQuery" -> method.invoke(null, update.callbackQuery());
|
||||
default -> method.invoke(null);
|
||||
}
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdownUpdateConsumer() {
|
||||
this.thread.close();
|
||||
}
|
||||
|
||||
public <T> T awaitExecute(TelegramApiMethod<T> telegramApiMethod) throws TelegramApiException, TelegramApiNetworkException, TelegramMethodParsingException {
|
||||
|
||||
RequestBody body = telegramApiMethod.getBody();
|
||||
|
||||
Request.Builder request = new Request.Builder()
|
||||
.url(TELEGRAM_API_URL + telegramApiMethod.getMethodName());
|
||||
if (body == null) {
|
||||
if (telegramApiMethod.getClass().isAnnotationPresent(Jsonable.class)) {
|
||||
try {
|
||||
request.post(RequestBody.create(json.writeValueAsString(telegramApiMethod), MediaType.get("application/json; charset=utf-8")));
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new TelegramMethodParsingException(e);
|
||||
}
|
||||
}
|
||||
} else request.post(body);
|
||||
|
||||
try (Response response = client.newCall(request.build()).execute()) {
|
||||
String responseBody = Objects.requireNonNull(response.body()).string();
|
||||
if (response.isSuccessful()) {
|
||||
JsonNode rootNode = json.readTree(responseBody);
|
||||
JsonNode resultNode = rootNode.path("result");
|
||||
return json.treeToValue(resultNode, telegramApiMethod.getResponseClass());
|
||||
} else {
|
||||
throw new TelegramApiException(json.readValue(responseBody, TelegramApiException.ErrorResponse.class));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new TelegramApiNetworkException(e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
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());
|
||||
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;
|
||||
Files.copy(responseBody.byteStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
return new File(filePath.toUri());
|
||||
} catch (IOException e) {
|
||||
throw new TelegramApiNetworkException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public File awaitDownloadFile(TelegramFile telegramFile, Path targetDirectory) {
|
||||
return getFile(telegramFile, targetDirectory);
|
||||
}
|
||||
|
||||
|
||||
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 Map<String, UserState> userStateStorage;
|
||||
private UserState userState;
|
||||
private UpdateConsumer updateConsumer;
|
||||
|
||||
public Builder(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public Builder updateConsumer(Main.Upd updateConsumer) {
|
||||
this.updateConsumer = updateConsumer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enableUserStateStorage(UserState defaultValue, Map<String, UserState> userStateStorage) {
|
||||
this.userState = defaultValue;
|
||||
this.userStateStorage = userStateStorage;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enableUserStateStorage(UserState defaultValue) {
|
||||
return enableUserStateStorage(defaultValue, new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
public Builder updateLimit(int updateLimit) {
|
||||
this.updateLimit = updateLimit;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder updateTimeout(int updateTimeout) {
|
||||
this.updateTimeout = updateTimeout;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
6
longpolling-okhttp/src/main/java/module-info.java
Normal file
6
longpolling-okhttp/src/main/java/module-info.java
Normal file
@@ -0,0 +1,6 @@
|
||||
module longpolling.okhttp {
|
||||
exports hdvtdev.telegram.longpolling;
|
||||
requires com.fasterxml.jackson.databind;
|
||||
requires core;
|
||||
requires okhttp3;
|
||||
}
|
||||
Reference in New Issue
Block a user