From e616ebbddb754c5b86a462cae8d40e1998e13244 Mon Sep 17 00:00:00 2001 From: "j.r@wiuwiu.de" Date: Thu, 27 Jun 2019 20:51:03 +0200 Subject: [PATCH 01/23] Made usernames case insensitive Fixed because username in Telegram are not case sensitive, eg @fooBar is the same user as @Foobar --- .../extensions/bots/commandbot/commands/CommandRegistry.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/commands/CommandRegistry.java b/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/commands/CommandRegistry.java index fdd3771b..f06e15fb 100644 --- a/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/commands/CommandRegistry.java +++ b/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/commands/CommandRegistry.java @@ -8,6 +8,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; +import java.util.regex.Pattern; /** * This class manages all the commands for a bot. You can register and deregister commands on demand @@ -122,7 +123,7 @@ public final class CommandRegistry implements ICommandRegistry { */ private String removeUsernameFromCommandIfNeeded(String command) { if (allowCommandsWithUsername) { - return command.replace("@" + botUsername, "").trim(); + return command.replaceAll("(?i)@" + Pattern.quote(botUsername), "").trim(); } return command; } From 01a7e7dc2120fbe010b1626661003fe692b3b53e Mon Sep 17 00:00:00 2001 From: "j.r@wiuwiu.de" Date: Thu, 27 Jun 2019 20:58:16 +0200 Subject: [PATCH 02/23] Fixed bot username Let the programmer of the bot decide where the username should come from --- .../bots/commandbot/TelegramLongPollingCommandBot.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/TelegramLongPollingCommandBot.java b/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/TelegramLongPollingCommandBot.java index 8bb39742..88f8b5c5 100644 --- a/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/TelegramLongPollingCommandBot.java +++ b/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/TelegramLongPollingCommandBot.java @@ -58,7 +58,7 @@ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingB public TelegramLongPollingCommandBot(DefaultBotOptions options, boolean allowCommandsWithUsername, String botUsername) { super(options); this.botUsername = botUsername; - this.commandRegistry = new CommandRegistry(allowCommandsWithUsername, botUsername); + this.commandRegistry = new CommandRegistry(allowCommandsWithUsername, this.getBotUsername()); } @Override @@ -143,9 +143,7 @@ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingB * @return Bot username */ @Override - public final String getBotUsername() { - return botUsername; - } + public abstract String getBotUsername(); /** * Process all updates, that are not commands. From 91508aeebbdead012bf698b725d180d3c674f7f1 Mon Sep 17 00:00:00 2001 From: "j.r@wiuwiu.de" Date: Thu, 27 Jun 2019 21:06:08 +0200 Subject: [PATCH 03/23] Removed unnecesarry botUsername argument --- .../commandbot/TelegramLongPollingCommandBot.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/TelegramLongPollingCommandBot.java b/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/TelegramLongPollingCommandBot.java index 88f8b5c5..05de2c72 100644 --- a/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/TelegramLongPollingCommandBot.java +++ b/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/TelegramLongPollingCommandBot.java @@ -22,16 +22,14 @@ import java.util.function.BiConsumer; */ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingBot implements ICommandRegistry { private final CommandRegistry commandRegistry; - private String botUsername; /** * Creates a TelegramLongPollingCommandBot using default options * Use ICommandRegistry's methods on this bot to register commands * - * @param botUsername Username of the bot */ - public TelegramLongPollingCommandBot(String botUsername) { - this(ApiContext.getInstance(DefaultBotOptions.class), botUsername); + public TelegramLongPollingCommandBot() { + this(ApiContext.getInstance(DefaultBotOptions.class)); } /** @@ -40,10 +38,9 @@ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingB * Use ICommandRegistry's methods on this bot to register commands * * @param options Bot options - * @param botUsername Username of the bot */ - public TelegramLongPollingCommandBot(DefaultBotOptions options, String botUsername) { - this(options, true, botUsername); + public TelegramLongPollingCommandBot(DefaultBotOptions options) { + this(options, true); } /** @@ -53,11 +50,9 @@ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingB * @param options Bot options * @param allowCommandsWithUsername true to allow commands with parameters (default), * false otherwise - * @param botUsername bot username of this bot */ - public TelegramLongPollingCommandBot(DefaultBotOptions options, boolean allowCommandsWithUsername, String botUsername) { + public TelegramLongPollingCommandBot(DefaultBotOptions options, boolean allowCommandsWithUsername) { super(options); - this.botUsername = botUsername; this.commandRegistry = new CommandRegistry(allowCommandsWithUsername, this.getBotUsername()); } From 727f5a7a3ed302439f9d3b359bfd601613a45cd6 Mon Sep 17 00:00:00 2001 From: Bernhard Kralofsky Date: Tue, 9 Jul 2019 21:39:02 +0200 Subject: [PATCH 04/23] prevent nullPointerExceptions when using message flags without a MESSAGE flag first --- .../org/telegram/abilitybots/api/objects/Flag.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Flag.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Flag.java index f59cc25c..0bf5a905 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Flag.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Flag.java @@ -27,12 +27,12 @@ public enum Flag implements Predicate { CHOSEN_INLINE_QUERY(Update::hasChosenInlineQuery), // Message Flags - REPLY(update -> update.getMessage().isReply()), - DOCUMENT(upd -> upd.getMessage().hasDocument()), - TEXT(upd -> upd.getMessage().hasText()), - PHOTO(upd -> upd.getMessage().hasPhoto()), - LOCATION(upd -> upd.getMessage().hasLocation()), - CAPTION(upd -> nonNull(upd.getMessage().getCaption())); + REPLY(upd -> upd.hasMessage() && upd.getMessage().isReply()), + DOCUMENT(upd -> upd.hasMessage() && upd.getMessage().hasDocument()), + TEXT(upd -> upd.hasMessage() && upd.getMessage().hasText()), + PHOTO(upd -> upd.hasMessage() && upd.getMessage().hasPhoto()), + LOCATION(upd -> upd.hasMessage() && upd.getMessage().hasLocation()), + CAPTION(upd -> upd.hasMessage() && nonNull(upd.getMessage().getCaption())); private final Predicate predicate; From afe35b4b142689bde2eaeaeb42786e92768e72cd Mon Sep 17 00:00:00 2001 From: nagorny Date: Sat, 24 Aug 2019 18:57:34 +0300 Subject: [PATCH 05/23] Fixed #652 --- .../java/org/telegram/telegrambots/bots/DefaultAbsSender.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java b/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java index 0372eeb9..b94d3a09 100644 --- a/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java +++ b/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java @@ -414,7 +414,7 @@ public abstract class DefaultAbsSender extends AbsSender { return sendAudio.deserializeResponse(sendHttpPostRequest(httppost)); } catch (IOException e) { - throw new TelegramApiException("Unable to send sticker", e); + throw new TelegramApiException("Unable to send audio", e); } } From ef1bd09a6ae312da059076e82de2ffafd98f45e8 Mon Sep 17 00:00:00 2001 From: UnAfraid Date: Mon, 9 Sep 2019 20:10:26 +0300 Subject: [PATCH 06/23] Replacing log4j with slf4j --- pom.xml | 14 +++++++------- telegrambots-abilities/pom.xml | 11 +++++++++++ .../abilitybots/api/bot/BaseAbilityBot.java | 6 +++--- .../telegram/abilitybots/api/db/MapDBContext.java | 6 +++--- .../telegram/abilitybots/api/objects/Ability.java | 6 +++--- .../abilitybots/api/sender/SilentSender.java | 6 +++--- .../org/telegram/telegrambots/meta/ApiContext.java | 6 +++--- .../exceptions/TelegramApiRequestException.java | 8 ++++---- .../starter/TelegramBotInitializer.java | 6 +++--- .../updatesreceivers/DefaultBotSession.java | 12 ++++++------ .../telegrambots/updatesreceivers/RestApi.java | 6 +++--- 11 files changed, 49 insertions(+), 38 deletions(-) diff --git a/pom.xml b/pom.xml index 37193d47..c6869154 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 3.0.0 2.9.9 2.9.9.1 - 2.12.0 + 1.7.28 @@ -78,9 +78,9 @@ ${jackson.version} - org.apache.logging.log4j - log4j-core - ${log4j.version} + org.slf4j + slf4j-api + ${slf4j.version} @@ -108,9 +108,9 @@ - org.apache.logging.log4j - log4j-core - ${log4j.version} + org.slf4j + slf4j-api + ${slf4j.version} \ No newline at end of file diff --git a/telegrambots-abilities/pom.xml b/telegrambots-abilities/pom.xml index 9f8dc9b5..7f420e0a 100644 --- a/telegrambots-abilities/pom.xml +++ b/telegrambots-abilities/pom.xml @@ -78,6 +78,7 @@ 3.9 3.0.7 + 2.12.1 @@ -102,6 +103,16 @@ + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java index d5f9c0ef..ca94dc0c 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java @@ -6,9 +6,9 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimap; import org.apache.commons.io.IOUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.telegram.abilitybots.api.db.DBContext; import org.telegram.abilitybots.api.objects.Ability; import org.telegram.abilitybots.api.objects.Locality; @@ -139,7 +139,7 @@ import static org.telegram.abilitybots.api.util.AbilityUtils.stripTag; */ @SuppressWarnings({"ConfusingArgumentToVarargsMethod", "UnusedReturnValue", "WeakerAccess", "unused", "ConstantConditions"}) public abstract class BaseAbilityBot extends DefaultAbsSender implements AbilityExtension { - private static final Logger log = LogManager.getLogger(BaseAbilityBot.class); + private static final Logger log = LoggerFactory.getLogger(BaseAbilityBot.class); // DB objects public static final String ADMINS = "ADMINS"; diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/db/MapDBContext.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/db/MapDBContext.java index a6977917..c39a7b3f 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/db/MapDBContext.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/db/MapDBContext.java @@ -3,12 +3,12 @@ package org.telegram.abilitybots.api.db; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.mapdb.Atomic; import org.mapdb.DB; import org.mapdb.DBMaker; import org.mapdb.Serializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.telegram.abilitybots.api.util.Pair; import java.io.IOException; @@ -35,7 +35,7 @@ import static org.mapdb.Serializer.JAVA; */ @SuppressWarnings({"unchecked", "WeakerAccess"}) public class MapDBContext implements DBContext { - private static final Logger log = LogManager.getLogger(MapDBContext.class); + private static final Logger log = LoggerFactory.getLogger(MapDBContext.class); private final DB db; private final ObjectMapper objectMapper; diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Ability.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Ability.java index c3004c8b..fb6b27a3 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Ability.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Ability.java @@ -2,8 +2,8 @@ package org.telegram.abilitybots.api.objects; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.telegram.telegrambots.meta.api.objects.Update; import java.util.Arrays; @@ -35,7 +35,7 @@ import static org.apache.commons.lang3.StringUtils.*; * @author Abbas Abou Daya */ public final class Ability { - private static final Logger log = LogManager.getLogger(Ability.class); + private static final Logger log = LoggerFactory.getLogger(Ability.class); private final String name; private final String info; diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/sender/SilentSender.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/sender/SilentSender.java index b5d48bfb..2745f757 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/sender/SilentSender.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/sender/SilentSender.java @@ -1,7 +1,7 @@ package org.telegram.abilitybots.api.sender; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.telegram.telegrambots.meta.api.methods.BotApiMethod; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.objects.Message; @@ -18,7 +18,7 @@ import java.util.Optional; * @author Abbas Abou Daya */ public class SilentSender { - private static final Logger log = LogManager.getLogger(SilentSender.class); + private static final Logger log = LoggerFactory.getLogger(SilentSender.class); private final MessageSender sender; diff --git a/telegrambots-meta/src/main/java/org/telegram/telegrambots/meta/ApiContext.java b/telegrambots-meta/src/main/java/org/telegram/telegrambots/meta/ApiContext.java index a5a2e0ce..3859fa91 100644 --- a/telegrambots-meta/src/main/java/org/telegram/telegrambots/meta/ApiContext.java +++ b/telegrambots-meta/src/main/java/org/telegram/telegrambots/meta/ApiContext.java @@ -4,8 +4,8 @@ import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Singleton; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.text.MessageFormat; import java.util.HashMap; @@ -16,7 +16,7 @@ import java.util.Map; * @version 1.0 */ public class ApiContext { - private static final Logger log = LogManager.getLogger(ApiContext.class); + private static final Logger log = LoggerFactory.getLogger(ApiContext.class); private static final Object lock = new Object(); private static Injector INJECTOR; diff --git a/telegrambots-meta/src/main/java/org/telegram/telegrambots/meta/exceptions/TelegramApiRequestException.java b/telegrambots-meta/src/main/java/org/telegram/telegrambots/meta/exceptions/TelegramApiRequestException.java index 01d52b38..1dd7bcf7 100644 --- a/telegrambots-meta/src/main/java/org/telegram/telegrambots/meta/exceptions/TelegramApiRequestException.java +++ b/telegrambots-meta/src/main/java/org/telegram/telegrambots/meta/exceptions/TelegramApiRequestException.java @@ -18,9 +18,9 @@ package org.telegram.telegrambots.meta.exceptions; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.telegram.telegrambots.meta.api.objects.ApiResponse; import org.telegram.telegrambots.meta.api.objects.ResponseParameters; @@ -33,7 +33,7 @@ import java.io.IOException; * Exception thrown when something goes wrong in the api */ public class TelegramApiRequestException extends TelegramApiException { - private static final Logger log = LogManager.getLogger(TelegramApiRequestException.class); + private static final Logger log = LoggerFactory.getLogger(TelegramApiRequestException.class); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String ERRORDESCRIPTIONFIELD = "description"; @@ -56,7 +56,7 @@ public class TelegramApiRequestException extends TelegramApiException { try { parameters = OBJECT_MAPPER.readValue(object.getJSONObject(PARAMETERSFIELD).toString(), ResponseParameters.class); } catch (IOException e) { - log.fatal(e.getLocalizedMessage(), e); + log.error(e.getLocalizedMessage(), e); } } } diff --git a/telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/TelegramBotInitializer.java b/telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/TelegramBotInitializer.java index 951dc3b8..c0911641 100644 --- a/telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/TelegramBotInitializer.java +++ b/telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/TelegramBotInitializer.java @@ -1,7 +1,7 @@ package org.telegram.telegrambots.starter; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.telegram.telegrambots.meta.TelegramBotsApi; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; @@ -21,7 +21,7 @@ import static java.lang.String.format; * Receives all beand which are #LongPollingBot and #WebhookBot and register them in #TelegramBotsApi. */ public class TelegramBotInitializer implements InitializingBean { - private static final Logger log = LogManager.getLogger(TelegramBotInitializer.class); + private static final Logger log = LoggerFactory.getLogger(TelegramBotInitializer.class); private final TelegramBotsApi telegramBotsApi; private final List longPollingBots; diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/DefaultBotSession.java b/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/DefaultBotSession.java index 319e39ac..78a84d8d 100644 --- a/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/DefaultBotSession.java +++ b/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/DefaultBotSession.java @@ -11,9 +11,9 @@ import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.json.JSONException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.telegram.telegrambots.meta.ApiConstants; import org.telegram.telegrambots.meta.api.methods.updates.GetUpdates; import org.telegram.telegrambots.meta.api.objects.Update; @@ -40,7 +40,7 @@ import static org.telegram.telegrambots.Constants.SOCKET_TIMEOUT; * Thread to request updates with active wait */ public class DefaultBotSession implements BotSession { - private static final Logger log = LogManager.getLogger(DefaultBotSession.class); + private static final Logger log = LoggerFactory.getLogger(DefaultBotSession.class); private AtomicBoolean running = new AtomicBoolean(false); @@ -207,7 +207,7 @@ public class DefaultBotSession implements BotSession { log.debug(e.getLocalizedMessage(), e); interrupt(); } catch (Exception global) { - log.fatal(global.getLocalizedMessage(), global); + log.error(global.getLocalizedMessage(), global); try { synchronized (lock) { lock.wait(exponentialBackOff.nextBackOffMillis()); @@ -263,7 +263,7 @@ public class DefaultBotSession implements BotSession { } } } catch (SocketException | InvalidObjectException | TelegramApiRequestException e) { - log.fatal(e.getLocalizedMessage(), e); + log.error(e.getLocalizedMessage(), e); } catch (SocketTimeoutException e) { log.info(e.getLocalizedMessage(), e); } catch (InterruptedException e) { @@ -309,7 +309,7 @@ public class DefaultBotSession implements BotSession { log.debug(e.getLocalizedMessage(), e); interrupt(); } catch (Exception e) { - log.fatal(e.getLocalizedMessage(), e); + log.error(e.getLocalizedMessage(), e); } } log.debug("Handler thread has being closed"); diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/RestApi.java b/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/RestApi.java index 35fbc7f1..a6b4725f 100644 --- a/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/RestApi.java +++ b/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/RestApi.java @@ -1,7 +1,7 @@ package org.telegram.telegrambots.updatesreceivers; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.telegram.telegrambots.meta.api.methods.BotApiMethod; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.exceptions.TelegramApiValidationException; @@ -24,7 +24,7 @@ import java.util.concurrent.ConcurrentHashMap; */ @Path("callback") public class RestApi { - private static final Logger log = LogManager.getLogger(RestApi.class); + private static final Logger log = LoggerFactory.getLogger(RestApi.class); private final ConcurrentHashMap callbacks = new ConcurrentHashMap<>(); From aa3448544e142594a77d2cda8c8756283f3dcff3 Mon Sep 17 00:00:00 2001 From: Abbas Abou Daya Date: Mon, 16 Sep 2019 22:37:16 -0700 Subject: [PATCH 07/23] Support backup and recovery of db vars --- .../abilitybots/api/db/BackupVar.java | 45 +++++++++++++++++++ .../abilitybots/api/db/MapDBContext.java | 15 +++---- .../abilitybots/api/db/MapDBContextTest.java | 19 ++++++++ 3 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/db/BackupVar.java diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/db/BackupVar.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/db/BackupVar.java new file mode 100644 index 00000000..d2b6d03c --- /dev/null +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/db/BackupVar.java @@ -0,0 +1,45 @@ +package org.telegram.abilitybots.api.db; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; + +import java.util.Objects; + +public class BackupVar { + @JsonProperty("var") + private final T var; + + private BackupVar(T var) { + this.var = var; + } + + @JsonCreator + public static BackupVar createVar(@JsonProperty("var") R var) { + return new BackupVar<>(var); + } + + public T var() { + return var; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BackupVar backupVar = (BackupVar) o; + return Objects.equals(var, backupVar.var); + } + + @Override + public int hashCode() { + return Objects.hash(var); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("var", var) + .toString(); + } +} diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/db/MapDBContext.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/db/MapDBContext.java index a6977917..ab0fa07f 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/db/MapDBContext.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/db/MapDBContext.java @@ -12,12 +12,7 @@ import org.mapdb.Serializer; import org.telegram.abilitybots.api.util.Pair; import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.StringJoiner; +import java.util.*; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Sets.newHashSet; @@ -188,15 +183,15 @@ public class MapDBContext implements DBContext { return Pair.of(entry.getKey(), newArrayList((List) struct)); else if (struct instanceof Map) return Pair.of(entry.getKey(), new BackupMap((Map) struct)); - else - return Pair.of(entry.getKey(), struct); + else if (struct instanceof Atomic.Var) + return Pair.of(entry.getKey(), BackupVar.createVar(((Atomic.Var) struct).get())); + return Pair.of(entry.getKey(), struct); }).collect(toMap(pair -> (String) pair.a(), Pair::b)); } private void doRecover(Map backupData) { clear(); backupData.forEach((name, value) -> { - if (value instanceof Set) { Set entrySet = (Set) value; getSet(name).addAll(entrySet); @@ -206,6 +201,8 @@ public class MapDBContext implements DBContext { } else if (value instanceof List) { List entryList = (List) value; getList(name).addAll(entryList); + } else if (value instanceof BackupVar) { + getVar(name).set(((BackupVar) value).var()); } else { log.error(format("Unable to identify object type during DB recovery, entry name: %s", name)); } diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/db/MapDBContextTest.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/db/MapDBContextTest.java index 267450fd..f67dc6ca 100644 --- a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/db/MapDBContextTest.java +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/db/MapDBContextTest.java @@ -12,6 +12,7 @@ import java.util.Set; import static com.google.common.collect.Maps.newHashMap; import static com.google.common.collect.Sets.newHashSet; +import static com.google.common.collect.Sets.toImmutableEnumSet; import static java.lang.String.format; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -38,6 +39,24 @@ class MapDBContextTest { db.close(); } + @Test + void canRecoverVar() { + Var test = db.getVar(TEST); + String val = "abilitybot"; + test.set(val); + + Object backup = db.backup(); + db.clear(); + // db.clear does not clear atomic variables + // TODO: get clear to remove all non-collection variables in DB + test.set("somevalue"); + boolean recovered = db.recover(backup); + String recoveredVal = db.getVar(TEST).get(); + + assertTrue(recovered, "Could not recover JSON backup file"); + assertEquals(val, recoveredVal, "Could not properly recover val from Var in DB"); + } + @Test void canRecoverDB() { Map users = db.getMap(USERS); From b3c6623eaa8e4bc96a0802af4e2075b4783049c8 Mon Sep 17 00:00:00 2001 From: Abbas Abou Daya Date: Mon, 16 Sep 2019 22:44:33 -0700 Subject: [PATCH 08/23] Change sender to silent for the usage of forceReply in wiki --- TelegramBots.wiki/abilities/Using-Replies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TelegramBots.wiki/abilities/Using-Replies.md b/TelegramBots.wiki/abilities/Using-Replies.md index 82307360..5ca5f1b8 100644 --- a/TelegramBots.wiki/abilities/Using-Replies.md +++ b/TelegramBots.wiki/abilities/Using-Replies.md @@ -32,7 +32,7 @@ public Ability playWithMe() { .privacy(PUBLIC) .locality(ALL) .input(0) - .action(ctx -> sender.forceReply(playMessage, ctx.chatId())) + .action(ctx -> silent.forceReply(playMessage, ctx.chatId())) // The signature of a reply is -> (Consumer action, Predicate... conditions) // So, we first declare the action that takes an update (NOT A MESSAGECONTEXT) like the action above // The reason of that is that a reply can be so versatile depending on the message, context becomes an inefficient wrapping From 7b7478f180eccad0b361dfb81221aec60fc0474b Mon Sep 17 00:00:00 2001 From: Abbas Abou Daya Date: Mon, 16 Sep 2019 22:48:22 -0700 Subject: [PATCH 09/23] Another sender to silent change --- TelegramBots.wiki/abilities/Using-Replies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TelegramBots.wiki/abilities/Using-Replies.md b/TelegramBots.wiki/abilities/Using-Replies.md index 5ca5f1b8..0067a67b 100644 --- a/TelegramBots.wiki/abilities/Using-Replies.md +++ b/TelegramBots.wiki/abilities/Using-Replies.md @@ -40,7 +40,7 @@ public Ability playWithMe() { // Prints to console System.out.println("I'm in a reply!"); // Sends message - sender.send("It's been nice playing with you!", upd.getMessage().getChatId()); + silent.send("It's been nice playing with you!", upd.getMessage().getChatId()); }, // Now we start declaring conditions, MESSAGE is a member of the enum Flag class // That class contains out-of-the-box predicates for your replies! From ed7333a21e9c62e6a93dd644b9cdf9e62adef224 Mon Sep 17 00:00:00 2001 From: Abbas Abou Daya Date: Mon, 16 Sep 2019 23:40:20 -0700 Subject: [PATCH 10/23] Fix executeAsync in SilentSender to properly call sender.executeAsync --- .../abilitybots/api/sender/SilentSender.java | 7 ++-- .../api/sender/SilentSenderTest.java | 38 ++++++++++++++++++- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/sender/SilentSender.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/sender/SilentSender.java index b5d48bfb..bc87c328 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/sender/SilentSender.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/sender/SilentSender.java @@ -7,6 +7,7 @@ import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.api.objects.replykeyboard.ForceReplyKeyboard; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; +import org.telegram.telegrambots.meta.updateshandlers.SentCallback; import java.io.Serializable; import java.util.Optional; @@ -52,12 +53,12 @@ public class SilentSender { } } - public > Optional executeAsync(Method method) { + public , Callback extends SentCallback> void + executeAsync(Method method, Callback callable) { try { - return Optional.ofNullable(sender.execute(method)); + sender.executeAsync(method, callable); } catch (TelegramApiException e) { log.error("Could not execute bot API method", e); - return Optional.empty(); } } diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/sender/SilentSenderTest.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/sender/SilentSenderTest.java index 96da662c..884cf72d 100644 --- a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/sender/SilentSenderTest.java +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/sender/SilentSenderTest.java @@ -2,15 +2,19 @@ package org.telegram.abilitybots.api.sender; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.telegram.telegrambots.meta.api.methods.BotApiMethod; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; +import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; +import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException; +import org.telegram.telegrambots.meta.updateshandlers.SentCallback; import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; class SilentSenderTest { private SilentSender silent; @@ -40,4 +44,34 @@ class SilentSenderTest { assertEquals(data, execute.get(), "Silent execution resulted in a different object"); } + + @Test + void callsAsyncVariantOfExecute() throws TelegramApiException { + SendMessage methodObject = new SendMessage(); + NoOpCallback callback = new NoOpCallback(); + + silent.executeAsync(methodObject, callback); + + verify(sender, only()).executeAsync(methodObject, callback); + } + + private class NoOpCallback implements SentCallback { + + @Override + public void onResult(BotApiMethod method, Message response) { + + } + + @Override + public void onError(BotApiMethod method, TelegramApiRequestException apiException) { + + } + + @Override + public void onException(BotApiMethod method, Exception exception) { + + } + } + + ; } \ No newline at end of file From 837b4d236073715dd2fef85017ca718abdb4ec4d Mon Sep 17 00:00:00 2001 From: Bernhard Date: Tue, 17 Sep 2019 10:51:14 +0300 Subject: [PATCH 11/23] requested syntax changes --- .../org/telegram/abilitybots/api/objects/Flag.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Flag.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Flag.java index 0bf5a905..3712dc9a 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Flag.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Flag.java @@ -27,12 +27,12 @@ public enum Flag implements Predicate { CHOSEN_INLINE_QUERY(Update::hasChosenInlineQuery), // Message Flags - REPLY(upd -> upd.hasMessage() && upd.getMessage().isReply()), - DOCUMENT(upd -> upd.hasMessage() && upd.getMessage().hasDocument()), - TEXT(upd -> upd.hasMessage() && upd.getMessage().hasText()), - PHOTO(upd -> upd.hasMessage() && upd.getMessage().hasPhoto()), - LOCATION(upd -> upd.hasMessage() && upd.getMessage().hasLocation()), - CAPTION(upd -> upd.hasMessage() && nonNull(upd.getMessage().getCaption())); + REPLY(upd -> MESSAGE.test(upd) && upd.getMessage().isReply()), + DOCUMENT(upd -> MESSAGE.test(upd) && upd.getMessage().hasDocument()), + TEXT(upd -> MESSAGE.test(upd) && upd.getMessage().hasText()), + PHOTO(upd -> MESSAGE.test(upd) && upd.getMessage().hasPhoto()), + LOCATION(upd -> MESSAGE.test(upd) && upd.getMessage().hasLocation()), + CAPTION(upd -> MESSAGE.test(upd) && nonNull(upd.getMessage().getCaption())); private final Predicate predicate; From 0ff63149f7e61ed8bf553a1726a2e031e5371b29 Mon Sep 17 00:00:00 2001 From: Abbas Abou Daya Date: Wed, 25 Sep 2019 23:25:11 -0700 Subject: [PATCH 12/23] Add ReplyFlow implementation, tests, and wiki This commit also has a some minor test refactoring. --- TelegramBots.wiki/_Sidebar.md | 1 + TelegramBots.wiki/abilities/State-Machines.md | 134 ++++++++++++++++ .../abilities/img/replyflow_diagram.svg | 3 + .../abilitybots/api/bot/BaseAbilityBot.java | 9 +- .../abilitybots/api/objects/Reply.java | 31 +++- .../abilitybots/api/objects/ReplyFlow.java | 117 ++++++++++++++ .../api/bot/AbilityBotI18nTest.java | 2 +- .../abilitybots/api/bot/AbilityBotTest.java | 70 ++------- .../abilitybots/api/bot/ReplyFlowTest.java | 145 ++++++++++++++++++ .../abilitybots/api/bot/TestUtils.java | 60 ++++++++ .../abilitybots/api/db/MapDBContextTest.java | 4 +- 11 files changed, 505 insertions(+), 71 deletions(-) create mode 100644 TelegramBots.wiki/abilities/State-Machines.md create mode 100644 TelegramBots.wiki/abilities/img/replyflow_diagram.svg create mode 100644 telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/ReplyFlow.java create mode 100644 telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/ReplyFlowTest.java create mode 100644 telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/TestUtils.java diff --git a/TelegramBots.wiki/_Sidebar.md b/TelegramBots.wiki/_Sidebar.md index f423f02e..7e68bf7c 100644 --- a/TelegramBots.wiki/_Sidebar.md +++ b/TelegramBots.wiki/_Sidebar.md @@ -8,6 +8,7 @@ * [[Simple Example]] * [[Hello Ability]] * [[Using Replies]] + * [[State Machines]] * [[Database Handling]] * [[Bot Testing]] * [[Bot Recovery]] diff --git a/TelegramBots.wiki/abilities/State-Machines.md b/TelegramBots.wiki/abilities/State-Machines.md new file mode 100644 index 00000000..92d2af42 --- /dev/null +++ b/TelegramBots.wiki/abilities/State-Machines.md @@ -0,0 +1,134 @@ +# State Machines + +AbilityBot supports state machines using ReplyFlows. Internally, they set and transition the state of the user based on their actions so far. +Developers may declare this flow control in either a bottom-up or a top-down approach. If you're already familiar with what a `Reply` is, consider ReplyFlows as the cherry on top. + +## Usage +A ReplyFlow can not be directly instantiated; it must be built. First, let's create +some basic replies. +```java +Reply saidLeft = Reply.of(upd -> + silent.send("Sir, I have gone left.", getChatId(upd)), + hasMessageWith("left")); + +Reply saidRight = Reply.of(upd -> + silent.send("Sir, I have gone right.", getChatId(upd)), + hasMessageWith("right")); +``` +The first `Reply` effectively replies to any message that has the text "left". Once such a message is received, the +bot replies with "Sir, I have gone left". Likewise, the bot acts for when "right" is encountered. + +What if now, you'd like to protect those two replies behind one more reply? Let's say, the bot first should ask the user to give it directions. +This means that people can't tell your bot to turn left or right UNLESS the bot asks for directions. Let's trigger that when the user sends "wake up" to the bot! + +```java +// We instantiate a ReplyFlow builder with our internal db (DBContext instance) passed +// State is always preserved in the db of the bot and remains even after termination +ReplyFlow.builder(db) + // Just like replies, a ReplyFlow can take an action, here we want to send a + // statement to prompt the user for directions! + .action(upd -> silent.send("Command me to go left or right!", getChatId(upd))) + // We should only trigger this flow when the user says "wake up" + .onlyIf(hasMessageWith("wake up")) + // The next method takes in an object of type Reply. + // Here we chain our replies together + .next(saidLeft) + // We chain one more reply, which is when the user commands your bot to go right + .next(saidRight) + // Finally, we build our ReplyFlow + .build(); +``` +For the sake of completeness, here's the auxiliary method `hasMessageWith`. +```java +private Predicate hasMessageWith(String msg) { + return upd -> upd.getMessage().getText().equalsIgnoreCase(msg); +} +``` +To run this example in your own AbilityBot, just have a method return that ReplyFlow we just built. Yup, it's that easy, just like how you're used to +building replies and abilities. +## More Complex States +Let's say that your bot becomes naughty when the user asks it to go left. We want the bot to say "I don't know how to go left." when the user commands it to go left. We would also like to chain more commands after this state. Here's +how that's done. +We must create a new ReplyFlow that would be chained to the initial one. Here's what our left flow would look like. +```java +ReplyFlow leftflow = ReplyFlow.builder(db) + .action(upd -> silent.send("I don't know how to go left.", getChatId(upd))) + .onlyIf(hasMessageWith("left")) + .next(saidLeft) + .build(); +``` +And now, saidLeft reply becomes: +```java +Reply saidLeft = Reply.of(upd -> silent.send("Sir, I have gone left.", getChatId(upd)), + hasMessageWith("go left or else")); +``` +Now, after your naughty bot retaliates, the user can say "go left or else" to force the bot to go left. Awesome, our logic now looks like this: +
+ +![Alt text](./img/replyflow_diagram.svg) + + +
+ +## Complete Example +```java +public static class ReplyFlowBot extends AbilityBot { + public class ReplyFlowBot extends AbilityBot { + public ReplyFlowBot(String botToken, String botUsername) { + super(botToken, botUsername); + } + + @Override + public int creatorId() { + return ; + } + + public ReplyFlow directionFlow() { + Reply saidLeft = Reply.of(upd -> silent.send("Sir, I have gone left.", getChatId(upd)), + hasMessageWith("go left or else")); + + ReplyFlow leftflow = ReplyFlow.builder(db) + .action(upd -> silent.send("I don't know how to go left.", getChatId(upd))) + .onlyIf(hasMessageWith("left")) + .next(saidLeft).build(); + + Reply saidRight = Reply.of(upd -> silent.send("Sir, I have gone right.", getChatId(upd)), + hasMessageWith("right")); + + return ReplyFlow.builder(db) + .action(upd -> silent.send("Command me to go left or right!", getChatId(upd))) + .onlyIf(hasMessageWith("wake up")) + .next(leftflow) + .next(saidRight) + .build(); + } + + @NotNull + private Predicate hasMessageWith(String msg) { + return upd -> upd.getMessage().getText().equalsIgnoreCase(msg); + } + } +``` +## Inline Declaration +As you can see in the above example, we used a bottom-up approach. We declared the leaf replies before we got to the root reply flow. +If you'd rather have a top-down approach, then you may declare your replies inline to achieve that. + +```java +ReplyFlow.builder(db) + .action(upd -> silent.send("Command me to go left or right!", getChatId(upd))) + .onlyIf(hasMessageWith("wake up")) + .next(Reply.of(upd -> + silent.send("Sir, I have gone left.", getChatId(upd)), + hasMessageWith("left"))) + .next(Reply.of(upd -> + silent.send("Sir, I have gone right.", getChatId(upd)), + hasMessageWith("right"))) + .build(); +``` +## State Consistency +Under the hood, AbilityBot will generate integers that represent the state of the instigating user. However, +if you add more replies and reply flows, these integers may no longer be consistent. If you'd like to always have consistent state IDs, you +should always pass a unique ID to the ReplyFlow builder like so: +```java +ReplyFlow.builder(db, ) +``` \ No newline at end of file diff --git a/TelegramBots.wiki/abilities/img/replyflow_diagram.svg b/TelegramBots.wiki/abilities/img/replyflow_diagram.svg new file mode 100644 index 00000000..b5311ead --- /dev/null +++ b/TelegramBots.wiki/abilities/img/replyflow_diagram.svg @@ -0,0 +1,3 @@ + + +
wake up
wake up
Command me to go left or right!
Command me to go left or right!
right
right
Sir, I have gone right. 
Sir, I have gone right. 
left
left
I don't know how to go left.
I don't know how to go left.
go left or else
go left or else
Sir, I have gone left.
Sir, I have gone left.
user
user
bot
bot
\ No newline at end of file diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java index d5f9c0ef..7a6143ab 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java @@ -680,7 +680,8 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability Stream extensionReplies = extensions.stream() .flatMap(ext -> stream(ext.getClass().getMethods()) .filter(checkReturnType(Reply.class)) - .map(returnReply(ext))); + .map(returnReply(ext))) + .flatMap(Reply::stream); // Replies can be standalone or attached to abilities, fetch those too Stream abilityReplies = abilities.values().stream() @@ -709,7 +710,7 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability /** * Invokes the method and retrieves its return {@link Reply}. * - * @param obj an bot or extension that this method is invoked with + * @param obj a bot or extension that this method is invoked with * @return a {@link Function} which returns the {@link Reply} returned by the given method */ private Function returnExtension(Object obj) { @@ -726,7 +727,7 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability /** * Invokes the method and retrieves its return {@link Ability}. * - * @param obj an bot or extension that this method is invoked with + * @param obj a bot or extension that this method is invoked with * @return a {@link Function} which returns the {@link Ability} returned by the given method */ private Function returnAbility(Object obj) { @@ -743,7 +744,7 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability /** * Invokes the method and retrieves its return {@link Reply}. * - * @param obj an bot or extension that this method is invoked with + * @param obj a bot or extension that this method is invoked with * @return a {@link Function} which returns the {@link Reply} returned by the given method */ private Function returnReply(Object obj) { diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Reply.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Reply.java index a74e4e2f..6af4c921 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Reply.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Reply.java @@ -1,14 +1,19 @@ package org.telegram.abilitybots.api.objects; import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import org.telegram.telegrambots.meta.api.objects.Update; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.stream.Stream; +import static com.google.common.collect.Lists.newArrayList; import static java.util.Arrays.asList; /** @@ -18,18 +23,24 @@ import static java.util.Arrays.asList; * * @author Abbas Abou Daya */ -public final class Reply { +public class Reply { public final List> conditions; public final Consumer action; - private Reply(List> conditions, Consumer action) { - this.conditions = conditions; + Reply(List> conditions, Consumer action) { + this.conditions = ImmutableList.>builder() + .addAll(conditions) + .build(); this.action = action; } + public static Reply of(Consumer action, List> conditions) { + return new Reply(conditions, action); + } + @SafeVarargs public static Reply of(Consumer action, Predicate... conditions) { - return new Reply(asList(conditions), action); + return Reply.of(action, newArrayList(conditions)); } public boolean isOkFor(Update update) { @@ -42,6 +53,18 @@ public final class Reply { action.accept(update); } + public List> conditions() { + return conditions; + } + + public Consumer action() { + return action; + } + + public Stream stream(){ + return Stream.of(this); + } + @Override public boolean equals(Object o) { if (this == o) diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/ReplyFlow.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/ReplyFlow.java new file mode 100644 index 00000000..799a1a9b --- /dev/null +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/ReplyFlow.java @@ -0,0 +1,117 @@ +package org.telegram.abilitybots.api.objects; + +import org.jetbrains.annotations.NotNull; +import org.telegram.abilitybots.api.db.DBContext; +import org.telegram.abilitybots.api.util.AbilityUtils; +import org.telegram.telegrambots.meta.api.objects.Update; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import static com.google.common.collect.Lists.newArrayList; + +public class ReplyFlow extends Reply { + + private final Set nextReplies; + + private ReplyFlow(List> conditions, Consumer action, Set nextReplies) { + super(conditions, action); + this.nextReplies = nextReplies; + } + + public static ReplyFlowBuilder builder(DBContext db) { + return new ReplyFlowBuilder(db); + } + + public static ReplyFlowBuilder builder(DBContext db, int id) { + return new ReplyFlowBuilder(db, id); + } + + public Set nextReplies() { + return nextReplies; + } + + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), nextReplies.stream().flatMap(Reply::stream)); + } + + public static class ReplyFlowBuilder { + public static final String STATES = "user_state_replies"; + private static AtomicInteger replyCounter = new AtomicInteger(); + private final DBContext db; + private final int id; + private List> conds; + private Consumer action; + private Set nextReplies; + + private ReplyFlowBuilder(DBContext db, int id) { + conds = new ArrayList<>(); + nextReplies = new HashSet<>(); + this.db = db; + this.id = id; + } + + private ReplyFlowBuilder(DBContext db) { + this(db, replyCounter.getAndIncrement()); + } + + public ReplyFlowBuilder action(Consumer action) { + this.action = action; + return this; + } + + public ReplyFlowBuilder onlyIf(Predicate pred) { + conds.add(pred); + return this; + } + + public ReplyFlowBuilder next(Reply nextReply) { + List> statefulConditions = toStateful(nextReply.conditions()); + Consumer statefulAction = nextReply.action().andThen(upd -> { + Long chatId = AbilityUtils.getChatId(upd); + db.getMap(STATES).remove(chatId); + }); + + Reply statefulReply = Reply.of(statefulAction, statefulConditions); + nextReplies.add(statefulReply); + return this; + } + + public ReplyFlowBuilder next(ReplyFlow nextReplyFlow) { + List> statefulConditions = toStateful(nextReplyFlow.conditions()); + + ReplyFlow statefulReplyFlow = new ReplyFlow(statefulConditions, nextReplyFlow.action(), nextReplyFlow.nextReplies()); + nextReplies.add(statefulReplyFlow); + return this; + } + + public ReplyFlow build() { + if (action == null) + action = upd -> {}; + Consumer statefulAction = action.andThen(upd -> { + Long chatId = AbilityUtils.getChatId(upd); + db.getMap(STATES).put(chatId, id); + }); + + return new ReplyFlow(conds, statefulAction, nextReplies); + } + + @NotNull + private List> toStateful(List> conditions) { + List> statefulConditions = newArrayList(conditions); + statefulConditions.add(0, upd -> { + Long chatId = AbilityUtils.getChatId(upd); + int stateId = db.getMap(STATES).getOrDefault(chatId, -1); + return id == stateId; + }); + return statefulConditions; + } + } +} diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotI18nTest.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotI18nTest.java index 5494c595..e92cb442 100644 --- a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotI18nTest.java +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotI18nTest.java @@ -15,7 +15,7 @@ import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.internal.verification.VerificationModeFactory.times; -import static org.telegram.abilitybots.api.bot.AbilityBotTest.mockContext; +import static org.telegram.abilitybots.api.bot.TestUtils.mockContext; import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance; class AbilityBotI18nTest { diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotTest.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotTest.java index a1c5e8a5..1fe24f5c 100644 --- a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotTest.java +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotTest.java @@ -15,12 +15,7 @@ import org.telegram.abilitybots.api.sender.SilentSender; import org.telegram.abilitybots.api.util.Pair; import org.telegram.abilitybots.api.util.Trio; import org.telegram.telegrambots.meta.api.methods.groupadministration.GetChatAdministrators; -import org.telegram.telegrambots.meta.api.objects.ChatMember; -import org.telegram.telegrambots.meta.api.objects.Document; -import org.telegram.telegrambots.meta.api.objects.File; -import org.telegram.telegrambots.meta.api.objects.Message; -import org.telegram.telegrambots.meta.api.objects.Update; -import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.api.objects.*; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import java.io.BufferedWriter; @@ -39,15 +34,12 @@ import static java.util.Optional.empty; import static org.apache.commons.io.FileUtils.deleteQuietly; import static org.apache.commons.lang3.ArrayUtils.addAll; import static org.apache.commons.lang3.StringUtils.EMPTY; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; import static org.mockito.internal.verification.VerificationModeFactory.times; import static org.telegram.abilitybots.api.bot.DefaultBot.getDefaultBuilder; +import static org.telegram.abilitybots.api.bot.TestUtils.*; +import static org.telegram.abilitybots.api.bot.TestUtils.CREATOR; import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance; import static org.telegram.abilitybots.api.objects.Flag.DOCUMENT; import static org.telegram.abilitybots.api.objects.Flag.MESSAGE; @@ -65,8 +57,6 @@ public class AbilityBotTest { private static final long GROUP_ID = 10L; private static final String TEST = "test"; private static final String[] TEXT = {TEST}; - public static final User USER = new User(1, "first", false, "last", "username", null); - public static final User CREATOR = new User(1337, "creatorFirst", false, "creatorLast", "creatorUsername", null); private DefaultBot bot; private DBContext db; @@ -93,7 +83,7 @@ public class AbilityBotTest { @Test void sendsPrivacyViolation() { - Update update = mockFullUpdate(USER, "/admin"); + Update update = mockFullUpdate(bot, USER, "/admin"); bot.onUpdateReceived(update); @@ -102,7 +92,7 @@ public class AbilityBotTest { @Test void sendsLocalityViolation() { - Update update = mockFullUpdate(USER, "/group"); + Update update = mockFullUpdate(bot, USER, "/group"); bot.onUpdateReceived(update); @@ -112,7 +102,7 @@ public class AbilityBotTest { @Test void sendsInputArgsViolation() { - Update update = mockFullUpdate(USER, "/count 1 2 3"); + Update update = mockFullUpdate(bot, USER, "/count 1 2 3"); bot.onUpdateReceived(update); @@ -121,7 +111,7 @@ public class AbilityBotTest { @Test void canProcessRepliesIfSatisfyRequirements() { - Update update = mockFullUpdate(USER, "must reply"); + Update update = mockFullUpdate(bot, USER, "must reply"); // False means the update was not pushed down the stream since it has been consumed by the reply assertFalse(bot.filterReply(update)); @@ -312,7 +302,7 @@ public class AbilityBotTest { @Test void canCheckInput() { - Update update = mockFullUpdate(USER, "/something"); + Update update = mockFullUpdate(bot, USER, "/something"); Ability abilityWithOneInput = getDefaultBuilder() .build(); Ability abilityWithZeroInput = getDefaultBuilder() @@ -549,25 +539,6 @@ public class AbilityBotTest { verify(silent, times(1)).send("default - dis iz default command", GROUP_ID); } - @NotNull - static MessageContext mockContext(User user) { - return mockContext(user, user.getId()); - } - - @NotNull - static MessageContext mockContext(User user, long groupId, String... args) { - Update update = mock(Update.class); - Message message = mock(Message.class); - - when(update.hasMessage()).thenReturn(true); - when(update.getMessage()).thenReturn(message); - - when(message.getFrom()).thenReturn(user); - when(message.hasText()).thenReturn(true); - - return newContext(update, user, groupId, args); - } - @Test void canPrintCommandsBasedOnPrivacy() { Update update = mock(Update.class); @@ -601,27 +572,6 @@ public class AbilityBotTest { verify(silent, times(1)).send(expected, GROUP_ID); } - @NotNull - private Update mockFullUpdate(User user, String args) { - bot.users().put(USER.getId(), USER); - bot.users().put(CREATOR.getId(), CREATOR); - bot.userIds().put(CREATOR.getUserName(), CREATOR.getId()); - bot.userIds().put(USER.getUserName(), USER.getId()); - - bot.admins().add(CREATOR.getId()); - - Update update = mock(Update.class); - when(update.hasMessage()).thenReturn(true); - Message message = mock(Message.class); - when(message.getFrom()).thenReturn(user); - when(message.getText()).thenReturn(args); - when(message.hasText()).thenReturn(true); - when(message.isUserMessage()).thenReturn(true); - when(message.getChatId()).thenReturn((long) user.getId()); - when(update.getMessage()).thenReturn(message); - return update; - } - private void mockUser(Update update, Message message, User user) { when(update.hasMessage()).thenReturn(true); when(update.getMessage()).thenReturn(message); diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/ReplyFlowTest.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/ReplyFlowTest.java new file mode 100644 index 00000000..fb198278 --- /dev/null +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/ReplyFlowTest.java @@ -0,0 +1,145 @@ +package org.telegram.abilitybots.api.bot; + +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.telegram.abilitybots.api.db.DBContext; +import org.telegram.abilitybots.api.objects.Reply; +import org.telegram.abilitybots.api.objects.ReplyFlow; +import org.telegram.abilitybots.api.sender.MessageSender; +import org.telegram.abilitybots.api.sender.SilentSender; +import org.telegram.telegrambots.meta.api.objects.Update; + +import java.io.IOException; +import java.util.function.Predicate; + +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +import static org.telegram.abilitybots.api.bot.TestUtils.USER; +import static org.telegram.abilitybots.api.bot.TestUtils.mockFullUpdate; +import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance; +import static org.telegram.abilitybots.api.objects.ReplyFlow.ReplyFlowBuilder.STATES; +import static org.telegram.abilitybots.api.util.AbilityUtils.getChatId; + +public class ReplyFlowTest { + private static final int INITIAL_STATE = 1; + private static final int INTERIM_STATE = 2; + private DBContext db; + private ReplyFlowBot bot; + + private MessageSender sender; + private SilentSender silent; + + @BeforeEach + void setUp() { + db = offlineInstance("db"); + bot = new ReplyFlowBot(EMPTY, EMPTY, db); + + sender = mock(MessageSender.class); + silent = mock(SilentSender.class); + + bot.sender = sender; + bot.silent = silent; + } + + @AfterEach + void tearDown() throws IOException { + db.clear(); + db.close(); + } + + @Test + void doesNotReplyIfFirstReplyFlowDoesNotMatch() { + Update update = mockFullUpdate(bot, USER, "this is not supported"); + long chatId = getChatId(update); + + assertTrue(bot.filterReply(update)); + + verify(silent, never()).send("Command me to go left or right!", chatId); + } + + @Test + void doesNotReplyIfLaterRepliesAreAttemptedButUserNotInRightState() { + Update update = mockFullUpdate(bot, USER, "left"); + long chatId = getChatId(update); + db.getMap(STATES).put(chatId, INTERIM_STATE); + + assertTrue(bot.filterReply(update)); + + verify(silent, never()).send("Sir, I have gone left.", chatId); + } + + @Test + void repliesIfFirstReplyFlowMatches() { + Update update = mockFullUpdate(bot, USER, "wake up"); + long chatId = getChatId(update); + + assertFalse(bot.filterReply(update)); + + verify(silent, only()).send("Command me to go left or right!", chatId); + assertEquals(INITIAL_STATE, db.getMap(STATES).get(chatId), "User is not in the proper initial state"); + } + + @Test + void stateIsNotResetOnFaultyReply() { + Update update = mockFullUpdate(bot, USER, "leffffft"); + long chatId = getChatId(update); + db.getMap(STATES).put(chatId, INITIAL_STATE); + + assertTrue(bot.filterReply(update)); + + verify(silent, never()).send("I don't know how to go left.", chatId); + assertEquals(INITIAL_STATE, db.getMap(STATES).get(chatId), "User is no longer in the initial state after faulty reply"); + } + + @Test + void terminalRepliesResetState() { + Update update = mockFullUpdate(bot, USER, "go left or else"); + long chatId = getChatId(update); + db.getMap(STATES).put(chatId, INTERIM_STATE); + + assertFalse(bot.filterReply(update)); + + verify(silent, only()).send("Sir, I have gone left.", chatId); + assertFalse(db.getMap(STATES).containsKey(chatId), "User still has state after terminal reply"); + } + + public static class ReplyFlowBot extends AbilityBot { + + private ReplyFlowBot(String botToken, String botUsername, DBContext db) { + super(botToken, botUsername, db); + } + + @Override + public int creatorId() { + return 0; + } + + public ReplyFlow directionFlow() { + Reply saidLeft = Reply.of(upd -> silent.send("Sir, I have gone left.", getChatId(upd)), + hasMessageWith("go left or else")); + + ReplyFlow leftflow = ReplyFlow.builder(db, 2) + .action(upd -> silent.send("I don't know how to go left.", getChatId(upd))) + .onlyIf(hasMessageWith("left")) + .next(saidLeft).build(); + + Reply saidRight = Reply.of(upd -> silent.send("Sir, I have gone right.", getChatId(upd)), + hasMessageWith("right")); + + return ReplyFlow.builder(db, 1) + .action(upd -> silent.send("Command me to go left or right!", getChatId(upd))) + .onlyIf(hasMessageWith("wake up")) + .next(leftflow) + .next(saidRight) + .build(); + } + + @NotNull + private Predicate hasMessageWith(String msg) { + return upd -> upd.getMessage().getText().equalsIgnoreCase(msg); + } + } +} diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/TestUtils.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/TestUtils.java new file mode 100644 index 00000000..19c531da --- /dev/null +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/TestUtils.java @@ -0,0 +1,60 @@ +package org.telegram.abilitybots.api.bot; + +import org.jetbrains.annotations.NotNull; +import org.telegram.abilitybots.api.objects.MessageContext; +import org.telegram.telegrambots.meta.api.objects.Message; +import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.api.objects.User; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.telegram.abilitybots.api.objects.MessageContext.newContext; + +public final class TestUtils { + public static final User USER = new User(1, "first", false, "last", "username", null); + public static final User CREATOR = new User(1337, "creatorFirst", false, "creatorLast", "creatorUsername", null); + + private TestUtils() { + + } + + @NotNull + static Update mockFullUpdate(AbilityBot bot, User user, String args) { + bot.users().put(USER.getId(), USER); + bot.users().put(CREATOR.getId(), CREATOR); + bot.userIds().put(CREATOR.getUserName(), CREATOR.getId()); + bot.userIds().put(USER.getUserName(), USER.getId()); + + bot.admins().add(CREATOR.getId()); + + Update update = mock(Update.class); + when(update.hasMessage()).thenReturn(true); + Message message = mock(Message.class); + when(message.getFrom()).thenReturn(user); + when(message.getText()).thenReturn(args); + when(message.hasText()).thenReturn(true); + when(message.isUserMessage()).thenReturn(true); + when(message.getChatId()).thenReturn((long) user.getId()); + when(update.getMessage()).thenReturn(message); + return update; + } + + @NotNull + static MessageContext mockContext(User user, long groupId, String... args) { + Update update = mock(Update.class); + Message message = mock(Message.class); + + when(update.hasMessage()).thenReturn(true); + when(update.getMessage()).thenReturn(message); + + when(message.getFrom()).thenReturn(user); + when(message.hasText()).thenReturn(true); + + return newContext(update, user, groupId, args); + } + + @NotNull + static MessageContext mockContext(User user) { + return mockContext(user, user.getId()); + } +} diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/db/MapDBContextTest.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/db/MapDBContextTest.java index 267450fd..594a38a8 100644 --- a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/db/MapDBContextTest.java +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/db/MapDBContextTest.java @@ -16,8 +16,8 @@ import static java.lang.String.format; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.telegram.abilitybots.api.bot.AbilityBotTest.CREATOR; -import static org.telegram.abilitybots.api.bot.AbilityBotTest.USER; +import static org.telegram.abilitybots.api.bot.TestUtils.CREATOR; +import static org.telegram.abilitybots.api.bot.TestUtils.USER; import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance; class MapDBContextTest { From 861b7f24f904f0aeaac9f79f264b6437bca18471 Mon Sep 17 00:00:00 2001 From: Abbas Abou Daya Date: Sun, 29 Sep 2019 23:00:16 -0700 Subject: [PATCH 13/23] Add Abillity toggles and export default abilities to their own class --- TelegramBots.wiki/_Sidebar.md | 1 + TelegramBots.wiki/abilities/Ability-Toggle.md | 42 ++ .../abilitybots/api/bot/AbilityBot.java | 26 +- .../api/bot/AbilityWebhookBot.java | 26 +- .../abilitybots/api/bot/BaseAbilityBot.java | 490 +----------------- .../abilitybots/api/bot/DefaultAbilities.java | 435 ++++++++++++++++ .../abilitybots/api/objects/Ability.java | 25 +- .../abilitybots/api/toggle/AbilityToggle.java | 21 + .../api/toggle/BareboneToggle.java | 20 + .../abilitybots/api/toggle/CustomToggle.java | 56 ++ .../abilitybots/api/toggle/DefaultToggle.java | 19 + .../abilitybots/api/util/AbilityUtils.java | 4 + .../api/bot/AbilityBotI18nTest.java | 6 +- .../abilitybots/api/bot/AbilityBotTest.java | 24 +- .../abilitybots/api/bot/DefaultBot.java | 5 + .../api/toggle/BareboneToggleTest.java | 47 ++ .../api/toggle/CustomToggleTest.java | 51 ++ .../api/toggle/DefaultToggleTest.java | 69 +++ 18 files changed, 877 insertions(+), 490 deletions(-) create mode 100644 TelegramBots.wiki/abilities/Ability-Toggle.md create mode 100644 telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/DefaultAbilities.java create mode 100644 telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/AbilityToggle.java create mode 100644 telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/BareboneToggle.java create mode 100644 telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/CustomToggle.java create mode 100644 telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/DefaultToggle.java create mode 100644 telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/toggle/BareboneToggleTest.java create mode 100644 telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/toggle/CustomToggleTest.java create mode 100644 telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/toggle/DefaultToggleTest.java diff --git a/TelegramBots.wiki/_Sidebar.md b/TelegramBots.wiki/_Sidebar.md index f423f02e..4fe6b64e 100644 --- a/TelegramBots.wiki/_Sidebar.md +++ b/TelegramBots.wiki/_Sidebar.md @@ -8,6 +8,7 @@ * [[Simple Example]] * [[Hello Ability]] * [[Using Replies]] + * [[Ability Toggle]] * [[Database Handling]] * [[Bot Testing]] * [[Bot Recovery]] diff --git a/TelegramBots.wiki/abilities/Ability-Toggle.md b/TelegramBots.wiki/abilities/Ability-Toggle.md new file mode 100644 index 00000000..0c15864c --- /dev/null +++ b/TelegramBots.wiki/abilities/Ability-Toggle.md @@ -0,0 +1,42 @@ +# Ability Toggle +Well, what if you don't like all the default abilities that AbilityBot supplies? Fear not, you are able to disable all of them, even rename them if you will! + +You may pass a custom toggle to your abilitybot constructor to customize how these abilities get registered. + +## The Barebone Toggle +The barebone toggle is used to **turn off** all the default abilities that come with the bot (ban, unban, demote, promte, etc...). To use the barebone toggle, call your super constructor with: +```java +import org.telegram.abilitybots.api.toggle.BareboneToggle; + +public class YourAwesomeBot extends AbilityBot { + public YourAwesomeBot(String token, String username) { + BareboneToggle toggle = new BareboneToggle(); + super(token, username, toggle); + } + + // Override ceatorId() +} +``` +Obviously, you can export this as a parameter that you can pass to your bot's constructor. + +## The Custom Toggle +The custom toggle allows you to customize or turn off the names of the abilities that the abilitybot exposes. +```java +import org.telegram.abilitybots.api.toggle.CustomToggle; + +public class YourAwesomeBot extends AbilityBot { + public YourAwesomeBot(String token, String username) { + CustomToggle toggle = new CustomToggle() + .turnOff("ban") + .toggle("promote", "upgrade"); + + super(token, username, toggle); + } + + // Override ceatorId() +} +``` + +With these changes, the ability "ban" is no longer available and the "promote" ability has been renamed to "upgrade". +## The Default Toggle +The default toggle is automatically used if the user does not specify a toggle. The default toggle allows all the abilities to be effective and unchanged. \ No newline at end of file diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityBot.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityBot.java index de3016e1..12894316 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityBot.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityBot.java @@ -1,6 +1,8 @@ package org.telegram.abilitybots.api.bot; import org.telegram.abilitybots.api.db.DBContext; +import org.telegram.abilitybots.api.toggle.AbilityToggle; +import org.telegram.abilitybots.api.toggle.DefaultToggle; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.bots.DefaultBotOptions; import org.telegram.telegrambots.bots.TelegramLongPollingBot; @@ -16,18 +18,34 @@ import static org.telegram.abilitybots.api.db.MapDBContext.onlineInstance; * @author Abbas Abou Daya */ public abstract class AbilityBot extends BaseAbilityBot implements LongPollingBot { - protected AbilityBot(String botToken, String botUsername, DBContext db, DefaultBotOptions botOptions) { - super(botToken, botUsername, db, botOptions); + protected AbilityBot(String botToken, String botUsername, DBContext db, AbilityToggle toggle, DefaultBotOptions botOptions) { + super(botToken, botUsername, db, toggle, botOptions); } - protected AbilityBot(String botToken, String botUsername, DBContext db) { - this(botToken, botUsername, db, new DefaultBotOptions()); + protected AbilityBot(String botToken, String botUsername, AbilityToggle toggle, DefaultBotOptions options) { + this(botToken, botUsername, onlineInstance(botUsername), toggle, options); + } + + protected AbilityBot(String botToken, String botUsername, DBContext db, AbilityToggle toggle) { + this(botToken, botUsername, db, toggle, new DefaultBotOptions()); + } + + protected AbilityBot(String botToken, String botUsername, DBContext db, DefaultBotOptions options) { + this(botToken, botUsername, db, new DefaultToggle(), options); } protected AbilityBot(String botToken, String botUsername, DefaultBotOptions botOptions) { this(botToken, botUsername, onlineInstance(botUsername), botOptions); } + protected AbilityBot(String botToken, String botUsername, AbilityToggle toggle) { + this(botToken, botUsername, onlineInstance(botUsername), toggle); + } + + protected AbilityBot(String botToken, String botUsername, DBContext db) { + this(botToken, botUsername, db, new DefaultToggle()); + } + protected AbilityBot(String botToken, String botUsername) { this(botToken, botUsername, onlineInstance(botUsername)); } diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityWebhookBot.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityWebhookBot.java index 04887d7c..2c9b3db6 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityWebhookBot.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/AbilityWebhookBot.java @@ -1,6 +1,8 @@ package org.telegram.abilitybots.api.bot; import org.telegram.abilitybots.api.db.DBContext; +import org.telegram.abilitybots.api.toggle.AbilityToggle; +import org.telegram.abilitybots.api.toggle.DefaultToggle; import org.telegram.telegrambots.meta.api.methods.BotApiMethod; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.bots.DefaultBotOptions; @@ -21,19 +23,35 @@ public abstract class AbilityWebhookBot extends BaseAbilityBot implements Webhoo private final String botPath; - protected AbilityWebhookBot(String botToken, String botUsername, String botPath, DBContext db, DefaultBotOptions botOptions) { - super(botToken, botUsername, db, botOptions); + protected AbilityWebhookBot(String botToken, String botUsername, String botPath, DBContext db, AbilityToggle toggle, DefaultBotOptions botOptions) { + super(botToken, botUsername, db, toggle, botOptions); this.botPath = botPath; } - protected AbilityWebhookBot(String botToken, String botUsername, String botPath, DBContext db) { - this(botToken, botUsername, botPath, db, new DefaultBotOptions()); + protected AbilityWebhookBot(String botToken, String botUsername, String botPath, AbilityToggle toggle, DefaultBotOptions options) { + this(botToken, botUsername, botPath, onlineInstance(botUsername), toggle, options); + } + + protected AbilityWebhookBot(String botToken, String botUsername, String botPath, DBContext db, AbilityToggle toggle) { + this(botToken, botUsername, botPath, db, toggle, new DefaultBotOptions()); + } + + protected AbilityWebhookBot(String botToken, String botUsername, String botPath, DBContext db, DefaultBotOptions options) { + this(botToken, botUsername, botPath, db, new DefaultToggle(), options); } protected AbilityWebhookBot(String botToken, String botUsername, String botPath, DefaultBotOptions botOptions) { this(botToken, botUsername, botPath, onlineInstance(botUsername), botOptions); } + protected AbilityWebhookBot(String botToken, String botUsername, String botPath, AbilityToggle toggle) { + this(botToken, botUsername, botPath, onlineInstance(botUsername), toggle); + } + + protected AbilityWebhookBot(String botToken, String botUsername, String botPath, DBContext db) { + this(botToken, botUsername, botPath, db, new DefaultToggle()); + } + protected AbilityWebhookBot(String botToken, String botUsername, String botPath) { this(botToken, botUsername, botPath, onlineInstance(botUsername)); } diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java index d5f9c0ef..0ce5ab39 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java @@ -3,21 +3,15 @@ package org.telegram.abilitybots.api.bot; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.Multimap; -import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.telegram.abilitybots.api.db.DBContext; -import org.telegram.abilitybots.api.objects.Ability; -import org.telegram.abilitybots.api.objects.Locality; -import org.telegram.abilitybots.api.objects.MessageContext; -import org.telegram.abilitybots.api.objects.Privacy; -import org.telegram.abilitybots.api.objects.Reply; +import org.telegram.abilitybots.api.objects.*; import org.telegram.abilitybots.api.sender.DefaultSender; import org.telegram.abilitybots.api.sender.MessageSender; import org.telegram.abilitybots.api.sender.SilentSender; +import org.telegram.abilitybots.api.toggle.AbilityToggle; import org.telegram.abilitybots.api.util.AbilityExtension; import org.telegram.abilitybots.api.util.AbilityUtils; import org.telegram.abilitybots.api.util.Pair; @@ -25,85 +19,31 @@ import org.telegram.abilitybots.api.util.Trio; import org.telegram.telegrambots.bots.DefaultAbsSender; import org.telegram.telegrambots.bots.DefaultBotOptions; import org.telegram.telegrambots.bots.TelegramLongPollingBot; -import org.telegram.telegrambots.meta.api.methods.GetFile; import org.telegram.telegrambots.meta.api.methods.groupadministration.GetChatAdministrators; -import org.telegram.telegrambots.meta.api.methods.send.SendDocument; import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.User; -import org.telegram.telegrambots.meta.exceptions.TelegramApiException; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Strings.isNullOrEmpty; -import static com.google.common.collect.MultimapBuilder.hashKeys; import static java.lang.String.format; import static java.time.ZonedDateTime.now; import static java.util.Arrays.stream; -import static java.util.Comparator.comparing; -import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import static java.util.regex.Pattern.CASE_INSENSITIVE; import static java.util.regex.Pattern.compile; -import static java.util.stream.Collectors.joining; -import static org.apache.commons.lang3.StringUtils.isEmpty; -import static org.telegram.abilitybots.api.objects.Ability.builder; -import static org.telegram.abilitybots.api.objects.Flag.DOCUMENT; -import static org.telegram.abilitybots.api.objects.Flag.MESSAGE; -import static org.telegram.abilitybots.api.objects.Flag.REPLY; -import static org.telegram.abilitybots.api.objects.Locality.ALL; -import static org.telegram.abilitybots.api.objects.Locality.GROUP; -import static org.telegram.abilitybots.api.objects.Locality.USER; +import static org.telegram.abilitybots.api.objects.Locality.*; import static org.telegram.abilitybots.api.objects.MessageContext.newContext; -import static org.telegram.abilitybots.api.objects.Privacy.ADMIN; -import static org.telegram.abilitybots.api.objects.Privacy.CREATOR; -import static org.telegram.abilitybots.api.objects.Privacy.GROUP_ADMIN; -import static org.telegram.abilitybots.api.objects.Privacy.PUBLIC; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_BAN_FAIL; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_BAN_SUCCESS; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_CLAIM_FAIL; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_CLAIM_SUCCESS; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_COMMANDS_NOT_FOUND; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_DEMOTE_FAIL; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_DEMOTE_SUCCESS; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_PROMOTE_FAIL; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_PROMOTE_SUCCESS; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_RECOVER_ERROR; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_RECOVER_FAIL; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_RECOVER_MESSAGE; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_RECOVER_SUCCESS; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_UNBAN_FAIL; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.ABILITY_UNBAN_SUCCESS; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.CHECK_INPUT_FAIL; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.CHECK_LOCALITY_FAIL; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.CHECK_PRIVACY_FAIL; -import static org.telegram.abilitybots.api.util.AbilityMessageCodes.USER_NOT_FOUND; -import static org.telegram.abilitybots.api.util.AbilityUtils.addTag; -import static org.telegram.abilitybots.api.util.AbilityUtils.commitTo; -import static org.telegram.abilitybots.api.util.AbilityUtils.getChatId; -import static org.telegram.abilitybots.api.util.AbilityUtils.getLocalizedMessage; -import static org.telegram.abilitybots.api.util.AbilityUtils.isGroupUpdate; -import static org.telegram.abilitybots.api.util.AbilityUtils.isSuperGroupUpdate; -import static org.telegram.abilitybots.api.util.AbilityUtils.isUserMessage; -import static org.telegram.abilitybots.api.util.AbilityUtils.shortName; -import static org.telegram.abilitybots.api.util.AbilityUtils.stripTag; +import static org.telegram.abilitybots.api.objects.Privacy.*; +import static org.telegram.abilitybots.api.util.AbilityMessageCodes.*; +import static org.telegram.abilitybots.api.util.AbilityUtils.*; /** * The father of all ability bots. Bots that need to utilize abilities need to extend this bot. @@ -141,29 +81,21 @@ import static org.telegram.abilitybots.api.util.AbilityUtils.stripTag; public abstract class BaseAbilityBot extends DefaultAbsSender implements AbilityExtension { private static final Logger log = LogManager.getLogger(BaseAbilityBot.class); + protected static final String DEFAULT = "default"; // DB objects public static final String ADMINS = "ADMINS"; public static final String USERS = "USERS"; public static final String USER_ID = "USER_ID"; public static final String BLACKLIST = "BLACKLIST"; - // Factory commands - protected static final String DEFAULT = "default"; - protected static final String CLAIM = "claim"; - protected static final String BAN = "ban"; - protected static final String PROMOTE = "promote"; - protected static final String DEMOTE = "demote"; - protected static final String UNBAN = "unban"; - protected static final String BACKUP = "backup"; - protected static final String RECOVER = "recover"; - protected static final String COMMANDS = "commands"; - protected static final String REPORT = "report"; - // DB and sender protected final DBContext db; protected MessageSender sender; protected SilentSender silent; + // Ability toggle + private final AbilityToggle toggle; + // Bot token and username private final String botToken; private final String botUsername; @@ -176,12 +108,13 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability public abstract int creatorId(); - protected BaseAbilityBot(String botToken, String botUsername, DBContext db, DefaultBotOptions botOptions) { + protected BaseAbilityBot(String botToken, String botUsername, DBContext db, AbilityToggle toggle, DefaultBotOptions botOptions) { super(botOptions); this.botToken = botToken; this.botUsername = botUsername; this.db = db; + this.toggle = toggle; this.sender = new DefaultSender(this); silent = new SilentSender(sender); @@ -281,374 +214,6 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability return true; } - /** - * Gets the user with the specified username. - * - * @param username the username of the required user - * @return the user - */ - protected User getUser(String username) { - Integer id = userIds().get(username.toLowerCase()); - if (id == null) { - throw new IllegalStateException(format("Could not find ID corresponding to username [%s]", username)); - } - - return getUser(id); - } - - /** - * Gets the user with the specified ID. - * - * @param id the id of the required user - * @return the user - */ - protected User getUser(int id) { - User user = users().get(id); - if (user == null) { - throw new IllegalStateException(format("Could not find user corresponding to id [%d]", id)); - } - - return user; - } - - /** - * Gets the user with the specified username. If user was not found, the bot will send a message on Telegram. - * - * @param username the username of the required user - * @param ctx the message context with the originating user - * @return the id of the user - */ - protected int getUserIdSendError(String username, MessageContext ctx) { - try { - return getUser(username).getId(); - } catch (IllegalStateException ex) { - silent.send(getLocalizedMessage(USER_NOT_FOUND, ctx.user().getLanguageCode(), username), ctx.chatId()); - throw ex; - } - } - - /** - *

- * Format of the report: - *

- * [command1] - [description1] - *

- * [command2] - [description2] - *

- * ... - *

- * Once you invoke it, the bot will send the available commands to the chat. This is a public ability so anyone can invoke it. - *

- * Usage: /commands - * - * @return the ability to report commands defined by the child bot. - */ - public Ability reportCommands() { - return builder() - .name(REPORT) - .locality(ALL) - .privacy(CREATOR) - .input(0) - .action(ctx -> { - String commands = abilities.values().stream() - .filter(ability -> nonNull(ability.info())) - .map(ability -> { - String name = ability.name(); - String info = ability.info(); - return format("%s - %s", name, info); - }) - .sorted() - .reduce((a, b) -> format("%s%n%s", a, b)) - .orElse(getLocalizedMessage(ABILITY_COMMANDS_NOT_FOUND, ctx.user().getLanguageCode())); - - silent.send(commands, ctx.chatId()); - }) - .build(); - } - - /** - * Default format: - *

- * PUBLIC - *

- * [command1] - [description1] - *

- * [command2] - [description2] - *

- * GROUP_ADMIN - *

- * [command1] - [description1] - *

- * ... - * - * @return the ability to print commands based on the privacy of the requesting user - */ - public Ability commands() { - return builder() - .name(COMMANDS) - .locality(USER) - .privacy(PUBLIC) - .input(0) - .action(ctx -> { - Privacy privacy = getPrivacy(ctx.update(), ctx.user().getId()); - - ListMultimap abilitiesPerPrivacy = abilities.values().stream() - .map(ability -> { - String name = ability.name(); - String info = ability.info(); - - if (!isEmpty(info)) - return Pair.of(ability.privacy(), format("/%s - %s", name, info)); - return Pair.of(ability.privacy(), format("/%s", name)); - }) - .sorted(comparing(Pair::b)) - .collect(() -> hashKeys().arrayListValues().build(), - (map, pair) -> map.put(pair.a(), pair.b()), - Multimap::putAll); - - String commands = abilitiesPerPrivacy.asMap().entrySet().stream() - .filter(entry -> privacy.compareTo(entry.getKey()) >= 0) - .sorted(comparing(Entry::getKey)) - .map(entry -> - entry.getValue().stream() - .reduce(entry.getKey().toString(), (a, b) -> format("%s\n%s", a, b)) - ) - .collect(joining("\n")); - - if (commands.isEmpty()) - commands = getLocalizedMessage(ABILITY_COMMANDS_NOT_FOUND, ctx.user().getLanguageCode()); - - silent.send(commands, ctx.chatId()); - }) - .build(); - } - - /** - * This backup ability returns the object defined by {@link DBContext#backup()} as a message document. - *

- * This is a high-profile ability and is restricted to the CREATOR only. - *

- * Usage: /backup - * - * @return the ability to back-up the database of the bot - */ - public Ability backupDB() { - return builder() - .name(BACKUP) - .locality(USER) - .privacy(CREATOR) - .input(0) - .action(ctx -> { - File backup = new File("backup.json"); - - try (PrintStream printStream = new PrintStream(backup)) { - printStream.print(db.backup()); - sender.sendDocument(new SendDocument() - .setDocument(backup) - .setChatId(ctx.chatId()) - ); - } catch (FileNotFoundException e) { - log.error("Error while fetching backup", e); - } catch (TelegramApiException e) { - log.error("Error while sending document/backup file", e); - } - }) - .build(); - } - - /** - * Recovers the bot database using {@link DBContext#recover(Object)}. - *

- * The bot recovery process hugely depends on the implementation of the recovery method of {@link DBContext}. - *

- * Usage: /recover - * - * @return the ability to recover the database of the bot - */ - public Ability recoverDB() { - return builder() - .name(RECOVER) - .locality(USER) - .privacy(CREATOR) - .input(0) - .action(ctx -> silent.forceReply( - getLocalizedMessage(ABILITY_RECOVER_MESSAGE, ctx.user().getLanguageCode()), ctx.chatId())) - .reply(update -> { - String replyToMsg = update.getMessage().getReplyToMessage().getText(); - String recoverMessage = getLocalizedMessage(ABILITY_RECOVER_MESSAGE, AbilityUtils.getUser(update).getLanguageCode()); - if (!replyToMsg.equals(recoverMessage)) - return; - - String fileId = update.getMessage().getDocument().getFileId(); - try (FileReader reader = new FileReader(downloadFileWithId(fileId))) { - String backupData = IOUtils.toString(reader); - if (db.recover(backupData)) { - send(ABILITY_RECOVER_SUCCESS, update); - } else { - send(ABILITY_RECOVER_FAIL, update); - } - } catch (Exception e) { - log.error("Could not recover DB from backup", e); - send(ABILITY_RECOVER_ERROR, update); - } - }, MESSAGE, DOCUMENT, REPLY) - .build(); - } - - /** - * Banned users are accumulated in the blacklist. Use {@link DBContext#getSet(String)} with name specified by {@link BaseAbilityBot#BLACKLIST}. - *

- * Usage: /ban @username - *

- * Note that admins who try to ban the creator, get banned. - * - * @return the ability to ban the user from any kind of bot interaction - */ - public Ability banUser() { - return builder() - .name(BAN) - .locality(ALL) - .privacy(ADMIN) - .input(1) - .action(ctx -> { - String username = stripTag(ctx.firstArg()); - int userId = getUserIdSendError(username, ctx); - String bannedUser; - - // Protection from abuse - if (userId == creatorId()) { - userId = ctx.user().getId(); - bannedUser = isNullOrEmpty(ctx.user().getUserName()) ? addTag(ctx.user().getUserName()) : shortName(ctx.user()); - } else { - bannedUser = addTag(username); - } - - Set blacklist = blacklist(); - if (blacklist.contains(userId)) - sendMd(ABILITY_BAN_FAIL, ctx, escape(bannedUser)); - else { - blacklist.add(userId); - sendMd(ABILITY_BAN_SUCCESS, ctx, escape(bannedUser)); - } - }) - .post(commitTo(db)) - .build(); - } - - /** - * Usage: /unban @username - * - * @return the ability to unban a user - */ - public Ability unbanUser() { - return builder() - .name(UNBAN) - .locality(ALL) - .privacy(ADMIN) - .input(1) - .action(ctx -> { - String username = stripTag(ctx.firstArg()); - Integer userId = getUserIdSendError(username, ctx); - - Set blacklist = blacklist(); - - if (!blacklist.remove(userId)) - silent.sendMd(getLocalizedMessage(ABILITY_UNBAN_FAIL, ctx.user().getLanguageCode(), escape(username)), ctx.chatId()); - else { - silent.sendMd(getLocalizedMessage(ABILITY_UNBAN_SUCCESS, ctx.user().getLanguageCode(), escape(username)), ctx.chatId()); - } - }) - .post(commitTo(db)) - .build(); - } - - /** - * @return the ability to promote a user to a bot admin - */ - public Ability promoteAdmin() { - return builder() - .name(PROMOTE) - .locality(ALL) - .privacy(ADMIN) - .input(1) - .action(ctx -> { - String username = stripTag(ctx.firstArg()); - Integer userId = getUserIdSendError(username, ctx); - - Set admins = admins(); - if (admins.contains(userId)) - sendMd(ABILITY_PROMOTE_FAIL, ctx, escape(username)); - else { - admins.add(userId); - sendMd(ABILITY_PROMOTE_SUCCESS, ctx, escape(username)); - } - }).post(commitTo(db)) - .build(); - } - - /** - * @return the ability to demote an admin to a user - */ - public Ability demoteAdmin() { - return builder() - .name(DEMOTE) - .locality(ALL) - .privacy(ADMIN) - .input(1) - .action(ctx -> { - String username = stripTag(ctx.firstArg()); - Integer userId = getUserIdSendError(username, ctx); - - Set admins = admins(); - if (admins.remove(userId)) { - sendMd(ABILITY_DEMOTE_SUCCESS, ctx, escape(username)); - } else { - sendMd(ABILITY_DEMOTE_FAIL, ctx, escape(username)); - } - }) - .post(commitTo(db)) - .build(); - } - - /** - * Regular users and admins who try to claim the bot will get banned. - * - * @return the ability to claim yourself as the master and creator of the bot - */ - public Ability claimCreator() { - return builder() - .name(CLAIM) - .locality(ALL) - .privacy(CREATOR) - .input(0) - .action(ctx -> { - Set admins = admins(); - int id = creatorId(); - - if (admins.contains(id)) - send(ABILITY_CLAIM_FAIL, ctx); - else { - admins.add(id); - send(ABILITY_CLAIM_SUCCESS, ctx); - } - }) - .post(commitTo(db)) - .build(); - } - - private Optional send(String message, MessageContext ctx, String... args) { - return silent.send(getLocalizedMessage(message, ctx.user().getLanguageCode(), args), ctx.chatId()); - } - - private Optional sendMd(String message, MessageContext ctx, String... args) { - return silent.sendMd(getLocalizedMessage(message, ctx.user().getLanguageCode(), args), ctx.chatId()); - } - - private Optional send(String message, Update upd) { - Long chatId = upd.getMessage().getChatId(); - return silent.send(getLocalizedMessage(message, AbilityUtils.getUser(upd).getLanguageCode()), chatId); - } - /** * Registers the declared abilities using method reflection. Also, replies are accumulated using the built abilities and standalone methods that return a Reply. *

@@ -665,11 +230,19 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability // Add the bot itself as it is an AbilityExtension extensions.add(this); + DefaultAbilities defaultAbs = new DefaultAbilities(this); + Stream defaultAbsStream = stream(DefaultAbilities.class.getMethods()) + .filter(checkReturnType(Ability.class)) + .map(returnAbility(defaultAbs)) + .filter(ab -> !toggle.isOff(ab)) + .map(toggle::processAbility); + // Extract all abilities from every single extension instance - abilities = extensions.stream() + abilities = Stream.concat(defaultAbsStream, + extensions.stream() .flatMap(ext -> stream(ext.getClass().getMethods()) .filter(checkReturnType(Ability.class)) - .map(returnAbility(ext))) + .map(returnAbility(ext)))) // Abilities are immutable, build it respectively .collect(ImmutableMap::builder, (b, a) -> b.put(a.name(), a), @@ -702,7 +275,7 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability * @param clazz the type to be tested * @return a predicate testing the return type of the method corresponding to the class parameter */ - private Predicate checkReturnType(Class clazz) { + private static Predicate checkReturnType(Class clazz) { return method -> clazz.isAssignableFrom(method.getReturnType()); } @@ -729,7 +302,7 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability * @param obj an bot or extension that this method is invoked with * @return a {@link Function} which returns the {@link Ability} returned by the given method */ - private Function returnAbility(Object obj) { + private static Function returnAbility(Object obj) { return method -> { try { return (Ability) method.invoke(obj); @@ -746,7 +319,7 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability * @param obj an bot or extension that this method is invoked with * @return a {@link Function} which returns the {@link Reply} returned by the given method */ - private Function returnReply(Object obj) { + private static Function returnReply(Object obj) { return method -> { try { return (Reply) method.invoke(obj); @@ -833,7 +406,7 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability } @NotNull - private Privacy getPrivacy(Update update, int id) { + Privacy getPrivacy(Update update, int id) { return isCreator(id) ? CREATOR : isAdmin(id) ? ADMIN : (isGroupUpdate(update) || isSuperGroupUpdate(update)) && isGroupAdmin(update, id) ? @@ -938,13 +511,4 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability return ability.flags().stream() .reduce(true, flagAnd, Boolean::logicalAnd); } - - private File downloadFileWithId(String fileId) throws TelegramApiException { - return sender.downloadFile(sender.execute(new GetFile().setFileId(fileId))); - } - - - private String escape(String username) { - return username.replace("_", "\\_"); - } } \ No newline at end of file diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/DefaultAbilities.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/DefaultAbilities.java new file mode 100644 index 00000000..3d79a5c1 --- /dev/null +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/DefaultAbilities.java @@ -0,0 +1,435 @@ +package org.telegram.abilitybots.api.bot; + +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimap; +import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.telegram.abilitybots.api.db.DBContext; +import org.telegram.abilitybots.api.objects.Ability; +import org.telegram.abilitybots.api.objects.MessageContext; +import org.telegram.abilitybots.api.objects.Privacy; +import org.telegram.abilitybots.api.util.AbilityExtension; +import org.telegram.abilitybots.api.util.AbilityUtils; +import org.telegram.abilitybots.api.util.Pair; +import org.telegram.telegrambots.meta.api.methods.GetFile; +import org.telegram.telegrambots.meta.api.methods.send.SendDocument; +import org.telegram.telegrambots.meta.api.objects.Message; +import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.exceptions.TelegramApiException; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.PrintStream; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static com.google.common.collect.MultimapBuilder.hashKeys; +import static java.lang.String.format; +import static java.util.Comparator.comparing; +import static java.util.Objects.nonNull; +import static java.util.stream.Collectors.joining; +import static org.apache.commons.lang3.StringUtils.isEmpty; +import static org.telegram.abilitybots.api.objects.Ability.builder; +import static org.telegram.abilitybots.api.objects.Flag.*; +import static org.telegram.abilitybots.api.objects.Locality.ALL; +import static org.telegram.abilitybots.api.objects.Locality.USER; +import static org.telegram.abilitybots.api.objects.Privacy.*; +import static org.telegram.abilitybots.api.util.AbilityMessageCodes.*; +import static org.telegram.abilitybots.api.util.AbilityUtils.*; + +public final class DefaultAbilities implements AbilityExtension { + // Default commands + public static final String CLAIM = "claim"; + public static final String BAN = "ban"; + public static final String PROMOTE = "promote"; + public static final String DEMOTE = "demote"; + public static final String UNBAN = "unban"; + public static final String BACKUP = "backup"; + public static final String RECOVER = "recover"; + public static final String COMMANDS = "commands"; + public static final String REPORT = "report"; + private static final Logger log = LogManager.getLogger(DefaultAbilities.class); + private final BaseAbilityBot bot; + + public DefaultAbilities(BaseAbilityBot bot) { + this.bot = bot; + } + + /** + *

+ * Format of the report: + *

+ * [command1] - [description1] + *

+ * [command2] - [description2] + *

+ * ... + *

+ * Once you invoke it, the bot will send the available commands to the chat. This is a public ability so anyone can invoke it. + *

+ * Usage: /commands + * + * @return the ability to report commands defined by the child bot. + */ + public Ability reportCommands() { + return builder() + .name(REPORT) + .locality(ALL) + .privacy(CREATOR) + .input(0) + .action(ctx -> { + String commands = bot.abilities().values().stream() + .filter(ability -> nonNull(ability.info())) + .map(ability -> { + String name = ability.name(); + String info = ability.info(); + return format("%s - %s", name, info); + }) + .sorted() + .reduce((a, b) -> format("%s%n%s", a, b)) + .orElse(getLocalizedMessage(ABILITY_COMMANDS_NOT_FOUND, ctx.user().getLanguageCode())); + + bot.silent.send(commands, ctx.chatId()); + }) + .build(); + } + + /** + * Default format: + *

+ * PUBLIC + *

+ * [command1] - [description1] + *

+ * [command2] - [description2] + *

+ * GROUP_ADMIN + *

+ * [command1] - [description1] + *

+ * ... + * + * @return the ability to print commands based on the privacy of the requesting user + */ + public Ability commands() { + return builder() + .name(COMMANDS) + .locality(USER) + .privacy(PUBLIC) + .input(0) + .action(ctx -> { + Privacy privacy = bot.getPrivacy(ctx.update(), ctx.user().getId()); + + ListMultimap abilitiesPerPrivacy = bot.abilities().values().stream() + .map(ability -> { + String name = ability.name(); + String info = ability.info(); + + if (!isEmpty(info)) + return Pair.of(ability.privacy(), format("/%s - %s", name, info)); + return Pair.of(ability.privacy(), format("/%s", name)); + }) + .sorted(comparing(Pair::b)) + .collect(() -> hashKeys().arrayListValues().build(), + (map, pair) -> map.put(pair.a(), pair.b()), + Multimap::putAll); + + String commands = abilitiesPerPrivacy.asMap().entrySet().stream() + .filter(entry -> privacy.compareTo(entry.getKey()) >= 0) + .sorted(comparing(Map.Entry::getKey)) + .map(entry -> + entry.getValue().stream() + .reduce(entry.getKey().toString(), (a, b) -> format("%s\n%s", a, b)) + ) + .collect(joining("\n")); + + if (commands.isEmpty()) + commands = getLocalizedMessage(ABILITY_COMMANDS_NOT_FOUND, ctx.user().getLanguageCode()); + + bot.silent.send(commands, ctx.chatId()); + }) + .build(); + } + + /** + * This backup ability returns the object defined by {@link DBContext#backup()} as a message document. + *

+ * This is a high-profile ability and is restricted to the CREATOR only. + *

+ * Usage: /backup + * + * @return the ability to back-up the database of the bot + */ + public Ability backupDB() { + return builder() + .name(BACKUP) + .locality(USER) + .privacy(CREATOR) + .input(0) + .action(ctx -> { + File backup = new File("backup.json"); + + try (PrintStream printStream = new PrintStream(backup)) { + printStream.print(bot.db.backup()); + bot.sender.sendDocument(new SendDocument() + .setDocument(backup) + .setChatId(ctx.chatId()) + ); + } catch (FileNotFoundException e) { + log.error("Error while fetching backup", e); + } catch (TelegramApiException e) { + log.error("Error while sending document/backup file", e); + } + }) + .build(); + } + + /** + * Recovers the bot database using {@link DBContext#recover(Object)}. + *

+ * The bot recovery process hugely depends on the implementation of the recovery method of {@link DBContext}. + *

+ * Usage: /recover + * + * @return the ability to recover the database of the bot + */ + public Ability recoverDB() { + return builder() + .name(RECOVER) + .locality(USER) + .privacy(CREATOR) + .input(0) + .action(ctx -> bot.silent.forceReply( + getLocalizedMessage(ABILITY_RECOVER_MESSAGE, ctx.user().getLanguageCode()), ctx.chatId())) + .reply(update -> { + String replyToMsg = update.getMessage().getReplyToMessage().getText(); + String recoverMessage = getLocalizedMessage(ABILITY_RECOVER_MESSAGE, AbilityUtils.getUser(update).getLanguageCode()); + if (!replyToMsg.equals(recoverMessage)) + return; + + String fileId = update.getMessage().getDocument().getFileId(); + try (FileReader reader = new FileReader(downloadFileWithId(fileId))) { + String backupData = IOUtils.toString(reader); + if (bot.db.recover(backupData)) { + send(ABILITY_RECOVER_SUCCESS, update); + } else { + send(ABILITY_RECOVER_FAIL, update); + } + } catch (Exception e) { + log.error("Could not recover DB from backup", e); + send(ABILITY_RECOVER_ERROR, update); + } + }, MESSAGE, DOCUMENT, REPLY) + .build(); + } + + /** + * Banned users are accumulated in the blacklist. Use {@link DBContext#getSet(String)} with name specified by {@link BaseAbilityBot#BLACKLIST}. + *

+ * Usage: /ban @username + *

+ * Note that admins who try to ban the creator, get banned. + * + * @return the ability to ban the user from any kind of bot interaction + */ + public Ability banUser() { + return builder() + .name(BAN) + .locality(ALL) + .privacy(ADMIN) + .input(1) + .action(ctx -> { + String username = stripTag(ctx.firstArg()); + int userId = getUserIdSendError(username, ctx); + String bannedUser; + + // Protection from abuse + if (userId == bot.creatorId()) { + userId = ctx.user().getId(); + bannedUser = isNullOrEmpty(ctx.user().getUserName()) ? addTag(ctx.user().getUserName()) : shortName(ctx.user()); + } else { + bannedUser = addTag(username); + } + + Set blacklist = bot.blacklist(); + if (blacklist.contains(userId)) + sendMd(ABILITY_BAN_FAIL, ctx, escape(bannedUser)); + else { + blacklist.add(userId); + sendMd(ABILITY_BAN_SUCCESS, ctx, escape(bannedUser)); + } + }) + .post(commitTo(bot.db)) + .build(); + } + + /** + * Usage: /unban @username + * + * @return the ability to unban a user + */ + public Ability unbanUser() { + return builder() + .name(UNBAN) + .locality(ALL) + .privacy(ADMIN) + .input(1) + .action(ctx -> { + String username = stripTag(ctx.firstArg()); + Integer userId = getUserIdSendError(username, ctx); + + Set blacklist = bot.blacklist(); + + if (!blacklist.remove(userId)) + bot.silent.sendMd(getLocalizedMessage(ABILITY_UNBAN_FAIL, ctx.user().getLanguageCode(), escape(username)), ctx.chatId()); + else { + bot.silent.sendMd(getLocalizedMessage(ABILITY_UNBAN_SUCCESS, ctx.user().getLanguageCode(), escape(username)), ctx.chatId()); + } + }) + .post(commitTo(bot.db)) + .build(); + } + + /** + * @return the ability to promote a user to a bot admin + */ + public Ability promoteAdmin() { + return builder() + .name(PROMOTE) + .locality(ALL) + .privacy(ADMIN) + .input(1) + .action(ctx -> { + String username = stripTag(ctx.firstArg()); + Integer userId = getUserIdSendError(username, ctx); + + Set admins = bot.admins(); + if (admins.contains(userId)) + sendMd(ABILITY_PROMOTE_FAIL, ctx, escape(username)); + else { + admins.add(userId); + sendMd(ABILITY_PROMOTE_SUCCESS, ctx, escape(username)); + } + }).post(commitTo(bot.db)) + .build(); + } + + /** + * @return the ability to demote an admin to a user + */ + public Ability demoteAdmin() { + return builder() + .name(DEMOTE) + .locality(ALL) + .privacy(ADMIN) + .input(1) + .action(ctx -> { + String username = stripTag(ctx.firstArg()); + Integer userId = getUserIdSendError(username, ctx); + + Set admins = bot.admins(); + if (admins.remove(userId)) { + sendMd(ABILITY_DEMOTE_SUCCESS, ctx, escape(username)); + } else { + sendMd(ABILITY_DEMOTE_FAIL, ctx, escape(username)); + } + }) + .post(commitTo(bot.db)) + .build(); + } + + /** + * Regular users and admins who try to claim the bot will get banned. + * + * @return the ability to claim yourself as the master and creator of the bot + */ + public Ability claimCreator() { + return builder() + .name(CLAIM) + .locality(ALL) + .privacy(CREATOR) + .input(0) + .action(ctx -> { + Set admins = bot.admins(); + int id = bot.creatorId(); + + if (admins.contains(id)) + send(ABILITY_CLAIM_FAIL, ctx); + else { + admins.add(id); + send(ABILITY_CLAIM_SUCCESS, ctx); + } + }) + .post(commitTo(bot.db)) + .build(); + } + + /** + * Gets the user with the specified username. + * + * @param username the username of the required user + * @return the user + */ + private User getUser(String username) { + Integer id = bot.userIds().get(username.toLowerCase()); + if (id == null) { + throw new IllegalStateException(format("Could not find ID corresponding to username [%s]", username)); + } + + return getUser(id); + } + + /** + * Gets the user with the specified ID. + * + * @param id the id of the required user + * @return the user + */ + private User getUser(int id) { + User user = bot.users().get(id); + if (user == null) { + throw new IllegalStateException(format("Could not find user corresponding to id [%d]", id)); + } + + return user; + } + + /** + * Gets the user with the specified username. If user was not found, the bot will send a message on Telegram. + * + * @param username the username of the required user + * @param ctx the message context with the originating user + * @return the id of the user + */ + private int getUserIdSendError(String username, MessageContext ctx) { + try { + return getUser(username).getId(); + } catch (IllegalStateException ex) { + bot.silent.send(getLocalizedMessage(USER_NOT_FOUND, ctx.user().getLanguageCode(), username), ctx.chatId()); + throw ex; + } + } + + + private Optional send(String message, MessageContext ctx, String... args) { + return bot.silent.send(getLocalizedMessage(message, ctx.user().getLanguageCode(), args), ctx.chatId()); + } + + private Optional sendMd(String message, MessageContext ctx, String... args) { + return bot.silent.sendMd(getLocalizedMessage(message, ctx.user().getLanguageCode(), args), ctx.chatId()); + } + + private Optional send(String message, Update upd) { + Long chatId = upd.getMessage().getChatId(); + return bot.silent.send(getLocalizedMessage(message, AbilityUtils.getUser(upd).getLanguageCode()), chatId); + } + + protected File downloadFileWithId(String fileId) throws TelegramApiException { + return bot.sender.downloadFile(bot.sender.execute(new GetFile().setFileId(fileId))); + } +} diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Ability.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Ability.java index c3004c8b..db2db4c3 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Ability.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/objects/Ability.java @@ -147,8 +147,8 @@ public final class Ability { private Privacy privacy; private Locality locality; private int argNum; - private Consumer consumer; - private Consumer postConsumer; + private Consumer action; + private Consumer postAction; private List replies; private Predicate[] flags; @@ -157,7 +157,7 @@ public final class Ability { } public AbilityBuilder action(Consumer consumer) { - this.consumer = consumer; + this.action = consumer; return this; } @@ -191,8 +191,8 @@ public final class Ability { return this; } - public AbilityBuilder post(Consumer postConsumer) { - this.postConsumer = postConsumer; + public AbilityBuilder post(Consumer postAction) { + this.postAction = postAction; return this; } @@ -202,8 +202,21 @@ public final class Ability { return this; } + public AbilityBuilder basedOn(Ability ability) { + replies.clear(); + replies.addAll(ability.replies()); + + return name(ability.name()) + .info(ability.info()) + .input(ability.tokens()) + .locality(ability.locality()) + .privacy(ability.privacy()) + .action(ability.action()) + .post(ability.postAction()); + } + public Ability build() { - return new Ability(name, info, locality, privacy, argNum, consumer, postConsumer, replies, flags); + return new Ability(name, info, locality, privacy, argNum, action, postAction, replies, flags); } } } diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/AbilityToggle.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/AbilityToggle.java new file mode 100644 index 00000000..01986b43 --- /dev/null +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/AbilityToggle.java @@ -0,0 +1,21 @@ +package org.telegram.abilitybots.api.toggle; + +import org.telegram.abilitybots.api.objects.Ability; + +/** + * This interface can be used to toggle or customize unwanted default abilities by the user. + */ +public interface AbilityToggle { + /** + * @param ab the target ability + * @return true if the ability has been turned off + */ + boolean isOff(Ability ab); + + /** + * Abilities that are ON (and have failed the {@link AbilityToggle#isOff} condition) will be processed by this method. + * @param ab the target ability + * @return the processed ability + */ + Ability processAbility(Ability ab); +} diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/BareboneToggle.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/BareboneToggle.java new file mode 100644 index 00000000..3e791923 --- /dev/null +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/BareboneToggle.java @@ -0,0 +1,20 @@ +package org.telegram.abilitybots.api.toggle; + +import org.telegram.abilitybots.api.objects.Ability; + +/** + * This toggle can be used as-is to turn off ALL the default abilities supplied by the library. + * This is for users who are interested in the barebone functionality of AbilityBot. + */ +public class BareboneToggle implements AbilityToggle { + @Override + public boolean isOff(Ability ability) { + return true; + } + + @Override + public Ability processAbility(Ability ab) { + // Should never hit this + throw new RuntimeException("Should not process any ability in a vanilla toggle"); + } +} diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/CustomToggle.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/CustomToggle.java new file mode 100644 index 00000000..22e02fa8 --- /dev/null +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/CustomToggle.java @@ -0,0 +1,56 @@ +package org.telegram.abilitybots.api.toggle; + +import org.telegram.abilitybots.api.objects.Ability; + +import java.util.HashMap; +import java.util.Map; + +/** + * This custom toggle can be used to customize default abilities supplied by the library. Users can call {@link CustomToggle#toggle} to + * rename the default abilites or {@link CustomToggle#turnOff} to simply turn off the said ability. + */ +public class CustomToggle implements AbilityToggle { + public static final String OFF = "turn_off_base_ability"; + + private final Map baseMapping; + + public CustomToggle() { + baseMapping = new HashMap<>(); + } + + @Override + public boolean isOff(Ability ability) { + return OFF.equalsIgnoreCase(baseMapping.get(ability.name())); + } + + @Override + public Ability processAbility(Ability ability) { + if (baseMapping.containsKey(ability.name())) { + return Ability.builder() + .basedOn(ability) + .name(baseMapping.get(ability.name())) + .build(); + } + + return ability; + } + + /** + * @param abilityName the ability you want to change + * @param targetName the final name for this ability + * @return the toggle instance + */ + public CustomToggle toggle(String abilityName, String targetName) { + baseMapping.put(abilityName, targetName); + return this; + } + + /** + * @param ability the ability name you would like turned off + * @return the toggle instance + */ + public CustomToggle turnOff(String ability) { + baseMapping.put(ability, OFF); + return this; + } +} diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/DefaultToggle.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/DefaultToggle.java new file mode 100644 index 00000000..3b9219ff --- /dev/null +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/toggle/DefaultToggle.java @@ -0,0 +1,19 @@ +package org.telegram.abilitybots.api.toggle; + +import org.telegram.abilitybots.api.objects.Ability; + +/** + * If the user does not supply a toggle to their constructor, the default toggle will be instantiated. + * This default toggle allows all the default abilities to be registered. + */ +public class DefaultToggle implements AbilityToggle { + @Override + public boolean isOff(Ability ability) { + return false; + } + + @Override + public Ability processAbility(Ability ab) { + return ab; + } +} diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/util/AbilityUtils.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/util/AbilityUtils.java index 749fd4a7..ec213667 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/util/AbilityUtils.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/util/AbilityUtils.java @@ -244,4 +244,8 @@ public final class AbilityUtils { return name.toString(); } + + public static String escape(String username) { + return username.replace("_", "\\_"); + } } \ No newline at end of file diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotI18nTest.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotI18nTest.java index 5494c595..3cf20a46 100644 --- a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotI18nTest.java +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotI18nTest.java @@ -24,6 +24,7 @@ class AbilityBotI18nTest { private DBContext db; private NoPublicCommandsBot bot; + private DefaultAbilities defaultAbs; private MessageSender sender; private SilentSender silent; @@ -32,6 +33,7 @@ class AbilityBotI18nTest { void setUp() { db = offlineInstance("db"); bot = new NoPublicCommandsBot(EMPTY, EMPTY, db); + defaultAbs = new DefaultAbilities(bot); sender = mock(MessageSender.class); silent = mock(SilentSender.class); @@ -50,7 +52,7 @@ class AbilityBotI18nTest { void missingPublicCommandsLocalizedInEnglishByDefault() { MessageContext context = mockContext(NO_LANGUAGE_USER); - bot.reportCommands().action().accept(context); + defaultAbs.reportCommands().action().accept(context); verify(silent, times(1)) .send("No available commands found.", NO_LANGUAGE_USER.getId()); @@ -60,7 +62,7 @@ class AbilityBotI18nTest { void missingPublicCommandsLocalizedInItalian() { MessageContext context = mockContext(ITALIAN_USER); - bot.reportCommands().action().accept(context); + defaultAbs.reportCommands().action().accept(context); verify(silent, times(1)) .send("Non sono presenti comandi disponibile.", ITALIAN_USER.getId()); diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotTest.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotTest.java index a1c5e8a5..a8bb77b8 100644 --- a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotTest.java +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/AbilityBotTest.java @@ -69,6 +69,7 @@ public class AbilityBotTest { public static final User CREATOR = new User(1337, "creatorFirst", false, "creatorLast", "creatorUsername", null); private DefaultBot bot; + private DefaultAbilities defaultAbs; private DBContext db; private MessageSender sender; private SilentSender silent; @@ -77,6 +78,7 @@ public class AbilityBotTest { void setUp() { db = offlineInstance("db"); bot = new DefaultBot(EMPTY, EMPTY, db); + defaultAbs = new DefaultAbilities(bot); sender = mock(MessageSender.class); silent = mock(SilentSender.class); @@ -132,7 +134,7 @@ public class AbilityBotTest { void canBackupDB() throws TelegramApiException { MessageContext context = defaultContext(); - bot.backupDB().action().accept(context); + defaultAbs.backupDB().action().accept(context); deleteQuietly(new java.io.File("backup.json")); verify(sender, times(1)).sendDocument(any()); @@ -147,7 +149,7 @@ public class AbilityBotTest { // Support for null parameter matching since due to mocking API changes when(sender.downloadFile(ArgumentMatchers.isNull())).thenReturn(backupFile); - bot.recoverDB().replies().get(0).actOn(update); + defaultAbs.recoverDB().replies().get(0).actOn(update); verify(silent, times(1)).send(RECOVER_SUCCESS, GROUP_ID); assertEquals(db.getSet(TEST), newHashSet(TEST), "Bot recovered but the DB is still not in sync"); @@ -169,7 +171,7 @@ public class AbilityBotTest { MessageContext context = defaultContext(); - bot.demoteAdmin().action().accept(context); + defaultAbs.demoteAdmin().action().accept(context); Set actual = bot.admins(); Set expected = emptySet(); @@ -182,7 +184,7 @@ public class AbilityBotTest { MessageContext context = defaultContext(); - bot.promoteAdmin().action().accept(context); + defaultAbs.promoteAdmin().action().accept(context); Set actual = bot.admins(); Set expected = newHashSet(USER.getId()); @@ -194,7 +196,7 @@ public class AbilityBotTest { addUsers(USER); MessageContext context = defaultContext(); - bot.banUser().action().accept(context); + defaultAbs.banUser().action().accept(context); Set actual = bot.blacklist(); Set expected = newHashSet(USER.getId()); @@ -208,7 +210,7 @@ public class AbilityBotTest { MessageContext context = defaultContext(); - bot.unbanUser().action().accept(context); + defaultAbs.unbanUser().action().accept(context); Set actual = bot.blacklist(); Set expected = newHashSet(); @@ -225,7 +227,7 @@ public class AbilityBotTest { addUsers(USER, CREATOR); MessageContext context = mockContext(USER, GROUP_ID, CREATOR.getUserName()); - bot.banUser().action().accept(context); + defaultAbs.banUser().action().accept(context); Set actual = bot.blacklist(); Set expected = newHashSet(USER.getId()); @@ -243,7 +245,7 @@ public class AbilityBotTest { void creatorCanClaimBot() { MessageContext context = mockContext(CREATOR, GROUP_ID); - bot.claimCreator().action().accept(context); + defaultAbs.claimCreator().action().accept(context); Set actual = bot.admins(); Set expected = newHashSet(CREATOR.getId()); @@ -544,7 +546,7 @@ public class AbilityBotTest { void canReportCommands() { MessageContext context = mockContext(USER, GROUP_ID); - bot.reportCommands().action().accept(context); + defaultAbs.reportCommands().action().accept(context); verify(silent, times(1)).send("default - dis iz default command", GROUP_ID); } @@ -578,7 +580,7 @@ public class AbilityBotTest { when(message.hasText()).thenReturn(true); MessageContext creatorCtx = newContext(update, CREATOR, GROUP_ID); - bot.commands().action().accept(creatorCtx); + defaultAbs.commands().action().accept(creatorCtx); String expected = "PUBLIC\n/commands\n/count\n/default - dis iz default command\n/group\n/test\nADMIN\n/admin\n/ban\n/demote\n/promote\n/unban\nCREATOR\n/backup\n/claim\n/recover\n/report"; verify(silent, times(1)).send(expected, GROUP_ID); @@ -595,7 +597,7 @@ public class AbilityBotTest { MessageContext userCtx = newContext(update, USER, GROUP_ID); - bot.commands().action().accept(userCtx); + defaultAbs.commands().action().accept(userCtx); String expected = "PUBLIC\n/commands\n/count\n/default - dis iz default command\n/group\n/test"; verify(silent, times(1)).send(expected, GROUP_ID); diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/DefaultBot.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/DefaultBot.java index c6f843d7..12db53f1 100644 --- a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/DefaultBot.java +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/bot/DefaultBot.java @@ -3,6 +3,7 @@ package org.telegram.abilitybots.api.bot; import org.telegram.abilitybots.api.db.DBContext; import org.telegram.abilitybots.api.objects.Ability; import org.telegram.abilitybots.api.objects.Ability.AbilityBuilder; +import org.telegram.abilitybots.api.toggle.AbilityToggle; import static org.telegram.abilitybots.api.objects.Ability.builder; import static org.telegram.abilitybots.api.objects.Flag.CALLBACK_QUERY; @@ -17,6 +18,10 @@ public class DefaultBot extends AbilityBot { super(token, username, db); } + public DefaultBot(String token, String username, DBContext db, AbilityToggle toggle) { + super(token, username, db, toggle); + } + public static AbilityBuilder getDefaultBuilder() { return builder() .name("test") diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/toggle/BareboneToggleTest.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/toggle/BareboneToggleTest.java new file mode 100644 index 00000000..9adca9d4 --- /dev/null +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/toggle/BareboneToggleTest.java @@ -0,0 +1,47 @@ +package org.telegram.abilitybots.api.toggle; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.telegram.abilitybots.api.bot.DefaultAbilities; +import org.telegram.abilitybots.api.bot.DefaultBot; +import org.telegram.abilitybots.api.db.DBContext; + +import java.io.IOException; + +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.junit.jupiter.api.Assertions.*; +import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance; + +public class BareboneToggleTest { + + private DBContext db; + private AbilityToggle toggle; + private DefaultBot bareboneBot; + private DefaultAbilities defaultAbs; + + @BeforeEach + void setUp() { + db = offlineInstance("db"); + toggle = new BareboneToggle(); + bareboneBot = new DefaultBot(EMPTY, EMPTY, db, toggle); + defaultAbs = new DefaultAbilities(bareboneBot); + } + + @AfterEach + void tearDown() throws IOException { + db.clear(); + db.close(); + } + + @Test + public void turnsOffAllAbilities() { + assertFalse(bareboneBot.abilities().containsKey(DefaultAbilities.CLAIM)); + } + + @Test + public void throwsOnProcessingAbility() { + Assertions.assertThrows(RuntimeException.class, () -> toggle.processAbility(defaultAbs.claimCreator())); + } +} \ No newline at end of file diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/toggle/CustomToggleTest.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/toggle/CustomToggleTest.java new file mode 100644 index 00000000..aeac3c8a --- /dev/null +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/toggle/CustomToggleTest.java @@ -0,0 +1,51 @@ +package org.telegram.abilitybots.api.toggle; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.telegram.abilitybots.api.bot.DefaultAbilities; +import org.telegram.abilitybots.api.bot.DefaultBot; +import org.telegram.abilitybots.api.db.DBContext; + +import java.io.IOException; + +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.junit.jupiter.api.Assertions.*; +import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance; + +class CustomToggleTest { + private DBContext db; + private AbilityToggle toggle; + private DefaultBot customBot; + private DefaultAbilities defaultAbs; + + @BeforeEach + void setUp() { + db = offlineInstance("db"); + defaultAbs = new DefaultAbilities(customBot); + } + + @AfterEach + void tearDown() throws IOException { + db.clear(); + db.close(); + } + + @Test + public void canTurnOffAbilities() { + toggle = new CustomToggle().turnOff(DefaultAbilities.CLAIM); + customBot = new DefaultBot(EMPTY, EMPTY, db, toggle); + assertFalse(customBot.abilities().containsKey(DefaultAbilities.CLAIM)); + } + + @Test + public void canProcessAbilities() { + String targetName = DefaultAbilities.CLAIM + "1toggle"; + toggle = new CustomToggle().toggle(DefaultAbilities.CLAIM, targetName); + customBot = new DefaultBot(EMPTY, EMPTY, db, toggle); + + assertTrue(customBot.abilities().containsKey(targetName)); + } + +} \ No newline at end of file diff --git a/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/toggle/DefaultToggleTest.java b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/toggle/DefaultToggleTest.java new file mode 100644 index 00000000..8095d6fa --- /dev/null +++ b/telegrambots-abilities/src/test/java/org/telegram/abilitybots/api/toggle/DefaultToggleTest.java @@ -0,0 +1,69 @@ +package org.telegram.abilitybots.api.toggle; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.telegram.abilitybots.api.bot.DefaultAbilities; +import org.telegram.abilitybots.api.bot.DefaultBot; +import org.telegram.abilitybots.api.db.DBContext; +import org.telegram.abilitybots.api.objects.Ability; + +import java.io.IOException; +import java.util.Set; + +import static com.google.common.collect.Sets.newHashSet; +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.junit.jupiter.api.Assertions.*; +import static org.telegram.abilitybots.api.bot.DefaultAbilities.*; +import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance; + +class DefaultToggleTest { + private DBContext db; + private AbilityToggle toggle; + private DefaultBot defaultBot; + private DefaultAbilities defaultAbs; + + @BeforeEach + void setUp() { + db = offlineInstance("db"); + defaultAbs = new DefaultAbilities(defaultBot); + } + + @AfterEach + void tearDown() throws IOException { + db.clear(); + db.close(); + } + + @Test + public void claimsEveryAbilityIsOn() { + Ability random = DefaultBot.getDefaultBuilder() + .name("randomsomethingrandom").build(); + toggle = new DefaultToggle(); + defaultBot = new DefaultBot(EMPTY, EMPTY, db, toggle); + + assertFalse(toggle.isOff(random)); + } + + @Test + public void passedSameAbilityRefOnProcess() { + Ability random = DefaultBot.getDefaultBuilder() + .name("randomsomethingrandom").build(); + toggle = new DefaultToggle(); + defaultBot = new DefaultBot(EMPTY, EMPTY, db, toggle); + + assertSame(random, toggle.processAbility(random), "Toggle returned a different ability"); + } + + @Test + public void allAbilitiesAreRegistered() { + toggle = new DefaultToggle(); + defaultBot = new DefaultBot(EMPTY, EMPTY, db, toggle); + Set defaultNames = newHashSet( + CLAIM, BAN, UNBAN, + PROMOTE, DEMOTE, RECOVER, + BACKUP, REPORT, COMMANDS); + + assertTrue(defaultBot.abilities().keySet().containsAll(defaultNames), "Toggle returned a different ability"); + } +} \ No newline at end of file From 5d60c72a46e8593aa075cb1b8764bab0c65c73fd Mon Sep 17 00:00:00 2001 From: Abbas Abou Daya Date: Sun, 29 Sep 2019 23:14:23 -0700 Subject: [PATCH 14/23] Add wiki for Ability Extensions --- TelegramBots.wiki/_Sidebar.md | 1 + .../abilities/Ability-Extensions.md | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 TelegramBots.wiki/abilities/Ability-Extensions.md diff --git a/TelegramBots.wiki/_Sidebar.md b/TelegramBots.wiki/_Sidebar.md index f423f02e..85647408 100644 --- a/TelegramBots.wiki/_Sidebar.md +++ b/TelegramBots.wiki/_Sidebar.md @@ -9,6 +9,7 @@ * [[Hello Ability]] * [[Using Replies]] * [[Database Handling]] + * [[Ability Extensions]] * [[Bot Testing]] * [[Bot Recovery]] * [[Advanced]] diff --git a/TelegramBots.wiki/abilities/Ability-Extensions.md b/TelegramBots.wiki/abilities/Ability-Extensions.md new file mode 100644 index 00000000..60f5a482 --- /dev/null +++ b/TelegramBots.wiki/abilities/Ability-Extensions.md @@ -0,0 +1,40 @@ +# Ability Extensions +You have around 100 abilities in your bot and you're looking for a way to refactor that mess into more modular classes. `AbillityExtension` is here to support just that! It's not a secret that AbilityBot uses refactoring backstage to be able to construct all of your abilities and map them accordingly. However, AbilityBot searches initially for all methods that return an `AbilityExtension` type. Then, those extensions will be used to search for declared abilities. Here's an example. +```java +public class MrGoodGuy implements AbilityExtension { + public Ability () { + return Ability.builder() + .name("nice") + .privacy(PUBLIC) + .locality(ALL) + .action(ctx -> silent.send("You're awesome!", ctx.chatId()) + ); + } +} + +public class MrBadGuy implements AbilityExtension { + public Ability () { + return Ability.builder() + .name("notnice") + .privacy(PUBLIC) + .locality(ALL) + .action(ctx -> silent.send("You're horrible!", ctx.chatId()) + ); + } + } + + public class YourAwesomeBot implements AbilityBot { + + // Constructor for your bot + + public AbilityExtension goodGuy() { + return new MrGoodGuy(); + } + + public AbilityExtension badGuy() { + return new MrBadGuy(); + } + + // Override creatorId + } +``` \ No newline at end of file From e526b9db142624c47e168c766b36b0357160ab68 Mon Sep 17 00:00:00 2001 From: "j.r@wiuwiu.de" Date: Sun, 13 Oct 2019 15:12:22 +0200 Subject: [PATCH 15/23] Added deprecated constructor with the old style --- .../TelegramLongPollingCommandBot.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/TelegramLongPollingCommandBot.java b/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/TelegramLongPollingCommandBot.java index 05de2c72..1796b364 100644 --- a/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/TelegramLongPollingCommandBot.java +++ b/telegrambots-extensions/src/main/java/org/telegram/telegrambots/extensions/bots/commandbot/TelegramLongPollingCommandBot.java @@ -22,6 +22,7 @@ import java.util.function.BiConsumer; */ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingBot implements ICommandRegistry { private final CommandRegistry commandRegistry; + private String botUsername; /** * Creates a TelegramLongPollingCommandBot using default options @@ -32,6 +33,19 @@ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingB this(ApiContext.getInstance(DefaultBotOptions.class)); } + /** + * Creates a TelegramLongPollingCommandBot using default options + * Use ICommandRegistry's methods on this bot to register commands + * + * @param botUsername Username of the bot + * @deprecated Overwrite {@link #getBotUsername() getBotUsername} instead + */ + @Deprecated + public TelegramLongPollingCommandBot(String botUsername){ + this(); + this.botUsername = botUsername; + } + /** * Creates a TelegramLongPollingCommandBot with custom options and allowing commands with * usernames @@ -138,7 +152,9 @@ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingB * @return Bot username */ @Override - public abstract String getBotUsername(); + public String getBotUsername(){ + return this.botUsername; + }; /** * Process all updates, that are not commands. From 2d27f9e60f68820c58c30fc846a6ba453f86e017 Mon Sep 17 00:00:00 2001 From: UnAfraid Date: Mon, 14 Oct 2019 20:08:16 +0300 Subject: [PATCH 16/23] Post merge fixes --- .../java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java index 616a5067..a237616d 100644 --- a/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java +++ b/telegrambots-abilities/src/main/java/org/telegram/abilitybots/api/bot/BaseAbilityBot.java @@ -6,8 +6,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimap; import org.apache.commons.io.IOUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From 7cfcca3ae37b250c1ad8124d64108baf7ad77afe Mon Sep 17 00:00:00 2001 From: rubenlagus Date: Tue, 10 Sep 2019 22:29:10 +0100 Subject: [PATCH 17/23] Fix error importing project --- .gitignore | 3 + README.md | 8 +- TelegramBots.wiki/Changelog.md | 3 + TelegramBots.wiki/Getting-Started.md | 4 +- TelegramBots.wiki/abilities/Simple-Example.md | 2 +- pom.xml | 87 ++++++++++++++++++- telegrambots-abilities/README.md | 8 +- telegrambots-abilities/pom.xml | 4 +- .../src/test/resources/log4j2-test.xml | 19 ++++ telegrambots-chat-session-bot/README.md | 2 +- telegrambots-chat-session-bot/pom.xml | 4 +- telegrambots-extensions/README.md | 4 +- telegrambots-extensions/pom.xml | 4 +- telegrambots-meta/pom.xml | 2 +- .../src/test/resources/log4j2-test.xml | 19 ++++ telegrambots-spring-boot-starter/pom.xml | 4 +- .../src/test/resources/log4j2-test.xml | 19 ++++ telegrambots/pom.xml | 4 +- .../src/test/resources/log4j2-test.xml | 19 ++++ 19 files changed, 191 insertions(+), 28 deletions(-) create mode 100644 telegrambots-abilities/src/test/resources/log4j2-test.xml create mode 100644 telegrambots-meta/src/test/resources/log4j2-test.xml create mode 100644 telegrambots-spring-boot-starter/src/test/resources/log4j2-test.xml create mode 100644 telegrambots/src/test/resources/log4j2-test.xml diff --git a/.gitignore b/.gitignore index 737c7ac7..c0b06615 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ copyright/ #File System specific files .DS_STORE + +# Default ignored files +/Bots.iws \ No newline at end of file diff --git a/README.md b/README.md index 5d45b712..15de0465 100644 --- a/README.md +++ b/README.md @@ -27,16 +27,16 @@ Just import add the library to your project with one of these options: org.telegram telegrambots - 4.4.0 + 4.4.0.1 ``` ```gradle - compile "org.telegram:telegrambots:4.4.0" + compile "org.telegram:telegrambots:4.4.0.1" ``` - 2. Using Jitpack from [here](https://jitpack.io/#rubenlagus/TelegramBots/4.4.0) - 3. Download the jar(including all dependencies) from [here](https://mvnrepository.com/artifact/org.telegram/telegrambots/4.4.0) + 2. Using Jitpack from [here](https://jitpack.io/#rubenlagus/TelegramBots/4.4.0.1) + 3. Download the jar(including all dependencies) from [here](https://mvnrepository.com/artifact/org.telegram/telegrambots/4.4.0.1) In order to use Long Polling mode, just create your own bot extending `org.telegram.telegrambots.bots.TelegramLongPollingBot`. diff --git a/TelegramBots.wiki/Changelog.md b/TelegramBots.wiki/Changelog.md index d234ab7c..4f686e71 100644 --- a/TelegramBots.wiki/Changelog.md +++ b/TelegramBots.wiki/Changelog.md @@ -1,3 +1,6 @@ +### 4.4.0.1 ### +1. Bug fix when importing the project + ### 4.4.0 ### 1. Update Api version [4.4](https://core.telegram.org/bots/api-changelog#july-29-2019) 2. Removed BotLogger, replaced with [log4j2](https://logging.apache.org/log4j/2.x/) diff --git a/TelegramBots.wiki/Getting-Started.md b/TelegramBots.wiki/Getting-Started.md index e0878bbe..0f9d2a6c 100644 --- a/TelegramBots.wiki/Getting-Started.md +++ b/TelegramBots.wiki/Getting-Started.md @@ -11,13 +11,13 @@ First you need ot get the library and add it to your project. There are few poss org.telegram telegrambots - 4.4.0 + 4.4.0.1 ``` * With **Gradle**: ```groovy - compile group: 'org.telegram', name: 'telegrambots', version: '4.4.0' + compile group: 'org.telegram', name: 'telegrambots', version: '4.4.0.1' ``` 2. Don't like **Maven Central Repository**? It can also be taken from [Jitpack](https://jitpack.io/#rubenlagus/TelegramBots). diff --git a/TelegramBots.wiki/abilities/Simple-Example.md b/TelegramBots.wiki/abilities/Simple-Example.md index 3aa89b8a..04bf57f5 100644 --- a/TelegramBots.wiki/abilities/Simple-Example.md +++ b/TelegramBots.wiki/abilities/Simple-Example.md @@ -9,7 +9,7 @@ As with any Java project, you will need to set your dependencies. org.telegram telegrambots-abilities - 4.4.0 + 4.4.0.1 ``` * **Gradle** diff --git a/pom.xml b/pom.xml index c6869154..5498fb4d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots pom - 4.4.0 + 4.4.0.1 telegrambots @@ -26,14 +26,47 @@ + Bots + https://github.com/rubenlagus/TelegramBots + Easy to use library to create Telegram Bots + + + https://github.com/rubenlagus/TelegramBots/issues + GitHub Issues + + + + https://github.com/rubenlagus/TelegramBots + scm:git:git://github.com/rubenlagus/TelegramBots.git + scm:git:git@github.com:rubenlagus/TelegramBots.git + + + + + rberlopez@gmail.com + Ruben Bermudez + https://github.com/rubenlagus + rubenlagus + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + 11 8 ${java.version} ${java.version} - true - 5.5.1 3.0.0 3.0.0 @@ -113,4 +146,52 @@ ${slf4j.version} + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M2 + + + enforce-versions + + enforce + + + + + + + + + + + \ No newline at end of file diff --git a/telegrambots-abilities/README.md b/telegrambots-abilities/README.md index d43fa96c..483334c0 100644 --- a/telegrambots-abilities/README.md +++ b/telegrambots-abilities/README.md @@ -18,19 +18,19 @@ Usage org.telegram telegrambots-abilities - 4.4.0 + 4.4.0.1 ``` **Gradle** ```gradle - compile "org.telegram:telegrambots-abilities:4.4.0" + compile "org.telegram:telegrambots-abilities:4.4.0.1" ``` -**JitPack** - [JitPack](https://jitpack.io/#rubenlagus/TelegramBots/v4.4.0) +**JitPack** - [JitPack](https://jitpack.io/#rubenlagus/TelegramBots/v4.4.0.1) -**Plain imports** - [Here](https://github.com/rubenlagus/TelegramBots/releases/tag/v4.4.0) +**Plain imports** - [Here](https://github.com/rubenlagus/TelegramBots/releases/tag/v4.4.0.1) Motivation ---------- diff --git a/telegrambots-abilities/pom.xml b/telegrambots-abilities/pom.xml index 7f420e0a..7325fd3a 100644 --- a/telegrambots-abilities/pom.xml +++ b/telegrambots-abilities/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots - 4.4.0 + 4.4.0.1 telegrambots-abilities @@ -85,7 +85,7 @@ org.telegram telegrambots - 4.4.0 + 4.4.0.1 org.apache.commons diff --git a/telegrambots-abilities/src/test/resources/log4j2-test.xml b/telegrambots-abilities/src/test/resources/log4j2-test.xml new file mode 100644 index 00000000..8054f7f7 --- /dev/null +++ b/telegrambots-abilities/src/test/resources/log4j2-test.xml @@ -0,0 +1,19 @@ + + + + %d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} [%t] %-5level %logger{36} - %msg%n + App + + + + + + + + + + + + + + \ No newline at end of file diff --git a/telegrambots-chat-session-bot/README.md b/telegrambots-chat-session-bot/README.md index 212ceb0a..f0616b41 100644 --- a/telegrambots-chat-session-bot/README.md +++ b/telegrambots-chat-session-bot/README.md @@ -15,7 +15,7 @@ Usage org.telegram telegrambots-chat-session-bot - 4.4.0 + 4.4.0.1 ``` diff --git a/telegrambots-chat-session-bot/pom.xml b/telegrambots-chat-session-bot/pom.xml index 3765c8f4..606db6ac 100644 --- a/telegrambots-chat-session-bot/pom.xml +++ b/telegrambots-chat-session-bot/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots - 4.4.0 + 4.4.0.1 telegrambots-chat-session-bot @@ -84,7 +84,7 @@ org.telegram telegrambots - 4.4.0 + 4.4.0.1 diff --git a/telegrambots-extensions/README.md b/telegrambots-extensions/README.md index 6e1d2173..448d404c 100644 --- a/telegrambots-extensions/README.md +++ b/telegrambots-extensions/README.md @@ -16,12 +16,12 @@ Just import add the library to your project with one of these options: org.telegram telegrambotsextensions - 4.4.0 + 4.4.0.1 ``` 2. Using Gradle: ```gradle - compile "org.telegram:telegrambotsextensions:4.4.0" + compile "org.telegram:telegrambotsextensions:4.4.0.1" ``` \ No newline at end of file diff --git a/telegrambots-extensions/pom.xml b/telegrambots-extensions/pom.xml index b5990489..ea44f782 100644 --- a/telegrambots-extensions/pom.xml +++ b/telegrambots-extensions/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots - 4.4.0 + 4.4.0.1 telegrambotsextensions @@ -75,7 +75,7 @@ org.telegram telegrambots - 4.4.0 + 4.4.0.1 diff --git a/telegrambots-meta/pom.xml b/telegrambots-meta/pom.xml index c2eb4616..78b37e06 100644 --- a/telegrambots-meta/pom.xml +++ b/telegrambots-meta/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots - 4.4.0 + 4.4.0.1 telegrambots-meta diff --git a/telegrambots-meta/src/test/resources/log4j2-test.xml b/telegrambots-meta/src/test/resources/log4j2-test.xml new file mode 100644 index 00000000..8054f7f7 --- /dev/null +++ b/telegrambots-meta/src/test/resources/log4j2-test.xml @@ -0,0 +1,19 @@ + + + + %d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} [%t] %-5level %logger{36} - %msg%n + App + + + + + + + + + + + + + + \ No newline at end of file diff --git a/telegrambots-spring-boot-starter/pom.xml b/telegrambots-spring-boot-starter/pom.xml index c28a9bdf..f2a6a2b9 100644 --- a/telegrambots-spring-boot-starter/pom.xml +++ b/telegrambots-spring-boot-starter/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots - 4.4.0 + 4.4.0.1 telegrambots-spring-boot-starter @@ -78,7 +78,7 @@ org.telegram telegrambots - 4.4.0 + 4.4.0.1 org.springframework.boot diff --git a/telegrambots-spring-boot-starter/src/test/resources/log4j2-test.xml b/telegrambots-spring-boot-starter/src/test/resources/log4j2-test.xml new file mode 100644 index 00000000..8054f7f7 --- /dev/null +++ b/telegrambots-spring-boot-starter/src/test/resources/log4j2-test.xml @@ -0,0 +1,19 @@ + + + + %d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} [%t] %-5level %logger{36} - %msg%n + App + + + + + + + + + + + + + + \ No newline at end of file diff --git a/telegrambots/pom.xml b/telegrambots/pom.xml index 1143fd5e..cb8e45ef 100644 --- a/telegrambots/pom.xml +++ b/telegrambots/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots - 4.4.0 + 4.4.0.1 telegrambots @@ -95,7 +95,7 @@ org.telegram telegrambots-meta - 4.4.0 + 4.4.0.1 com.fasterxml.jackson.core diff --git a/telegrambots/src/test/resources/log4j2-test.xml b/telegrambots/src/test/resources/log4j2-test.xml new file mode 100644 index 00000000..8054f7f7 --- /dev/null +++ b/telegrambots/src/test/resources/log4j2-test.xml @@ -0,0 +1,19 @@ + + + + %d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} [%t] %-5level %logger{36} - %msg%n + App + + + + + + + + + + + + + + \ No newline at end of file From 44c814256d61615dca5626ac186d1eb6166ef2ae Mon Sep 17 00:00:00 2001 From: rubenlagus Date: Tue, 10 Sep 2019 23:44:28 +0100 Subject: [PATCH 18/23] Bug fixes and pom dependencies upgrades --- Bots.ipr | 367 +++++++++--------- pom.xml | 10 +- telegrambots-meta/pom.xml | 4 +- .../meta/api/objects/ChatPermissions.java | 32 ++ telegrambots-spring-boot-starter/pom.xml | 4 +- telegrambots/pom.xml | 8 +- .../telegrambots/bots/DefaultAbsSender.java | 15 +- .../facilities/TelegramHttpClientBuilder.java | 3 +- .../updatesreceivers/DefaultBotSession.java | 23 +- .../telegrambots/util/WebhookUtils.java | 24 +- 10 files changed, 268 insertions(+), 222 deletions(-) diff --git a/Bots.ipr b/Bots.ipr index 48a1815b..3cba11c9 100644 --- a/Bots.ipr +++ b/Bots.ipr @@ -22,10 +22,10 @@ - - - + + + @@ -920,26 +920,15 @@ - + - + - + - - - - - - - - - - - - + @@ -1008,15 +997,15 @@ - + - + - + - + @@ -1129,37 +1118,48 @@ - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + @@ -1184,17 +1184,6 @@ - - - - - - - - - - - @@ -1250,37 +1239,37 @@ - + - + - + - + - + - + - + - + - + - + - + - + @@ -1415,15 +1404,15 @@ - + - + - + - + @@ -1437,15 +1426,15 @@ - + - + - + - + @@ -1525,59 +1514,59 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1591,147 +1580,147 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1800,48 +1789,48 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1921,103 +1910,103 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/pom.xml b/pom.xml index 5498fb4d..df092cfe 100644 --- a/pom.xml +++ b/pom.xml @@ -67,12 +67,13 @@ ${java.version} ${java.version} - 5.5.1 + 5.5.2 3.0.0 3.0.0 2.9.9 - 2.9.9.1 + 2.9.9.2 1.7.28 + 1.3.5 @@ -115,6 +116,11 @@ slf4j-api ${slf4j.version} + + jakarta.annotation + jakarta.annotation-api + ${jakarta.annotation.version} + diff --git a/telegrambots-meta/pom.xml b/telegrambots-meta/pom.xml index 78b37e06..e60b3e72 100644 --- a/telegrambots-meta/pom.xml +++ b/telegrambots-meta/pom.xml @@ -70,10 +70,10 @@ UTF-8 UTF-8 4.2.2 - 2.9.9 + 2.9.9.2 2.9.9 20180813 - 28.0-jre + 28.1-jre diff --git a/telegrambots-meta/src/main/java/org/telegram/telegrambots/meta/api/objects/ChatPermissions.java b/telegrambots-meta/src/main/java/org/telegram/telegrambots/meta/api/objects/ChatPermissions.java index 7a42bd41..97c48de7 100644 --- a/telegrambots-meta/src/main/java/org/telegram/telegrambots/meta/api/objects/ChatPermissions.java +++ b/telegrambots-meta/src/main/java/org/telegram/telegrambots/meta/api/objects/ChatPermissions.java @@ -73,6 +73,38 @@ public class ChatPermissions implements BotApiObject { return canPinMessages; } + public void setCanSendMessages(Boolean canSendMessages) { + this.canSendMessages = canSendMessages; + } + + public void setGetCanSendMediaMessages(Boolean getCanSendMediaMessages) { + this.getCanSendMediaMessages = getCanSendMediaMessages; + } + + public void setCanSendPolls(Boolean canSendPolls) { + this.canSendPolls = canSendPolls; + } + + public void setCanSendOtherMessages(Boolean canSendOtherMessages) { + this.canSendOtherMessages = canSendOtherMessages; + } + + public void setCanAddWebPagePreviews(Boolean canAddWebPagePreviews) { + this.canAddWebPagePreviews = canAddWebPagePreviews; + } + + public void setCanChangeInfo(Boolean canChangeInfo) { + this.canChangeInfo = canChangeInfo; + } + + public void setCanInviteUsers(Boolean canInviteUsers) { + this.canInviteUsers = canInviteUsers; + } + + public void setCanPinMessages(Boolean canPinMessages) { + this.canPinMessages = canPinMessages; + } + @Override public String toString() { return "ChatPermissions{" + diff --git a/telegrambots-spring-boot-starter/pom.xml b/telegrambots-spring-boot-starter/pom.xml index f2a6a2b9..0ba8111f 100644 --- a/telegrambots-spring-boot-starter/pom.xml +++ b/telegrambots-spring-boot-starter/pom.xml @@ -70,7 +70,7 @@ UTF-8 UTF-8 - 2.1.6.RELEASE + 2.1.8.RELEASE @@ -103,7 +103,7 @@ org.assertj assertj-core test - 3.13.1 + 3.13.2 diff --git a/telegrambots/pom.xml b/telegrambots/pom.xml index cb8e45ef..113bc407 100644 --- a/telegrambots/pom.xml +++ b/telegrambots/pom.xml @@ -70,11 +70,11 @@ UTF-8 UTF-8 - 2.29 + 2.29.1 1.19.3 - 4.5.9 + 4.5.10 20180813 - 2.9.9 + 2.9.9.2 2.9.9 2.6 @@ -105,7 +105,7 @@ com.fasterxml.jackson.jaxrs jackson-jaxrs-json-provider - ${jackson.version} + ${jacksonbase.version} com.fasterxml.jackson.module diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java b/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java index b94d3a09..71c6d4ed 100644 --- a/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java +++ b/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java @@ -6,7 +6,6 @@ import org.apache.http.HttpEntity; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.BufferedHttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.MultipartEntityBuilder; @@ -16,7 +15,15 @@ import org.telegram.telegrambots.facilities.TelegramHttpClientBuilder; import org.telegram.telegrambots.facilities.filedownloader.TelegramFileDownloader; import org.telegram.telegrambots.meta.api.methods.BotApiMethod; import org.telegram.telegrambots.meta.api.methods.groupadministration.SetChatPhoto; -import org.telegram.telegrambots.meta.api.methods.send.*; +import org.telegram.telegrambots.meta.api.methods.send.SendAnimation; +import org.telegram.telegrambots.meta.api.methods.send.SendAudio; +import org.telegram.telegrambots.meta.api.methods.send.SendDocument; +import org.telegram.telegrambots.meta.api.methods.send.SendMediaGroup; +import org.telegram.telegrambots.meta.api.methods.send.SendPhoto; +import org.telegram.telegrambots.meta.api.methods.send.SendSticker; +import org.telegram.telegrambots.meta.api.methods.send.SendVideo; +import org.telegram.telegrambots.meta.api.methods.send.SendVideoNote; +import org.telegram.telegrambots.meta.api.methods.send.SendVoice; import org.telegram.telegrambots.meta.api.methods.stickers.AddStickerToSet; import org.telegram.telegrambots.meta.api.methods.stickers.CreateNewStickerSet; import org.telegram.telegrambots.meta.api.methods.stickers.UploadStickerFile; @@ -729,9 +736,7 @@ public abstract class DefaultAbsSender extends AbsSender { private String sendHttpPostRequest(HttpPost httppost) throws IOException { try (CloseableHttpResponse response = httpClient.execute(httppost, options.getHttpContext())) { - HttpEntity ht = response.getEntity(); - BufferedHttpEntity buf = new BufferedHttpEntity(ht); - return EntityUtils.toString(buf, StandardCharsets.UTF_8); + return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); } } diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/facilities/TelegramHttpClientBuilder.java b/telegrambots/src/main/java/org/telegram/telegrambots/facilities/TelegramHttpClientBuilder.java index 38a3a17a..3993fc79 100644 --- a/telegrambots/src/main/java/org/telegram/telegrambots/facilities/TelegramHttpClientBuilder.java +++ b/telegrambots/src/main/java/org/telegram/telegrambots/facilities/TelegramHttpClientBuilder.java @@ -45,7 +45,8 @@ public class TelegramHttpClientBuilder { case SOCKS5: registry = RegistryBuilder. create() .register("http", new SocksConnectionSocketFactory()) - .register("https", new SocksSSLConnectionSocketFactory(SSLContexts.createSystemDefault())).build(); + .register("https", new SocksSSLConnectionSocketFactory(SSLContexts.createSystemDefault())) + .build(); return new PoolingHttpClientConnectionManager(registry); } return null; diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/DefaultBotSession.java b/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/DefaultBotSession.java index 78a84d8d..0f9f6334 100644 --- a/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/DefaultBotSession.java +++ b/telegrambots/src/main/java/org/telegram/telegrambots/updatesreceivers/DefaultBotSession.java @@ -2,25 +2,27 @@ package org.telegram.telegrambots.updatesreceivers; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.inject.Inject; -import org.apache.http.HttpEntity; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.BufferedHttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import org.json.JSONException; +import org.telegram.telegrambots.bots.DefaultBotOptions; +import org.telegram.telegrambots.facilities.TelegramHttpClientBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.telegram.telegrambots.meta.ApiConstants; import org.telegram.telegrambots.meta.api.methods.updates.GetUpdates; import org.telegram.telegrambots.meta.api.objects.Update; -import org.telegram.telegrambots.bots.DefaultBotOptions; import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException; -import org.telegram.telegrambots.facilities.TelegramHttpClientBuilder; -import org.telegram.telegrambots.meta.generics.*; +import org.telegram.telegrambots.meta.generics.BotOptions; +import org.telegram.telegrambots.meta.generics.BotSession; +import org.telegram.telegrambots.meta.generics.LongPollingBot; +import org.telegram.telegrambots.meta.generics.UpdatesHandler; +import org.telegram.telegrambots.meta.generics.UpdatesReader; import java.io.IOException; import java.io.InvalidObjectException; @@ -28,7 +30,11 @@ import java.net.SocketException; import java.net.SocketTimeoutException; import java.nio.charset.StandardCharsets; import java.security.InvalidParameterException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.atomic.AtomicBoolean; @@ -132,6 +138,7 @@ public class DefaultBotSession implements BotSession { return running.get(); } + @SuppressWarnings("WeakerAccess") private class ReaderThread extends Thread implements UpdatesReader { private final UpdatesSupplier updatesSupplier; @@ -244,9 +251,7 @@ public class DefaultBotSession implements BotSession { httpPost.setEntity(new StringEntity(objectMapper.writeValueAsString(request), ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(httpPost, options.getHttpContext())) { - HttpEntity ht = response.getEntity(); - BufferedHttpEntity buf = new BufferedHttpEntity(ht); - String responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8); + String responseContent = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); if (response.getStatusLine().getStatusCode() >= 500) { log.warn(responseContent); diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/util/WebhookUtils.java b/telegrambots/src/main/java/org/telegram/telegrambots/util/WebhookUtils.java index a294738d..5210cd6c 100644 --- a/telegrambots/src/main/java/org/telegram/telegrambots/util/WebhookUtils.java +++ b/telegrambots/src/main/java/org/telegram/telegrambots/util/WebhookUtils.java @@ -1,9 +1,9 @@ package org.telegram.telegrambots.util; import org.apache.http.HttpEntity; +import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.BufferedHttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; @@ -11,19 +11,21 @@ import org.apache.http.util.EntityUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.telegram.telegrambots.meta.ApiConstants; -import org.telegram.telegrambots.meta.api.methods.updates.DeleteWebhook; import org.telegram.telegrambots.bots.DefaultAbsSender; import org.telegram.telegrambots.bots.DefaultBotOptions; -import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import org.telegram.telegrambots.facilities.TelegramHttpClientBuilder; +import org.telegram.telegrambots.meta.ApiConstants; +import org.telegram.telegrambots.meta.api.methods.updates.DeleteWebhook; import org.telegram.telegrambots.meta.api.methods.updates.SetWebhook; +import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; +import static org.telegram.telegrambots.Constants.SOCKET_TIMEOUT; + public final class WebhookUtils { private WebhookUtils() { @@ -35,8 +37,16 @@ public final class WebhookUtils { try (CloseableHttpClient httpclient = TelegramHttpClientBuilder.build(botOptions)) { String requestUrl = bot.getBaseUrl() + SetWebhook.PATH; + RequestConfig requestConfig = botOptions.getRequestConfig(); + if (requestConfig == null) { + requestConfig = RequestConfig.copy(RequestConfig.custom().build()) + .setSocketTimeout(SOCKET_TIMEOUT) + .setConnectTimeout(SOCKET_TIMEOUT) + .setConnectionRequestTimeout(SOCKET_TIMEOUT).build(); + } + HttpPost httppost = new HttpPost(requestUrl); - httppost.setConfig(botOptions.getRequestConfig()); + httppost.setConfig(requestConfig); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.addTextBody(SetWebhook.URL_FIELD, url); if (botOptions.getMaxWebhookConnections() != null) { @@ -54,9 +64,7 @@ public final class WebhookUtils { HttpEntity multipart = builder.build(); httppost.setEntity(multipart); try (CloseableHttpResponse response = httpclient.execute(httppost, botOptions.getHttpContext())) { - HttpEntity ht = response.getEntity(); - BufferedHttpEntity buf = new BufferedHttpEntity(ht); - String responseContent = EntityUtils.toString(buf, StandardCharsets.UTF_8); + String responseContent = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); JSONObject jsonObject = new JSONObject(responseContent); if (!jsonObject.getBoolean(ApiConstants.RESPONSE_FIELD_OK)) { throw new TelegramApiRequestException("Error setting webhook", jsonObject); From 0cbc21b1425e6a8cd008651693f91cf074f4d59b Mon Sep 17 00:00:00 2001 From: Ruben Bermudez Date: Tue, 19 Nov 2019 00:22:04 +0000 Subject: [PATCH 19/23] Update version 4.4.0.2 --- Bots.ipr | 16 ++++++++++++---- README.md | 8 ++++---- TelegramBots.wiki/Changelog.md | 8 ++++++++ TelegramBots.wiki/Getting-Started.md | 4 ++-- TelegramBots.wiki/How-To-Update.md | 3 +++ TelegramBots.wiki/abilities/Simple-Example.md | 2 +- pom.xml | 2 +- telegrambots-abilities/README.md | 8 ++++---- telegrambots-abilities/pom.xml | 4 ++-- telegrambots-chat-session-bot/README.md | 2 +- telegrambots-chat-session-bot/pom.xml | 4 ++-- telegrambots-extensions/README.md | 4 ++-- telegrambots-extensions/pom.xml | 4 ++-- telegrambots-meta/pom.xml | 2 +- telegrambots-spring-boot-starter/pom.xml | 4 ++-- telegrambots/pom.xml | 4 ++-- 16 files changed, 49 insertions(+), 30 deletions(-) diff --git a/Bots.ipr b/Bots.ipr index 3cba11c9..164a7410 100644 --- a/Bots.ipr +++ b/Bots.ipr @@ -506,10 +506,18 @@ + + + + + + + + @@ -1118,15 +1126,15 @@ - + - + - + - + diff --git a/README.md b/README.md index 15de0465..d66ba1f3 100644 --- a/README.md +++ b/README.md @@ -27,16 +27,16 @@ Just import add the library to your project with one of these options: org.telegram telegrambots - 4.4.0.1 + 4.4.0.2 ``` ```gradle - compile "org.telegram:telegrambots:4.4.0.1" + compile "org.telegram:telegrambots:4.4.0.2" ``` - 2. Using Jitpack from [here](https://jitpack.io/#rubenlagus/TelegramBots/4.4.0.1) - 3. Download the jar(including all dependencies) from [here](https://mvnrepository.com/artifact/org.telegram/telegrambots/4.4.0.1) + 2. Using Jitpack from [here](https://jitpack.io/#rubenlagus/TelegramBots/4.4.0.2) + 3. Download the jar(including all dependencies) from [here](https://mvnrepository.com/artifact/org.telegram/telegrambots/4.4.0.2) In order to use Long Polling mode, just create your own bot extending `org.telegram.telegrambots.bots.TelegramLongPollingBot`. diff --git a/TelegramBots.wiki/Changelog.md b/TelegramBots.wiki/Changelog.md index 4f686e71..aa6bbe75 100644 --- a/TelegramBots.wiki/Changelog.md +++ b/TelegramBots.wiki/Changelog.md @@ -1,3 +1,11 @@ +### 4.4.0.2 ### +1. Use SLF4J +2. Support case-insensitive usernames +3. Add Ability toggles and export default abilities to their own class +4. Add state machine capability to AbilityBot via ReplyFlow +5. Support backup and recovery of db vars +6. Fixes: #602, #641, #652 + ### 4.4.0.1 ### 1. Bug fix when importing the project diff --git a/TelegramBots.wiki/Getting-Started.md b/TelegramBots.wiki/Getting-Started.md index 0f9d2a6c..64a63865 100644 --- a/TelegramBots.wiki/Getting-Started.md +++ b/TelegramBots.wiki/Getting-Started.md @@ -11,13 +11,13 @@ First you need ot get the library and add it to your project. There are few poss org.telegram telegrambots - 4.4.0.1 + 4.4.0.2 ``` * With **Gradle**: ```groovy - compile group: 'org.telegram', name: 'telegrambots', version: '4.4.0.1' + compile group: 'org.telegram', name: 'telegrambots', version: '4.4.0.2' ``` 2. Don't like **Maven Central Repository**? It can also be taken from [Jitpack](https://jitpack.io/#rubenlagus/TelegramBots). diff --git a/TelegramBots.wiki/How-To-Update.md b/TelegramBots.wiki/How-To-Update.md index 0bc9c26b..11fe80e8 100644 --- a/TelegramBots.wiki/How-To-Update.md +++ b/TelegramBots.wiki/How-To-Update.md @@ -1,3 +1,6 @@ +### To version 4.4.0.2 ### +1. Logging framework has been replaced by slf4j, so now you'll need to manage your own implementation. + ### To version 4.0.0 ### 1. Replace removed method from AbsSender with `execute` requests. 2. Everything under "Telegrambots-meta" has been moved to package `org.telegram.telegrambots.meta`. diff --git a/TelegramBots.wiki/abilities/Simple-Example.md b/TelegramBots.wiki/abilities/Simple-Example.md index 04bf57f5..f541cd66 100644 --- a/TelegramBots.wiki/abilities/Simple-Example.md +++ b/TelegramBots.wiki/abilities/Simple-Example.md @@ -9,7 +9,7 @@ As with any Java project, you will need to set your dependencies. org.telegram telegrambots-abilities - 4.4.0.1 + 4.4.0.2 ``` * **Gradle** diff --git a/pom.xml b/pom.xml index df092cfe..c1deab65 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots pom - 4.4.0.1 + 4.4.0.2 telegrambots diff --git a/telegrambots-abilities/README.md b/telegrambots-abilities/README.md index 483334c0..d4d1401a 100644 --- a/telegrambots-abilities/README.md +++ b/telegrambots-abilities/README.md @@ -18,19 +18,19 @@ Usage org.telegram telegrambots-abilities - 4.4.0.1 + 4.4.0.2 ``` **Gradle** ```gradle - compile "org.telegram:telegrambots-abilities:4.4.0.1" + compile "org.telegram:telegrambots-abilities:4.4.0.2" ``` -**JitPack** - [JitPack](https://jitpack.io/#rubenlagus/TelegramBots/v4.4.0.1) +**JitPack** - [JitPack](https://jitpack.io/#rubenlagus/TelegramBots/v4.4.0.2) -**Plain imports** - [Here](https://github.com/rubenlagus/TelegramBots/releases/tag/v4.4.0.1) +**Plain imports** - [Here](https://github.com/rubenlagus/TelegramBots/releases/tag/v4.4.0.2) Motivation ---------- diff --git a/telegrambots-abilities/pom.xml b/telegrambots-abilities/pom.xml index 7325fd3a..20599cad 100644 --- a/telegrambots-abilities/pom.xml +++ b/telegrambots-abilities/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots - 4.4.0.1 + 4.4.0.2 telegrambots-abilities @@ -85,7 +85,7 @@ org.telegram telegrambots - 4.4.0.1 + 4.4.0.2 org.apache.commons diff --git a/telegrambots-chat-session-bot/README.md b/telegrambots-chat-session-bot/README.md index f0616b41..095f2bc9 100644 --- a/telegrambots-chat-session-bot/README.md +++ b/telegrambots-chat-session-bot/README.md @@ -15,7 +15,7 @@ Usage org.telegram telegrambots-chat-session-bot - 4.4.0.1 + 4.4.0.2 ``` diff --git a/telegrambots-chat-session-bot/pom.xml b/telegrambots-chat-session-bot/pom.xml index 606db6ac..856fdaa3 100644 --- a/telegrambots-chat-session-bot/pom.xml +++ b/telegrambots-chat-session-bot/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots - 4.4.0.1 + 4.4.0.2 telegrambots-chat-session-bot @@ -84,7 +84,7 @@ org.telegram telegrambots - 4.4.0.1 + 4.4.0.2 diff --git a/telegrambots-extensions/README.md b/telegrambots-extensions/README.md index 448d404c..72abbcf3 100644 --- a/telegrambots-extensions/README.md +++ b/telegrambots-extensions/README.md @@ -16,12 +16,12 @@ Just import add the library to your project with one of these options: org.telegram telegrambotsextensions - 4.4.0.1 + 4.4.0.2 ``` 2. Using Gradle: ```gradle - compile "org.telegram:telegrambotsextensions:4.4.0.1" + compile "org.telegram:telegrambotsextensions:4.4.0.2" ``` \ No newline at end of file diff --git a/telegrambots-extensions/pom.xml b/telegrambots-extensions/pom.xml index ea44f782..8c346d7e 100644 --- a/telegrambots-extensions/pom.xml +++ b/telegrambots-extensions/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots - 4.4.0.1 + 4.4.0.2 telegrambotsextensions @@ -75,7 +75,7 @@ org.telegram telegrambots - 4.4.0.1 + 4.4.0.2 diff --git a/telegrambots-meta/pom.xml b/telegrambots-meta/pom.xml index e60b3e72..aed7c4ed 100644 --- a/telegrambots-meta/pom.xml +++ b/telegrambots-meta/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots - 4.4.0.1 + 4.4.0.2 telegrambots-meta diff --git a/telegrambots-spring-boot-starter/pom.xml b/telegrambots-spring-boot-starter/pom.xml index 0ba8111f..1eeee6f8 100644 --- a/telegrambots-spring-boot-starter/pom.xml +++ b/telegrambots-spring-boot-starter/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots - 4.4.0.1 + 4.4.0.2 telegrambots-spring-boot-starter @@ -78,7 +78,7 @@ org.telegram telegrambots - 4.4.0.1 + 4.4.0.2 org.springframework.boot diff --git a/telegrambots/pom.xml b/telegrambots/pom.xml index 113bc407..c25a7b4b 100644 --- a/telegrambots/pom.xml +++ b/telegrambots/pom.xml @@ -7,7 +7,7 @@ org.telegram Bots - 4.4.0.1 + 4.4.0.2 telegrambots @@ -95,7 +95,7 @@ org.telegram telegrambots-meta - 4.4.0.1 + 4.4.0.2 com.fasterxml.jackson.core From 030aed2756b08122444b7444772d4d1d90d059eb Mon Sep 17 00:00:00 2001 From: Ruben Bermudez Date: Tue, 19 Nov 2019 00:30:27 +0000 Subject: [PATCH 20/23] Add comment --- Bots.ipr | 4 ++-- pom.xml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Bots.ipr b/Bots.ipr index 164a7410..a1fedc3a 100644 --- a/Bots.ipr +++ b/Bots.ipr @@ -22,10 +22,10 @@ - - + + diff --git a/pom.xml b/pom.xml index c1deab65..2f7a5268 100644 --- a/pom.xml +++ b/pom.xml @@ -116,6 +116,7 @@ slf4j-api ${slf4j.version} + jakarta.annotation jakarta.annotation-api From 27649a99cf51bb7f8452ce5adad04ec683870568 Mon Sep 17 00:00:00 2001 From: Ruben Bermudez Date: Tue, 19 Nov 2019 01:01:49 +0000 Subject: [PATCH 21/23] Update dependencies --- Bots.ipr | 179 ++++++++++++----------- pom.xml | 10 +- telegrambots-chat-session-bot/pom.xml | 2 +- telegrambots-meta/pom.xml | 4 +- telegrambots-spring-boot-starter/pom.xml | 2 +- telegrambots/pom.xml | 6 +- 6 files changed, 107 insertions(+), 96 deletions(-) diff --git a/Bots.ipr b/Bots.ipr index a1fedc3a..33b4badd 100644 --- a/Bots.ipr +++ b/Bots.ipr @@ -906,70 +906,70 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1280,125 +1280,136 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + @@ -1412,15 +1423,15 @@ - + - + - + - + @@ -1863,26 +1874,26 @@ - + - + - + - + - + - + - + - + @@ -1907,15 +1918,15 @@ - + - + - + - + diff --git a/pom.xml b/pom.xml index 2f7a5268..fb6636f4 100644 --- a/pom.xml +++ b/pom.xml @@ -68,11 +68,11 @@ ${java.version} 5.5.2 - 3.0.0 - 3.0.0 - 2.9.9 - 2.9.9.2 - 1.7.28 + 3.1.0 + 3.1.0 + 2.10.1 + 2.10.1 + 1.7.29 1.3.5 diff --git a/telegrambots-chat-session-bot/pom.xml b/telegrambots-chat-session-bot/pom.xml index 856fdaa3..fef0e5d2 100644 --- a/telegrambots-chat-session-bot/pom.xml +++ b/telegrambots-chat-session-bot/pom.xml @@ -76,7 +76,7 @@ UTF-8 UTF-8 - 1.4.1 + 1.4.2 diff --git a/telegrambots-meta/pom.xml b/telegrambots-meta/pom.xml index aed7c4ed..a6167035 100644 --- a/telegrambots-meta/pom.xml +++ b/telegrambots-meta/pom.xml @@ -70,8 +70,8 @@ UTF-8 UTF-8 4.2.2 - 2.9.9.2 - 2.9.9 + 2.10.1 + 2.10.1 20180813 28.1-jre diff --git a/telegrambots-spring-boot-starter/pom.xml b/telegrambots-spring-boot-starter/pom.xml index 1eeee6f8..dbd57c85 100644 --- a/telegrambots-spring-boot-starter/pom.xml +++ b/telegrambots-spring-boot-starter/pom.xml @@ -103,7 +103,7 @@ org.assertj assertj-core test - 3.13.2 + 3.14.0 diff --git a/telegrambots/pom.xml b/telegrambots/pom.xml index c25a7b4b..9ea6cb51 100644 --- a/telegrambots/pom.xml +++ b/telegrambots/pom.xml @@ -74,8 +74,8 @@ 1.19.3 4.5.10 20180813 - 2.9.9.2 - 2.9.9 + 2.10.1 + 2.10.1 2.6 @@ -110,7 +110,7 @@ com.fasterxml.jackson.module jackson-module-jaxb-annotations - 2.9.9 + 2.10.1 com.fasterxml.jackson.core From ff96318fefc3937bac0a5e9e64f81c18e9e60d07 Mon Sep 17 00:00:00 2001 From: Eugene Kortov Date: Wed, 20 Nov 2019 16:45:38 +0400 Subject: [PATCH 22/23] Add DI for TelegramFileDownloader, fix it for proxy usage - add DI for TelegramFileDownloader - replace volatile variables with final --- .../telegrambots/bots/DefaultAbsSender.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java b/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java index 71c6d4ed..f6c522d3 100644 --- a/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java +++ b/telegrambots/src/main/java/org/telegram/telegrambots/bots/DefaultAbsSender.java @@ -65,9 +65,9 @@ public abstract class DefaultAbsSender extends AbsSender { protected final ExecutorService exe; private final ObjectMapper objectMapper = new ObjectMapper(); private final DefaultBotOptions options; - private volatile CloseableHttpClient httpClient; - private volatile RequestConfig requestConfig; - private final TelegramFileDownloader telegramFileDownloader = new TelegramFileDownloader(this::getBotToken); + private final CloseableHttpClient httpClient; + private final RequestConfig requestConfig; + private final TelegramFileDownloader telegramFileDownloader; protected DefaultAbsSender(DefaultBotOptions options) { super(); @@ -76,11 +76,14 @@ public abstract class DefaultAbsSender extends AbsSender { this.options = options; httpClient = TelegramHttpClientBuilder.build(options); + this.telegramFileDownloader = new TelegramFileDownloader(httpClient, this::getBotToken); configureHttpContext(); - requestConfig = options.getRequestConfig(); - if (requestConfig == null) { - requestConfig = RequestConfig.copy(RequestConfig.custom().build()) + final RequestConfig configFromOptions = options.getRequestConfig(); + if (configFromOptions != null) { + this.requestConfig = configFromOptions; + } else { + this.requestConfig = RequestConfig.copy(RequestConfig.custom().build()) .setSocketTimeout(SOCKET_TIMEOUT) .setConnectTimeout(SOCKET_TIMEOUT) .setConnectionRequestTimeout(SOCKET_TIMEOUT).build(); From f58def63265d286d5881d00be30eacbfb5eaeb2c Mon Sep 17 00:00:00 2001 From: Ruben Bermudez Date: Sat, 23 Nov 2019 16:45:03 +0000 Subject: [PATCH 23/23] Update changelog --- TelegramBots.wiki/Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TelegramBots.wiki/Changelog.md b/TelegramBots.wiki/Changelog.md index aa6bbe75..e568d16a 100644 --- a/TelegramBots.wiki/Changelog.md +++ b/TelegramBots.wiki/Changelog.md @@ -4,7 +4,7 @@ 3. Add Ability toggles and export default abilities to their own class 4. Add state machine capability to AbilityBot via ReplyFlow 5. Support backup and recovery of db vars -6. Fixes: #602, #641, #652 +6. Fixes: #602, #641, #652, #691 ### 4.4.0.1 ### 1. Bug fix when importing the project