Merge pull request #780 from addo37/improve-token-fetch

Add customization options for command processing
This commit is contained in:
Ruben Bermudez 2020-07-31 01:12:55 +01:00 committed by GitHub
commit 280b9f2686
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 163 additions and 9 deletions

View File

@ -33,5 +33,35 @@ As an example, if you want to restrict the updates to photos only, then you may
} }
``` ```
## Custom Command Processing
### Command Prefix
Customizing the command prefix is as simple as overriding the `getCommandPrefix` method as shown below.
```java
@Override
protected String getCommandPrefix() {
return "!";
}
```
### Command Regex Split
The method that the bot uses to capture command tokens is through the regex splitters. By default, it's set to `" "`. However, this can be customized. For example,
if you'd like to split on digits and whitespaces, then you may do the following:
```java
@Override
protected String getCommandRegexSplit() {
return "\\s\\d";
}
```
### Commands with Continuous Text
Feeling ambitious? You may allow your bot to process tokens that are technically attached to your command. Imagine you have a command
`/do` and you'd like users to send commands as `/do1` and still trigger the `do` ability. In order to do that, override the `allowContinuousText` function.
```java
@Override
protected boolean allowContinuousText() {
return true;
}
```
Please note that this may cause ability overlap. If multiple abilities can match the same command, the longest match will be taken. For example,
if you have two abilities `do` and `do1`, the command `/do1` will trigger the `do1` ability.
## Statistics ## 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. 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.

View File

@ -260,6 +260,18 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
return true; return true;
} }
protected String getCommandPrefix() {
return "/";
}
protected String getCommandRegexSplit() {
return " ";
}
protected boolean allowContinuousText() {
return false;
}
/** /**
* Registers the declared abilities using method reflection. Also, replies are accumulated using the built abilities and standalone methods that return a Reply. * Registers the declared abilities using method reflection. Also, replies are accumulated using the built abilities and standalone methods that return a Reply.
* <p> * <p>
@ -501,17 +513,27 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
if (!update.hasMessage() || !msg.hasText()) if (!update.hasMessage() || !msg.hasText())
return Trio.of(update, abilities.get(DEFAULT), new String[]{}); return Trio.of(update, abilities.get(DEFAULT), new String[]{});
String[] tokens = msg.getText().split(" "); Ability ability;
String[] tokens;
if (tokens[0].startsWith("/")) { if (allowContinuousText()) {
String abilityToken = stripBotUsername(tokens[0].substring(1)).toLowerCase(); String abName = abilities.keySet().stream()
Ability ability = abilities.get(abilityToken); .filter(name -> msg.getText().startsWith(format("%s%s", getCommandPrefix(), name)))
tokens = Arrays.copyOfRange(tokens, 1, tokens.length); .findFirst().orElse(DEFAULT);
return Trio.of(update, ability, tokens); tokens = msg.getText()
.replaceFirst(getCommandPrefix() + abName, "")
.split(getCommandRegexSplit());
ability = abilities.get(abName);
} else { } else {
Ability ability = abilities.get(DEFAULT); tokens = msg.getText().split(getCommandRegexSplit());
return Trio.of(update, ability, tokens); if (tokens[0].startsWith(getCommandPrefix())) {
String abilityToken = stripBotUsername(tokens[0].substring(1)).toLowerCase();
ability = abilities.get(abilityToken);
tokens = Arrays.copyOfRange(tokens, 1, tokens.length);
} else {
ability = abilities.get(DEFAULT);
}
} }
return Trio.of(update, ability, tokens);
} }
private String stripBotUsername(String token) { private String stripBotUsername(String token) {

View File

@ -0,0 +1,102 @@
package org.telegram.abilitybots.api.bot;
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.Ability;
import org.telegram.abilitybots.api.sender.SilentSender;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User;
import java.io.IOException;
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.TestUtils.mockFullUpdate;
import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance;
import static org.telegram.abilitybots.api.objects.Ability.builder;
import static org.telegram.abilitybots.api.objects.Locality.ALL;
import static org.telegram.abilitybots.api.objects.Privacy.PUBLIC;
public class ContinuousTextTest {
private static final User USER = new User(1, "first", false, "last", "username", null);
private DBContext db;
private SilentSender silent;
private ContinuousTextBot bot;
@BeforeEach
void setUp() {
db = offlineInstance("db");
bot = new ContinuousTextBot(EMPTY, EMPTY, db);
silent = mock(SilentSender.class);
bot.silent = silent;
}
@AfterEach
void tearDown() throws IOException {
db.clear();
db.close();
}
@Test
void processesContinuousText() {
Update update = mockFullUpdate(bot, USER, "/do2");
bot.onUpdateReceived(update);
verify(silent, times(1))
.send("2", USER.getId());
}
@Test
void matchesLongestAbilityName() {
Update update = mockFullUpdate(bot, USER, "/do1");
bot.onUpdateReceived(update);
verify(silent, times(1))
.send("longer ability name", USER.getId());
}
public static class ContinuousTextBot extends AbilityBot {
public ContinuousTextBot(String token, String username, DBContext db) {
super(token, username, db);
}
@Override
public int creatorId() {
return 1337;
}
@Override
protected boolean allowContinuousText() {
return true;
}
public Ability continuousTextAbility() {
return builder()
.name("do")
.privacy(PUBLIC)
.locality(ALL)
.input(0)
.action(ctx -> silent.send(ctx.firstArg(), ctx.chatId()))
.build();
}
public Ability continuousTextSimilarAbility() {
return builder()
.name("do1")
.privacy(PUBLIC)
.locality(ALL)
.input(0)
.action(ctx -> silent.send("longer ability name", ctx.chatId()))
.build();
}
}
}