Compare commits
2 Commits
d80d4e3b94
...
b8615d3b90
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8615d3b90 | ||
|
|
15f77a945a |
20
annotation-processor/build.gradle
Normal file
20
annotation-processor/build.gradle
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'com.github.hdvtdev'
|
||||||
|
version = '1.0.0'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'com.google.auto.service:auto-service:1.1.1'
|
||||||
|
implementation project(':core')
|
||||||
|
implementation 'org.ow2.asm:asm:9.9'
|
||||||
|
annotationProcessor 'com.google.auto.service:auto-service:1.1.1'
|
||||||
|
//implementation 'com.fasterxml.jackson.core:jackson-annotations:2.18.3'
|
||||||
|
implementation project(":event-handlers-annotations")
|
||||||
|
}
|
||||||
|
|
||||||
172
annotation-processor/src/main/java/EventHandlersProcessor.java
Normal file
172
annotation-processor/src/main/java/EventHandlersProcessor.java
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
|
||||||
|
import javax.annotation.processing.*;
|
||||||
|
import javax.lang.model.SourceVersion;
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
import hdvtdev.telegram.handler.annotations.*;
|
||||||
|
|
||||||
|
@AutoService(Processor.class)
|
||||||
|
@SupportedAnnotationTypes({
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnChosenInlineResult",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnChatMemberUpdated",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnChatBoostRemoved",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnPreCheckoutQuery",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnBusinessConnection",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnPaidMediaPurchased",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnUpdate",
|
||||||
|
"hdvtdev.telegram.handler.annotations.BotCommand",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnPoll",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnMessage",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnChatJoinRequest",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnChatBoostUpdated",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnCallbackQuery",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnShippingQuery",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnInlineQuery",
|
||||||
|
"hdvtdev.telegram.handler.annotations.OnPollAnswer"})
|
||||||
|
@SupportedSourceVersion(SourceVersion.RELEASE_21)
|
||||||
|
public class EventHandlersProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
|
private final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
private static final String PACKAGE = "hdvtdev.telegram.handler.annotations.";
|
||||||
|
private static final String HANDLER_PACKAGE = "hdvtdev.telegram.handler.";
|
||||||
|
|
||||||
|
private static final Set<String> SUPPORTED_ANNOTATIONS = Set.of(
|
||||||
|
OnChosenInlineResult.class,
|
||||||
|
OnChatMemberUpdated.class,
|
||||||
|
OnChatBoostRemoved.class,
|
||||||
|
OnPreCheckoutQuery.class,
|
||||||
|
OnBusinessConnection.class,
|
||||||
|
OnPaidMediaPurchased.class,
|
||||||
|
OnUpdate.class,
|
||||||
|
OnPoll.class,
|
||||||
|
OnMessage.class,
|
||||||
|
OnChatJoinRequest.class,
|
||||||
|
OnChatBoostUpdated.class,
|
||||||
|
OnCallbackQuery.class,
|
||||||
|
OnShippingQuery.class,
|
||||||
|
OnInlineQuery.class,
|
||||||
|
OnPollAnswer.class
|
||||||
|
).stream().map(Class::getName).collect(Collectors.toSet());
|
||||||
|
|
||||||
|
private static final Map<String, String> SUPPORTED_PARAMETERS = Map.ofEntries(
|
||||||
|
Map.entry("Update", "hdvtdev.telegram.core.objects.Update"),
|
||||||
|
Map.entry("InlineQuery", "hdvtdev.telegram.core.objects.InlineQuery"),
|
||||||
|
Map.entry("ChatMemberUpdated", "hdvtdev.telegram.core.objects.chat.ChatMemberUpdated"),
|
||||||
|
Map.entry("ChatBoostRemoved", "hdvtdev.telegram.core.objects.chatboost.ChatBoostRemoved"),
|
||||||
|
Map.entry("PreCheckoutQuery", "hdvtdev.telegram.core.objects.payment.PreCheckoutQuery"),
|
||||||
|
Map.entry("BusinessConnection", "hdvtdev.telegram.core.objects.business.BusinessConnection"),
|
||||||
|
Map.entry("PaidMediaPurchased", "hdvtdev.telegram.core.objects.media.paidmedia.PaidMediaPurchased"),
|
||||||
|
Map.entry("Poll", "hdvtdev.telegram.core.objects.poll.Poll"),
|
||||||
|
Map.entry("Message", "hdvtdev.telegram.core.objects.Message"),
|
||||||
|
Map.entry("ChatJoinRequest", "hdvtdev.telegram.core.objects.chat.ChatJoinRequest"),
|
||||||
|
Map.entry("ChatBoostUpdated", "hdvtdev.telegram.core.objects.chatboost.ChatBoostUpdated"),
|
||||||
|
Map.entry("CallbackQuery", "hdvtdev.telegram.core.objects.callback.CallbackQuery"),
|
||||||
|
Map.entry("ShippingQuery", "hdvtdev.telegram.core.objects.payment.ShippingQuery"),
|
||||||
|
Map.entry("PollAnswer", "hdvtdev.telegram.core.objects.poll.PollAnswer"),
|
||||||
|
Map.entry("ChosenInlineResult", "hdvtdev.telegram.core.objects.ChosenInlineResult")
|
||||||
|
);
|
||||||
|
|
||||||
|
public static String getFullParameterName(String simpleName) {
|
||||||
|
return SUPPORTED_PARAMETERS.get(simpleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Messager messager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void init(ProcessingEnvironment processingEnv) {
|
||||||
|
super.init(processingEnv);
|
||||||
|
messager = processingEnv.getMessager();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
|
||||||
|
messager.printNote("Called");
|
||||||
|
for (Element element : roundEnvironment.getRootElements()) {
|
||||||
|
for (Element enclosedElement : element.getEnclosedElements()) {
|
||||||
|
if (enclosedElement.getKind() == ElementKind.METHOD) {
|
||||||
|
processMethod((ExecutableElement) enclosedElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roundEnvironment.processingOver()) {
|
||||||
|
write();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processMethod(ExecutableElement methodElement) {
|
||||||
|
String methodName = methodElement.getSimpleName().toString();
|
||||||
|
|
||||||
|
List<AnnotationMirror> foundAnnotations = new ArrayList<>();
|
||||||
|
for (AnnotationMirror mirror : methodElement.getAnnotationMirrors()) {
|
||||||
|
String mirrorAnnotationName = Util.getAnnotationMirrorName(mirror);
|
||||||
|
if (SUPPORTED_ANNOTATIONS.contains(PACKAGE + mirrorAnnotationName)) {
|
||||||
|
foundAnnotations.add(mirror);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundAnnotations.size() > 1) {
|
||||||
|
List<String> annotationNames = new ArrayList<>();
|
||||||
|
for (AnnotationMirror mirror : foundAnnotations) {
|
||||||
|
annotationNames.add("@" + Util.getAnnotationMirrorName(mirror));
|
||||||
|
}
|
||||||
|
|
||||||
|
messager.printError(String.format("Method %s cannot have multiple mutually exclusive annotations. Found: %s. Please leave only one.",
|
||||||
|
methodName, String.join(", ", annotationNames)), methodElement);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundAnnotations.size() == 1) {
|
||||||
|
AnnotationMirror annotation = foundAnnotations.getFirst();
|
||||||
|
String annotationSimpleName = Util.getAnnotationMirrorName(annotation);
|
||||||
|
|
||||||
|
var params = methodElement.getParameters();
|
||||||
|
if (params.isEmpty()) {
|
||||||
|
messager.printError(String.format("Method %s, annotated with @%s, must have at least one parameter.",
|
||||||
|
methodName, annotationSimpleName), methodElement);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String firstParameterType = Util.getParameterClassName(params.getFirst().asType());
|
||||||
|
String requiredParameter = Util.checkParameter(annotationSimpleName, firstParameterType);
|
||||||
|
|
||||||
|
if (!requiredParameter.isEmpty()) {
|
||||||
|
messager.printError(String.format("Incorrect parameter type for method %s. Annotation @%s requires %s, but found %s.",
|
||||||
|
methodName, annotationSimpleName, requiredParameter, firstParameterType), params.getFirst());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!annotationSimpleName.equals("BotCommand")) {
|
||||||
|
String paramName = params.getFirst().getSimpleName().toString();
|
||||||
|
Set<String> filters = new HashSet<>();
|
||||||
|
|
||||||
|
AnnotationValue vv = Util.getAnnotationValue(annotation, "filters");
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<? extends AnnotationValue> enumValues = vv == null ? List.of() : (List<? extends AnnotationValue>) vv.getValue();
|
||||||
|
for (AnnotationValue v : enumValues) {
|
||||||
|
if (v.getValue() instanceof VariableElement enumConstant) {
|
||||||
|
filters.add(enumConstant.getSimpleName().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(Generator.generateVoid(methodName, String.format("%s %s", firstParameterType, paramName),
|
||||||
|
Generator.generateFilterBlock(filters, paramName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
42
annotation-processor/src/main/java/Generator.java
Normal file
42
annotation-processor/src/main/java/Generator.java
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public final class Generator {
|
||||||
|
|
||||||
|
private Generator() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateVoid(String name, String obj, String block) {
|
||||||
|
return String.format("""
|
||||||
|
public static void %s(%s) {
|
||||||
|
%s
|
||||||
|
}
|
||||||
|
""", name, obj, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateFilterBlock(Set<String> filters, String objName) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String filter : filters) {
|
||||||
|
boolean skip = true;
|
||||||
|
sb.append("if (");
|
||||||
|
sb.append(objName);
|
||||||
|
sb.append(".");
|
||||||
|
for (String part : filter.split("_")) {
|
||||||
|
part = part.toLowerCase();
|
||||||
|
if (skip) {
|
||||||
|
sb.append(part);
|
||||||
|
skip = false;
|
||||||
|
} else {
|
||||||
|
char[] chars = part.toCharArray();
|
||||||
|
chars[0] = Character.toUpperCase(chars[0]);
|
||||||
|
sb.append(chars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append("()) {");
|
||||||
|
sb.append("\n /* some code */ \n}\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
108
annotation-processor/src/main/java/HandlerWriter.java
Normal file
108
annotation-processor/src/main/java/HandlerWriter.java
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import org.objectweb.asm.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class HandlerWriter {
|
||||||
|
|
||||||
|
public static void write(String body) throws IOException {
|
||||||
|
|
||||||
|
String classFilePath = "models/Handlers.class"; // Путь к вашему файлу
|
||||||
|
Path path = Paths.get(classFilePath);
|
||||||
|
|
||||||
|
// 1. Читаем исходный класс в байты
|
||||||
|
byte[] originalBytecode = Files.readAllBytes(path);
|
||||||
|
|
||||||
|
// 2. Инициализируем ASM
|
||||||
|
ClassReader classReader = new ClassReader(originalBytecode);
|
||||||
|
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||||
|
|
||||||
|
// 3. Создаем наш визитор, который найдет и заменит метод
|
||||||
|
ClassVisitor classVisitor = new MethodReplacer(classWriter);
|
||||||
|
|
||||||
|
// 4. Запускаем процесс
|
||||||
|
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
|
||||||
|
|
||||||
|
// 5. Получаем измененный байт-код
|
||||||
|
byte[] modifiedBytecode = classWriter.toByteArray();
|
||||||
|
|
||||||
|
// 6. Перезаписываем исходный файл
|
||||||
|
Files.write(path, modifiedBytecode);
|
||||||
|
|
||||||
|
System.out.println("Метод invoke в файле " + classFilePath + " был успешно заменен.");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MethodReplacer extends ClassVisitor {
|
||||||
|
|
||||||
|
public MethodReplacer(ClassVisitor classVisitor) {
|
||||||
|
super(Opcodes.ASM9, classVisitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||||
|
String targetDescriptor = "(L" + EventHandlersProcessor.getFullParameterName("Update").replace('.', '/') +
|
||||||
|
";)V";
|
||||||
|
|
||||||
|
if ("invoke".equals(name) && targetDescriptor.equals(descriptor)) {
|
||||||
|
System.out.println("Найден метод 'invoke'. Заменяем его тело...");
|
||||||
|
// Найден нужный метод. Не передаем его дальше (старое тело удаляется).
|
||||||
|
// Вместо этого создаем новый MethodVisitor для генерации нового тела.
|
||||||
|
MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
|
generateNewBody(mv);
|
||||||
|
// Возвращаем null, так как мы уже создали метод через cv.visitMethod
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Для всех остальных методов - просто передаем их дальше без изменений
|
||||||
|
return super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateNewBody(MethodVisitor mv) {
|
||||||
|
mv.visitCode();
|
||||||
|
|
||||||
|
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
|
||||||
|
mv.visitLdcInsn("Hello from the new invoke method!");
|
||||||
|
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
|
||||||
|
|
||||||
|
// Код для: Integer updateId = update.getUpdateId();
|
||||||
|
// `update` - это первый аргумент, поэтому он в локальной переменной 1 (0 это `this`)
|
||||||
|
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||||
|
// Предполагаем, что getUpdateId() возвращает Integer. Если он возвращает int, нужно использовать valueOf.
|
||||||
|
// Для примера, пусть он возвращает Integer.
|
||||||
|
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, EventHandlersProcessor.getFullParameterName("Update"),
|
||||||
|
"getUpdateId",
|
||||||
|
"()Ljava/lang/Long;", false);
|
||||||
|
// Сохраняем результат в локальную переменную 2
|
||||||
|
mv.visitVarInsn(Opcodes.ASTORE, 2);
|
||||||
|
|
||||||
|
// Код для: System.out.println("Update ID: " + updateId);
|
||||||
|
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
|
||||||
|
// Создаем новый StringBuilder для конкатенации строк
|
||||||
|
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
|
||||||
|
mv.visitInsn(Opcodes.DUP);
|
||||||
|
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
|
||||||
|
mv.visitLdcInsn("Update ID: ");
|
||||||
|
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
|
||||||
|
// Загружаем updateId из локальной переменной 2
|
||||||
|
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||||
|
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;", false);
|
||||||
|
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
|
||||||
|
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
|
||||||
|
|
||||||
|
|
||||||
|
// Завершаем метод (поскольку он void, используем RETURN)
|
||||||
|
mv.visitInsn(Opcodes.RETURN);
|
||||||
|
|
||||||
|
// Указываем максимальный размер стека и количество локальных переменных.
|
||||||
|
// ASM может вычислить это за нас, если использовать ClassWriter.COMPUTE_FRAMES
|
||||||
|
mv.visitMaxs(0, 0); // Значения игнорируются при COMPUTE_FRAMES
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
63
annotation-processor/src/main/java/Util.java
Normal file
63
annotation-processor/src/main/java/Util.java
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
|
import javax.lang.model.element.AnnotationValue;
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.lang.model.element.ExecutableElement;
|
||||||
|
import javax.lang.model.type.DeclaredType;
|
||||||
|
import javax.lang.model.type.TypeMirror;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class Util {
|
||||||
|
|
||||||
|
private Util() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static String checkParameter(String simpleAnnotationName, String simpleParameterName) {
|
||||||
|
String reqParameter = switch (simpleAnnotationName) {
|
||||||
|
case "OnMessage", "BotCommand" -> "Message";
|
||||||
|
case "OnBusinessConnection" -> "BusinessConnection";
|
||||||
|
case "OnCallbackQuery" -> "CallbackQuery";
|
||||||
|
case "OnChatBoostRemoved" -> "ChatBoostRemoved";
|
||||||
|
case "OnChatBoostUpdated" -> "ChatBoostUpdated";
|
||||||
|
case "OnChatJoinRequest" -> "ChatJoinRequest";
|
||||||
|
case "OnChatMemberUpdated" -> "ChatMemberUpdated";
|
||||||
|
case "OnChosenInlineResult" -> "ChosenInlineResult";
|
||||||
|
case "OnInlineQuery" -> "InlineQuery";
|
||||||
|
case "OnPaidMediaPurchased" -> "PaidMediaPurchased";
|
||||||
|
case "OnPoll" -> "Poll";
|
||||||
|
case "OnPollAnswer" -> "PollAnswer";
|
||||||
|
case "OnPreCheckoutQuery" -> "PreCheckoutQuery";
|
||||||
|
case "OnShippingQuery" -> "ShippingQuery";
|
||||||
|
case "OnUpdate" -> "Update";
|
||||||
|
default -> throw new IllegalStateException("Unexpected value: " + simpleAnnotationName);
|
||||||
|
};
|
||||||
|
return reqParameter.equals(simpleParameterName) ? "" : reqParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getParameterClassName(TypeMirror typeMirror) {
|
||||||
|
String methodParameterName = "unknown (probably primitive type)";
|
||||||
|
if (typeMirror instanceof DeclaredType declaredType) {
|
||||||
|
Element typeElement = declaredType.asElement();
|
||||||
|
methodParameterName = typeElement.getSimpleName().toString();
|
||||||
|
}
|
||||||
|
return methodParameterName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getAnnotationMirrorName(AnnotationMirror mirror) {
|
||||||
|
return mirror.getAnnotationType().asElement().getSimpleName().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String attributeName) {
|
||||||
|
Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = annotationMirror.getElementValues();
|
||||||
|
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : elementValues.entrySet()) {
|
||||||
|
if (entry.getKey().getSimpleName().toString().equals(attributeName)) {
|
||||||
|
return entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
10
build.gradle
10
build.gradle
@@ -1,5 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
|
id 'application'
|
||||||
}
|
}
|
||||||
|
|
||||||
group = 'com.github.hdvtdev'
|
group = 'com.github.hdvtdev'
|
||||||
@@ -11,7 +12,16 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":core"))
|
implementation(project(":core"))
|
||||||
|
annotationProcessor(project(":annotation-processor"))
|
||||||
implementation(project(":longpolling-okhttp"))
|
implementation(project(":longpolling-okhttp"))
|
||||||
|
implementation(project(":event-handlers"))
|
||||||
|
implementation(project(":event-handlers-annotations"))
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainClass = "Main"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register('fat', Jar) {
|
tasks.register('fat', Jar) {
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package hdvtdev.telegram.core;
|
||||||
|
|
||||||
|
import hdvtdev.telegram.core.objects.Update;
|
||||||
|
|
||||||
|
public interface HandlersModule {
|
||||||
|
|
||||||
|
void dispatch(String botId, Update update);
|
||||||
|
|
||||||
|
}
|
||||||
8
core/src/main/java/hdvtdev/telegram/core/JsonModule.java
Normal file
8
core/src/main/java/hdvtdev/telegram/core/JsonModule.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package hdvtdev.telegram.core;
|
||||||
|
|
||||||
|
public interface JsonModule {
|
||||||
|
|
||||||
|
<T> T fromJson(String json, Class<T> type);
|
||||||
|
String toJson(Object object);
|
||||||
|
|
||||||
|
}
|
||||||
10
core/src/main/java/hdvtdev/telegram/core/UpdateExecutor.java
Normal file
10
core/src/main/java/hdvtdev/telegram/core/UpdateExecutor.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package hdvtdev.telegram.core;
|
||||||
|
|
||||||
|
import hdvtdev.telegram.core.objects.Update;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface UpdateExecutor {
|
||||||
|
|
||||||
|
void execute(Update update);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package hdvtdev.telegram.core.annotations;
|
||||||
|
|
||||||
|
public @interface TelegramAPI {
|
||||||
|
|
||||||
|
String since();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package hdvtdev.telegram.core.objects;
|
||||||
|
|
||||||
|
public interface GeneralObject {
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
module core {
|
module core {
|
||||||
requires com.fasterxml.jackson.databind;
|
requires com.fasterxml.jackson.databind;
|
||||||
|
requires org.jetbrains.annotations;
|
||||||
|
|
||||||
exports hdvtdev.telegram.core.exceptions;
|
exports hdvtdev.telegram.core.exceptions;
|
||||||
exports hdvtdev.telegram.core.objects.command;
|
exports hdvtdev.telegram.core.objects.command;
|
||||||
exports hdvtdev.telegram.core.annotaions;
|
|
||||||
exports hdvtdev.telegram.core.methods;
|
exports hdvtdev.telegram.core.methods;
|
||||||
exports hdvtdev.telegram.core;
|
exports hdvtdev.telegram.core;
|
||||||
exports hdvtdev.telegram.core.objects;
|
exports hdvtdev.telegram.core.objects;
|
||||||
|
|||||||
15
event-handlers-annotations/build.gradle
Normal file
15
event-handlers-annotations/build.gradle
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'com.github.hdvtdev'
|
||||||
|
version = '1.0.0'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface BotCommand {
|
||||||
|
|
||||||
|
String name() default "";
|
||||||
|
String description();
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface OnBusinessConnection {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OnCallbackQuery {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
HAS_MESSAGE,
|
||||||
|
HAS_INLINE_MESSAGE_ID,
|
||||||
|
HAS_CHAT_INSTANCE,
|
||||||
|
HAS_DATA,
|
||||||
|
HAS_GAME_SHORT_NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OnChatBoostRemoved {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OnChatBoostUpdated {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OnChatJoinRequest {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OnChatMemberUpdated {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OnChosenInlineResult {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OnInlineQuery {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface OnMessage {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
enum Filter {
|
||||||
|
HAS_MESSAGE_THREAD_ID,
|
||||||
|
HAS_SENDER_CHAT,
|
||||||
|
HAS_SENDER_BOOST_COUNT,
|
||||||
|
HAS_SENDER_BUSINESS_BOT,
|
||||||
|
HAS_BUSINESS_CONNECTION_ID,
|
||||||
|
HAS_FORWARD_ORIGIN,
|
||||||
|
HAS_REPLY_TO_MESSAGE,
|
||||||
|
HAS_EXTERNAL_REPLY,
|
||||||
|
HAS_QUOTE,
|
||||||
|
HAS_REPLY_TO_STORY,
|
||||||
|
HAS_VIA_BOT,
|
||||||
|
HAS_EDIT_DATE,
|
||||||
|
HAS_MEDIA_GROUP_ID,
|
||||||
|
HAS_AUTHOR_SIGNATURE,
|
||||||
|
HAS_TEXT,
|
||||||
|
HAS_ENTITIES,
|
||||||
|
HAS_LINK_PREVIEW_OPTIONS,
|
||||||
|
HAS_EFFECT_ID,
|
||||||
|
HAS_ANIMATION,
|
||||||
|
HAS_AUDIO,
|
||||||
|
HAS_DOCUMENT,
|
||||||
|
HAS_PAID_MEDIA_INFO,
|
||||||
|
HAS_PHOTO,
|
||||||
|
HAS_STICKER,
|
||||||
|
HAS_STORY,
|
||||||
|
HAS_VIDEO,
|
||||||
|
HAS_VIDEO_NOTE,
|
||||||
|
HAS_VOICE,
|
||||||
|
HAS_CAPTION,
|
||||||
|
HAS_CAPTION_ENTITIES,
|
||||||
|
HAS_CONTACT,
|
||||||
|
HAS_DICE,
|
||||||
|
HAS_GAME,
|
||||||
|
HAS_POLL,
|
||||||
|
HAS_VENUE,
|
||||||
|
HAS_LOCATION,
|
||||||
|
HAS_NEW_CHAT_MEMBERS,
|
||||||
|
HAS_LEFT_CHAT_MEMBER,
|
||||||
|
HAS_NEW_CHAT_TITLE,
|
||||||
|
HAS_NEW_CHAT_PHOTO,
|
||||||
|
HAS_MESSAGE_AUTO_DELETE_TIMER_CHANGED,
|
||||||
|
HAS_MIGRATE_TO_CHAT_ID,
|
||||||
|
HAS_MIGRATE_FROM_CHAT_ID
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OnPaidMediaPurchased {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OnPoll {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OnPollAnswer {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OnPreCheckoutQuery {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OnShippingQuery {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface OnUpdate {
|
||||||
|
|
||||||
|
String botId() default "primary";
|
||||||
|
|
||||||
|
Filter[] filters() default {};
|
||||||
|
|
||||||
|
enum Filter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package hdvtdev.telegram.handler.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface TelegramBotInstance {
|
||||||
|
String id() default "primary";
|
||||||
|
|
||||||
|
boolean primary();
|
||||||
|
|
||||||
|
}
|
||||||
18
event-handlers/build.gradle
Normal file
18
event-handlers/build.gradle
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'hdvtdev'
|
||||||
|
version = '1.0.0'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':core')
|
||||||
|
//annotationProcessor 'com.fasterxml.jackson.core:jackson-annotations:2.18.3'
|
||||||
|
annotationProcessor project(':annotation-processor')
|
||||||
|
implementation project(":event-handlers-annotations")
|
||||||
|
implementation 'org.jetbrains:annotations:26.0.2-1'
|
||||||
|
}
|
||||||
18
event-handlers/src/main/java/models/Handlers.java
Normal file
18
event-handlers/src/main/java/models/Handlers.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package models;
|
||||||
|
|
||||||
|
import hdvtdev.telegram.core.HandlersModule;
|
||||||
|
import hdvtdev.telegram.core.objects.Update;
|
||||||
|
|
||||||
|
public final class Handlers implements HandlersModule {
|
||||||
|
|
||||||
|
private Handlers() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispatch(String botId, Update update) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,15 +10,14 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.3'
|
implementation platform('com.fasterxml.jackson:jackson-bom:2.18.3')
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
|
||||||
|
|
||||||
|
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"))
|
implementation(project(":core"))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register('fat') {
|
|
||||||
jar {
|
|
||||||
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
|
|
||||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
this.updateExecutor = pool == null ? (Update update) -> CompletableFuture.runAsync(() -> updateConsumer.onUpdate(update))
|
||||||
Map<Class<?>, Map<String, InvokeMethod>> handlers = builder.enableScan ? ClassFinder.getClasses() : ClassFinder.localScan(updateConsumerClass);
|
: (Update update) -> pool.execute(() -> updateConsumer.onUpdate(update));
|
||||||
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_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()) {
|
for (Update update : updates) {
|
||||||
if (updateConsumer != null) CompletableFuture.runAsync(() -> updateConsumer.onUpdates(updates));
|
updateExecutor.execute(update);
|
||||||
lastUpdateId.set(updates.getLast().updateId());
|
|
||||||
}
|
}
|
||||||
} 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);
|
||||||
}
|
}
|
||||||
|
|||||||
9
longpolling-okhttp/src/main/java/module-info.java
Normal file
9
longpolling-okhttp/src/main/java/module-info.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
module longpolling.okhttp {
|
||||||
|
exports hdvtdev.telegram.longpolling.okhttp;
|
||||||
|
requires core;
|
||||||
|
requires okhttp3;
|
||||||
|
requires com.fasterxml.jackson.core;
|
||||||
|
requires com.fasterxml.jackson.databind;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,3 +2,8 @@ rootProject.name = 'TeleJ'
|
|||||||
include 'core'
|
include 'core'
|
||||||
include 'longpolling-okhttp'
|
include 'longpolling-okhttp'
|
||||||
|
|
||||||
|
|
||||||
|
include 'test'
|
||||||
|
include 'event-handlers'
|
||||||
|
include 'annotation-processor'
|
||||||
|
include 'event-handlers-annotations'
|
||||||
Reference in New Issue
Block a user