add multiple reply declaration support
This commit is contained in:
parent
6342d1ff4e
commit
aac8afe209
@ -248,9 +248,9 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
|
||||
|
||||
public Privacy getPrivacy(Update update, int id) {
|
||||
return isCreator(id) ?
|
||||
CREATOR : isAdmin(id) ?
|
||||
ADMIN : (isGroupUpdate(update) || isSuperGroupUpdate(update)) && isGroupAdmin(update, id) ?
|
||||
GROUP_ADMIN : PUBLIC;
|
||||
CREATOR : isAdmin(id) ?
|
||||
ADMIN : (isGroupUpdate(update) || isSuperGroupUpdate(update)) && isGroupAdmin(update, id) ?
|
||||
GROUP_ADMIN : PUBLIC;
|
||||
}
|
||||
|
||||
public boolean isGroupAdmin(Update update, int id) {
|
||||
@ -260,8 +260,8 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
|
||||
public boolean isGroupAdmin(long chatId, int id) {
|
||||
GetChatAdministrators admins = GetChatAdministrators.builder().chatId(Long.toString(chatId)).build();
|
||||
return silent.execute(admins)
|
||||
.orElse(new ArrayList<>()).stream()
|
||||
.anyMatch(member -> member.getUser().getId() == id);
|
||||
.orElse(new ArrayList<>()).stream()
|
||||
.anyMatch(member -> member.getUser().getId() == id);
|
||||
}
|
||||
|
||||
public boolean isCreator(int id) {
|
||||
@ -326,17 +326,17 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
|
||||
|
||||
DefaultAbilities defaultAbs = new DefaultAbilities(this);
|
||||
Stream<Ability> defaultAbsStream = stream(DefaultAbilities.class.getMethods())
|
||||
.filter(checkReturnType(Ability.class))
|
||||
.map(returnAbility(defaultAbs))
|
||||
.filter(ab -> !toggle.isOff(ab))
|
||||
.map(toggle::processAbility);
|
||||
.filter(checkReturnType(Ability.class))
|
||||
.map(returnAbility(defaultAbs))
|
||||
.filter(ab -> !toggle.isOff(ab))
|
||||
.map(toggle::processAbility);
|
||||
|
||||
// Extract all abilities from every single extension instance
|
||||
abilities = Stream.concat(defaultAbsStream,
|
||||
extensions.stream()
|
||||
.flatMap(ext -> stream(ext.getClass().getMethods())
|
||||
.filter(checkReturnType(Ability.class))
|
||||
.map(returnAbility(ext))))
|
||||
extensions.stream()
|
||||
.flatMap(ext -> stream(ext.getClass().getMethods())
|
||||
.filter(checkReturnType(Ability.class))
|
||||
.map(returnAbility(ext))))
|
||||
// Abilities are immutable, build it respectively
|
||||
.collect(ImmutableMap::<String, Ability>builder,
|
||||
(b, a) -> b.put(a.name(), a),
|
||||
@ -348,7 +348,14 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
|
||||
.flatMap(ext -> stream(ext.getClass().getMethods())
|
||||
.filter(checkReturnType(Reply.class))
|
||||
.map(returnReply(ext)))
|
||||
.flatMap(Reply::stream);
|
||||
.flatMap(Reply::stream);
|
||||
|
||||
Stream<Reply> extensionCollectionReplies = extensions.stream()
|
||||
.flatMap(extension -> stream(extension.getClass().getMethods())
|
||||
.filter(checkReturnType(ReplyCollection.class))
|
||||
.map(returnReplyCollection(extension))
|
||||
.map(ReplyCollection::getReplies))
|
||||
.flatMap(Collection::stream);
|
||||
|
||||
// Replies can be standalone or attached to abilities, fetch those too
|
||||
Stream<Reply> abilityReplies = abilities.values().stream()
|
||||
@ -356,10 +363,12 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
|
||||
.flatMap(Reply::stream);
|
||||
|
||||
// Now create the replies registry (list)
|
||||
replies = Stream.concat(abilityReplies, extensionReplies).collect(
|
||||
ImmutableList::<Reply>builder,
|
||||
Builder::add,
|
||||
(b1, b2) -> b1.addAll(b2.build()))
|
||||
replies = Stream.of(abilityReplies, extensionReplies, extensionCollectionReplies)
|
||||
.flatMap(replyStream -> replyStream)
|
||||
.collect(
|
||||
ImmutableList::<Reply>builder,
|
||||
Builder::add,
|
||||
(b1, b2) -> b1.addAll(b2.build()))
|
||||
.build();
|
||||
} catch (IllegalStateException e) {
|
||||
log.error("Duplicate names found while registering abilities. Make sure that the abilities declared don't clash with the reserved ones.", e);
|
||||
@ -369,15 +378,15 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
|
||||
|
||||
private void initStats() {
|
||||
Set<String> enabledStats = Stream.concat(
|
||||
replies.stream().filter(Reply::statsEnabled).map(Reply::name),
|
||||
abilities.entrySet().stream()
|
||||
.filter(entry -> entry.getValue().statsEnabled())
|
||||
.map(Map.Entry::getKey)).collect(toSet());
|
||||
replies.stream().filter(Reply::statsEnabled).map(Reply::name),
|
||||
abilities.entrySet().stream()
|
||||
.filter(entry -> entry.getValue().statsEnabled())
|
||||
.map(Map.Entry::getKey)).collect(toSet());
|
||||
stats = db.getMap(STATS);
|
||||
Set<String> toBeRemoved = difference(stats.keySet(), enabledStats);
|
||||
toBeRemoved.forEach(stats::remove);
|
||||
enabledStats.forEach(abName -> stats.computeIfAbsent(abName,
|
||||
name -> createStats(abName, 0)));
|
||||
name -> createStats(abName, 0)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -439,6 +448,23 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the method and retrieves its return {@link ReplyCollection}.
|
||||
*
|
||||
* @param obj a bot or extension that this method is invoked with
|
||||
* @return a {@link Function} which returns the {@link ReplyCollection} returned by the given method
|
||||
*/
|
||||
private static Function<? super Method, ReplyCollection> returnReplyCollection(Object obj) {
|
||||
return method -> {
|
||||
try {
|
||||
return (ReplyCollection) method.invoke(obj);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
log.error("Could not add Reply Collection", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void postConsumption(Pair<MessageContext, Ability> pair) {
|
||||
ofNullable(pair.b().postAction())
|
||||
.ifPresent(consumer -> consumer.accept(pair.a()));
|
||||
@ -553,12 +579,12 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
|
||||
String[] tokens;
|
||||
if (allowContinuousText()) {
|
||||
String abName = abilities.keySet().stream()
|
||||
.filter(name -> msg.getText().startsWith(format("%s%s", getCommandPrefix(), name)))
|
||||
.max(comparingInt(String::length))
|
||||
.orElse(DEFAULT);
|
||||
.filter(name -> msg.getText().startsWith(format("%s%s", getCommandPrefix(), name)))
|
||||
.max(comparingInt(String::length))
|
||||
.orElse(DEFAULT);
|
||||
tokens = msg.getText()
|
||||
.replaceFirst(getCommandPrefix() + abName, "")
|
||||
.split(getCommandRegexSplit());
|
||||
.replaceFirst(getCommandPrefix() + abName, "")
|
||||
.split(getCommandRegexSplit());
|
||||
ability = abilities.get(abName);
|
||||
} else {
|
||||
tokens = msg.getText().split(getCommandRegexSplit());
|
||||
@ -637,7 +663,7 @@ public abstract class BaseAbilityBot extends DefaultAbsSender implements Ability
|
||||
return callable.call();
|
||||
} catch(Exception ex) {
|
||||
log.error(format("Reply [%s] failed to check for conditions. " +
|
||||
"Make sure you're safeguarding against all possible updates.", name));
|
||||
"Make sure you're safeguarding against all possible updates.", name));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
package org.telegram.abilitybots.api.objects;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class ReplyCollection {
|
||||
|
||||
public final Collection<Reply> replies;
|
||||
|
||||
public ReplyCollection(Collection<Reply> replies) {
|
||||
this.replies = replies;
|
||||
}
|
||||
|
||||
public Collection<Reply> getReplies() {
|
||||
return replies;
|
||||
}
|
||||
|
||||
}
|
@ -40,7 +40,7 @@ import static org.apache.commons.lang3.StringUtils.EMPTY;
|
||||
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.DefaultBot.*;
|
||||
import static org.telegram.abilitybots.api.bot.TestUtils.CREATOR;
|
||||
import static org.telegram.abilitybots.api.bot.TestUtils.*;
|
||||
import static org.telegram.abilitybots.api.db.MapDBContext.offlineInstance;
|
||||
@ -661,27 +661,51 @@ public class AbilityBotTest {
|
||||
verify(silent, times(1)).send(expected, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void canProcessRepliesRegisteredInCollection() {
|
||||
Update firstUpdate = mock(Update.class);
|
||||
Message firstMessage = mock(Message.class);
|
||||
when(firstMessage.getText()).thenReturn(FIRST_REPLY_KEY_MESSAGE);
|
||||
when(firstMessage.getChatId()).thenReturn(1L);
|
||||
|
||||
Update secondUpdate = mock(Update.class);
|
||||
Message secondMessage = mock(Message.class);
|
||||
when(secondMessage.getText()).thenReturn(SECOND_REPLY_KEY_MESSAGE);
|
||||
when(secondMessage.getChatId()).thenReturn(1L);
|
||||
|
||||
mockUser(firstUpdate, firstMessage, USER);
|
||||
mockUser(secondUpdate, secondMessage, USER);
|
||||
|
||||
|
||||
bot.onUpdateReceived(firstUpdate);
|
||||
bot.onUpdateReceived(secondUpdate);
|
||||
|
||||
verify(silent, times(2)).send(anyString(), anyLong());
|
||||
verify(silent, times(1)).send("first reply answer", 1);
|
||||
verify(silent, times(1)).send("second reply answer", 1);
|
||||
}
|
||||
|
||||
private void handlesAllUpdates(Consumer<Update> utilMethod) {
|
||||
Arrays.stream(Update.class.getMethods())
|
||||
// filter to all these methods of hasXXX (hasPoll, hasMessage, etc...)
|
||||
.filter(method -> method.getName().startsWith("has"))
|
||||
// Gotta filter out hashCode
|
||||
.filter(method -> method.getReturnType().getName().equals("boolean"))
|
||||
.forEach(method -> {
|
||||
Update update = mock(Update.class);
|
||||
try {
|
||||
// Mock the method and make sure it returns true so that it gets processed by the following method
|
||||
when(method.invoke(update)).thenReturn(true);
|
||||
// Call the function, throws an IllegalStateException if there's an update that can't be processed
|
||||
utilMethod.accept(update);
|
||||
} catch (IllegalStateException e) {
|
||||
throw new RuntimeException(
|
||||
format("Found an update variation that is not handled by the getChatId util method [%s]", method.getName()), e);
|
||||
} catch (NullPointerException | ReflectiveOperationException e) {
|
||||
// This is fine, the mock isn't complete and we're only
|
||||
// looking for IllegalStateExceptions thrown by the method
|
||||
}
|
||||
});
|
||||
// filter to all these methods of hasXXX (hasPoll, hasMessage, etc...)
|
||||
.filter(method -> method.getName().startsWith("has"))
|
||||
// Gotta filter out hashCode
|
||||
.filter(method -> method.getReturnType().getName().equals("boolean"))
|
||||
.forEach(method -> {
|
||||
Update update = mock(Update.class);
|
||||
try {
|
||||
// Mock the method and make sure it returns true so that it gets processed by the following method
|
||||
when(method.invoke(update)).thenReturn(true);
|
||||
// Call the function, throws an IllegalStateException if there's an update that can't be processed
|
||||
utilMethod.accept(update);
|
||||
} catch (IllegalStateException e) {
|
||||
throw new RuntimeException(
|
||||
format("Found an update variation that is not handled by the getChatId util method [%s]", method.getName()), e);
|
||||
} catch (NullPointerException | ReflectiveOperationException e) {
|
||||
// This is fine, the mock isn't complete and we're only
|
||||
// looking for IllegalStateExceptions thrown by the method
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void mockUser(Update update, Message message, User user) {
|
||||
|
@ -5,8 +5,12 @@ import org.telegram.abilitybots.api.objects.Ability;
|
||||
import org.telegram.abilitybots.api.objects.Ability.AbilityBuilder;
|
||||
import org.telegram.abilitybots.api.objects.Flag;
|
||||
import org.telegram.abilitybots.api.objects.Reply;
|
||||
import org.telegram.abilitybots.api.objects.ReplyCollection;
|
||||
import org.telegram.abilitybots.api.toggle.AbilityToggle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.telegram.abilitybots.api.objects.Ability.builder;
|
||||
import static org.telegram.abilitybots.api.objects.Flag.CALLBACK_QUERY;
|
||||
import static org.telegram.abilitybots.api.objects.Flag.MESSAGE;
|
||||
@ -15,6 +19,8 @@ import static org.telegram.abilitybots.api.objects.Privacy.ADMIN;
|
||||
import static org.telegram.abilitybots.api.objects.Privacy.PUBLIC;
|
||||
|
||||
public class DefaultBot extends AbilityBot {
|
||||
public static final String FIRST_REPLY_KEY_MESSAGE = "first reply key string";
|
||||
public static final String SECOND_REPLY_KEY_MESSAGE = "second reply key string";
|
||||
|
||||
public DefaultBot(String token, String username, DBContext db) {
|
||||
super(token, username, db);
|
||||
@ -79,6 +85,24 @@ public class DefaultBot extends AbilityBot {
|
||||
);
|
||||
}
|
||||
|
||||
public ReplyCollection createReplyCollection() {
|
||||
List<Reply> replyList = new ArrayList<>();
|
||||
replyList.add(
|
||||
Reply.of(
|
||||
upd -> silent.send("first reply answer", upd.getMessage().getChatId()),
|
||||
update -> update.getMessage().getText().equalsIgnoreCase(FIRST_REPLY_KEY_MESSAGE)
|
||||
)
|
||||
);
|
||||
replyList.add(
|
||||
Reply.of(
|
||||
upd -> silent.send("second reply answer", upd.getMessage().getChatId()),
|
||||
update -> update.getMessage().getText().equalsIgnoreCase(SECOND_REPLY_KEY_MESSAGE)
|
||||
)
|
||||
|
||||
);
|
||||
return new ReplyCollection(replyList);
|
||||
}
|
||||
|
||||
public Ability testAbility() {
|
||||
return getDefaultBuilder().build();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user