Merge pull request #823 from rubenlagus/dev

Dev
This commit is contained in:
Ruben Bermudez 2020-10-29 00:21:38 +00:00 committed by GitHub
commit 48dea8184c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 601 additions and 329 deletions

View File

@ -1,5 +1,5 @@
# Telegram Bot Java Library
[![Telegram](http://trellobot.doomdns.org/telegrambadge.svg)](https://telegram.me/JavaBotsApi)
[![Telegram](/TelegramBots.svg)](https://telegram.me/JavaBotsApi)
[![Build Status](https://travis-ci.org/rubenlagus/TelegramBots.svg?branch=master)](https://travis-ci.org/rubenlagus/TelegramBots)
@ -27,16 +27,16 @@ Just import add the library to your project with one of these options:
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</dependency>
```
```gradle
compile "org.telegram:telegrambots:4.9.1"
compile "org.telegram:telegrambots:4.9.2"
```
2. Using Jitpack from [here](https://jitpack.io/#rubenlagus/TelegramBots/4.9.1)
3. Download the jar(including all dependencies) from [here](https://mvnrepository.com/artifact/org.telegram/telegrambots/4.9.1)
2. Using Jitpack from [here](https://jitpack.io/#rubenlagus/TelegramBots/4.9.2)
3. Download the jar(including all dependencies) from [here](https://mvnrepository.com/artifact/org.telegram/telegrambots/4.9.2)
In order to use Long Polling mode, just create your own bot extending `org.telegram.telegrambots.bots.TelegramLongPollingBot`.

22
TelegramBots.svg Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1" />
<stop offset="1" stop-opacity=".1" />
</linearGradient>
<mask id="a">
<rect width="100" height="20" rx="3" fill="#fff" />
</mask>
<g mask="url(#a)">
<path fill="#555" d="M0 0h34v20H0z" />
<path fill="#54a9eb" d="M34 0h68v20H34z" />
<path fill="url(#b)" d="M0 0h92v20H0z" />
</g>
<g fill="#fff" text-anchor="middle" font-family="HelveticaNeue-Light,Helvetica Neue Light, Helvetica Light,Helvetica,Arial,Verdana,sans-serif" font-size="11" color="#fff" font-weight="bold">
<text x="16" y="15" fill="#010101" fill-opacity=".3">chat</text>
<text x="16" y="14">chat</text>
<text x="67" y="15" fill="#010101" fill-opacity=".3">on telegram</text>
<text x="67" y="14">on telegram</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 998 B

View File

@ -1,3 +1,6 @@
### <a id="4.9.2"></a>4.9.2 ###
1. Bug fixing: #792, #801, #804, #810, #812, #813, #820 and #814
### <a id="4.9.1"></a>4.9.1 ###
1. Bug fixing: #767, #766, #761, #763, #776, #772, #771, #780

View File

@ -11,13 +11,13 @@ First you need ot get the library and add it to your project. There are few poss
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</dependency>
```
* With **Gradle**:
```groovy
compile group: 'org.telegram', name: 'telegrambots', version: '4.9.1'
compile group: 'org.telegram', name: 'telegrambots', version: '4.9.2'
```
2. Don't like **Maven Central Repository**? It can also be taken from [Jitpack](https://jitpack.io/#rubenlagus/TelegramBots).

View File

@ -38,3 +38,17 @@ public class MrBadGuy implements AbilityExtension {
// Override creatorId
}
```
It's also possible to add extensions in the constructor by using the `addExtension()` or `addExtensions()` method:
```java
public class YourAwesomeBot implements AbilityBot {
public YourAwesomeBot() {
super(/* pass required args ... */);
addExtensions(new MrGoodGuy(), new MrBadGuy());
}
// Override creatorId
}
```

View File

@ -65,3 +65,14 @@ Please note that this may cause ability overlap. If multiple abilities can match
if you have two abilities `do` and `do1`, the command `/do1` will trigger the `do1` ability.
## Statistics
AbilityBot can accrue basic statistics about the usage of your abilities and replies. Simply `enableStats()` on an Ability builder or `enableStats(<name>)` on replies to activate this feature. Once activated, you may call `/stats` and the bot will print a basic list of statistics. At the moment, AbilityBot only tracks hits. In the future, this will be enhanced to track more stats.
## Execute code on bot registration
If you want to execute custom logic to initialize your bot, but you can't do it in the constructor,
you can override the `onRegister()` method:
```
@Override
public void onRegister() {
super.onRegister();
// Execute custom initialize logic here
}
```

View File

@ -104,6 +104,8 @@ public class ExampleBotTest {
public void setUp() {
// Create your bot
bot = new ExampleBot();
// Call onRegister() to initialize abilities etc.
bot.onRegister();
// Create a new sender as a mock
silent = mock(SilentSender.class);
// Set your bot silent sender to the mocked sender
@ -156,6 +158,7 @@ public class ExampleBotTest {
// Offline instance will get deleted at JVM shutdown
db = MapDBContext.offlineInstance("test");
bot = new ExampleBot(db);
bot.onRegister();
...
}
@ -180,6 +183,7 @@ public class ExampleBotTest {
@Before
public void setUp() {
bot = new ExampleBot(db);
bot.onRegister();
sender = mock(MessageSender.class);
SilentSender silent = new SilentSender(sender);
// Create setter in your bot

View File

@ -9,12 +9,12 @@ As with any Java project, you will need to set your dependencies.
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-abilities</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</dependency>
```
* **Gradle**
```groovy
implementation group: 'org.telegram', name: 'telegrambots-abilities', version: '4.9.1'
implementation group: 'org.telegram', name: 'telegrambots-abilities', version: '4.9.2'
```
* [JitPack](https://jitpack.io/#rubenlagus/TelegramBots)

View File

@ -7,7 +7,7 @@
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<packaging>pom</packaging>
<version>4.9.1</version>
<version>4.9.2</version>
<modules>
<module>telegrambots</module>
@ -190,6 +190,7 @@
</executions>
<configuration>
<doclint>none</doclint>
<javadocExecutable>${java.home}/bin/javadoc</javadocExecutable>
</configuration>
</plugin>
<plugin>

View File

@ -18,19 +18,19 @@ Usage
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-abilities</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</dependency>
```
**Gradle**
```gradle
compile "org.telegram:telegrambots-abilities:4.9.1"
compile "org.telegram:telegrambots-abilities:4.9.2"
```
**JitPack** - [JitPack](https://jitpack.io/#rubenlagus/TelegramBots/v4.9.1)
**JitPack** - [JitPack](https://jitpack.io/#rubenlagus/TelegramBots/v4.9.2)
**Plain imports** - [Here](https://github.com/rubenlagus/TelegramBots/releases/tag/v4.9.1)
**Plain imports** - [Here](https://github.com/rubenlagus/TelegramBots/releases/tag/v4.9.2)
Motivation
----------

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</parent>
<artifactId>telegrambots-abilities</artifactId>
@ -84,7 +84,7 @@
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>

View File

@ -106,6 +106,7 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
private final String botUsername;
// Ability registry
private final List<AbilityExtension> extensions = new ArrayList<>();
private Map<String, Ability> abilities;
private Map<String, Stats> stats;
@ -123,11 +124,34 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
this.toggle = toggle;
this.sender = new DefaultSender(this);
silent = new SilentSender(sender);
}
public void onRegister() {
registerAbilities();
initStats();
}
/**
* @return the database of this bot
*/
public DBContext db() {
return db;
}
/**
* @return the message sender for this bot
*/
public MessageSender sender() {
return sender;
}
/**
* @return the silent sender for this bot
*/
public SilentSender silent() {
return silent;
}
/**
* @return the map of <ID,User>
*/
@ -272,6 +296,18 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
return false;
}
protected void addExtension(AbilityExtension extension) {
this.extensions.add(extension);
}
protected void addExtensions(AbilityExtension... extensions) {
this.extensions.addAll(Arrays.asList(extensions));
}
protected void addExtensions(Collection<AbilityExtension> extensions) {
this.extensions.addAll(extensions);
}
/**
* Registers the declared abilities using method reflection. Also, replies are accumulated using the built abilities and standalone methods that return a Reply.
* <p>
@ -280,10 +316,10 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
private void registerAbilities() {
try {
// Collect all classes that implement AbilityExtension declared in the bot
List<AbilityExtension> extensions = stream(getClass().getMethods())
extensions.addAll(stream(getClass().getMethods())
.filter(checkReturnType(AbilityExtension.class))
.map(returnExtension(this))
.collect(Collectors.toList());
.collect(Collectors.toList()));
// Add the bot itself as it is an AbilityExtension
extensions.add(this);
@ -437,7 +473,7 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
Update update = trio.a();
User user = AbilityUtils.getUser(update);
return Pair.of(newContext(update, user, getChatId(update), trio.c()), trio.b());
return Pair.of(newContext(update, user, getChatId(update), this, trio.c()), trio.b());
}
boolean checkBlacklist(Update update) {

View File

@ -17,7 +17,7 @@ import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
import static java.util.Objects.hash;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.*;
import static org.telegram.abilitybots.api.util.AbilityUtils.isValidCommandName;
/**
* An ability is a fully-fledged bot action that contains all the necessary information to process:
@ -50,9 +50,8 @@ public final class Ability {
@SafeVarargs
private Ability(String name, String info, Locality locality, Privacy privacy, int argNum, boolean statsEnabled, Consumer<MessageContext> action, Consumer<MessageContext> postAction, List<Reply> replies, Predicate<Update>... flags) {
checkArgument(!isEmpty(name), "Method name cannot be empty");
checkArgument(!containsWhitespace(name), "Method name cannot contain spaces");
checkArgument(isAlphanumeric(name), "Method name can only be alpha-numeric", name);
checkArgument(isValidCommandName(name), "Method name can only contain alpha-numeric characters and underscores," +
" cannot be longer than 31 characters, empty or null", name);
this.name = name;
this.info = info;

View File

@ -2,6 +2,7 @@ package org.telegram.abilitybots.api.objects;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import org.telegram.abilitybots.api.bot.BaseAbilityBot;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User;
@ -19,16 +20,18 @@ public class MessageContext {
private final Long chatId;
private final String[] arguments;
private final Update update;
private final BaseAbilityBot bot;
private MessageContext(Update update, User user, Long chatId, String[] arguments) {
private MessageContext(Update update, User user, Long chatId, BaseAbilityBot bot, String[] arguments) {
this.user = user;
this.chatId = chatId;
this.update = update;
this.bot = bot;
this.arguments = arguments;
}
public static MessageContext newContext(Update update, User user, Long chatId, String... arguments) {
return new MessageContext(update, user, chatId, arguments);
public static MessageContext newContext(Update update, User user, Long chatId, BaseAbilityBot bot, String... arguments) {
return new MessageContext(update, user, chatId, bot, arguments);
}
/**
@ -45,6 +48,13 @@ public class MessageContext {
return chatId;
}
/**
* @return the bot in which this message is executed
*/
public BaseAbilityBot bot() {
return bot;
}
/**
* If there's no message in the update, then this will an empty array.
*

View File

@ -269,4 +269,29 @@ public final class AbilityUtils {
public static String escape(String username) {
return username.replace("_", "\\_");
}
/**
* Checks if the passed string is a valid bot command according to the requirements of Telegram Bot API:
* "A command must always start with the '/' symbol and may not be longer than 32 characters.
* Commands can use latin letters, numbers and underscores."
* (https://core.telegram.org/bots#commands)
*
* @param command String representation of a command to be checked for validity
* @return whether the command is valid
*/
public static boolean isValidCommand(String command){
if (command == null || command.length() > 32) return false;
return command.matches("/[A-Za-z_0-9]+");
}
/**
* Checks if the passed String is a valid command name. Command name is text of a command without leading '/'
*
* @param commandName the command name to be checked for validity
* @return whether the command name is valid
*/
public static boolean isValidCommandName(String commandName){
if (commandName == null || commandName.length() > 31) return false;
return commandName.matches("[A-Za-z_0-9]+");
}
}

View File

@ -33,6 +33,7 @@ class AbilityBotI18nTest {
void setUp() {
db = offlineInstance("db");
bot = new NoPublicCommandsBot(EMPTY, EMPTY, db);
bot.onRegister();
defaultAbs = new DefaultAbilities(bot);
sender = mock(MessageSender.class);

View File

@ -71,6 +71,7 @@ public class AbilityBotTest {
void setUp() {
db = offlineInstance("db");
bot = new DefaultBot(EMPTY, EMPTY, db);
bot.onRegister();
defaultAbs = new DefaultAbilities(bot);
sender = mock(MessageSender.class);
@ -503,7 +504,7 @@ public class AbilityBotTest {
mockUser(update, message, USER);
Pair<MessageContext, Ability> actualPair = bot.getContext(trio);
Pair<MessageContext, Ability> expectedPair = Pair.of(newContext(update, USER, GROUP_ID, TEXT), ability);
Pair<MessageContext, Ability> expectedPair = Pair.of(newContext(update, USER, GROUP_ID, bot, TEXT), ability);
assertEquals(expectedPair, actualPair, "Unexpected result when fetching for context");
}
@ -619,7 +620,7 @@ public class AbilityBotTest {
when(update.hasMessage()).thenReturn(true);
when(update.getMessage()).thenReturn(message);
when(message.hasText()).thenReturn(true);
MessageContext creatorCtx = newContext(update, CREATOR, GROUP_ID);
MessageContext creatorCtx = newContext(update, CREATOR, GROUP_ID, bot);
defaultAbs.commands().action().accept(creatorCtx);
@ -636,7 +637,7 @@ public class AbilityBotTest {
when(update.getMessage()).thenReturn(message);
when(message.hasText()).thenReturn(true);
MessageContext userCtx = newContext(update, USER, GROUP_ID);
MessageContext userCtx = newContext(update, USER, GROUP_ID, bot);
defaultAbs.commands().action().accept(userCtx);

View File

@ -33,6 +33,7 @@ public class ContinuousTextTest {
void setUp() {
db = offlineInstance("db");
bot = new ContinuousTextBot(EMPTY, EMPTY, db);
bot.onRegister();
silent = mock(SilentSender.class);
bot.silent = silent;
}

View File

@ -19,6 +19,7 @@ class ExtensionTest {
@BeforeEach
void setUp() {
bot = new ExtensionUsingBot();
bot.onRegister();
}
@AfterEach
@ -32,6 +33,7 @@ class ExtensionTest {
assertTrue(hasAbilityNamed("direct"), "Failed to find Ability in directly declared in root extension/bot");
assertTrue(hasAbilityNamed("returningSuperClass0abc"), "Failed to find Ability in directly declared in extension returned by method returning the AbilityExtension class");
assertTrue(hasAbilityNamed("returningSubClass0abc"), "Failed to find Ability in directly declared in extension returned by method returning the AbilityExtension subclass");
assertTrue(hasAbilityNamed("addedInConstructor0abc"), "Failed to find Ability in directly declared in extension added in the constructor");
}
private boolean hasAbilityNamed(String name) {
@ -41,6 +43,7 @@ class ExtensionTest {
public static class ExtensionUsingBot extends AbilityBot {
ExtensionUsingBot() {
super("", "", offlineInstance("testing"));
addExtension(new AbilityBotExtension("addedInConstructor"));
}
@Override

View File

@ -39,6 +39,7 @@ public class ReplyFlowTest {
void setUp() {
db = offlineInstance("db");
bot = new ReplyFlowBot(EMPTY, EMPTY, db);
bot.onRegister();
sender = mock(MessageSender.class);
silent = mock(SilentSender.class);

View File

@ -43,6 +43,7 @@ public final class TestUtils {
static MessageContext mockContext(User user, long groupId, String... args) {
Update update = mock(Update.class);
Message message = mock(Message.class);
BaseAbilityBot bot = mock(BaseAbilityBot.class);
when(update.hasMessage()).thenReturn(true);
when(update.getMessage()).thenReturn(message);
@ -50,7 +51,7 @@ public final class TestUtils {
when(message.getFrom()).thenReturn(user);
when(message.hasText()).thenReturn(true);
return newContext(update, user, groupId, args);
return newContext(update, user, groupId, bot, args);
}
@NotNull

View File

@ -26,6 +26,7 @@ public class BareboneToggleTest {
db = offlineInstance("db");
toggle = new BareboneToggle();
bareboneBot = new DefaultBot(EMPTY, EMPTY, db, toggle);
bareboneBot.onRegister();
defaultAbs = new DefaultAbilities(bareboneBot);
}

View File

@ -1,7 +1,6 @@
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;
@ -18,12 +17,10 @@ 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
@ -36,6 +33,7 @@ class CustomToggleTest {
public void canTurnOffAbilities() {
toggle = new CustomToggle().turnOff(DefaultAbilities.CLAIM);
customBot = new DefaultBot(EMPTY, EMPTY, db, toggle);
customBot.onRegister();
assertFalse(customBot.abilities().containsKey(DefaultAbilities.CLAIM));
}
@ -44,6 +42,7 @@ class CustomToggleTest {
String targetName = DefaultAbilities.CLAIM + "1toggle";
toggle = new CustomToggle().toggle(DefaultAbilities.CLAIM, targetName);
customBot = new DefaultBot(EMPTY, EMPTY, db, toggle);
customBot.onRegister();
assertTrue(customBot.abilities().containsKey(targetName));
}

View File

@ -3,7 +3,6 @@ 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;
@ -21,12 +20,10 @@ 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
@ -41,6 +38,7 @@ class DefaultToggleTest {
.name("randomsomethingrandom").build();
toggle = new DefaultToggle();
defaultBot = new DefaultBot(EMPTY, EMPTY, db, toggle);
defaultBot.onRegister();
assertFalse(toggle.isOff(random));
}
@ -51,6 +49,7 @@ class DefaultToggleTest {
.name("randomsomethingrandom").build();
toggle = new DefaultToggle();
defaultBot = new DefaultBot(EMPTY, EMPTY, db, toggle);
defaultBot.onRegister();
assertSame(random, toggle.processAbility(random), "Toggle returned a different ability");
}
@ -59,6 +58,7 @@ class DefaultToggleTest {
public void allAbilitiesAreRegistered() {
toggle = new DefaultToggle();
defaultBot = new DefaultBot(EMPTY, EMPTY, db, toggle);
defaultBot.onRegister();
Set<String> defaultNames = newHashSet(
CLAIM, BAN, UNBAN,
PROMOTE, DEMOTE, RECOVER,

View File

@ -15,7 +15,7 @@ Usage
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-chat-session-bot</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</dependency>
```

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</parent>
<artifactId>telegrambots-chat-session-bot</artifactId>
@ -84,7 +84,7 @@
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->

View File

@ -16,12 +16,12 @@ Just import add the library to your project with one of these options:
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambotsextensions</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</dependency>
```
2. Using Gradle:
```gradle
compile "org.telegram:telegrambotsextensions:4.9.1"
compile "org.telegram:telegrambotsextensions:4.9.2"
```

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</parent>
<artifactId>telegrambotsextensions</artifactId>
@ -75,7 +75,7 @@
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</dependency>
</dependencies>

View File

@ -53,7 +53,7 @@ public abstract class TelegramLongPollingCommandBot extends TelegramLongPollingB
*/
public TelegramLongPollingCommandBot(DefaultBotOptions options, boolean allowCommandsWithUsername) {
super(options);
this.commandRegistry = new CommandRegistry(allowCommandsWithUsername, this.getBotUsername());
this.commandRegistry = new CommandRegistry(allowCommandsWithUsername, this::getBotUsername);
}
@Override

View File

@ -7,7 +7,9 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.regex.Pattern;
/**
@ -19,17 +21,32 @@ public final class CommandRegistry implements ICommandRegistry {
private final Map<String, IBotCommand> commandRegistryMap = new HashMap<>();
private final boolean allowCommandsWithUsername;
private final String botUsername;
private final Supplier<String> botUsernameSupplier;
private BiConsumer<AbsSender, Message> defaultConsumer;
/**
* Creates a Command registry
*
* @param allowCommandsWithUsername True to allow commands with username, false otherwise
* @param botUsername Bot username
* @throws java.lang.NullPointerException if {@code botUsername} is {@code null}
* @deprecated Use {@link #CommandRegistry(boolean, java.util.function.Supplier)} instead
*/
@Deprecated
public CommandRegistry(boolean allowCommandsWithUsername, String botUsername) {
Objects.requireNonNull(botUsername, "Bot username must not be null");
this.allowCommandsWithUsername = allowCommandsWithUsername;
this.botUsername = botUsername;
this.botUsernameSupplier = () -> botUsername;
}
/**
* Creates a Command registry
* @param allowCommandsWithUsername True to allow commands with username, false otherwise
* @param botUsernameSupplier Bot username supplier
*/
public CommandRegistry(boolean allowCommandsWithUsername, Supplier<String> botUsernameSupplier) {
this.allowCommandsWithUsername = allowCommandsWithUsername;
this.botUsernameSupplier = botUsernameSupplier;
}
@Override
@ -120,9 +137,12 @@ public final class CommandRegistry implements ICommandRegistry {
* the command
* @param command Command to simplify
* @return Simplified command
* @throws java.lang.NullPointerException if {@code allowCommandsWithUsername} is {@code true}
* and {@code botUsernameSupplier} returns {@code null}
*/
private String removeUsernameFromCommandIfNeeded(String command) {
if (allowCommandsWithUsername) {
String botUsername = Objects.requireNonNull(botUsernameSupplier.get(), "Bot username must not be null");
return command.replaceAll("(?i)@" + Pattern.quote(botUsername), "").trim();
}
return command;

View File

@ -0,0 +1,51 @@
package org.telegram.telegrambots.extensions.bots.commandbot.commands;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.bots.AbsSender;
class CommandRegistryTest {
@Test
void should_create_registry() {
CommandRegistry registry = new CommandRegistry(true, "BotUsername");
Assertions.assertNotNull(registry, "CommandRegistry is not created");
NullPointerException exception = Assertions.assertThrows(NullPointerException.class,
() -> new CommandRegistry(true, (String) null));
Assertions.assertEquals("Bot username must not be null", exception.getMessage(), "Invalid exception message");
}
@Test
void should_executes_commandWithBotUsername() {
CommandRegistry registry = new CommandRegistry(true, () -> "BotUsername");
IBotCommand command = Mockito.mock(IBotCommand.class);
Mockito.when(command.getCommandIdentifier()).thenReturn("command");
registry.register(command);
AbsSender absSender = Mockito.mock(AbsSender.class);
Message message = Mockito.mock(Message.class);
Mockito.when(message.hasText()).thenReturn(true);
Mockito.when(message.getText()).thenReturn("/command@BotUsername I'll be test!");
Assertions.assertTrue(registry.executeCommand(absSender, message), "Command must be executed");
Mockito.verify(message).hasText();
Mockito.verify(message).getText();
Mockito.verify(command).processMessage(
Mockito.same(absSender), Mockito.same(message), Mockito.eq(new String[]{"I'll", "be", "test!"}));
}
@Test
void should_throws_NPE_on_executes_commandWithNullableBotUsername() {
CommandRegistry registry = new CommandRegistry(true, () -> null);
IBotCommand command = Mockito.mock(IBotCommand.class);
Mockito.when(command.getCommandIdentifier()).thenReturn("command");
registry.register(command);
AbsSender absSender = Mockito.mock(AbsSender.class);
Message message = Mockito.mock(Message.class);
Mockito.when(message.hasText()).thenReturn(true);
Mockito.when(message.getText()).thenReturn("/command@BotUsername ignore");
NullPointerException exception = Assertions.assertThrows(
NullPointerException.class, () -> registry.executeCommand(absSender, message), "Bot username is null");
Assertions.assertEquals("Bot username must not be null", exception.getMessage(), "Invalid exception message");
}
}

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</parent>
<artifactId>telegrambots-meta</artifactId>

View File

@ -117,6 +117,7 @@ public class TelegramBotsApi {
* @param bot the bot to register
*/
public BotSession registerBot(LongPollingBot bot) throws TelegramApiRequestException {
bot.onRegister();
bot.clearWebhook();
BotSession session = ApiContext.getInstance(BotSession.class);
session.setToken(bot.getBotToken());
@ -132,6 +133,7 @@ public class TelegramBotsApi {
*/
public void registerBot(WebhookBot bot) throws TelegramApiRequestException {
if (useWebhook) {
bot.onRegister();
webhook.registerWebhook(bot);
bot.setWebhook(externalUrl + bot.getBotPath(), pathToCertificate);
}

View File

@ -11,7 +11,7 @@ import java.util.List;
* @brief Callback to handle updates.
* @date 20 of June of 2015
*/
public interface LongPollingBot {
public interface LongPollingBot extends TelegramBot {
/**
* This method is called when receiving updates via GetUpdates method
* @param update Update received
@ -27,16 +27,6 @@ public interface LongPollingBot {
updates.forEach(this::onUpdateReceived);
}
/**
* Return bot username of this bot
*/
String getBotUsername();
/**
* Return bot token to access Telegram API
*/
String getBotToken();
/**
* Gets options for current bot
* @return BotOptions object with options information

View File

@ -0,0 +1,24 @@
package org.telegram.telegrambots.meta.generics;
/**
* Main interface for telegram bots.
*/
public interface TelegramBot {
/**
* Return username of this bot
*/
String getBotUsername();
/**
* Return bot token to access Telegram API
*/
String getBotToken();
/**
* Is called when bot gets registered
*/
default void onRegister() {
}
}

View File

@ -10,25 +10,13 @@ import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException;
* @brief Callback to handle updates.
* @date 20 of June of 2015
*/
public interface WebhookBot {
public interface WebhookBot extends TelegramBot {
/**
* This method is called when receiving updates via webhook
* @param update Update received
*/
BotApiMethod onWebhookUpdateReceived(Update update);
/**
* Gets bot username of this bot
* @return Bot username
*/
String getBotUsername();
/**
* Gets bot token to access Telegram API
* @return Bot token
*/
String getBotToken();
/**
* Execute setWebhook method to set up the url of the webhook
* @param url Url for the webhook

View File

@ -18,14 +18,14 @@ Usage
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-spring-boot-starter</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</dependency>
```
**Gradle**
```gradle
compile "org.telegram:telegrambots-spring-boot-starter:4.9.1"
compile "org.telegram:telegrambots-spring-boot-starter:4.9.2"
```
Motivation

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</parent>
<artifactId>telegrambots-spring-boot-starter</artifactId>
@ -70,8 +70,21 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<telegrambots.version>4.9.2</telegrambots.version>
<spring-boot.version>2.3.3.RELEASE</spring-boot.version>
<assertj-core.version>3.14.0</assertj-core.version>
<spring-boot.version>2.2.2.RELEASE</spring-boot.version>
<maven-gpg-plugin.version>1.6</maven-gpg-plugin.version>
<nexus-staging-maven-plugin.version>1.6.8</nexus-staging-maven-plugin.version>
<maven-clean-plugin.version>3.1.0</maven-clean-plugin.version>
<maven-assembly-plugin.version>3.1.1</maven-assembly-plugin.version>
<maven-source-plugin.version>3.1.0</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.1.0</maven-javadoc-plugin.version>
<jacoco-maven-plugin.version>0.8.4</jacoco-maven-plugin.version>
<maven-enforcer-plugin.version>3.0.0-M2</maven-enforcer-plugin.version>
<maven-dependency-plugin.version>3.1.1</maven-dependency-plugin.version>
<maven-surefire-plugin.version>3.0.0-M3</maven-surefire-plugin.version>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
</properties>
<dependencies>
@ -79,7 +92,7 @@
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>4.9.1</version>
<version>${telegrambots.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -104,7 +117,7 @@
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
<version>3.14.0</version>
<version>${assertj-core.version}</version>
</dependency>
</dependencies>
@ -118,7 +131,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<version>${maven-gpg-plugin.version}</version>
<executions>
<execution>
<id>sign-artifacts</id>
@ -132,7 +145,7 @@
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.8</version>
<version>${nexus-staging-maven-plugin.version}</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
@ -142,7 +155,7 @@
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<version>${maven-clean-plugin.version}</version>
<executions>
<execution>
<id>clean-project</id>
@ -155,7 +168,7 @@
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.1</version>
<version>${maven-assembly-plugin.version}</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
@ -174,7 +187,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.1.0</version>
<version>${maven-source-plugin.version}</version>
<executions>
<execution>
<goals>
@ -186,7 +199,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.1.0</version>
<version>${maven-javadoc-plugin.version}</version>
<executions>
<execution>
<goals>
@ -201,7 +214,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.4</version>
<version>${jacoco-maven-plugin.version}</version>
<executions>
<execution>
<goals>
@ -220,7 +233,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0-M2</version>
<version>${maven-enforcer-plugin.version}</version>
<executions>
<execution>
<id>enforce-versions</id>
@ -238,7 +251,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<version>${maven-dependency-plugin.version}</version>
<executions>
<execution>
<id>copy</id>
@ -249,7 +262,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<version>${maven-surefire-plugin.version}</version>
</plugin>
</plugins>
<pluginManagement>
@ -257,7 +270,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>

View File

@ -10,15 +10,14 @@ import org.telegram.telegrambots.meta.generics.LongPollingBot;
import org.telegram.telegrambots.meta.generics.WebhookBot;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.*;
class TestTelegramBotStarterConfiguration {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MockTelegramBotsApi.class, TelegramBotStarterConfiguration.class));
.withAllowBeanDefinitionOverriding(true)
.withConfiguration(AutoConfigurations.of(MockTelegramBotsApi.class,
TelegramBotStarterConfiguration.class));
@Test
void createMockTelegramBotsApiWithDefaultSettings() {
@ -40,7 +39,8 @@ class TestTelegramBotStarterConfiguration {
TelegramBotsApi telegramBotsApi = context.getBean(TelegramBotsApi.class);
verify(telegramBotsApi, times(1)).registerBot( context.getBean(LongPollingBot.class) );
verify(telegramBotsApi,
times(1)).registerBot(context.getBean(LongPollingBot.class));
verifyNoMoreInteractions(telegramBotsApi);
});
}
@ -54,22 +54,26 @@ class TestTelegramBotStarterConfiguration {
TelegramBotsApi telegramBotsApi = context.getBean(TelegramBotsApi.class);
verify(telegramBotsApi, times(1)).registerBot( context.getBean(WebhookBot.class) );
verify(telegramBotsApi,
times(1)).registerBot(context.getBean(WebhookBot.class));
verifyNoMoreInteractions(telegramBotsApi);
});
}
@Test
void createLongPoolingBotAndWebhookBot() {
this.contextRunner.withUserConfiguration(LongPollingBotConfig.class, WebhookBotConfig.class)
this.contextRunner.withUserConfiguration(LongPollingBotConfig.class,
WebhookBotConfig.class)
.run((context) -> {
assertThat(context).hasSingleBean(LongPollingBot.class);
assertThat(context).hasSingleBean(WebhookBot.class);
TelegramBotsApi telegramBotsApi = context.getBean(TelegramBotsApi.class);
verify(telegramBotsApi, times(1)).registerBot( context.getBean(LongPollingBot.class) );
verify(telegramBotsApi, times(1)).registerBot( context.getBean(WebhookBot.class) );
verify(telegramBotsApi,
times(1)).registerBot(context.getBean(LongPollingBot.class));
verify(telegramBotsApi,
times(1)).registerBot(context.getBean(WebhookBot.class));
//verifyNoMoreInteractions(telegramBotsApi);
});
}

View File

@ -14,24 +14,19 @@ import org.telegram.telegrambots.meta.generics.LongPollingBot;
import org.telegram.telegrambots.updatesreceivers.DefaultBotSession;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.*;
class TestTelegramBotStarterRegistrationHooks {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MockTelegramBotsApi.class, TelegramBotStarterConfiguration.class));
private static final DefaultBotSession someBotSession = new DefaultBotSession();
private static final TelegramBotsApi mockTelegramBotsApi = mock(TelegramBotsApi.class);
// Terrible workaround for mockito loosing annotations on methods
private static boolean hookCalled = false;
private static boolean hookCalledWithSession = false;
private static final DefaultBotSession someBotSession = new DefaultBotSession();
private static final TelegramBotsApi mockTelegramBotsApi = mock(TelegramBotsApi.class);
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withAllowBeanDefinitionOverriding(true)
.withConfiguration(AutoConfigurations.of(MockTelegramBotsApi.class,
TelegramBotStarterConfiguration.class));
@Test
void longPollingBotWithAnnotatedMethodshouldBeCalled() throws TelegramApiRequestException {
@ -47,7 +42,8 @@ class TestTelegramBotStarterRegistrationHooks {
assertThat(hookCalled).isTrue();
assertThat(hookCalledWithSession).isTrue();
verify(telegramBotsApi, times(1)).registerBot(bot);
verify(telegramBotsApi,
times(1)).registerBot(bot);
verifyNoMoreInteractions(telegramBotsApi);
});
}
@ -65,19 +61,26 @@ class TestTelegramBotStarterRegistrationHooks {
@Configuration
static class LongPollingBotConfig {
@Bean
public LongPollingBot longPollingBot() { return new AnnotatedLongPollingBot(); }
public LongPollingBot longPollingBot() {
return new AnnotatedLongPollingBot();
}
}
static class AnnotatedLongPollingBot extends TelegramLongPollingBot {
@Override
public void onUpdateReceived(final Update update) {}
public void onUpdateReceived(final Update update) {
}
@Override
public String getBotUsername() { return null; }
public String getBotUsername() {
return null;
}
@Override
public String getBotToken() { return null; }
public String getBotToken() {
return null;
}
@AfterBotRegistration
public void afterBotHook() {

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</parent>
<artifactId>telegrambots</artifactId>
@ -95,7 +95,7 @@
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-meta</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>

View File

@ -26,6 +26,8 @@ public class DefaultBotOptions implements BotOptions {
private ProxyType proxyType;
private String proxyHost;
private int proxyPort;
private int getUpdatesTimeout;
private int getUpdatesLimit;
public enum ProxyType {
NO_PROXY,
@ -39,6 +41,8 @@ public class DefaultBotOptions implements BotOptions {
baseUrl = ApiConstants.BASE_URL;
httpContext = HttpClientContext.create();
proxyType = ProxyType.NO_PROXY;
getUpdatesLimit = ApiConstants.GETUPDATES_TIMEOUT;
getUpdatesLimit = 100;
}
@Override
@ -129,4 +133,20 @@ public class DefaultBotOptions implements BotOptions {
public void setProxyPort(int proxyPort) {
this.proxyPort = proxyPort;
}
public int getGetUpdatesTimeout() {
return getUpdatesTimeout;
}
public void setGetUpdatesTimeout(int getUpdatesTimeout) {
this.getUpdatesTimeout = getUpdatesTimeout;
}
public int getGetUpdatesLimit() {
return getUpdatesLimit;
}
public void setGetUpdatesLimit(int getUpdatesLimit) {
this.getUpdatesLimit = getUpdatesLimit;
}
}

View File

@ -236,8 +236,8 @@ public class DefaultBotSession implements BotSession {
private List<Update> getUpdatesFromServer() throws IOException {
GetUpdates request = new GetUpdates()
.setLimit(100)
.setTimeout(ApiConstants.GETUPDATES_TIMEOUT)
.setLimit(options.getGetUpdatesLimit())
.setTimeout(options.getGetUpdatesTimeout())
.setOffset(lastReceivedUpdate + 1);
if (options.getAllowedUpdates() != null) {

View File

@ -0,0 +1,23 @@
package org.telegram.telegrambots.updatesreceivers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
/**
* Prints exceptions in webhook bots to stderr
*
* @author Mouamle
* @version 1.0
*/
public class DefaultExceptionMapper implements ExceptionMapper<Throwable> {
private static final Logger log = LoggerFactory.getLogger(DefaultExceptionMapper.class);
@Override
public Response toResponse(Throwable exception) {
log.error("Exception caught: ", exception);
return Response.serverError().build();
}
}

View File

@ -52,6 +52,7 @@ public class DefaultWebhook implements Webhook {
ResourceConfig rc = new ResourceConfig();
rc.register(restApi);
rc.register(JacksonFeature.class);
rc.register(DefaultExceptionMapper.class);
final HttpServer grizzlyServer;
if (keystoreServerFile != null && keystoreServerPwd != null) {