chore: perform refactor for db-cleaning and more testing
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
* Add Javadoc where missing * Solve some FIXMEs here and therepull/3/head
parent
3e094ec72a
commit
9ff5748964
|
@ -5,7 +5,8 @@ import com.github.polpetta.mezzotre.route.RouteDI;
|
||||||
import com.github.polpetta.mezzotre.route.Telegram;
|
import com.github.polpetta.mezzotre.route.Telegram;
|
||||||
import com.github.polpetta.mezzotre.telegram.callbackquery.CallbackQueryDI;
|
import com.github.polpetta.mezzotre.telegram.callbackquery.CallbackQueryDI;
|
||||||
import com.github.polpetta.mezzotre.telegram.command.CommandDI;
|
import com.github.polpetta.mezzotre.telegram.command.CommandDI;
|
||||||
import com.github.polpetta.mezzotre.util.di.ThreadPool;
|
import com.github.polpetta.mezzotre.util.ServiceModule;
|
||||||
|
import com.github.polpetta.mezzotre.util.UtilDI;
|
||||||
import com.google.inject.*;
|
import com.google.inject.*;
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
import com.google.inject.name.Names;
|
import com.google.inject.name.Names;
|
||||||
|
@ -24,7 +25,7 @@ public class App extends Jooby {
|
||||||
(jooby) -> {
|
(jooby) -> {
|
||||||
final HashSet<Module> modules = new HashSet<>();
|
final HashSet<Module> modules = new HashSet<>();
|
||||||
modules.add(new OrmDI());
|
modules.add(new OrmDI());
|
||||||
modules.add(new ThreadPool());
|
modules.add(new UtilDI());
|
||||||
modules.add(new RouteDI());
|
modules.add(new RouteDI());
|
||||||
modules.add(new CommandDI());
|
modules.add(new CommandDI());
|
||||||
modules.add(new CallbackQueryDI());
|
modules.add(new CallbackQueryDI());
|
||||||
|
@ -55,6 +56,7 @@ public class App extends Jooby {
|
||||||
install(
|
install(
|
||||||
injector.getInstance(Key.get(Extension.class, Names.named("flyWayMigrationExtension"))));
|
injector.getInstance(Key.get(Extension.class, Names.named("flyWayMigrationExtension"))));
|
||||||
install(injector.getInstance(Key.get(Extension.class, Names.named("ebeanExtension"))));
|
install(injector.getInstance(Key.get(Extension.class, Names.named("ebeanExtension"))));
|
||||||
|
install(injector.getInstance(Key.get(ServiceModule.class, Names.named("serviceModule"))));
|
||||||
|
|
||||||
decorator(new AccessLogHandler());
|
decorator(new AccessLogHandler());
|
||||||
decorator(new TransactionalRequest());
|
decorator(new TransactionalRequest());
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
package com.github.polpetta.mezzotre;
|
package com.github.polpetta.mezzotre;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.orm.BatchBeanCleanerService;
|
||||||
|
import com.google.common.util.concurrent.Service;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import io.jooby.Jooby;
|
import io.jooby.Jooby;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
public class AppDI extends AbstractModule {
|
public class AppDI extends AbstractModule {
|
||||||
|
@ -28,4 +33,13 @@ public class AppDI extends AbstractModule {
|
||||||
public String getAppName() {
|
public String getAppName() {
|
||||||
return APPLICATION_NAME;
|
return APPLICATION_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("services")
|
||||||
|
public List<Service> getApplicationServices(
|
||||||
|
Logger logger,
|
||||||
|
@Named("serviceRunningCheckTime") Pair<Integer, TimeUnit> serviceRunningCheckTime) {
|
||||||
|
return List.of(new BatchBeanCleanerService(logger, serviceRunningCheckTime));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
package com.github.polpetta.mezzotre.orm;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.AbstractExecutionThreadService;
|
||||||
|
import io.ebean.typequery.PString;
|
||||||
|
import io.ebean.typequery.TQRootBean;
|
||||||
|
import io.vavr.Tuple;
|
||||||
|
import io.vavr.Tuple3;
|
||||||
|
import io.vavr.control.Try;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
public class BatchBeanCleanerService extends AbstractExecutionThreadService {
|
||||||
|
|
||||||
|
private final LinkedBlockingDeque<
|
||||||
|
Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>
|
||||||
|
entriesToRemove;
|
||||||
|
private final Logger log;
|
||||||
|
private final Pair<Integer, TimeUnit> serviceRunningCheckTime;
|
||||||
|
private final LinkedList<
|
||||||
|
Consumer<Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>>
|
||||||
|
deletionListener;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public BatchBeanCleanerService(
|
||||||
|
Logger logger,
|
||||||
|
@Named("serviceRunningCheckTime") Pair<Integer, TimeUnit> serviceRunningCheckTime) {
|
||||||
|
this.log = logger;
|
||||||
|
this.serviceRunningCheckTime = serviceRunningCheckTime;
|
||||||
|
this.entriesToRemove = new LinkedBlockingDeque<>();
|
||||||
|
this.deletionListener = new LinkedList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() throws Exception {
|
||||||
|
while (isRunning()) {
|
||||||
|
// This statement is blocking for 1 sec if the queue is empty, and then it goes on - this way
|
||||||
|
// we check if the service is still supposed to be up or what
|
||||||
|
Optional.ofNullable(
|
||||||
|
entriesToRemove.poll(
|
||||||
|
serviceRunningCheckTime.getLeft(), serviceRunningCheckTime.getRight()))
|
||||||
|
.ifPresent(
|
||||||
|
entryToRemove -> {
|
||||||
|
Try.of(
|
||||||
|
() -> {
|
||||||
|
final int deleted = entryToRemove._2().eq(entryToRemove._1()).delete();
|
||||||
|
if (deleted > 0) {
|
||||||
|
log.trace(
|
||||||
|
"Bean with id " + entryToRemove._1() + " removed successfully ");
|
||||||
|
} else {
|
||||||
|
log.warn(
|
||||||
|
"Bean(s) with id "
|
||||||
|
+ entryToRemove._1()
|
||||||
|
+ " for query "
|
||||||
|
+ entryToRemove._2()
|
||||||
|
+ " was not removed from the database because it was not"
|
||||||
|
+ " found");
|
||||||
|
}
|
||||||
|
entryToRemove._3().complete(deleted);
|
||||||
|
deletionListener.parallelStream().forEach(l -> l.accept(entryToRemove));
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.onFailure(ex -> entryToRemove._3().completeExceptionally(ex));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void shutDown() throws Exception {
|
||||||
|
super.shutDown();
|
||||||
|
entriesToRemove.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Integer> removeAsync(
|
||||||
|
String id, PString<? extends TQRootBean<?, ?>> row) {
|
||||||
|
final CompletableFuture<Integer> jobExecution = new CompletableFuture<>();
|
||||||
|
entriesToRemove.offer(Tuple.of(id, row, jobExecution));
|
||||||
|
return jobExecution;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(
|
||||||
|
Consumer<Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>
|
||||||
|
listener) {
|
||||||
|
deletionListener.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeListener(
|
||||||
|
Consumer<Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>
|
||||||
|
listener) {
|
||||||
|
deletionListener.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.github.polpetta.mezzotre.orm;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||||
|
import io.ebean.typequery.PString;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class CallbackQueryContextCleaner {
|
||||||
|
|
||||||
|
private static final Supplier<PString<QCallbackQueryContext>> ENTRY_GROUP =
|
||||||
|
() -> new QCallbackQueryContext().entryGroup;
|
||||||
|
private static final Supplier<PString<QCallbackQueryContext>> SINGLE_ENTRY =
|
||||||
|
() -> new QCallbackQueryContext().id;
|
||||||
|
|
||||||
|
private final BatchBeanCleanerService batchBeanCleanerService;
|
||||||
|
private final Logger log;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CallbackQueryContextCleaner(BatchBeanCleanerService batchBeanCleanerService, Logger log) {
|
||||||
|
this.batchBeanCleanerService = batchBeanCleanerService;
|
||||||
|
this.log = log;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Integer> removeGroupAsync(String id) {
|
||||||
|
log.trace("CallbackQueryContext entry group " + id + " queued for removal");
|
||||||
|
return batchBeanCleanerService.removeAsync(id, ENTRY_GROUP.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Integer> removeIdAsync(String id) {
|
||||||
|
log.trace("CallbackQueryContext single entity " + id + " queued for removal");
|
||||||
|
return batchBeanCleanerService.removeAsync(id, SINGLE_ENTRY.get());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package com.github.polpetta.mezzotre.orm.telegram;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
||||||
|
import com.github.polpetta.mezzotre.util.Clock;
|
||||||
|
import com.github.polpetta.types.json.ChatContext;
|
||||||
|
import com.pengrad.telegrambot.model.Message;
|
||||||
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChatUtil provides utilities for interacting in an easier way when manipulating chat related data.
|
||||||
|
* In particular, it provides an easy and DRY way to modify a set of frequently-accessed fields, or
|
||||||
|
* simply to extract data performing all the necessary steps
|
||||||
|
*
|
||||||
|
* @author Davide Polonio
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class ChatUtil {
|
||||||
|
|
||||||
|
private final Clock clock;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ChatUtil(Clock clock) {
|
||||||
|
this.clock = clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the {@link ChatContext} with the values passed to the method. The updated values are
|
||||||
|
* then saved in the database
|
||||||
|
*
|
||||||
|
* @param chat the chat that will be updated with the new {@link ChatContext} values
|
||||||
|
* @param stepName the step name to set
|
||||||
|
* @param stageNumber the stage number to set
|
||||||
|
* @param additionalFields if there are, additional custom fields that will be added to {@link
|
||||||
|
* ChatContext}. Note that these values will have to be manually retrieved since no method is
|
||||||
|
* available for custom entries. Use {@link Collections#emptyMap()} if you don't wish to add
|
||||||
|
* any additional field
|
||||||
|
*/
|
||||||
|
public void updateChatContext(
|
||||||
|
TgChat chat, String stepName, int stageNumber, Map<String, Object> additionalFields) {
|
||||||
|
final ChatContext chatContext = chat.getChatContext();
|
||||||
|
chatContext.setStage(stepName);
|
||||||
|
chatContext.setStep(stageNumber);
|
||||||
|
chatContext.setPreviousMessageUnixTimestampInSeconds(clock.now());
|
||||||
|
additionalFields.forEach(chatContext::setAdditionalProperty);
|
||||||
|
|
||||||
|
chat.setChatContext(chatContext);
|
||||||
|
chat.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a possible {@link TgChat} from the given {@link CallbackQueryContext} or the {@link
|
||||||
|
* Update}. Note that first the {@link CallbackQueryContext} is checked, then {@link Update} is
|
||||||
|
* used as fallback. Once the Telegram chat id is retrieved from one of these two, the persistence
|
||||||
|
* layer is queried and if there is any {@link TgChat} which the corresponding ID it is returned
|
||||||
|
* to the caller.
|
||||||
|
*
|
||||||
|
* @param callbackQueryContext the {@link CallbackQueryContext} coming from a Telegram callback
|
||||||
|
* query
|
||||||
|
* @param update the whole {@link Update} object that is sent from Telegram servers
|
||||||
|
* @return an {@link Optional} that may contain a {@link TgChat} if the ID is found in the
|
||||||
|
* persistence layer, otherwise {@link Optional#empty()} is given back either if the id is not
|
||||||
|
* present or if the chat is not present in the persistence layer.
|
||||||
|
*/
|
||||||
|
public Optional<TgChat> extractChat(CallbackQueryContext callbackQueryContext, Update update) {
|
||||||
|
return Optional.of(callbackQueryContext.getFields().getTelegramChatId())
|
||||||
|
.map(Double::longValue)
|
||||||
|
// If we're desperate, search in the message for the chat id
|
||||||
|
.filter(chatId -> chatId != 0L && chatId != Long.MIN_VALUE)
|
||||||
|
.or(
|
||||||
|
() ->
|
||||||
|
Optional.ofNullable(update.callbackQuery().message())
|
||||||
|
.map(Message::messageId)
|
||||||
|
.map(Long::valueOf))
|
||||||
|
.filter(chatId -> chatId != 0L && chatId != Long.MIN_VALUE)
|
||||||
|
.flatMap(chatId -> new QTgChat().id.eq(chatId).findOneOrEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T cleanCallbackQuery(T toReturn, CallbackQueryContext callbackQueryContext) {
|
||||||
|
new QCallbackQueryContext().entryGroup.eq(callbackQueryContext.getId()).delete();
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
import com.pengrad.telegrambot.model.Update;
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
@ -12,11 +13,16 @@ import org.slf4j.Logger;
|
||||||
public class NotFound implements Processor {
|
public class NotFound implements Processor {
|
||||||
|
|
||||||
private final Logger log;
|
private final Logger log;
|
||||||
|
private final CallbackQueryContextCleaner callbackQueryContextCleaner;
|
||||||
private final String eventName;
|
private final String eventName;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public NotFound(Logger logger, @Assisted String eventName) {
|
public NotFound(
|
||||||
|
Logger logger,
|
||||||
|
CallbackQueryContextCleaner callbackQueryContextCleaner,
|
||||||
|
@Assisted String eventName) {
|
||||||
this.log = logger;
|
this.log = logger;
|
||||||
|
this.callbackQueryContextCleaner = callbackQueryContextCleaner;
|
||||||
this.eventName = eventName;
|
this.eventName = eventName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +39,7 @@ public class NotFound implements Processor {
|
||||||
+ callbackQueryContext.getId()
|
+ callbackQueryContext.getId()
|
||||||
+ " event name "
|
+ " event name "
|
||||||
+ eventName);
|
+ eventName);
|
||||||
|
callbackQueryContextCleaner.removeGroupAsync(callbackQueryContext.getEntryGroup());
|
||||||
return CompletableFuture.completedFuture(Optional.empty());
|
return CompletableFuture.completedFuture(Optional.empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||||
|
|
||||||
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||||
|
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||||
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||||
import com.pengrad.telegrambot.model.Update;
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
@ -36,17 +37,23 @@ public class SelectLanguageTutorial implements Processor {
|
||||||
private final TemplateContentGenerator templateContentGenerator;
|
private final TemplateContentGenerator templateContentGenerator;
|
||||||
private final Logger log;
|
private final Logger log;
|
||||||
private final UUIDGenerator uuidGenerator;
|
private final UUIDGenerator uuidGenerator;
|
||||||
|
private final ChatUtil chatUtil;
|
||||||
|
private final CallbackQueryContextCleaner callbackQueryContextCleaner;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SelectLanguageTutorial(
|
public SelectLanguageTutorial(
|
||||||
@Named("eventThreadPool") Executor threadPool,
|
@Named("eventThreadPool") Executor threadPool,
|
||||||
TemplateContentGenerator templateContentGenerator,
|
TemplateContentGenerator templateContentGenerator,
|
||||||
Logger log,
|
Logger log,
|
||||||
UUIDGenerator uuidGenerator) {
|
UUIDGenerator uuidGenerator,
|
||||||
|
ChatUtil chatUtil,
|
||||||
|
CallbackQueryContextCleaner callbackQueryContextCleaner) {
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
this.templateContentGenerator = templateContentGenerator;
|
this.templateContentGenerator = templateContentGenerator;
|
||||||
this.log = log;
|
this.log = log;
|
||||||
this.uuidGenerator = uuidGenerator;
|
this.uuidGenerator = uuidGenerator;
|
||||||
|
this.chatUtil = chatUtil;
|
||||||
|
this.callbackQueryContextCleaner = callbackQueryContextCleaner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -59,7 +66,8 @@ public class SelectLanguageTutorial implements Processor {
|
||||||
CallbackQueryContext callbackQueryContext, Update update) {
|
CallbackQueryContext callbackQueryContext, Update update) {
|
||||||
return CompletableFuture.supplyAsync(
|
return CompletableFuture.supplyAsync(
|
||||||
() ->
|
() ->
|
||||||
Util.extractChat(callbackQueryContext, update)
|
chatUtil
|
||||||
|
.extractChat(callbackQueryContext, update)
|
||||||
.map(
|
.map(
|
||||||
tgChat -> {
|
tgChat -> {
|
||||||
tgChat.setLocale(
|
tgChat.setLocale(
|
||||||
|
@ -90,6 +98,14 @@ public class SelectLanguageTutorial implements Processor {
|
||||||
.thenApplyAsync(
|
.thenApplyAsync(
|
||||||
// If we are here then we're sure there is at least a chat associated with this callback
|
// If we are here then we're sure there is at least a chat associated with this callback
|
||||||
tgChat -> {
|
tgChat -> {
|
||||||
|
if (tgChat.getHasHelpBeenShown()) {
|
||||||
|
log.trace(
|
||||||
|
"Help message has already been shown for this user - no help button will be"
|
||||||
|
+ " present");
|
||||||
|
} else {
|
||||||
|
log.trace("No help message shown yet - the help button will be added");
|
||||||
|
}
|
||||||
|
|
||||||
final String message =
|
final String message =
|
||||||
templateContentGenerator.mergeTemplate(
|
templateContentGenerator.mergeTemplate(
|
||||||
velocityContext ->
|
velocityContext ->
|
||||||
|
@ -99,14 +115,7 @@ public class SelectLanguageTutorial implements Processor {
|
||||||
|
|
||||||
log.trace("SelectLanguageTutorial event - message to send back: " + message);
|
log.trace("SelectLanguageTutorial event - message to send back: " + message);
|
||||||
|
|
||||||
final String callBackGroupToDelete = callbackQueryContext.getEntryGroup();
|
callbackQueryContextCleaner.removeGroupAsync(callbackQueryContext.getEntryGroup());
|
||||||
final int delete =
|
|
||||||
new QCallbackQueryContext().entryGroup.eq(callBackGroupToDelete).delete();
|
|
||||||
log.trace(
|
|
||||||
"Deleted "
|
|
||||||
+ delete
|
|
||||||
+ " entries regarding callback group "
|
|
||||||
+ callBackGroupToDelete);
|
|
||||||
|
|
||||||
final Optional<Integer> messageId = Util.extractMessageId(update);
|
final Optional<Integer> messageId = Util.extractMessageId(update);
|
||||||
BaseRequest<?, ?> baseRequest;
|
BaseRequest<?, ?> baseRequest;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||||
import com.github.polpetta.mezzotre.telegram.model.Help;
|
import com.github.polpetta.mezzotre.telegram.model.Help;
|
||||||
import com.pengrad.telegrambot.model.Update;
|
import com.pengrad.telegrambot.model.Update;
|
||||||
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||||
|
@ -9,13 +11,28 @@ import com.pengrad.telegrambot.model.request.ParseMode;
|
||||||
import com.pengrad.telegrambot.request.BaseRequest;
|
import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
import com.pengrad.telegrambot.request.EditMessageText;
|
import com.pengrad.telegrambot.request.EditMessageText;
|
||||||
import com.pengrad.telegrambot.request.SendMessage;
|
import com.pengrad.telegrambot.request.SendMessage;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ShowHelp callback query event edits a previous message (if available), otherwise it sends a new
|
||||||
|
* message with a list of available commands (the one implemented by {@link
|
||||||
|
* com.github.polpetta.mezzotre.telegram.command.Processor}), and a list of buttons that are
|
||||||
|
* callback queries (the ones implemented by {@link Processor}). The class is a Singleton since it
|
||||||
|
* is stateless and having multiple instances would only be a waste of memory.
|
||||||
|
*
|
||||||
|
* @author Davide Polonio
|
||||||
|
* @since 1.0
|
||||||
|
* @see com.github.polpetta.mezzotre.telegram.command.Help same functionality but as {@link
|
||||||
|
* com.github.polpetta.mezzotre.telegram.command.Processor} command
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
class ShowHelp implements Processor {
|
class ShowHelp implements Processor {
|
||||||
|
|
||||||
public static String EVENT_NAME = "showHelp";
|
public static String EVENT_NAME = "showHelp";
|
||||||
|
@ -24,8 +41,9 @@ class ShowHelp implements Processor {
|
||||||
private final Map<String, com.github.polpetta.mezzotre.telegram.command.Processor>
|
private final Map<String, com.github.polpetta.mezzotre.telegram.command.Processor>
|
||||||
tgCommandProcessors;
|
tgCommandProcessors;
|
||||||
private final Map<String, Processor> eventProcessor;
|
private final Map<String, Processor> eventProcessor;
|
||||||
|
private final ChatUtil chatUtil;
|
||||||
|
private final CallbackQueryContextCleaner callbackQueryContextCleaner;
|
||||||
|
|
||||||
// FIXME tests
|
|
||||||
@Inject
|
@Inject
|
||||||
public ShowHelp(
|
public ShowHelp(
|
||||||
@Named("eventThreadPool") Executor threadPool,
|
@Named("eventThreadPool") Executor threadPool,
|
||||||
|
@ -33,12 +51,15 @@ class ShowHelp implements Processor {
|
||||||
@Named("commandProcessor")
|
@Named("commandProcessor")
|
||||||
Map<String, com.github.polpetta.mezzotre.telegram.command.Processor> tgCommandProcessors,
|
Map<String, com.github.polpetta.mezzotre.telegram.command.Processor> tgCommandProcessors,
|
||||||
@Named("eventProcessors")
|
@Named("eventProcessors")
|
||||||
Map<String, com.github.polpetta.mezzotre.telegram.callbackquery.Processor>
|
Map<String, com.github.polpetta.mezzotre.telegram.callbackquery.Processor> eventProcessor,
|
||||||
eventProcessor) {
|
ChatUtil chatUtil,
|
||||||
|
CallbackQueryContextCleaner callbackQueryContextCleaner) {
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
this.modelHelp = modelHelp;
|
this.modelHelp = modelHelp;
|
||||||
this.tgCommandProcessors = tgCommandProcessors;
|
this.tgCommandProcessors = tgCommandProcessors;
|
||||||
this.eventProcessor = eventProcessor;
|
this.eventProcessor = eventProcessor;
|
||||||
|
this.chatUtil = chatUtil;
|
||||||
|
this.callbackQueryContextCleaner = callbackQueryContextCleaner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,26 +72,40 @@ class ShowHelp implements Processor {
|
||||||
CallbackQueryContext callbackQueryContext, Update update) {
|
CallbackQueryContext callbackQueryContext, Update update) {
|
||||||
return CompletableFuture.supplyAsync(
|
return CompletableFuture.supplyAsync(
|
||||||
() ->
|
() ->
|
||||||
Util.extractChat(callbackQueryContext, update)
|
chatUtil
|
||||||
|
.extractChat(callbackQueryContext, update) // FIXME callbackquerycontext removal?
|
||||||
.map(
|
.map(
|
||||||
chat -> {
|
chat -> {
|
||||||
final String message = modelHelp.getMessage(chat, tgCommandProcessors);
|
final String message = modelHelp.getMessage(chat, tgCommandProcessors);
|
||||||
|
chatUtil.updateChatContext(chat, EVENT_NAME, 0, Collections.emptyMap());
|
||||||
|
chat.setHasHelpBeenShown(true);
|
||||||
|
chat.save();
|
||||||
final Optional<Integer> messageId = Util.extractMessageId(update);
|
final Optional<Integer> messageId = Util.extractMessageId(update);
|
||||||
final InlineKeyboardButton[] buttons =
|
final InlineKeyboardButton[] buttons =
|
||||||
modelHelp.generateInlineKeyBoardButton(chat, eventProcessor);
|
modelHelp.generateInlineKeyBoardButton(chat, eventProcessor);
|
||||||
BaseRequest<?, ?> request;
|
BaseRequest<?, ?> request;
|
||||||
if (messageId.isPresent()) {
|
if (messageId.isPresent()) {
|
||||||
final EditMessageText editMessageText =
|
final EditMessageText editMessageText =
|
||||||
new EditMessageText(chat.getId(), messageId.get(), message);
|
new EditMessageText(chat.getId(), messageId.get(), message)
|
||||||
editMessageText.replyMarkup(new InlineKeyboardMarkup(buttons));
|
.parseMode(ParseMode.Markdown);
|
||||||
|
if (buttons.length > 0) {
|
||||||
|
editMessageText.replyMarkup(new InlineKeyboardMarkup(buttons));
|
||||||
|
}
|
||||||
request = editMessageText;
|
request = editMessageText;
|
||||||
} else {
|
} else {
|
||||||
final SendMessage sendMessage =
|
final SendMessage sendMessage =
|
||||||
new SendMessage(chat.getId(), message).parseMode(ParseMode.Markdown);
|
new SendMessage(chat.getId(), message).parseMode(ParseMode.Markdown);
|
||||||
sendMessage.replyMarkup(new InlineKeyboardMarkup(buttons));
|
if (buttons.length > 0) {
|
||||||
|
sendMessage.replyMarkup(new InlineKeyboardMarkup(buttons));
|
||||||
|
}
|
||||||
request = sendMessage;
|
request = sendMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We don't check if the element is removed or what - we just schedule its
|
||||||
|
// removal, then it is someone else problem
|
||||||
|
callbackQueryContextCleaner.removeGroupAsync(
|
||||||
|
callbackQueryContext.getEntryGroup());
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
}),
|
}),
|
||||||
threadPool);
|
threadPool);
|
||||||
|
|
|
@ -1,28 +1,12 @@
|
||||||
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||||
|
|
||||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
|
||||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
|
||||||
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
|
||||||
import com.pengrad.telegrambot.model.Message;
|
import com.pengrad.telegrambot.model.Message;
|
||||||
import com.pengrad.telegrambot.model.Update;
|
import com.pengrad.telegrambot.model.Update;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class Util {
|
public class Util {
|
||||||
|
|
||||||
public static Optional<TgChat> extractChat(
|
// FIXME tests, doc
|
||||||
CallbackQueryContext callbackQueryContext, Update update) {
|
|
||||||
return Optional.of(callbackQueryContext.getFields().getTelegramChatId())
|
|
||||||
.map(Double::longValue)
|
|
||||||
// If we're desperate, search in the message for the chat id
|
|
||||||
.or(
|
|
||||||
() ->
|
|
||||||
Optional.ofNullable(update.callbackQuery().message())
|
|
||||||
.map(Message::messageId)
|
|
||||||
.map(Long::valueOf))
|
|
||||||
.filter(chatId -> chatId != 0L && chatId != Long.MIN_VALUE)
|
|
||||||
.flatMap(chatId -> new QTgChat().id.eq(chatId).findOneOrEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Optional<Integer> extractMessageId(Update update) {
|
public static Optional<Integer> extractMessageId(Update update) {
|
||||||
return Optional.ofNullable(update.callbackQuery().message()).map(Message::messageId);
|
return Optional.ofNullable(update.callbackQuery().message()).map(Message::messageId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package com.github.polpetta.mezzotre.telegram.command;
|
package com.github.polpetta.mezzotre.telegram.command;
|
||||||
|
|
||||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||||
import com.pengrad.telegrambot.model.Update;
|
import com.pengrad.telegrambot.model.Update;
|
||||||
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||||
import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
|
import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
|
||||||
import com.pengrad.telegrambot.model.request.ParseMode;
|
import com.pengrad.telegrambot.model.request.ParseMode;
|
||||||
import com.pengrad.telegrambot.request.BaseRequest;
|
import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
import com.pengrad.telegrambot.request.SendMessage;
|
import com.pengrad.telegrambot.request.SendMessage;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -14,8 +16,19 @@ import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
public class Help implements Processor {
|
/**
|
||||||
|
* The Help command class allows the user to be informed in any moment of commands that are
|
||||||
|
* available in the system. If possible, it also provides a {@code reply_markup} keyboard ({@link
|
||||||
|
* InlineKeyboardMarkup} for easier bot interactions
|
||||||
|
*
|
||||||
|
* @author Davide Polonio
|
||||||
|
* @since 1.0
|
||||||
|
* @see com.github.polpetta.mezzotre.telegram.callbackquery.ShowHelp for event-based help
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
class Help implements Processor {
|
||||||
|
|
||||||
private static final String TRIGGERING_STAGING_NAME = "/help";
|
private static final String TRIGGERING_STAGING_NAME = "/help";
|
||||||
|
|
||||||
|
@ -24,6 +37,7 @@ public class Help implements Processor {
|
||||||
private final Map<String, com.github.polpetta.mezzotre.telegram.callbackquery.Processor>
|
private final Map<String, com.github.polpetta.mezzotre.telegram.callbackquery.Processor>
|
||||||
eventProcessor;
|
eventProcessor;
|
||||||
private final com.github.polpetta.mezzotre.telegram.model.Help modelHelp;
|
private final com.github.polpetta.mezzotre.telegram.model.Help modelHelp;
|
||||||
|
private final ChatUtil chatUtil;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Help(
|
public Help(
|
||||||
|
@ -31,11 +45,13 @@ public class Help implements Processor {
|
||||||
@Named("commandProcessor") Map<String, Processor> tgCommandProcessors,
|
@Named("commandProcessor") Map<String, Processor> tgCommandProcessors,
|
||||||
@Named("eventProcessors")
|
@Named("eventProcessors")
|
||||||
Map<String, com.github.polpetta.mezzotre.telegram.callbackquery.Processor> eventProcessor,
|
Map<String, com.github.polpetta.mezzotre.telegram.callbackquery.Processor> eventProcessor,
|
||||||
com.github.polpetta.mezzotre.telegram.model.Help modelHelp) {
|
com.github.polpetta.mezzotre.telegram.model.Help modelHelp,
|
||||||
|
ChatUtil chatUtil) {
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
this.tgCommandProcessors = tgCommandProcessors;
|
this.tgCommandProcessors = tgCommandProcessors;
|
||||||
this.eventProcessor = eventProcessor;
|
this.eventProcessor = eventProcessor;
|
||||||
this.modelHelp = modelHelp;
|
this.modelHelp = modelHelp;
|
||||||
|
this.chatUtil = chatUtil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,6 +64,9 @@ public class Help implements Processor {
|
||||||
return CompletableFuture.supplyAsync(
|
return CompletableFuture.supplyAsync(
|
||||||
() -> {
|
() -> {
|
||||||
final String message = modelHelp.getMessage(chat, tgCommandProcessors);
|
final String message = modelHelp.getMessage(chat, tgCommandProcessors);
|
||||||
|
chatUtil.updateChatContext(chat, TRIGGERING_STAGING_NAME, 0, Collections.emptyMap());
|
||||||
|
chat.setHasHelpBeenShown(true);
|
||||||
|
chat.save();
|
||||||
|
|
||||||
final SendMessage sendMessage =
|
final SendMessage sendMessage =
|
||||||
new SendMessage(chat.getId(), message).parseMode(ParseMode.Markdown);
|
new SendMessage(chat.getId(), message).parseMode(ParseMode.Markdown);
|
||||||
|
|
|
@ -11,7 +11,14 @@ import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
public class NotFound implements Processor {
|
/**
|
||||||
|
* Generates a "Command not found" message
|
||||||
|
*
|
||||||
|
* @author Davide Polonio
|
||||||
|
* @since 1.0
|
||||||
|
* @see com.github.polpetta.mezzotre.telegram.callbackquery.NotFound for the event-based version
|
||||||
|
*/
|
||||||
|
class NotFound implements Processor {
|
||||||
|
|
||||||
private final String commandName;
|
private final String commandName;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package com.github.polpetta.mezzotre.telegram.command;
|
package com.github.polpetta.mezzotre.telegram.command;
|
||||||
|
|
||||||
public interface NotFoundFactory {
|
interface NotFoundFactory {
|
||||||
NotFound create(String commandName);
|
NotFound create(String commandName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,11 @@ package com.github.polpetta.mezzotre.telegram.command;
|
||||||
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||||
import com.github.polpetta.mezzotre.telegram.callbackquery.Field;
|
import com.github.polpetta.mezzotre.telegram.callbackquery.Field;
|
||||||
import com.github.polpetta.mezzotre.telegram.callbackquery.Value;
|
import com.github.polpetta.mezzotre.telegram.callbackquery.Value;
|
||||||
import com.github.polpetta.mezzotre.util.Clock;
|
|
||||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||||
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||||
import com.github.polpetta.types.json.ChatContext;
|
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import com.pengrad.telegrambot.model.Update;
|
import com.pengrad.telegrambot.model.Update;
|
||||||
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||||
|
@ -16,6 +15,7 @@ import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
|
||||||
import com.pengrad.telegrambot.model.request.ParseMode;
|
import com.pengrad.telegrambot.model.request.ParseMode;
|
||||||
import com.pengrad.telegrambot.request.BaseRequest;
|
import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
import com.pengrad.telegrambot.request.SendMessage;
|
import com.pengrad.telegrambot.request.SendMessage;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -32,15 +32,15 @@ import org.slf4j.Logger;
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class Start implements Processor {
|
class Start implements Processor {
|
||||||
|
|
||||||
private static final String TRIGGERING_STAGING_NAME = "/start";
|
private static final String TRIGGERING_STAGING_NAME = "/start";
|
||||||
|
|
||||||
private final Executor threadPool;
|
private final Executor threadPool;
|
||||||
private final Logger log;
|
private final Logger log;
|
||||||
private final UUIDGenerator uuidGenerator;
|
private final UUIDGenerator uuidGenerator;
|
||||||
private final Clock clock;
|
|
||||||
private final String applicationName;
|
private final String applicationName;
|
||||||
|
private final ChatUtil chatUtil;
|
||||||
|
|
||||||
private final TemplateContentGenerator templateContentGenerator;
|
private final TemplateContentGenerator templateContentGenerator;
|
||||||
|
|
||||||
|
@ -50,14 +50,14 @@ public class Start implements Processor {
|
||||||
@Named("eventThreadPool") Executor threadPool,
|
@Named("eventThreadPool") Executor threadPool,
|
||||||
Logger log,
|
Logger log,
|
||||||
UUIDGenerator uuidGenerator,
|
UUIDGenerator uuidGenerator,
|
||||||
Clock clock,
|
@Named("applicationName") String applicationName,
|
||||||
@Named("applicationName") String applicationName) {
|
ChatUtil chatUtil) {
|
||||||
this.templateContentGenerator = templateContentGenerator;
|
this.templateContentGenerator = templateContentGenerator;
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
this.log = log;
|
this.log = log;
|
||||||
this.uuidGenerator = uuidGenerator;
|
this.uuidGenerator = uuidGenerator;
|
||||||
this.clock = clock;
|
|
||||||
this.applicationName = applicationName;
|
this.applicationName = applicationName;
|
||||||
|
this.chatUtil = chatUtil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,12 +87,8 @@ public class Start implements Processor {
|
||||||
"template/telegram/start.vm");
|
"template/telegram/start.vm");
|
||||||
log.trace("Start command - message to send back: " + message);
|
log.trace("Start command - message to send back: " + message);
|
||||||
|
|
||||||
final ChatContext chatContext = chat.getChatContext();
|
// FIXME bug!! Show help button set to true but its fake news
|
||||||
chatContext.setStage(TRIGGERING_STAGING_NAME);
|
chatUtil.updateChatContext(chat, TRIGGERING_STAGING_NAME, 0, Collections.emptyMap());
|
||||||
chatContext.setStep(0);
|
|
||||||
chatContext.setPreviousMessageUnixTimestampInSeconds(clock.now());
|
|
||||||
chat.setChatContext(chatContext);
|
|
||||||
chat.save();
|
|
||||||
|
|
||||||
// To get the messageId we should send the message first, then save it in the database!
|
// To get the messageId we should send the message first, then save it in the database!
|
||||||
final String groupId = uuidGenerator.generateAsString();
|
final String groupId = uuidGenerator.generateAsString();
|
||||||
|
|
|
@ -5,10 +5,8 @@ import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
import com.github.polpetta.mezzotre.telegram.callbackquery.Field;
|
import com.github.polpetta.mezzotre.telegram.callbackquery.Field;
|
||||||
import com.github.polpetta.mezzotre.telegram.command.Processor;
|
import com.github.polpetta.mezzotre.telegram.command.Processor;
|
||||||
import com.github.polpetta.mezzotre.util.Clock;
|
|
||||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||||
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||||
import com.github.polpetta.types.json.ChatContext;
|
|
||||||
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -21,47 +19,30 @@ public class Help {
|
||||||
private static final String TRIGGERING_STAGING_NAME = "/help";
|
private static final String TRIGGERING_STAGING_NAME = "/help";
|
||||||
|
|
||||||
private final TemplateContentGenerator templateContentGenerator;
|
private final TemplateContentGenerator templateContentGenerator;
|
||||||
private final Clock clock;
|
|
||||||
private final UUIDGenerator uuidGenerator;
|
private final UUIDGenerator uuidGenerator;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Help(
|
public Help(TemplateContentGenerator templateContentGenerator, UUIDGenerator uuidGenerator) {
|
||||||
TemplateContentGenerator templateContentGenerator, Clock clock, UUIDGenerator uuidGenerator) {
|
|
||||||
|
|
||||||
this.templateContentGenerator = templateContentGenerator;
|
this.templateContentGenerator = templateContentGenerator;
|
||||||
this.clock = clock;
|
|
||||||
this.uuidGenerator = uuidGenerator;
|
this.uuidGenerator = uuidGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage(TgChat chat, Map<String, Processor> tgCommandProcessors) {
|
public String getMessage(TgChat chat, Map<String, Processor> tgCommandProcessors) {
|
||||||
final String message =
|
return templateContentGenerator.mergeTemplate(
|
||||||
templateContentGenerator.mergeTemplate(
|
velocityContext -> {
|
||||||
velocityContext -> {
|
velocityContext.put(
|
||||||
velocityContext.put(
|
"commands",
|
||||||
"commands",
|
tgCommandProcessors.values().stream()
|
||||||
tgCommandProcessors.values().stream()
|
.distinct()
|
||||||
.distinct()
|
.map(
|
||||||
.map(
|
p ->
|
||||||
p ->
|
Pair.of(
|
||||||
Pair.of(
|
p.getTriggerKeywords().stream().sorted().collect(Collectors.toList()),
|
||||||
p.getTriggerKeywords().stream()
|
p.getLocaleDescriptionKeyword()))
|
||||||
.sorted()
|
.collect(Collectors.toList()));
|
||||||
.collect(Collectors.toList()),
|
},
|
||||||
p.getLocaleDescriptionKeyword()))
|
chat.getLocale(),
|
||||||
.collect(Collectors.toList()));
|
"template/telegram/help.vm");
|
||||||
},
|
|
||||||
chat.getLocale(),
|
|
||||||
"template/telegram/help.vm");
|
|
||||||
|
|
||||||
// FIXME this shouldn't stay here. We need to move it into another method
|
|
||||||
final ChatContext chatContext = chat.getChatContext();
|
|
||||||
chatContext.setStage(TRIGGERING_STAGING_NAME);
|
|
||||||
chatContext.setStep(0);
|
|
||||||
chatContext.setPreviousMessageUnixTimestampInSeconds(clock.now());
|
|
||||||
chat.setChatContext(chatContext);
|
|
||||||
chat.setHasHelpBeenShown(true);
|
|
||||||
chat.save();
|
|
||||||
return message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public InlineKeyboardButton[] generateInlineKeyBoardButton(
|
public InlineKeyboardButton[] generateInlineKeyBoardButton(
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package com.github.polpetta.mezzotre.util;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
import com.google.common.util.concurrent.Service;
|
||||||
|
import com.google.common.util.concurrent.ServiceManager;
|
||||||
|
import io.jooby.Extension;
|
||||||
|
import io.jooby.Jooby;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class ServiceModule implements Extension {
|
||||||
|
|
||||||
|
private final ServiceManager serviceManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ServiceModule(@Named("services") List<Service> services) {
|
||||||
|
serviceManager = new ServiceManager(services);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean lateinit() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void install(@NotNull Jooby application) throws Exception {
|
||||||
|
final CompletableFuture<Void> initialization = new CompletableFuture<>();
|
||||||
|
serviceManager.addListener(
|
||||||
|
new ServiceManager.Listener() {
|
||||||
|
@Override
|
||||||
|
public void healthy() {
|
||||||
|
super.healthy();
|
||||||
|
application.getLog().info("All internal application services are up and running");
|
||||||
|
initialization.complete(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failure(@NotNull Service service) {
|
||||||
|
super.failure(service);
|
||||||
|
initialization.completeExceptionally(service.failureCause());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MoreExecutors.directExecutor());
|
||||||
|
|
||||||
|
Runtime.getRuntime()
|
||||||
|
.addShutdownHook(
|
||||||
|
new Thread(
|
||||||
|
() -> {
|
||||||
|
try {
|
||||||
|
serviceManager.stopAsync().awaitStopped(Duration.ofSeconds(15));
|
||||||
|
} catch (TimeoutException ex) {
|
||||||
|
application
|
||||||
|
.getLog()
|
||||||
|
.warn(
|
||||||
|
"Unable to correctly stop all the services, got the following error"
|
||||||
|
+ " while stopping: "
|
||||||
|
+ ex.getMessage());
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Blocking call to wait for all the services to be up and running
|
||||||
|
serviceManager.startAsync();
|
||||||
|
initialization.get();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.github.polpetta.mezzotre.util;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.Service;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ForkJoinPool;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
public class UtilDI extends AbstractModule {
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("eventThreadPool")
|
||||||
|
public Executor getEventExecutor() {
|
||||||
|
return ForkJoinPool.commonPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("longJobThreadPool")
|
||||||
|
public Executor getLongJobExecutor() {
|
||||||
|
return Executors.newCachedThreadPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("serviceModule")
|
||||||
|
public ServiceModule getServiceModule(@Named("services") List<Service> moduleList) {
|
||||||
|
return new ServiceModule(moduleList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("serviceRunningCheckTime")
|
||||||
|
public Pair<Integer, TimeUnit> getTime() {
|
||||||
|
return Pair.of(5, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +0,0 @@
|
||||||
package com.github.polpetta.mezzotre.util.di;
|
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
|
||||||
import com.google.inject.Provides;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ForkJoinPool;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
public class ThreadPool extends AbstractModule {
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
@Named("eventThreadPool")
|
|
||||||
public Executor getEventExecutor() {
|
|
||||||
return ForkJoinPool.commonPool();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
@Named("longJobThreadPool")
|
|
||||||
public Executor getLongJobExecutor() {
|
|
||||||
return Executors.newCachedThreadPool();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,7 @@
|
||||||
${i18n.help.description}:
|
${i18n.help.description}:
|
||||||
|
|
||||||
#foreach(${command} in ${commands})
|
#foreach(${command} in ${commands})
|
||||||
## FIXME this is not displayed correctly in telegram!
|
-#foreach(${key} in ${command.left}) ${key}#end: ${i18n.get(${command.right})}
|
||||||
*#foreach(${key} in ${command.left}) ${key}#end: ${i18n.get(${command.right})}
|
|
||||||
#end
|
#end
|
||||||
|
|
||||||
${i18n.help.buttonsToo}
|
${i18n.help.buttonsToo}
|
|
@ -1,3 +1,3 @@
|
||||||
**${i18n.start.helloFirstName.insert(${firstName})}**
|
*${i18n.start.helloFirstName.insert(${firstName})}*
|
||||||
|
|
||||||
${i18n.start.description.insert(${programName})}
|
${i18n.start.description.insert(${programName})}
|
|
@ -3,15 +3,21 @@ package com.github.polpetta.mezzotre;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
import com.google.inject.*;
|
import com.github.polpetta.mezzotre.util.ServiceModule;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import com.google.inject.Stage;
|
||||||
import io.jooby.*;
|
import io.jooby.*;
|
||||||
import io.jooby.ebean.EbeanModule;
|
import io.jooby.ebean.EbeanModule;
|
||||||
import io.jooby.flyway.FlywayModule;
|
import io.jooby.flyway.FlywayModule;
|
||||||
import io.jooby.hikari.HikariModule;
|
import io.jooby.hikari.HikariModule;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class UnitTest {
|
public class UnitTest {
|
||||||
|
@ -37,6 +43,20 @@ public class UnitTest {
|
||||||
public Extension getEbeanExtension() {
|
public Extension getEbeanExtension() {
|
||||||
return mock(EbeanModule.class);
|
return mock(EbeanModule.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("serviceModule")
|
||||||
|
public ServiceModule getServiceModule() {
|
||||||
|
return mock(ServiceModule.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("serviceRunningCheckTime")
|
||||||
|
public Pair<Integer, TimeUnit> getTime() {
|
||||||
|
return Pair.of(0, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -2,18 +2,22 @@ package com.github.polpetta.mezzotre.helper;
|
||||||
|
|
||||||
import com.github.polpetta.mezzotre.App;
|
import com.github.polpetta.mezzotre.App;
|
||||||
import com.github.polpetta.mezzotre.route.RouteDI;
|
import com.github.polpetta.mezzotre.route.RouteDI;
|
||||||
|
import com.github.polpetta.mezzotre.util.ServiceModule;
|
||||||
|
import com.google.common.util.concurrent.Service;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import com.google.inject.Singleton;
|
|
||||||
import com.google.inject.Stage;
|
import com.google.inject.Stage;
|
||||||
import com.zaxxer.hikari.HikariConfig;
|
import com.zaxxer.hikari.HikariConfig;
|
||||||
import io.jooby.Extension;
|
import io.jooby.Extension;
|
||||||
import io.jooby.ebean.EbeanModule;
|
import io.jooby.ebean.EbeanModule;
|
||||||
import io.jooby.flyway.FlywayModule;
|
import io.jooby.flyway.FlywayModule;
|
||||||
import io.jooby.hikari.HikariModule;
|
import io.jooby.hikari.HikariModule;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.testcontainers.containers.PostgreSQLContainer;
|
import org.testcontainers.containers.PostgreSQLContainer;
|
||||||
|
|
||||||
|
@ -50,6 +54,20 @@ public class IntegrationAppFactory {
|
||||||
public Extension getEbeanExtension() {
|
public Extension getEbeanExtension() {
|
||||||
return new EbeanModule();
|
return new EbeanModule();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
@Named("serviceModule")
|
||||||
|
public ServiceModule getServiceModule(@Named("services") List<Service> moduleList) {
|
||||||
|
return new ServiceModule(moduleList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("serviceRunningCheckTime")
|
||||||
|
public Pair<Integer, TimeUnit> getTime() {
|
||||||
|
return Pair.of(0, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static App loadCustomDbApplication() {
|
public static App loadCustomDbApplication() {
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
package com.github.polpetta.mezzotre.orm;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.helper.Loader;
|
||||||
|
import com.github.polpetta.mezzotre.helper.TestConfig;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||||
|
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||||
|
import io.ebean.Database;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.testcontainers.containers.PostgreSQLContainer;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
@Tag("slow")
|
||||||
|
@Tag("database")
|
||||||
|
@Tag("velocity")
|
||||||
|
@Testcontainers
|
||||||
|
class BatchBeanCleanerServiceIntegrationTest {
|
||||||
|
|
||||||
|
@Container
|
||||||
|
private final PostgreSQLContainer<?> postgresServer =
|
||||||
|
new PostgreSQLContainer<>(TestConfig.POSTGRES_DOCKER_IMAGE);
|
||||||
|
|
||||||
|
private Database database;
|
||||||
|
private BatchBeanCleanerService batchBeanCleanerService;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws Exception {
|
||||||
|
database =
|
||||||
|
Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer));
|
||||||
|
batchBeanCleanerService =
|
||||||
|
new BatchBeanCleanerService(
|
||||||
|
LoggerFactory.getLogger(BatchBeanCleanerService.class),
|
||||||
|
Pair.of(0, TimeUnit.MILLISECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() throws Exception {
|
||||||
|
if (batchBeanCleanerService != null) {
|
||||||
|
batchBeanCleanerService.stopAsync().awaitTerminated(Duration.ofSeconds(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Timeout(value = 1, unit = TimeUnit.MINUTES)
|
||||||
|
void shouldRemoveEntryFromDatabase() throws Exception {
|
||||||
|
final CallbackQueryContext callbackQueryContext =
|
||||||
|
new CallbackQueryContext("1234", "4567", new CallbackQueryMetadata());
|
||||||
|
callbackQueryContext.save();
|
||||||
|
|
||||||
|
batchBeanCleanerService.startAsync();
|
||||||
|
batchBeanCleanerService.awaitRunning();
|
||||||
|
|
||||||
|
final CompletableFuture<Integer> integerCompletableFuture =
|
||||||
|
batchBeanCleanerService.removeAsync("1234", new QCallbackQueryContext().id);
|
||||||
|
|
||||||
|
final Integer gotDeletion = integerCompletableFuture.get();
|
||||||
|
|
||||||
|
assertEquals(1, gotDeletion);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Timeout(value = 1, unit = TimeUnit.MINUTES)
|
||||||
|
void shouldRemoveMultipleEntriesFromDatabase() throws Exception {
|
||||||
|
final CallbackQueryContext callbackQueryContext =
|
||||||
|
new CallbackQueryContext("1234", "4567", new CallbackQueryMetadata());
|
||||||
|
callbackQueryContext.save();
|
||||||
|
final CallbackQueryContext callbackQueryContext2 =
|
||||||
|
new CallbackQueryContext("4321", "4567", new CallbackQueryMetadata());
|
||||||
|
callbackQueryContext2.save();
|
||||||
|
|
||||||
|
batchBeanCleanerService.startAsync();
|
||||||
|
batchBeanCleanerService.awaitRunning();
|
||||||
|
|
||||||
|
final CompletableFuture<Integer> integerCompletableFuture =
|
||||||
|
batchBeanCleanerService.removeAsync("4567", new QCallbackQueryContext().entryGroup);
|
||||||
|
final CompletableFuture<Integer> integerCompletableFuture2 =
|
||||||
|
batchBeanCleanerService.removeAsync("4567", new QCallbackQueryContext().entryGroup);
|
||||||
|
|
||||||
|
final Integer gotDeletion1 = integerCompletableFuture.get();
|
||||||
|
final Integer gotDeletion2 = integerCompletableFuture2.get();
|
||||||
|
|
||||||
|
assertEquals(2, gotDeletion1);
|
||||||
|
assertEquals(0, gotDeletion2);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
package com.github.polpetta.mezzotre.orm.telegram;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.helper.Loader;
|
||||||
|
import com.github.polpetta.mezzotre.helper.TestConfig;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
|
import com.github.polpetta.mezzotre.util.Clock;
|
||||||
|
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||||
|
import com.github.polpetta.types.json.ChatContext;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
import io.ebean.Database;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.testcontainers.containers.PostgreSQLContainer;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
@Tag("slow")
|
||||||
|
@Tag("database")
|
||||||
|
@Testcontainers
|
||||||
|
class ChatUtilIntegrationTest {
|
||||||
|
|
||||||
|
private static Gson gson;
|
||||||
|
|
||||||
|
@Container
|
||||||
|
private final PostgreSQLContainer<?> postgresServer =
|
||||||
|
new PostgreSQLContainer<>(TestConfig.POSTGRES_DOCKER_IMAGE);
|
||||||
|
|
||||||
|
private Database database;
|
||||||
|
private Clock fakeClock;
|
||||||
|
private ChatUtil chatUtil;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void beforeAll() {
|
||||||
|
gson = new Gson();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws Exception {
|
||||||
|
database =
|
||||||
|
Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer));
|
||||||
|
|
||||||
|
fakeClock = mock(Clock.class);
|
||||||
|
chatUtil = new ChatUtil(fakeClock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExtractChatFromCallBackQueryContext() {
|
||||||
|
final Update update =
|
||||||
|
gson.fromJson(
|
||||||
|
"{\n"
|
||||||
|
+ "\"update_id\":10000,\n"
|
||||||
|
+ "\"message\":{\n"
|
||||||
|
+ " \"date\":1441645532,\n"
|
||||||
|
+ " \"chat\":{\n"
|
||||||
|
+ " \"last_name\":\"Test Lastname\",\n"
|
||||||
|
+ " \"id\":1111111,\n"
|
||||||
|
+ " \"type\": \"private\",\n"
|
||||||
|
+ " \"first_name\":\"Test Firstname\",\n"
|
||||||
|
+ " \"username\":\"Testusername\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"message_id\":1365,\n"
|
||||||
|
+ " \"from\":{\n"
|
||||||
|
+ " \"last_name\":\"Test Lastname\",\n"
|
||||||
|
+ " \"id\":1111111,\n"
|
||||||
|
+ " \"first_name\":\"Test Firstname\",\n"
|
||||||
|
+ " \"username\":\"Testusername\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"text\":\"/help\"\n"
|
||||||
|
+ "}\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class);
|
||||||
|
final CallbackQueryContext callbackQueryContext =
|
||||||
|
new CallbackQueryContext(
|
||||||
|
"123",
|
||||||
|
"456",
|
||||||
|
new CallbackQueryMetadata.CallbackQueryMetadataBuilder()
|
||||||
|
.withTelegramChatId(69420L)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
final TgChat expectedChat = new TgChat(69420L, new ChatContext());
|
||||||
|
expectedChat.save();
|
||||||
|
|
||||||
|
final Optional<TgChat> tgChatOptional =
|
||||||
|
chatUtil.extractChat(
|
||||||
|
callbackQueryContext, null); // null just for test purposes, not usually expected
|
||||||
|
|
||||||
|
assertEquals(expectedChat, tgChatOptional.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExtractChatFromUpdateIfCallbackQueryContextIsEmpty() {
|
||||||
|
final Update update =
|
||||||
|
gson.fromJson(
|
||||||
|
"{\n"
|
||||||
|
+ " \"update_id\": 158712614,\n"
|
||||||
|
+ " \"callback_query\": {\n"
|
||||||
|
+ " \"id\": \"20496049451114620\",\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"is_bot\": false,\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"language_code\": \"en\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"message\": {\n"
|
||||||
|
+ " \"message_id\": 69420,\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 244745330,\n"
|
||||||
|
+ " \"is_bot\": true,\n"
|
||||||
|
+ " \"first_name\": \"Dev - DavideBot\",\n"
|
||||||
|
+ " \"username\": \"devdavidebot\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"date\": 1681218838,\n"
|
||||||
|
+ " \"chat\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"type\": \"private\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"text\": \"Hello xxxxxx! \uD83D\uDC4B\\n"
|
||||||
|
+ "\\n"
|
||||||
|
+ "This is Mezzotre, a simple bot focused on DnD content management! Please start"
|
||||||
|
+ " by choosing a language down below \uD83D\uDC47\",\n"
|
||||||
|
+ " \"entities\": [\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"type\": \"bold\",\n"
|
||||||
|
+ " \"offset\": 0,\n"
|
||||||
|
+ " \"length\": 16\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"type\": \"italic\",\n"
|
||||||
|
+ " \"offset\": 26,\n"
|
||||||
|
+ " \"length\": 8\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " ],\n"
|
||||||
|
+ " \"reply_markup\": {\n"
|
||||||
|
+ " \"inline_keyboard\": [\n"
|
||||||
|
+ " [\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"text\": \"English\",\n"
|
||||||
|
+ " \"callback_data\": \"9a64be11-d086-4bd9-859f-720c43dedcb5\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"text\": \"Italian\",\n"
|
||||||
|
+ " \"callback_data\": \"8768d660-f05f-4f4b-bda5-3451ab573d56\"\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class);
|
||||||
|
final CallbackQueryContext callbackQueryContext =
|
||||||
|
new CallbackQueryContext("123", "456", new CallbackQueryMetadata());
|
||||||
|
|
||||||
|
final TgChat expectedChat = new TgChat(69420L, new ChatContext());
|
||||||
|
expectedChat.save();
|
||||||
|
|
||||||
|
final Optional<TgChat> tgChatOptional =
|
||||||
|
chatUtil.extractChat(
|
||||||
|
callbackQueryContext, update); // null just for test purposes, not usually expected
|
||||||
|
|
||||||
|
assertEquals(expectedChat, tgChatOptional.get());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.github.polpetta.mezzotre.orm.telegram;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
|
import com.github.polpetta.mezzotre.util.Clock;
|
||||||
|
import com.github.polpetta.types.json.ChatContext;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.parallel.Execution;
|
||||||
|
import org.junit.jupiter.api.parallel.ExecutionMode;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
|
@Execution(ExecutionMode.CONCURRENT)
|
||||||
|
class ChatUtilTest {
|
||||||
|
private Clock fakeClock;
|
||||||
|
private ChatUtil chatUtil;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
fakeClock = mock(Clock.class);
|
||||||
|
chatUtil = new ChatUtil(fakeClock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUpdateChatContext() {
|
||||||
|
final TgChat fakeTgChat = mock(TgChat.class);
|
||||||
|
final ChatContext chatContext = new ChatContext();
|
||||||
|
when(fakeTgChat.getChatContext()).thenReturn(chatContext);
|
||||||
|
when(fakeClock.now()).thenReturn(69420L);
|
||||||
|
chatUtil.updateChatContext(fakeTgChat, "/test1", 42, Collections.emptyMap());
|
||||||
|
|
||||||
|
verify(fakeTgChat, times(1)).getChatContext();
|
||||||
|
final ArgumentCaptor<ChatContext> chatContextArgumentCaptor =
|
||||||
|
ArgumentCaptor.forClass(ChatContext.class);
|
||||||
|
verify(fakeTgChat, times(1)).setChatContext(chatContextArgumentCaptor.capture());
|
||||||
|
verify(fakeTgChat, times(0)).setHasHelpBeenShown(eq(true));
|
||||||
|
final ChatContext capturedChatContextValue = chatContextArgumentCaptor.getValue();
|
||||||
|
assertEquals("/test1", capturedChatContextValue.getStage());
|
||||||
|
assertEquals(42, capturedChatContextValue.getStep());
|
||||||
|
assertEquals(69420L, capturedChatContextValue.getPreviousMessageUnixTimestampInSeconds());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAddMapElementsToChatContextToo() {
|
||||||
|
final TgChat fakeTgChat = mock(TgChat.class);
|
||||||
|
final ChatContext chatContext = new ChatContext();
|
||||||
|
when(fakeTgChat.getChatContext()).thenReturn(chatContext);
|
||||||
|
when(fakeClock.now()).thenReturn(69420L);
|
||||||
|
|
||||||
|
final Object obj1 = new Object();
|
||||||
|
final Object obj2 = new Object();
|
||||||
|
|
||||||
|
chatUtil.updateChatContext(fakeTgChat, "/test1", 42, Map.of("field1", obj1, "field2", obj2));
|
||||||
|
|
||||||
|
verify(fakeTgChat, times(1)).getChatContext();
|
||||||
|
final ArgumentCaptor<ChatContext> chatContextArgumentCaptor =
|
||||||
|
ArgumentCaptor.forClass(ChatContext.class);
|
||||||
|
verify(fakeTgChat, times(1)).setChatContext(chatContextArgumentCaptor.capture());
|
||||||
|
final ChatContext capturedChatContextValue = chatContextArgumentCaptor.getValue();
|
||||||
|
final Map<String, Object> gotAdditionalProperties =
|
||||||
|
capturedChatContextValue.getAdditionalProperties();
|
||||||
|
assertEquals(2, gotAdditionalProperties.size());
|
||||||
|
assertEquals(obj1, gotAdditionalProperties.get("field1"));
|
||||||
|
assertEquals(obj2, gotAdditionalProperties.get("field2"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.parallel.Execution;
|
||||||
|
import org.junit.jupiter.api.parallel.ExecutionMode;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@Execution(ExecutionMode.CONCURRENT)
|
||||||
|
class NotFoundTest {
|
||||||
|
|
||||||
|
private CallbackQueryContextCleaner fakeCallbackQueryCleaner;
|
||||||
|
private NotFound notFound;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
|
||||||
|
fakeCallbackQueryCleaner = mock(CallbackQueryContextCleaner.class);
|
||||||
|
|
||||||
|
notFound =
|
||||||
|
new NotFound(LoggerFactory.getLogger(NotFound.class), fakeCallbackQueryCleaner, "anEvent");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCallCallbackQueryContextCleaner() throws Exception {
|
||||||
|
final CallbackQueryContext fakeCallbackQuery = mock(CallbackQueryContext.class);
|
||||||
|
when(fakeCallbackQuery.getId()).thenReturn("anId");
|
||||||
|
when(fakeCallbackQuery.getEntryGroup()).thenReturn("123");
|
||||||
|
final Update fakeUpdate = mock(Update.class);
|
||||||
|
|
||||||
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResult =
|
||||||
|
notFound.process(fakeCallbackQuery, fakeUpdate);
|
||||||
|
|
||||||
|
verify(fakeCallbackQueryCleaner, times(1)).removeGroupAsync(eq("123"));
|
||||||
|
assertEquals(Optional.empty(), gotResult.get());
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,10 +7,14 @@ import com.github.polpetta.mezzotre.helper.Loader;
|
||||||
import com.github.polpetta.mezzotre.helper.TestConfig;
|
import com.github.polpetta.mezzotre.helper.TestConfig;
|
||||||
import com.github.polpetta.mezzotre.i18n.LocalizedMessageFactory;
|
import com.github.polpetta.mezzotre.i18n.LocalizedMessageFactory;
|
||||||
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||||
|
import com.github.polpetta.mezzotre.orm.BatchBeanCleanerService;
|
||||||
|
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||||
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||||
|
import com.github.polpetta.mezzotre.util.Clock;
|
||||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||||
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||||
import com.github.polpetta.types.json.ChatContext;
|
import com.github.polpetta.types.json.ChatContext;
|
||||||
|
@ -22,12 +26,17 @@ import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
import com.pengrad.telegrambot.request.EditMessageText;
|
import com.pengrad.telegrambot.request.EditMessageText;
|
||||||
import com.pengrad.telegrambot.request.SendMessage;
|
import com.pengrad.telegrambot.request.SendMessage;
|
||||||
import io.ebean.Database;
|
import io.ebean.Database;
|
||||||
|
import io.ebean.typequery.PString;
|
||||||
|
import io.ebean.typequery.TQRootBean;
|
||||||
|
import io.vavr.Tuple3;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.junit.jupiter.api.*;
|
import org.junit.jupiter.api.*;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
@ -52,6 +61,8 @@ class SelectLanguageTutorialIntegrationTest {
|
||||||
private Database database;
|
private Database database;
|
||||||
private SelectLanguageTutorial selectLanguageTutorial;
|
private SelectLanguageTutorial selectLanguageTutorial;
|
||||||
private UUIDGenerator fakeUUIDGenerator;
|
private UUIDGenerator fakeUUIDGenerator;
|
||||||
|
private ChatUtil chatUtil;
|
||||||
|
private BatchBeanCleanerService batchBeanCleanerService;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
static void beforeAll() {
|
static void beforeAll() {
|
||||||
|
@ -64,6 +75,12 @@ class SelectLanguageTutorialIntegrationTest {
|
||||||
Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer));
|
Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer));
|
||||||
|
|
||||||
fakeUUIDGenerator = mock(UUIDGenerator.class);
|
fakeUUIDGenerator = mock(UUIDGenerator.class);
|
||||||
|
chatUtil = new ChatUtil(new Clock());
|
||||||
|
batchBeanCleanerService =
|
||||||
|
new BatchBeanCleanerService(
|
||||||
|
LoggerFactory.getLogger(BatchBeanCleanerService.class),
|
||||||
|
Pair.of(0, TimeUnit.MILLISECONDS));
|
||||||
|
batchBeanCleanerService.startAsync().awaitRunning(Duration.ofSeconds(10));
|
||||||
|
|
||||||
selectLanguageTutorial =
|
selectLanguageTutorial =
|
||||||
new SelectLanguageTutorial(
|
new SelectLanguageTutorial(
|
||||||
|
@ -71,7 +88,16 @@ class SelectLanguageTutorialIntegrationTest {
|
||||||
new TemplateContentGenerator(
|
new TemplateContentGenerator(
|
||||||
new LocalizedMessageFactory(Loader.defaultVelocityEngine())),
|
new LocalizedMessageFactory(Loader.defaultVelocityEngine())),
|
||||||
LoggerFactory.getLogger(SelectLanguageTutorial.class),
|
LoggerFactory.getLogger(SelectLanguageTutorial.class),
|
||||||
fakeUUIDGenerator);
|
fakeUUIDGenerator,
|
||||||
|
chatUtil,
|
||||||
|
new CallbackQueryContextCleaner(
|
||||||
|
batchBeanCleanerService,
|
||||||
|
LoggerFactory.getLogger(CallbackQueryContextCleaner.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() throws Exception {
|
||||||
|
batchBeanCleanerService.stopAsync().awaitTerminated(Duration.ofSeconds(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream<Arguments> getTestLocales() {
|
private static Stream<Arguments> getTestLocales() {
|
||||||
|
@ -193,6 +219,11 @@ class SelectLanguageTutorialIntegrationTest {
|
||||||
"c018108f-6612-4848-8fca-cf301460d4eb", entryGroupId, callbackQueryMetadata);
|
"c018108f-6612-4848-8fca-cf301460d4eb", entryGroupId, callbackQueryMetadata);
|
||||||
changeLanguageCallbackQueryContext.save();
|
changeLanguageCallbackQueryContext.save();
|
||||||
|
|
||||||
|
final CompletableFuture<
|
||||||
|
Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>
|
||||||
|
callBackFuture = new CompletableFuture<>();
|
||||||
|
batchBeanCleanerService.addListener(callBackFuture::complete);
|
||||||
|
|
||||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> processFuture =
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> processFuture =
|
||||||
this.selectLanguageTutorial.process(changeLanguageCallbackQueryContext, update);
|
this.selectLanguageTutorial.process(changeLanguageCallbackQueryContext, update);
|
||||||
final Optional<BaseRequest<?, ?>> gotResponseOpt = processFuture.get();
|
final Optional<BaseRequest<?, ?>> gotResponseOpt = processFuture.get();
|
||||||
|
@ -213,6 +244,7 @@ class SelectLanguageTutorialIntegrationTest {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
1, new QCallbackQueryContext().id.eq("e86e6fa1-fdd4-4120-b85d-a5482db2e8b5").findCount());
|
1, new QCallbackQueryContext().id.eq("e86e6fa1-fdd4-4120-b85d-a5482db2e8b5").findCount());
|
||||||
} else {
|
} else {
|
||||||
|
callBackFuture.get(); // Await that callback are cleaned out first
|
||||||
assertEquals(0, keyboardButtons.size());
|
assertEquals(0, keyboardButtons.size());
|
||||||
assertEquals(0, new QCallbackQueryContext().findCount());
|
assertEquals(0, new QCallbackQueryContext().findCount());
|
||||||
}
|
}
|
||||||
|
@ -221,6 +253,11 @@ class SelectLanguageTutorialIntegrationTest {
|
||||||
assertNotNull(retrievedTgChat);
|
assertNotNull(retrievedTgChat);
|
||||||
assertEquals(selectLanguageTutorial.getLocale(), retrievedTgChat.getLocale());
|
assertEquals(selectLanguageTutorial.getLocale(), retrievedTgChat.getLocale());
|
||||||
|
|
||||||
|
final Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>
|
||||||
|
deletionCallback = callBackFuture.get();
|
||||||
|
|
||||||
|
assertEquals(entryGroupId, deletionCallback._1());
|
||||||
|
assertEquals(1, deletionCallback._3().get());
|
||||||
assertEquals(0, new QCallbackQueryContext().entryGroup.eq(entryGroupId).findCount());
|
assertEquals(0, new QCallbackQueryContext().entryGroup.eq(entryGroupId).findCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,6 +345,10 @@ class SelectLanguageTutorialIntegrationTest {
|
||||||
new CallbackQueryContext(
|
new CallbackQueryContext(
|
||||||
"c018108f-6612-4848-8fca-cf301460d4eb", entryGroupId, callbackQueryMetadata);
|
"c018108f-6612-4848-8fca-cf301460d4eb", entryGroupId, callbackQueryMetadata);
|
||||||
changeLanguageCallbackQueryContext.save();
|
changeLanguageCallbackQueryContext.save();
|
||||||
|
final CompletableFuture<
|
||||||
|
Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>
|
||||||
|
callBackFuture = new CompletableFuture<>();
|
||||||
|
batchBeanCleanerService.addListener(callBackFuture::complete);
|
||||||
|
|
||||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> processFuture =
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> processFuture =
|
||||||
this.selectLanguageTutorial.process(changeLanguageCallbackQueryContext, update);
|
this.selectLanguageTutorial.process(changeLanguageCallbackQueryContext, update);
|
||||||
|
@ -329,6 +370,7 @@ class SelectLanguageTutorialIntegrationTest {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
1, new QCallbackQueryContext().id.eq("e86e6fa1-fdd4-4120-b85d-a5482db2e8b5").findCount());
|
1, new QCallbackQueryContext().id.eq("e86e6fa1-fdd4-4120-b85d-a5482db2e8b5").findCount());
|
||||||
} else {
|
} else {
|
||||||
|
callBackFuture.get(); // Await that callback are cleaned out first
|
||||||
assertEquals(0, keyboardButtons.size());
|
assertEquals(0, keyboardButtons.size());
|
||||||
assertEquals(0, new QCallbackQueryContext().findCount());
|
assertEquals(0, new QCallbackQueryContext().findCount());
|
||||||
}
|
}
|
||||||
|
@ -337,6 +379,11 @@ class SelectLanguageTutorialIntegrationTest {
|
||||||
assertNotNull(retrievedTgChat);
|
assertNotNull(retrievedTgChat);
|
||||||
assertEquals(selectLanguageTutorial.getLocale(), retrievedTgChat.getLocale());
|
assertEquals(selectLanguageTutorial.getLocale(), retrievedTgChat.getLocale());
|
||||||
|
|
||||||
|
final Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>
|
||||||
|
deletionCallback = callBackFuture.get();
|
||||||
|
|
||||||
|
assertEquals(entryGroupId, deletionCallback._1());
|
||||||
|
assertEquals(1, deletionCallback._3().get());
|
||||||
assertEquals(0, new QCallbackQueryContext().entryGroup.eq(entryGroupId).findCount());
|
assertEquals(0, new QCallbackQueryContext().entryGroup.eq(entryGroupId).findCount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,457 @@
|
||||||
|
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.helper.Loader;
|
||||||
|
import com.github.polpetta.mezzotre.helper.TestConfig;
|
||||||
|
import com.github.polpetta.mezzotre.i18n.LocalizedMessageFactory;
|
||||||
|
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||||
|
import com.github.polpetta.mezzotre.orm.BatchBeanCleanerService;
|
||||||
|
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||||
|
import com.github.polpetta.mezzotre.telegram.model.Help;
|
||||||
|
import com.github.polpetta.mezzotre.util.Clock;
|
||||||
|
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||||
|
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||||
|
import com.github.polpetta.types.json.ChatContext;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||||
|
import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
|
||||||
|
import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
|
import com.pengrad.telegrambot.request.EditMessageText;
|
||||||
|
import com.pengrad.telegrambot.request.SendMessage;
|
||||||
|
import io.ebean.Database;
|
||||||
|
import io.ebean.typequery.PString;
|
||||||
|
import io.ebean.typequery.TQRootBean;
|
||||||
|
import io.vavr.Tuple3;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import org.apache.velocity.app.VelocityEngine;
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.testcontainers.containers.PostgreSQLContainer;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
@Tag("slow")
|
||||||
|
@Tag("database")
|
||||||
|
@Tag("velocity")
|
||||||
|
@Testcontainers
|
||||||
|
class ShowHelpIntegrationTest {
|
||||||
|
|
||||||
|
private static Gson gson;
|
||||||
|
private static Logger testLog;
|
||||||
|
|
||||||
|
@Container
|
||||||
|
private final PostgreSQLContainer<?> postgresServer =
|
||||||
|
new PostgreSQLContainer<>(TestConfig.POSTGRES_DOCKER_IMAGE);
|
||||||
|
|
||||||
|
private Database database;
|
||||||
|
private VelocityEngine velocityEngine;
|
||||||
|
private UUIDGenerator fakeUUIDGenerator;
|
||||||
|
private Clock fakeClock;
|
||||||
|
private BatchBeanCleanerService batchBeanCleanerService;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void beforeAll() {
|
||||||
|
gson = new Gson();
|
||||||
|
testLog = LoggerFactory.getLogger(ShowHelpIntegrationTest.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws Exception {
|
||||||
|
database =
|
||||||
|
Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer));
|
||||||
|
velocityEngine = Loader.defaultVelocityEngine();
|
||||||
|
|
||||||
|
final Logger log = LoggerFactory.getLogger(ShowHelp.class);
|
||||||
|
fakeUUIDGenerator = mock(UUIDGenerator.class);
|
||||||
|
fakeClock = mock(Clock.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() throws Exception {
|
||||||
|
if (batchBeanCleanerService != null) {
|
||||||
|
batchBeanCleanerService.stopAsync().awaitTerminated(Duration.ofSeconds(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShowHelp generateShowHelpWithCustomCommands(
|
||||||
|
Map<String, Processor> eventProcessors,
|
||||||
|
Map<String, com.github.polpetta.mezzotre.telegram.command.Processor> commandProcessors)
|
||||||
|
throws TimeoutException {
|
||||||
|
|
||||||
|
batchBeanCleanerService =
|
||||||
|
new BatchBeanCleanerService(
|
||||||
|
LoggerFactory.getLogger(CallbackQueryContextCleaner.class),
|
||||||
|
Pair.of(0, TimeUnit.MILLISECONDS));
|
||||||
|
batchBeanCleanerService.startAsync().awaitRunning(Duration.ofSeconds(10));
|
||||||
|
|
||||||
|
return new ShowHelp(
|
||||||
|
Executors.newSingleThreadExecutor(),
|
||||||
|
new Help(
|
||||||
|
new TemplateContentGenerator(new LocalizedMessageFactory(velocityEngine)),
|
||||||
|
fakeUUIDGenerator),
|
||||||
|
commandProcessors,
|
||||||
|
eventProcessors,
|
||||||
|
// We need to implement a service that is sync - no thread creation!
|
||||||
|
new ChatUtil(fakeClock),
|
||||||
|
new CallbackQueryContextCleaner(
|
||||||
|
batchBeanCleanerService, LoggerFactory.getLogger(CallbackQueryContextCleaner.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream<Arguments> getMessageIdOrNot() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(
|
||||||
|
gson.fromJson(
|
||||||
|
"{\n"
|
||||||
|
+ " \"update_id\": 158712614,\n"
|
||||||
|
+ " \"callback_query\": {\n"
|
||||||
|
+ " \"id\": \"20496049451114620\",\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"is_bot\": false,\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"language_code\": \"en\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"message\": {\n"
|
||||||
|
+ " \"message_id\": 2723,\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 244745330,\n"
|
||||||
|
+ " \"is_bot\": true,\n"
|
||||||
|
+ " \"first_name\": \"Dev - DavideBot\",\n"
|
||||||
|
+ " \"username\": \"devdavidebot\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"date\": 1681218838,\n"
|
||||||
|
+ " \"chat\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"type\": \"private\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"text\": \"a message\",\n"
|
||||||
|
+ " \"reply_markup\": {\n"
|
||||||
|
+ " \"inline_keyboard\": [\n"
|
||||||
|
+ " [\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"text\": \"English\",\n"
|
||||||
|
+ " \"callback_data\": \"9a64be11-d086-4bd9-859f-720c43dedcb5\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"text\": \"Italian\",\n"
|
||||||
|
+ " \"callback_data\": \"8768d660-f05f-4f4b-bda5-3451ab573d56\"\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class),
|
||||||
|
EditMessageText.class),
|
||||||
|
Arguments.of(
|
||||||
|
gson.fromJson(
|
||||||
|
"{\n"
|
||||||
|
+ " \"update_id\": 158712614,\n"
|
||||||
|
+ " \"callback_query\": {\n"
|
||||||
|
+ " \"id\": \"20496049451114620\",\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"is_bot\": false,\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"language_code\": \"en\"\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class),
|
||||||
|
SendMessage.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@Timeout(value = 1, unit = TimeUnit.MINUTES)
|
||||||
|
@MethodSource("getMessageIdOrNot")
|
||||||
|
void shouldShowHelpMessageWithButtons(Update update, Class<?> typeOfMessage) throws Exception {
|
||||||
|
// we have 2 events + group uuid
|
||||||
|
when(fakeUUIDGenerator.generateAsString())
|
||||||
|
.thenReturn("e86e6fa1-fdd4-4120-b85d-a5482db2e8b5")
|
||||||
|
.thenReturn("16507fbd-9f28-48a8-9de1-3ea1c943af67")
|
||||||
|
.thenReturn("0b0ac18e-f621-484e-aa8d-9b176be5b930");
|
||||||
|
when(fakeClock.now()).thenReturn(42L);
|
||||||
|
|
||||||
|
final HashMap<String, com.github.polpetta.mezzotre.telegram.command.Processor> commands =
|
||||||
|
new HashMap<>();
|
||||||
|
final com.github.polpetta.mezzotre.telegram.command.Processor dummy1 =
|
||||||
|
new com.github.polpetta.mezzotre.telegram.command.Processor() {
|
||||||
|
@Override
|
||||||
|
public Set<String> getTriggerKeywords() {
|
||||||
|
return Set.of("/a", "/b");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Optional<BaseRequest<?, ?>>> process(
|
||||||
|
TgChat chat, Update update) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLocaleDescriptionKeyword() {
|
||||||
|
return "start.cmdDescription";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final com.github.polpetta.mezzotre.telegram.command.Processor dummy2 =
|
||||||
|
new com.github.polpetta.mezzotre.telegram.command.Processor() {
|
||||||
|
@Override
|
||||||
|
public Set<String> getTriggerKeywords() {
|
||||||
|
return Set.of("/different");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Optional<BaseRequest<?, ?>>> process(
|
||||||
|
TgChat chat, Update update) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLocaleDescriptionKeyword() {
|
||||||
|
return "help.cmdDescription";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
commands.put("/a", dummy1);
|
||||||
|
commands.put("/b", dummy1);
|
||||||
|
commands.put("/different", dummy2);
|
||||||
|
|
||||||
|
final Map<String, Processor> events = new HashMap<>();
|
||||||
|
|
||||||
|
final Processor dummyEvent1 =
|
||||||
|
new Processor() {
|
||||||
|
@Override
|
||||||
|
public String getEventName() {
|
||||||
|
return "exampleEvent";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeDirectlyInvokedByTheUser() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<String> getPrettyPrintLocaleKeyName() {
|
||||||
|
return Optional.of("changeLanguage.inlineKeyboardButtonName");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Optional<BaseRequest<?, ?>>> process(
|
||||||
|
CallbackQueryContext callbackQueryContext, Update update) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final Processor dummyEvent2 =
|
||||||
|
new com.github.polpetta.mezzotre.telegram.callbackquery.Processor() {
|
||||||
|
@Override
|
||||||
|
public String getEventName() {
|
||||||
|
return "secondExampleEvent";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeDirectlyInvokedByTheUser() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<String> getPrettyPrintLocaleKeyName() {
|
||||||
|
return Optional.of("selectLanguageTutorial.inlineKeyboardButtonName");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Optional<BaseRequest<?, ?>>> process(
|
||||||
|
CallbackQueryContext callbackQueryContext, Update update) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
events.put(dummyEvent1.getEventName(), dummyEvent1);
|
||||||
|
events.put(dummyEvent2.getEventName(), dummyEvent2);
|
||||||
|
|
||||||
|
final ShowHelp showHelp = generateShowHelpWithCustomCommands(events, commands);
|
||||||
|
final TgChat tgChat = new TgChat(1111111L, new ChatContext());
|
||||||
|
tgChat.save();
|
||||||
|
final CallbackQueryContext callbackQueryContext =
|
||||||
|
new CallbackQueryContext(
|
||||||
|
"1234",
|
||||||
|
"5678",
|
||||||
|
new CallbackQueryMetadata.CallbackQueryMetadataBuilder()
|
||||||
|
.withTelegramChatId(1111111L)
|
||||||
|
.build());
|
||||||
|
callbackQueryContext.save();
|
||||||
|
|
||||||
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResultFuture =
|
||||||
|
showHelp.process(callbackQueryContext, update);
|
||||||
|
|
||||||
|
final Optional<BaseRequest<?, ?>> baseRequestOptional = gotResultFuture.get();
|
||||||
|
final BaseRequest<?, ?> gotResponse = baseRequestOptional.get();
|
||||||
|
assertInstanceOf(typeOfMessage, gotResponse);
|
||||||
|
final String message = (String) gotResponse.getParameters().get("text");
|
||||||
|
assertEquals(
|
||||||
|
"Here is a list of what I can do:\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "- /a /b: Trigger this very bot\n"
|
||||||
|
+ "- /different: Print the help message\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "You can do the same operations you'd do with the commands aforementioned by"
|
||||||
|
+ " selecting the corresponding button below \uD83D\uDC47",
|
||||||
|
message);
|
||||||
|
final InlineKeyboardButton[][] keyboard =
|
||||||
|
((InlineKeyboardMarkup)
|
||||||
|
gotResponse
|
||||||
|
.getParameters()
|
||||||
|
.getOrDefault("reply_markup", new InlineKeyboardMarkup()))
|
||||||
|
.inlineKeyboard();
|
||||||
|
|
||||||
|
final List<InlineKeyboardButton> keyboardButtons =
|
||||||
|
Stream.of(keyboard).flatMap(Stream::of).toList();
|
||||||
|
|
||||||
|
assertEquals(2, keyboardButtons.size());
|
||||||
|
|
||||||
|
assertFalse(keyboardButtons.get(0).callbackData().isBlank());
|
||||||
|
assertFalse(keyboardButtons.get(1).callbackData().isBlank());
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
keyboardButtons.stream()
|
||||||
|
.map(InlineKeyboardButton::text)
|
||||||
|
.anyMatch("Select language"::equals));
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
keyboardButtons.stream()
|
||||||
|
.map(InlineKeyboardButton::text)
|
||||||
|
.anyMatch("Change language"::equals));
|
||||||
|
|
||||||
|
final TgChat gotChat = new QTgChat().id.eq(1111111L).findOne();
|
||||||
|
assertNotNull(gotChat);
|
||||||
|
assertTrue(gotChat.getHasHelpBeenShown());
|
||||||
|
final ChatContext gotChatChatContext = gotChat.getChatContext();
|
||||||
|
assertEquals(0, gotChatChatContext.getStep());
|
||||||
|
assertEquals("showHelp", gotChatChatContext.getStage());
|
||||||
|
assertEquals(42, gotChatChatContext.getPreviousMessageUnixTimestampInSeconds());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Timeout(value = 1, unit = TimeUnit.MINUTES)
|
||||||
|
void shouldCleanUpCallbackQueryContextAfterRun() throws Exception {
|
||||||
|
when(fakeClock.now()).thenReturn(42L);
|
||||||
|
|
||||||
|
final ShowHelp showHelp =
|
||||||
|
generateShowHelpWithCustomCommands(Collections.emptyMap(), Collections.emptyMap());
|
||||||
|
final TgChat tgChat = new TgChat(1111111L, new ChatContext());
|
||||||
|
tgChat.save();
|
||||||
|
final Update update =
|
||||||
|
gson.fromJson(
|
||||||
|
"{\n"
|
||||||
|
+ " \"update_id\": 158712614,\n"
|
||||||
|
+ " \"callback_query\": {\n"
|
||||||
|
+ " \"id\": \"20496049451114620\",\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"is_bot\": false,\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"language_code\": \"en\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"message\": {\n"
|
||||||
|
+ " \"message_id\": 2723,\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 244745330,\n"
|
||||||
|
+ " \"is_bot\": true,\n"
|
||||||
|
+ " \"first_name\": \"Dev - DavideBot\",\n"
|
||||||
|
+ " \"username\": \"devdavidebot\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"date\": 1681218838,\n"
|
||||||
|
+ " \"chat\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"type\": \"private\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"text\": \"Hello xxxxxx! \uD83D\uDC4B\\n"
|
||||||
|
+ "\\n"
|
||||||
|
+ "This is Mezzotre, a simple bot focused on DnD content management! Please start"
|
||||||
|
+ " by choosing a language down below \uD83D\uDC47\",\n"
|
||||||
|
+ " \"entities\": [\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"type\": \"bold\",\n"
|
||||||
|
+ " \"offset\": 0,\n"
|
||||||
|
+ " \"length\": 16\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"type\": \"italic\",\n"
|
||||||
|
+ " \"offset\": 26,\n"
|
||||||
|
+ " \"length\": 8\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class);
|
||||||
|
final CallbackQueryContext callbackQueryContext =
|
||||||
|
new CallbackQueryContext(
|
||||||
|
"1234",
|
||||||
|
"5678",
|
||||||
|
new CallbackQueryMetadata.CallbackQueryMetadataBuilder()
|
||||||
|
.withTelegramChatId(1111111L)
|
||||||
|
.build());
|
||||||
|
callbackQueryContext.save();
|
||||||
|
// Create another CallbackQuery context of the same group
|
||||||
|
final CallbackQueryContext callbackQueryContext2 =
|
||||||
|
new CallbackQueryContext(
|
||||||
|
"7891",
|
||||||
|
"5678",
|
||||||
|
new CallbackQueryMetadata.CallbackQueryMetadataBuilder()
|
||||||
|
.withTelegramChatId(1111111L)
|
||||||
|
.build());
|
||||||
|
callbackQueryContext2.save();
|
||||||
|
|
||||||
|
// I know I know, a future containing a future...but it is for testing purposes, otherwise we'd
|
||||||
|
// have to do some while/sleep thread hack which I find horrible to do
|
||||||
|
final CompletableFuture<
|
||||||
|
Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>
|
||||||
|
callBackFuture = new CompletableFuture<>();
|
||||||
|
batchBeanCleanerService.addListener(callBackFuture::complete);
|
||||||
|
|
||||||
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResultFuture =
|
||||||
|
showHelp.process(callbackQueryContext, update);
|
||||||
|
|
||||||
|
final Optional<BaseRequest<?, ?>> baseRequestOptional = gotResultFuture.get();
|
||||||
|
assertDoesNotThrow(baseRequestOptional::get);
|
||||||
|
|
||||||
|
final Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>
|
||||||
|
deletionCallback = callBackFuture.get();
|
||||||
|
|
||||||
|
assertEquals("5678", deletionCallback._1());
|
||||||
|
assertEquals(2, deletionCallback._3().get());
|
||||||
|
assertEquals(
|
||||||
|
0,
|
||||||
|
new QCallbackQueryContext().findCount(),
|
||||||
|
"The CallbackQuery context group has not been cleaned properly!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,314 @@
|
||||||
|
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||||
|
import com.github.polpetta.mezzotre.telegram.model.Help;
|
||||||
|
import com.github.polpetta.types.json.ChatContext;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||||
|
import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
|
||||||
|
import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.parallel.Execution;
|
||||||
|
import org.junit.jupiter.api.parallel.ExecutionMode;
|
||||||
|
|
||||||
|
@Execution(ExecutionMode.CONCURRENT)
|
||||||
|
class ShowHelpTest {
|
||||||
|
|
||||||
|
private static Gson gson;
|
||||||
|
private ShowHelp showHelp;
|
||||||
|
private Help fakeModelHelp;
|
||||||
|
private ChatUtil fakeChatUtil;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void beforeAll() {
|
||||||
|
gson = new Gson();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
|
||||||
|
fakeModelHelp = mock(Help.class);
|
||||||
|
fakeChatUtil = mock(ChatUtil.class);
|
||||||
|
final CallbackQueryContextCleaner fakeCallbackQueryContextCleaner =
|
||||||
|
mock(CallbackQueryContextCleaner.class);
|
||||||
|
|
||||||
|
showHelp =
|
||||||
|
new ShowHelp(
|
||||||
|
Executors.newSingleThreadExecutor(),
|
||||||
|
fakeModelHelp,
|
||||||
|
Collections.emptyMap(),
|
||||||
|
Collections.emptyMap(),
|
||||||
|
fakeChatUtil,
|
||||||
|
fakeCallbackQueryContextCleaner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUpdateChatContext() throws Exception {
|
||||||
|
final CallbackQueryContext fakeCallbackQueryContext = mock(CallbackQueryContext.class);
|
||||||
|
final Update update =
|
||||||
|
gson.fromJson(
|
||||||
|
"{\n"
|
||||||
|
+ " \"update_id\": 158712614,\n"
|
||||||
|
+ " \"callback_query\": {\n"
|
||||||
|
+ " \"id\": \"20496049451114620\",\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"is_bot\": false,\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"language_code\": \"en\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"message\": {\n"
|
||||||
|
+ " \"message_id\": 2723,\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 244745330,\n"
|
||||||
|
+ " \"is_bot\": true,\n"
|
||||||
|
+ " \"first_name\": \"Dev - DavideBot\",\n"
|
||||||
|
+ " \"username\": \"devdavidebot\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"date\": 1681218838,\n"
|
||||||
|
+ " \"chat\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"type\": \"private\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"text\": \"Hello xxxxxx! \uD83D\uDC4B\\n"
|
||||||
|
+ "\\n"
|
||||||
|
+ "This is Mezzotre, a simple bot focused on DnD content management! Please start"
|
||||||
|
+ " by choosing a language down below \uD83D\uDC47\",\n"
|
||||||
|
+ " \"entities\": [\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"type\": \"bold\",\n"
|
||||||
|
+ " \"offset\": 0,\n"
|
||||||
|
+ " \"length\": 16\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"type\": \"italic\",\n"
|
||||||
|
+ " \"offset\": 26,\n"
|
||||||
|
+ " \"length\": 8\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " ],\n"
|
||||||
|
+ " \"reply_markup\": {\n"
|
||||||
|
+ " \"inline_keyboard\": [\n"
|
||||||
|
+ " [\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"text\": \"English\",\n"
|
||||||
|
+ " \"callback_data\": \"9a64be11-d086-4bd9-859f-720c43dedcb5\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"text\": \"Italian\",\n"
|
||||||
|
+ " \"callback_data\": \"8768d660-f05f-4f4b-bda5-3451ab573d56\"\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class);
|
||||||
|
final TgChat fakeTgChat = mock(TgChat.class);
|
||||||
|
when(fakeTgChat.getId()).thenReturn(1111111L);
|
||||||
|
when(fakeTgChat.getChatContext()).thenReturn(new ChatContext());
|
||||||
|
when(fakeModelHelp.getMessage(eq(fakeTgChat), any())).thenReturn("doesn't matter");
|
||||||
|
when(fakeModelHelp.generateInlineKeyBoardButton(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||||
|
.thenReturn(new InlineKeyboardButton[] {});
|
||||||
|
when(fakeChatUtil.extractChat(eq(fakeCallbackQueryContext), eq(update)))
|
||||||
|
.thenReturn(Optional.of(fakeTgChat));
|
||||||
|
|
||||||
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResponseFuture =
|
||||||
|
showHelp.process(fakeCallbackQueryContext, update);
|
||||||
|
final Optional<BaseRequest<?, ?>> gotResponseOpt = gotResponseFuture.get();
|
||||||
|
|
||||||
|
verify(fakeChatUtil, times(1))
|
||||||
|
.updateChatContext(eq(fakeTgChat), eq("showHelp"), eq(0), eq(Collections.emptyMap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAddInlineKeyboardIfButtonsAreReturned() throws Exception {
|
||||||
|
final CallbackQueryContext fakeCallbackQueryContext = mock(CallbackQueryContext.class);
|
||||||
|
final Update update =
|
||||||
|
gson.fromJson(
|
||||||
|
"{\n"
|
||||||
|
+ " \"update_id\": 158712614,\n"
|
||||||
|
+ " \"callback_query\": {\n"
|
||||||
|
+ " \"id\": \"20496049451114620\",\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"is_bot\": false,\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"language_code\": \"en\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"message\": {\n"
|
||||||
|
+ " \"message_id\": 2723,\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 244745330,\n"
|
||||||
|
+ " \"is_bot\": true,\n"
|
||||||
|
+ " \"first_name\": \"Dev - DavideBot\",\n"
|
||||||
|
+ " \"username\": \"devdavidebot\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"date\": 1681218838,\n"
|
||||||
|
+ " \"chat\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"type\": \"private\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"text\": \"Hello xxxxxx! \uD83D\uDC4B\\n"
|
||||||
|
+ "\\n"
|
||||||
|
+ "This is Mezzotre, a simple bot focused on DnD content management! Please start"
|
||||||
|
+ " by choosing a language down below \uD83D\uDC47\",\n"
|
||||||
|
+ " \"entities\": [\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"type\": \"bold\",\n"
|
||||||
|
+ " \"offset\": 0,\n"
|
||||||
|
+ " \"length\": 16\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"type\": \"italic\",\n"
|
||||||
|
+ " \"offset\": 26,\n"
|
||||||
|
+ " \"length\": 8\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " ],\n"
|
||||||
|
+ " \"reply_markup\": {\n"
|
||||||
|
+ " \"inline_keyboard\": [\n"
|
||||||
|
+ " [\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"text\": \"English\",\n"
|
||||||
|
+ " \"callback_data\": \"9a64be11-d086-4bd9-859f-720c43dedcb5\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"text\": \"Italian\",\n"
|
||||||
|
+ " \"callback_data\": \"8768d660-f05f-4f4b-bda5-3451ab573d56\"\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class);
|
||||||
|
final TgChat fakeTgChat = mock(TgChat.class);
|
||||||
|
when(fakeTgChat.getId()).thenReturn(1111111L);
|
||||||
|
when(fakeTgChat.getChatContext()).thenReturn(new ChatContext());
|
||||||
|
when(fakeModelHelp.getMessage(eq(fakeTgChat), any())).thenReturn("doesn't matter");
|
||||||
|
when(fakeModelHelp.generateInlineKeyBoardButton(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||||
|
.thenReturn(new InlineKeyboardButton[] {new InlineKeyboardButton("example1")});
|
||||||
|
when(fakeChatUtil.extractChat(eq(fakeCallbackQueryContext), eq(update)))
|
||||||
|
.thenReturn(Optional.of(fakeTgChat));
|
||||||
|
|
||||||
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResponseFuture =
|
||||||
|
showHelp.process(fakeCallbackQueryContext, update);
|
||||||
|
final Optional<BaseRequest<?, ?>> gotResponseOpt = gotResponseFuture.get();
|
||||||
|
final BaseRequest<?, ?> gotResponse = gotResponseOpt.get();
|
||||||
|
|
||||||
|
final InlineKeyboardMarkup replyMarkup =
|
||||||
|
(InlineKeyboardMarkup) gotResponse.getParameters().get("reply_markup");
|
||||||
|
assertEquals(1, replyMarkup.inlineKeyboard()[0].length);
|
||||||
|
assertEquals("example1", replyMarkup.inlineKeyboard()[0][0].text());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotAddAnyKeyboardIfThereAreNoButtons() throws Exception {
|
||||||
|
final CallbackQueryContext fakeCallbackQueryContext = mock(CallbackQueryContext.class);
|
||||||
|
final Update update =
|
||||||
|
gson.fromJson(
|
||||||
|
"{\n"
|
||||||
|
+ " \"update_id\": 158712614,\n"
|
||||||
|
+ " \"callback_query\": {\n"
|
||||||
|
+ " \"id\": \"20496049451114620\",\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"is_bot\": false,\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"language_code\": \"en\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"message\": {\n"
|
||||||
|
+ " \"message_id\": 2723,\n"
|
||||||
|
+ " \"from\": {\n"
|
||||||
|
+ " \"id\": 244745330,\n"
|
||||||
|
+ " \"is_bot\": true,\n"
|
||||||
|
+ " \"first_name\": \"Dev - DavideBot\",\n"
|
||||||
|
+ " \"username\": \"devdavidebot\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"date\": 1681218838,\n"
|
||||||
|
+ " \"chat\": {\n"
|
||||||
|
+ " \"id\": 1111111,\n"
|
||||||
|
+ " \"type\": \"private\",\n"
|
||||||
|
+ " \"username\": \"Testusername\",\n"
|
||||||
|
+ " \"first_name\": \"Test Firstname\",\n"
|
||||||
|
+ " \"last_name\": \"Test Lastname\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"text\": \"Hello xxxxxx! \uD83D\uDC4B\\n"
|
||||||
|
+ "\\n"
|
||||||
|
+ "This is Mezzotre, a simple bot focused on DnD content management! Please start"
|
||||||
|
+ " by choosing a language down below \uD83D\uDC47\",\n"
|
||||||
|
+ " \"entities\": [\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"type\": \"bold\",\n"
|
||||||
|
+ " \"offset\": 0,\n"
|
||||||
|
+ " \"length\": 16\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"type\": \"italic\",\n"
|
||||||
|
+ " \"offset\": 26,\n"
|
||||||
|
+ " \"length\": 8\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " ],\n"
|
||||||
|
+ " \"reply_markup\": {\n"
|
||||||
|
+ " \"inline_keyboard\": [\n"
|
||||||
|
+ " [\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"text\": \"English\",\n"
|
||||||
|
+ " \"callback_data\": \"9a64be11-d086-4bd9-859f-720c43dedcb5\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " {\n"
|
||||||
|
+ " \"text\": \"Italian\",\n"
|
||||||
|
+ " \"callback_data\": \"8768d660-f05f-4f4b-bda5-3451ab573d56\"\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " ]\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ " }\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class);
|
||||||
|
final TgChat fakeTgChat = mock(TgChat.class);
|
||||||
|
when(fakeTgChat.getId()).thenReturn(1111111L);
|
||||||
|
when(fakeTgChat.getChatContext()).thenReturn(new ChatContext());
|
||||||
|
when(fakeModelHelp.getMessage(eq(fakeTgChat), any())).thenReturn("doesn't matter");
|
||||||
|
when(fakeModelHelp.generateInlineKeyBoardButton(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||||
|
.thenReturn(new InlineKeyboardButton[] {});
|
||||||
|
when(fakeChatUtil.extractChat(eq(fakeCallbackQueryContext), eq(update)))
|
||||||
|
.thenReturn(Optional.of(fakeTgChat));
|
||||||
|
|
||||||
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResponseFuture =
|
||||||
|
showHelp.process(fakeCallbackQueryContext, update);
|
||||||
|
final Optional<BaseRequest<?, ?>> gotResponseOpt = gotResponseFuture.get();
|
||||||
|
final BaseRequest<?, ?> gotResponse = gotResponseOpt.get();
|
||||||
|
final InlineKeyboardMarkup replyMarkup =
|
||||||
|
(InlineKeyboardMarkup) gotResponse.getParameters().get("reply_markup");
|
||||||
|
assertNull(replyMarkup);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||||
import com.github.polpetta.mezzotre.util.Clock;
|
import com.github.polpetta.mezzotre.util.Clock;
|
||||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||||
import com.github.polpetta.types.json.ChatContext;
|
import com.github.polpetta.types.json.ChatContext;
|
||||||
|
@ -172,10 +173,15 @@ class HelpIntegrationTest {
|
||||||
final com.github.polpetta.mezzotre.telegram.model.Help modelHelp =
|
final com.github.polpetta.mezzotre.telegram.model.Help modelHelp =
|
||||||
new com.github.polpetta.mezzotre.telegram.model.Help(
|
new com.github.polpetta.mezzotre.telegram.model.Help(
|
||||||
new TemplateContentGenerator(new LocalizedMessageFactory(velocityEngine)),
|
new TemplateContentGenerator(new LocalizedMessageFactory(velocityEngine)),
|
||||||
fakeClock,
|
|
||||||
new UUIDGenerator());
|
new UUIDGenerator());
|
||||||
|
|
||||||
help = new Help(Executors.newSingleThreadExecutor(), commands, events, modelHelp);
|
help =
|
||||||
|
new Help(
|
||||||
|
Executors.newSingleThreadExecutor(),
|
||||||
|
commands,
|
||||||
|
events,
|
||||||
|
modelHelp,
|
||||||
|
new ChatUtil(fakeClock));
|
||||||
|
|
||||||
final Update update =
|
final Update update =
|
||||||
gson.fromJson(
|
gson.fromJson(
|
||||||
|
@ -210,8 +216,8 @@ class HelpIntegrationTest {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Here is a list of what I can do:\n"
|
"Here is a list of what I can do:\n"
|
||||||
+ "\n"
|
+ "\n"
|
||||||
+ "* /a /b: Trigger this very bot\n"
|
+ "- /a /b: Trigger this very bot\n"
|
||||||
+ "* /different: Print the help message\n"
|
+ "- /different: Print the help message\n"
|
||||||
+ "\n"
|
+ "\n"
|
||||||
+ "You can do the same operations you'd do with the commands aforementioned by"
|
+ "You can do the same operations you'd do with the commands aforementioned by"
|
||||||
+ " selecting the corresponding button below \uD83D\uDC47",
|
+ " selecting the corresponding button below \uD83D\uDC47",
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
package com.github.polpetta.mezzotre.telegram.command;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||||
|
import com.github.polpetta.types.json.ChatContext;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||||
|
import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
|
||||||
|
import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.parallel.Execution;
|
||||||
|
import org.junit.jupiter.api.parallel.ExecutionMode;
|
||||||
|
|
||||||
|
@Execution(ExecutionMode.CONCURRENT)
|
||||||
|
class HelpTest {
|
||||||
|
|
||||||
|
private static Gson gson;
|
||||||
|
private com.github.polpetta.mezzotre.telegram.model.Help fakeModelHelp;
|
||||||
|
private ChatUtil fakeChatUtil;
|
||||||
|
private Help help;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void beforeAll() {
|
||||||
|
gson = new Gson();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
|
||||||
|
final Map<String, Processor> commands = Collections.emptyMap();
|
||||||
|
final Map<String, com.github.polpetta.mezzotre.telegram.callbackquery.Processor> events =
|
||||||
|
Collections.emptyMap();
|
||||||
|
|
||||||
|
fakeModelHelp = mock(com.github.polpetta.mezzotre.telegram.model.Help.class);
|
||||||
|
fakeChatUtil = mock(ChatUtil.class);
|
||||||
|
|
||||||
|
help =
|
||||||
|
new Help(
|
||||||
|
Executors.newSingleThreadExecutor(), commands, events, fakeModelHelp, fakeChatUtil);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUpdateChatContext() throws Exception {
|
||||||
|
final TgChat fakeTgChat = mock(TgChat.class);
|
||||||
|
when(fakeTgChat.getId()).thenReturn(1111111L);
|
||||||
|
when(fakeTgChat.getLocale()).thenReturn("en-US");
|
||||||
|
when(fakeTgChat.getChatContext()).thenReturn(new ChatContext());
|
||||||
|
when(fakeModelHelp.getMessage(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||||
|
.thenReturn("doesn't matter");
|
||||||
|
when(fakeModelHelp.generateInlineKeyBoardButton(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||||
|
.thenReturn(new InlineKeyboardButton[] {});
|
||||||
|
|
||||||
|
final Update update =
|
||||||
|
gson.fromJson(
|
||||||
|
"{\n"
|
||||||
|
+ "\"update_id\":10000,\n"
|
||||||
|
+ "\"message\":{\n"
|
||||||
|
+ " \"date\":1441645532,\n"
|
||||||
|
+ " \"chat\":{\n"
|
||||||
|
+ " \"last_name\":\"Test Lastname\",\n"
|
||||||
|
+ " \"id\":1111111,\n"
|
||||||
|
+ " \"type\": \"private\",\n"
|
||||||
|
+ " \"first_name\":\"Test Firstname\",\n"
|
||||||
|
+ " \"username\":\"Testusername\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"message_id\":1365,\n"
|
||||||
|
+ " \"from\":{\n"
|
||||||
|
+ " \"last_name\":\"Test Lastname\",\n"
|
||||||
|
+ " \"id\":1111111,\n"
|
||||||
|
+ " \"first_name\":\"Test Firstname\",\n"
|
||||||
|
+ " \"username\":\"Testusername\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"text\":\"/help\"\n"
|
||||||
|
+ "}\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class);
|
||||||
|
|
||||||
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResponseFuture =
|
||||||
|
help.process(fakeTgChat, update);
|
||||||
|
assertDoesNotThrow(() -> gotResponseFuture.get());
|
||||||
|
verify(fakeChatUtil, times(1))
|
||||||
|
.updateChatContext(eq(fakeTgChat), eq("/help"), eq(0), eq(Collections.emptyMap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAppendKeyboardIfButtonsAreReturned() throws Exception {
|
||||||
|
final TgChat fakeTgChat = mock(TgChat.class);
|
||||||
|
when(fakeTgChat.getId()).thenReturn(1111111L);
|
||||||
|
when(fakeTgChat.getLocale()).thenReturn("en-US");
|
||||||
|
when(fakeTgChat.getChatContext()).thenReturn(new ChatContext());
|
||||||
|
when(fakeModelHelp.getMessage(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||||
|
.thenReturn("doesn't matter");
|
||||||
|
when(fakeModelHelp.generateInlineKeyBoardButton(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||||
|
.thenReturn(new InlineKeyboardButton[] {new InlineKeyboardButton("example1")});
|
||||||
|
|
||||||
|
final Update update =
|
||||||
|
gson.fromJson(
|
||||||
|
"{\n"
|
||||||
|
+ "\"update_id\":10000,\n"
|
||||||
|
+ "\"message\":{\n"
|
||||||
|
+ " \"date\":1441645532,\n"
|
||||||
|
+ " \"chat\":{\n"
|
||||||
|
+ " \"last_name\":\"Test Lastname\",\n"
|
||||||
|
+ " \"id\":1111111,\n"
|
||||||
|
+ " \"type\": \"private\",\n"
|
||||||
|
+ " \"first_name\":\"Test Firstname\",\n"
|
||||||
|
+ " \"username\":\"Testusername\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"message_id\":1365,\n"
|
||||||
|
+ " \"from\":{\n"
|
||||||
|
+ " \"last_name\":\"Test Lastname\",\n"
|
||||||
|
+ " \"id\":1111111,\n"
|
||||||
|
+ " \"first_name\":\"Test Firstname\",\n"
|
||||||
|
+ " \"username\":\"Testusername\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"text\":\"/help\"\n"
|
||||||
|
+ "}\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class);
|
||||||
|
|
||||||
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResponseFuture =
|
||||||
|
help.process(fakeTgChat, update);
|
||||||
|
final Optional<BaseRequest<?, ?>> gotResponseOpt = gotResponseFuture.get();
|
||||||
|
final BaseRequest<?, ?> gotResponse = gotResponseOpt.get();
|
||||||
|
final InlineKeyboardMarkup replyMarkup =
|
||||||
|
(InlineKeyboardMarkup) gotResponse.getParameters().get("reply_markup");
|
||||||
|
assertEquals(1, replyMarkup.inlineKeyboard()[0].length);
|
||||||
|
assertEquals("example1", replyMarkup.inlineKeyboard()[0][0].text());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotAppendKeyboardIfNoButtonIsPresent() throws Exception {
|
||||||
|
final TgChat fakeTgChat = mock(TgChat.class);
|
||||||
|
when(fakeTgChat.getId()).thenReturn(1111111L);
|
||||||
|
when(fakeTgChat.getLocale()).thenReturn("en-US");
|
||||||
|
when(fakeTgChat.getChatContext()).thenReturn(new ChatContext());
|
||||||
|
when(fakeModelHelp.getMessage(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||||
|
.thenReturn("doesn't matter");
|
||||||
|
when(fakeModelHelp.generateInlineKeyBoardButton(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||||
|
.thenReturn(new InlineKeyboardButton[] {});
|
||||||
|
|
||||||
|
final Update update =
|
||||||
|
gson.fromJson(
|
||||||
|
"{\n"
|
||||||
|
+ "\"update_id\":10000,\n"
|
||||||
|
+ "\"message\":{\n"
|
||||||
|
+ " \"date\":1441645532,\n"
|
||||||
|
+ " \"chat\":{\n"
|
||||||
|
+ " \"last_name\":\"Test Lastname\",\n"
|
||||||
|
+ " \"id\":1111111,\n"
|
||||||
|
+ " \"type\": \"private\",\n"
|
||||||
|
+ " \"first_name\":\"Test Firstname\",\n"
|
||||||
|
+ " \"username\":\"Testusername\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"message_id\":1365,\n"
|
||||||
|
+ " \"from\":{\n"
|
||||||
|
+ " \"last_name\":\"Test Lastname\",\n"
|
||||||
|
+ " \"id\":1111111,\n"
|
||||||
|
+ " \"first_name\":\"Test Firstname\",\n"
|
||||||
|
+ " \"username\":\"Testusername\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"text\":\"/help\"\n"
|
||||||
|
+ "}\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class);
|
||||||
|
|
||||||
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResponseFuture =
|
||||||
|
help.process(fakeTgChat, update);
|
||||||
|
final Optional<BaseRequest<?, ?>> gotResponseOpt = gotResponseFuture.get();
|
||||||
|
final BaseRequest<?, ?> gotResponse = gotResponseOpt.get();
|
||||||
|
final InlineKeyboardMarkup replyMarkup =
|
||||||
|
(InlineKeyboardMarkup) gotResponse.getParameters().get("reply_markup");
|
||||||
|
assertNull(replyMarkup);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||||
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||||
import com.github.polpetta.mezzotre.util.Clock;
|
import com.github.polpetta.mezzotre.util.Clock;
|
||||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||||
import com.github.polpetta.types.json.ChatContext;
|
import com.github.polpetta.types.json.ChatContext;
|
||||||
|
@ -74,8 +75,8 @@ class StartIntegrationTest {
|
||||||
Executors.newSingleThreadExecutor(),
|
Executors.newSingleThreadExecutor(),
|
||||||
log,
|
log,
|
||||||
fakeUUIDGenerator,
|
fakeUUIDGenerator,
|
||||||
fakeClock,
|
"Mezzotre",
|
||||||
"Mezzotre");
|
new ChatUtil(fakeClock));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -122,7 +123,7 @@ class StartIntegrationTest {
|
||||||
assertInstanceOf(SendMessage.class, gotMessage);
|
assertInstanceOf(SendMessage.class, gotMessage);
|
||||||
final String message = (String) gotMessage.getParameters().get("text");
|
final String message = (String) gotMessage.getParameters().get("text");
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"**Hello Test Firstname! \uD83D\uDC4B**\n\n"
|
"*Hello Test Firstname! \uD83D\uDC4B*\n\n"
|
||||||
+ "This is _Mezzotre_, a simple bot focused on DnD content management! Please start by"
|
+ "This is _Mezzotre_, a simple bot focused on DnD content management! Please start by"
|
||||||
+ " choosing a language down below \uD83D\uDC47",
|
+ " choosing a language down below \uD83D\uDC47",
|
||||||
message);
|
message);
|
||||||
|
@ -180,7 +181,7 @@ class StartIntegrationTest {
|
||||||
assertInstanceOf(SendMessage.class, gotMessage);
|
assertInstanceOf(SendMessage.class, gotMessage);
|
||||||
final String message = (String) gotMessage.getParameters().get("text");
|
final String message = (String) gotMessage.getParameters().get("text");
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"**Hello Test Firstname! \uD83D\uDC4B**\n\n"
|
"*Hello Test Firstname! \uD83D\uDC4B*\n\n"
|
||||||
+ "This is _Mezzotre_, a simple bot focused on DnD content management! Please start by"
|
+ "This is _Mezzotre_, a simple bot focused on DnD content management! Please start by"
|
||||||
+ " choosing a language down below \uD83D\uDC47",
|
+ " choosing a language down below \uD83D\uDC47",
|
||||||
message);
|
message);
|
||||||
|
|
Loading…
Reference in New Issue