Merge pull request #780 from addo37/improve-token-fetch
Add customization options for command processing
This commit is contained in:
commit
280b9f2686
@ -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
|
||||
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.
|
@ -260,6 +260,18 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
|
||||
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.
|
||||
* <p>
|
||||
@ -501,17 +513,27 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
|
||||
if (!update.hasMessage() || !msg.hasText())
|
||||
return Trio.of(update, abilities.get(DEFAULT), new String[]{});
|
||||
|
||||
String[] tokens = msg.getText().split(" ");
|
||||
|
||||
if (tokens[0].startsWith("/")) {
|
||||
String abilityToken = stripBotUsername(tokens[0].substring(1)).toLowerCase();
|
||||
Ability ability = abilities.get(abilityToken);
|
||||
tokens = Arrays.copyOfRange(tokens, 1, tokens.length);
|
||||
return Trio.of(update, ability, tokens);
|
||||
Ability ability;
|
||||
String[] tokens;
|
||||
if (allowContinuousText()) {
|
||||
String abName = abilities.keySet().stream()
|
||||
.filter(name -> msg.getText().startsWith(format("%s%s", getCommandPrefix(), name)))
|
||||
.findFirst().orElse(DEFAULT);
|
||||
tokens = msg.getText()
|
||||
.replaceFirst(getCommandPrefix() + abName, "")
|
||||
.split(getCommandRegexSplit());
|
||||
ability = abilities.get(abName);
|
||||
} else {
|
||||
Ability ability = abilities.get(DEFAULT);
|
||||
return Trio.of(update, ability, tokens);
|
||||
tokens = msg.getText().split(getCommandRegexSplit());
|
||||
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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user