feat: first implementation of help command, wip
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
00bac97e59
commit
c4db3be4cb
|
@ -36,22 +36,22 @@ curl -F "url=https://example.com/api/tg" \
|
||||||
Build is achieved through Maven. To build a `jar` run:
|
Build is achieved through Maven. To build a `jar` run:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
mvn package -DskipTests=true -Dmaven.test.skip=true -Dmaven.site.skip=true -Dmaven.javadoc.skip=true
|
./mvnw package -DskipTests=true -Dmaven.test.skip=true -Dmaven.site.skip=true -Dmaven.javadoc.skip=true
|
||||||
```
|
```
|
||||||
|
|
||||||
In the `target/` folder wou will find an uber-jar and optionally the possibility to run it via a script and to setup
|
In the `target/` folder you will find an uber-jar and optionally the possibility to run it via a script and to setup
|
||||||
auto-startup via systemd or openrc units.
|
auto-startup via systemd or openrc units.
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
|
||||||
### Automatic testing
|
### Automatic testing
|
||||||
|
|
||||||
You can simply run tests with `mvn test`. This will run `UT` and `IT` tests together.
|
You can simply run tests with `./mvnw test`. This will run `UT` and `IT` tests together.
|
||||||
|
|
||||||
### Manual testing
|
### Manual testing
|
||||||
|
|
||||||
For a manual approach, just open a terminal and type `mvn jooby:run`. Assuming you have a database locally available
|
For a manual approach, just open a terminal and type `mvn jooby:run`. Assuming you have a database locally available
|
||||||
(check out [application.conf](conf/application.conf)) and a valid Telegram token set (maybe as enviroment variable) you
|
(check out [application.conf](conf/application.conf)) and a valid Telegram token set (maybe as environment variable) you
|
||||||
can develop and see live changes of your Mezzotre on the fly. Finally, by using Postman, you can simulate incoming
|
can develop and see live changes of your Mezzotre on the fly. Finally, by using Postman, you can simulate incoming
|
||||||
Telegram events.
|
Telegram events.
|
||||||
|
|
||||||
|
|
7
pom.xml
7
pom.xml
|
@ -41,6 +41,7 @@
|
||||||
<jsonschema2pojo.version>1.1.1</jsonschema2pojo.version>
|
<jsonschema2pojo.version>1.1.1</jsonschema2pojo.version>
|
||||||
<jackson-databind.version>2.13.3</jackson-databind.version>
|
<jackson-databind.version>2.13.3</jackson-databind.version>
|
||||||
<junit-jupiter-params.version>5.9.1</junit-jupiter-params.version>
|
<junit-jupiter-params.version>5.9.1</junit-jupiter-params.version>
|
||||||
|
<google-guice.version>5.1.0</google-guice.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -86,6 +87,12 @@
|
||||||
<artifactId>jooby-guice</artifactId>
|
<artifactId>jooby-guice</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.inject.extensions</groupId>
|
||||||
|
<artifactId>guice-assistedinject</artifactId>
|
||||||
|
<version>${google-guice.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.swagger.core.v3</groupId>
|
<groupId>io.swagger.core.v3</groupId>
|
||||||
<artifactId>swagger-annotations</artifactId>
|
<artifactId>swagger-annotations</artifactId>
|
||||||
|
|
|
@ -3,9 +3,14 @@ package com.github.polpetta.mezzotre;
|
||||||
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 javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
public class InjectionModule extends AbstractModule {
|
public class InjectionModule extends AbstractModule {
|
||||||
|
|
||||||
|
// In the future we can get this name from mvn, by now it is good as it is
|
||||||
|
public static final String APPLICATION_NAME = "Mezzotre";
|
||||||
private final Jooby jooby;
|
private final Jooby jooby;
|
||||||
|
|
||||||
public InjectionModule(Jooby jooby) {
|
public InjectionModule(Jooby jooby) {
|
||||||
|
@ -16,4 +21,11 @@ public class InjectionModule extends AbstractModule {
|
||||||
public Logger getLogger() {
|
public Logger getLogger() {
|
||||||
return jooby.getLog();
|
return jooby.getLog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Named("applicationName")
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
public String getAppName() {
|
||||||
|
return APPLICATION_NAME;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.github.polpetta.mezzotre.i18n;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.apache.velocity.VelocityContext;
|
||||||
|
import org.apache.velocity.tools.ToolManager;
|
||||||
|
import org.apache.velocity.util.StringBuilderWriter;
|
||||||
|
|
||||||
|
public class TemplateContentGenerator {
|
||||||
|
|
||||||
|
private final LocalizedMessageFactory localizedMessageFactory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public TemplateContentGenerator(LocalizedMessageFactory localizedMessageFactory) {
|
||||||
|
this.localizedMessageFactory = localizedMessageFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String mergeTemplate(
|
||||||
|
Consumer<VelocityContext> velocityContextConsumer,
|
||||||
|
String localeAsString,
|
||||||
|
String templateName) {
|
||||||
|
final Locale locale = Locale.forLanguageTag(localeAsString);
|
||||||
|
final ToolManager toolManager = localizedMessageFactory.createVelocityToolManager(locale);
|
||||||
|
final VelocityContext velocityContext = new VelocityContext(toolManager.createContext());
|
||||||
|
|
||||||
|
velocityContextConsumer.accept(velocityContext);
|
||||||
|
|
||||||
|
final StringBuilder content = new StringBuilder();
|
||||||
|
final StringBuilderWriter stringBuilderWriter = new StringBuilderWriter(content);
|
||||||
|
|
||||||
|
toolManager
|
||||||
|
.getVelocityEngine()
|
||||||
|
.mergeTemplate(
|
||||||
|
templateName, StandardCharsets.UTF_8.name(), velocityContext, stringBuilderWriter);
|
||||||
|
|
||||||
|
stringBuilderWriter.close();
|
||||||
|
return content.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString(Locale locale, String key) {
|
||||||
|
return localizedMessageFactory.createResourceBundle(locale).getString(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString(String localeAsString, String key) {
|
||||||
|
return getString(Locale.forLanguageTag(localeAsString), key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||||
|
|
||||||
import com.github.polpetta.mezzotre.i18n.LocalizedMessageFactory;
|
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.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;
|
||||||
|
@ -14,9 +14,6 @@ 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 io.vavr.control.Try;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
@ -24,9 +21,6 @@ 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;
|
import javax.inject.Singleton;
|
||||||
import org.apache.velocity.VelocityContext;
|
|
||||||
import org.apache.velocity.tools.ToolManager;
|
|
||||||
import org.apache.velocity.util.StringBuilderWriter;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,7 +35,7 @@ public class SelectLanguageTutorial implements Processor {
|
||||||
|
|
||||||
public static final String EVENT_NAME = "selectLanguageTutorial";
|
public static final String EVENT_NAME = "selectLanguageTutorial";
|
||||||
private final Executor threadPool;
|
private final Executor threadPool;
|
||||||
private final LocalizedMessageFactory localizedMessageFactory;
|
private final TemplateContentGenerator templateContentGenerator;
|
||||||
private final Logger log;
|
private final Logger log;
|
||||||
private final UUIDGenerator uuidGenerator;
|
private final UUIDGenerator uuidGenerator;
|
||||||
|
|
||||||
|
@ -89,11 +83,11 @@ public class SelectLanguageTutorial implements Processor {
|
||||||
@Inject
|
@Inject
|
||||||
public SelectLanguageTutorial(
|
public SelectLanguageTutorial(
|
||||||
@Named("eventThreadPool") Executor threadPool,
|
@Named("eventThreadPool") Executor threadPool,
|
||||||
LocalizedMessageFactory localizedMessageFactory,
|
TemplateContentGenerator templateContentGenerator,
|
||||||
Logger log,
|
Logger log,
|
||||||
UUIDGenerator uuidGenerator) {
|
UUIDGenerator uuidGenerator) {
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
this.localizedMessageFactory = localizedMessageFactory;
|
this.templateContentGenerator = templateContentGenerator;
|
||||||
this.log = log;
|
this.log = log;
|
||||||
this.uuidGenerator = uuidGenerator;
|
this.uuidGenerator = uuidGenerator;
|
||||||
}
|
}
|
||||||
|
@ -148,32 +142,11 @@ public class SelectLanguageTutorial implements Processor {
|
||||||
// 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 -> {
|
||||||
final String message =
|
final String message =
|
||||||
Try.of(
|
templateContentGenerator.mergeTemplate(
|
||||||
() -> {
|
velocityContext ->
|
||||||
final Locale locale = Locale.forLanguageTag(tgChat.getLocale());
|
velocityContext.put("hasHelpBeenShown", tgChat.getHasHelpBeenShown()),
|
||||||
final ToolManager toolManager =
|
tgChat.getLocale(),
|
||||||
localizedMessageFactory.createVelocityToolManager(locale);
|
"/template/telegram/selectLanguageTutorial.vm");
|
||||||
final VelocityContext velocityContext =
|
|
||||||
new VelocityContext(toolManager.createContext());
|
|
||||||
|
|
||||||
velocityContext.put("hasHelpBeenShown", tgChat.getHasHelpBeenShown());
|
|
||||||
|
|
||||||
final StringBuilder content = new StringBuilder();
|
|
||||||
final StringBuilderWriter stringBuilderWriter =
|
|
||||||
new StringBuilderWriter(content);
|
|
||||||
|
|
||||||
toolManager
|
|
||||||
.getVelocityEngine()
|
|
||||||
.mergeTemplate(
|
|
||||||
"/template/callbackQuery/selectLanguageTutorial.vm",
|
|
||||||
StandardCharsets.UTF_8.name(),
|
|
||||||
velocityContext,
|
|
||||||
stringBuilderWriter);
|
|
||||||
|
|
||||||
stringBuilderWriter.close();
|
|
||||||
return content.toString();
|
|
||||||
})
|
|
||||||
.get();
|
|
||||||
|
|
||||||
log.trace("SelectLanguageTutorial event - message to send back: " + message);
|
log.trace("SelectLanguageTutorial event - message to send back: " + message);
|
||||||
|
|
||||||
|
@ -193,9 +166,7 @@ public class SelectLanguageTutorial implements Processor {
|
||||||
if (!tgChat.getHasHelpBeenShown()) {
|
if (!tgChat.getHasHelpBeenShown()) {
|
||||||
// Add a button to show all the possible commands
|
// Add a button to show all the possible commands
|
||||||
final String showMeTutorialString =
|
final String showMeTutorialString =
|
||||||
localizedMessageFactory
|
templateContentGenerator.getString(tgChat.getLocale(), "button.showMeTutorial");
|
||||||
.createResourceBundle(Locale.forLanguageTag(tgChat.getLocale()))
|
|
||||||
.getString("button.showMeTutorial");
|
|
||||||
|
|
||||||
final CallbackQueryMetadata callbackQueryMetadata =
|
final CallbackQueryMetadata callbackQueryMetadata =
|
||||||
new CallbackQueryMetadata.CallbackQueryMetadataBuilder()
|
new CallbackQueryMetadata.CallbackQueryMetadataBuilder()
|
||||||
|
|
|
@ -5,16 +5,21 @@ import com.github.polpetta.mezzotre.telegram.callbackquery.SelectLanguageTutoria
|
||||||
import com.github.polpetta.mezzotre.telegram.callbackquery.ShowHelp;
|
import com.github.polpetta.mezzotre.telegram.callbackquery.ShowHelp;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import java.util.Set;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
public class CallbackQuery extends AbstractModule {
|
public class CallbackQuery extends AbstractModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@Named("eventProcessors")
|
@Named("eventProcessors")
|
||||||
public Set<Processor> getEventProcessor(
|
public Map<String, Processor> getEventProcessor(
|
||||||
SelectLanguageTutorial selectLanguageTutorial, ShowHelp showHelp) {
|
SelectLanguageTutorial selectLanguageTutorial, ShowHelp showHelp) {
|
||||||
return Set.of(selectLanguageTutorial, showHelp);
|
final HashMap<String, Processor> commandMap = new HashMap<>();
|
||||||
|
commandMap.put(selectLanguageTutorial.getEventName(), selectLanguageTutorial);
|
||||||
|
commandMap.put(showHelp.getEventName(), showHelp);
|
||||||
|
return commandMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
package com.github.polpetta.mezzotre.telegram.command;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
|
import com.github.polpetta.mezzotre.util.Clock;
|
||||||
|
import com.github.polpetta.types.json.ChatContext;
|
||||||
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
|
import com.pengrad.telegrambot.request.SendMessage;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
public class Help implements Processor {
|
||||||
|
|
||||||
|
private static final String TRIGGERING_STAGING_NAME = "/help";
|
||||||
|
|
||||||
|
private final TemplateContentGenerator templateContentGenerator;
|
||||||
|
private final Executor threadPool;
|
||||||
|
private final Clock clock;
|
||||||
|
private final Map<String, Processor> tgCommandProcessors;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public Help(
|
||||||
|
TemplateContentGenerator templateContentGenerator,
|
||||||
|
@Named("eventThreadPool") Executor threadPool,
|
||||||
|
Clock clock,
|
||||||
|
@Named("commandProcessor") Map<String, Processor> tgCommandProcessors) {
|
||||||
|
this.templateContentGenerator = templateContentGenerator;
|
||||||
|
this.threadPool = threadPool;
|
||||||
|
this.clock = clock;
|
||||||
|
this.tgCommandProcessors = tgCommandProcessors;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getTriggerKeywords() {
|
||||||
|
return Set.of(TRIGGERING_STAGING_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Optional<BaseRequest<?, ?>>> process(TgChat chat, Update update) {
|
||||||
|
return CompletableFuture.supplyAsync(
|
||||||
|
() -> {
|
||||||
|
final String message =
|
||||||
|
templateContentGenerator.mergeTemplate(
|
||||||
|
velocityContext -> {
|
||||||
|
velocityContext.put(
|
||||||
|
"commands",
|
||||||
|
tgCommandProcessors.values().stream()
|
||||||
|
.distinct()
|
||||||
|
.map(
|
||||||
|
p ->
|
||||||
|
Pair.of(
|
||||||
|
p.getTriggerKeywords().stream()
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList()),
|
||||||
|
p.getLocaleDescriptionKeyword()))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
},
|
||||||
|
chat.getLocale(),
|
||||||
|
"template/telegram/help.vm");
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// FIXME put all the buttons here
|
||||||
|
|
||||||
|
return Optional.of(new SendMessage(chat.getId(), message));
|
||||||
|
},
|
||||||
|
threadPool);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.github.polpetta.mezzotre.telegram.command;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
|
import com.pengrad.telegrambot.request.SendMessage;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class NotFound implements Processor {
|
||||||
|
|
||||||
|
private final String commandName;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public NotFound(@Assisted String commandName) {
|
||||||
|
this.commandName = commandName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getTriggerKeywords() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Optional<BaseRequest<?, ?>>> process(TgChat chat, Update update) {
|
||||||
|
// FIXME complete it with: localization, callbackQuery to show help message
|
||||||
|
return CompletableFuture.completedFuture(update)
|
||||||
|
.thenApply(
|
||||||
|
ignored ->
|
||||||
|
Optional.of(
|
||||||
|
new SendMessage(chat.getId(), "Command " + commandName + " is not valid")));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.github.polpetta.mezzotre.telegram.command;
|
||||||
|
|
||||||
|
public interface NotFoundFactory {
|
||||||
|
NotFound create(String commandName);
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
import com.pengrad.telegrambot.model.Update;
|
import com.pengrad.telegrambot.model.Update;
|
||||||
import com.pengrad.telegrambot.request.BaseRequest;
|
import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,7 +22,7 @@ public interface Processor {
|
||||||
*
|
*
|
||||||
* @return a {@link String} providing the keyword to trigger the current {@link Processor}
|
* @return a {@link String} providing the keyword to trigger the current {@link Processor}
|
||||||
*/
|
*/
|
||||||
String getTriggerKeyword();
|
Set<String> getTriggerKeywords();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the current update
|
* Process the current update
|
||||||
|
@ -31,4 +32,17 @@ public interface Processor {
|
||||||
* @return a {@link CompletableFuture} with the result of the computation
|
* @return a {@link CompletableFuture} with the result of the computation
|
||||||
*/
|
*/
|
||||||
CompletableFuture<Optional<BaseRequest<?, ?>>> process(TgChat chat, Update update);
|
CompletableFuture<Optional<BaseRequest<?, ?>>> process(TgChat chat, Update update);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide the key to retrieve the current processor descriptor. This is useful for help messages
|
||||||
|
* or to provide a user with a description of what this command does. Whilst a default
|
||||||
|
* implementation is provided, we suggest to rename it accordingly since the name generation is
|
||||||
|
* based on reflection and can be fragile and subject to code refactor changes.
|
||||||
|
*
|
||||||
|
* @return a {@link String} with the name of the localization key containing the command
|
||||||
|
* description
|
||||||
|
*/
|
||||||
|
default String getLocaleDescriptionKeyword() {
|
||||||
|
return this.getClass().getName().toLowerCase() + ".cmdDescription";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@ import com.google.inject.Inject;
|
||||||
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.request.BaseRequest;
|
import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
@ -21,15 +21,18 @@ import javax.inject.Named;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class Router {
|
public class Router {
|
||||||
|
|
||||||
private final Set<Processor> tgCommandProcessors;
|
private final Map<String, Processor> tgCommandProcessors;
|
||||||
private final Executor threadPool;
|
private final Executor threadPool;
|
||||||
|
private final NotFoundFactory notFoundFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Router(
|
public Router(
|
||||||
@Named("commandProcessor") Set<Processor> tgCommandProcessors,
|
@Named("commandProcessor") Map<String, Processor> tgCommandProcessors,
|
||||||
@Named("eventThreadPool") Executor threadPool) {
|
@Named("eventThreadPool") Executor threadPool,
|
||||||
|
NotFoundFactory notFoundFactory) {
|
||||||
this.tgCommandProcessors = tgCommandProcessors;
|
this.tgCommandProcessors = tgCommandProcessors;
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
|
this.notFoundFactory = notFoundFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,15 +61,13 @@ public class Router {
|
||||||
.map(list -> list[0])
|
.map(list -> list[0])
|
||||||
.filter(wannabeCommand -> wannabeCommand.startsWith("/"))
|
.filter(wannabeCommand -> wannabeCommand.startsWith("/"))
|
||||||
.or(() -> Optional.ofNullable(chat.getChatContext().getStage()))
|
.or(() -> Optional.ofNullable(chat.getChatContext().getStage()))
|
||||||
.flatMap(
|
.map(
|
||||||
command ->
|
command ->
|
||||||
tgCommandProcessors.stream()
|
tgCommandProcessors
|
||||||
// FIXME this is fucking stupid, why iterate over, just use a map!
|
.getOrDefault(command, notFoundFactory.create(command))
|
||||||
// Make mapping at startup then we're gucci for the rest of the run
|
.process(chat, update))
|
||||||
.filter(ex -> ex.getTriggerKeyword().equals(command))
|
// This should never happen
|
||||||
.findAny())
|
.orElse(CompletableFuture.failedFuture(new IllegalStateException())),
|
||||||
.map(executor -> executor.process(chat, update))
|
|
||||||
.orElse(CompletableFuture.failedFuture(new CommandNotFoundException())),
|
|
||||||
threadPool);
|
threadPool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.github.polpetta.mezzotre.telegram.command;
|
package com.github.polpetta.mezzotre.telegram.command;
|
||||||
|
|
||||||
import com.github.polpetta.mezzotre.i18n.LocalizedMessageFactory;
|
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.telegram.callbackquery.SelectLanguageTutorial;
|
import com.github.polpetta.mezzotre.telegram.callbackquery.SelectLanguageTutorial;
|
||||||
|
@ -15,17 +15,13 @@ 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 io.vavr.control.Try;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
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 org.apache.velocity.VelocityContext;
|
|
||||||
import org.apache.velocity.tools.ToolManager;
|
|
||||||
import org.apache.velocity.util.StringBuilderWriter;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,29 +33,35 @@ import org.slf4j.Logger;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class Start implements Processor {
|
public class Start implements Processor {
|
||||||
|
|
||||||
|
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 Clock clock;
|
||||||
private final LocalizedMessageFactory localizedMessageFactory;
|
private final String applicationName;
|
||||||
|
|
||||||
|
private final TemplateContentGenerator templateContentGenerator;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Start(
|
public Start(
|
||||||
LocalizedMessageFactory localizedMessageFactory,
|
TemplateContentGenerator templateContentGenerator,
|
||||||
@Named("eventThreadPool") Executor threadPool,
|
@Named("eventThreadPool") Executor threadPool,
|
||||||
Logger log,
|
Logger log,
|
||||||
UUIDGenerator uuidGenerator,
|
UUIDGenerator uuidGenerator,
|
||||||
Clock clock) {
|
Clock clock,
|
||||||
this.localizedMessageFactory = localizedMessageFactory;
|
@Named("applicationName") String applicationName) {
|
||||||
|
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.clock = clock;
|
||||||
|
this.applicationName = applicationName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTriggerKeyword() {
|
public Set<String> getTriggerKeywords() {
|
||||||
return "/start";
|
return Set.of(TRIGGERING_STAGING_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -73,36 +75,19 @@ public class Start implements Processor {
|
||||||
3 - Reply to Telegram
|
3 - Reply to Telegram
|
||||||
*/
|
*/
|
||||||
final String message =
|
final String message =
|
||||||
Try.of(
|
templateContentGenerator.mergeTemplate(
|
||||||
() -> {
|
velocityContext -> {
|
||||||
final Locale locale = Locale.forLanguageTag(chat.getLocale());
|
velocityContext.put("firstName", update.message().chat().firstName());
|
||||||
final ToolManager toolManager =
|
// FIXME add some very cool markdown formatter instead of concatenating stuff
|
||||||
localizedMessageFactory.createVelocityToolManager(locale);
|
// this way
|
||||||
final VelocityContext context =
|
velocityContext.put("programName", "_" + applicationName + "_");
|
||||||
new VelocityContext(toolManager.createContext());
|
},
|
||||||
context.put("firstName", update.message().chat().firstName());
|
chat.getLocale(),
|
||||||
context.put("programName", "_Mezzotre_");
|
"template/telegram/start.vm");
|
||||||
|
|
||||||
final StringBuilder content = new StringBuilder();
|
|
||||||
final StringBuilderWriter stringBuilderWriter =
|
|
||||||
new StringBuilderWriter(content);
|
|
||||||
|
|
||||||
toolManager
|
|
||||||
.getVelocityEngine()
|
|
||||||
.mergeTemplate(
|
|
||||||
"template/command/start.0.vm",
|
|
||||||
StandardCharsets.UTF_8.name(),
|
|
||||||
context,
|
|
||||||
stringBuilderWriter);
|
|
||||||
|
|
||||||
stringBuilderWriter.close();
|
|
||||||
return content.toString();
|
|
||||||
})
|
|
||||||
.get();
|
|
||||||
log.trace("Start command - message to send back: " + message);
|
log.trace("Start command - message to send back: " + message);
|
||||||
|
|
||||||
final ChatContext chatContext = chat.getChatContext();
|
final ChatContext chatContext = chat.getChatContext();
|
||||||
chatContext.setStage(getTriggerKeyword());
|
chatContext.setStage(TRIGGERING_STAGING_NAME);
|
||||||
chatContext.setStep(0);
|
chatContext.setStep(0);
|
||||||
chatContext.setPreviousMessageUnixTimestampInSeconds(clock.now());
|
chatContext.setPreviousMessageUnixTimestampInSeconds(clock.now());
|
||||||
chat.setChatContext(chatContext);
|
chat.setChatContext(chatContext);
|
||||||
|
@ -135,13 +120,9 @@ public class Start implements Processor {
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
final String englishButton =
|
final String englishButton =
|
||||||
localizedMessageFactory
|
templateContentGenerator.getString(Locale.US, "changeLanguage.english");
|
||||||
.createResourceBundle(Locale.US)
|
|
||||||
.getString("changeLanguage.english");
|
|
||||||
final String italianButton =
|
final String italianButton =
|
||||||
localizedMessageFactory
|
templateContentGenerator.getString(Locale.ITALY, "changeLanguage.italian");
|
||||||
.createResourceBundle(Locale.ITALY)
|
|
||||||
.getString("changeLanguage.italian");
|
|
||||||
|
|
||||||
final SendMessage messageToSend =
|
final SendMessage messageToSend =
|
||||||
new SendMessage(chat.getId(), message)
|
new SendMessage(chat.getId(), message)
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
package com.github.polpetta.mezzotre.telegram.command.di;
|
package com.github.polpetta.mezzotre.telegram.command.di;
|
||||||
|
|
||||||
import com.github.polpetta.mezzotre.telegram.command.Processor;
|
import com.github.polpetta.mezzotre.telegram.command.*;
|
||||||
import com.github.polpetta.mezzotre.telegram.command.Start;
|
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import java.util.Set;
|
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import org.apache.velocity.app.VelocityEngine;
|
import org.apache.velocity.app.VelocityEngine;
|
||||||
|
@ -12,11 +13,37 @@ import org.apache.velocity.runtime.RuntimeConstants;
|
||||||
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
|
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
|
||||||
|
|
||||||
public class Command extends AbstractModule {
|
public class Command extends AbstractModule {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
super.configure();
|
||||||
|
|
||||||
|
install(
|
||||||
|
new FactoryModuleBuilder()
|
||||||
|
.implement(Processor.class, NotFound.class)
|
||||||
|
.build(NotFoundFactory.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Processor> mapForProcessor(Processor processor) {
|
||||||
|
final HashMap<String, Processor> commandMap = new HashMap<>();
|
||||||
|
processor
|
||||||
|
.getTriggerKeywords()
|
||||||
|
.forEach(
|
||||||
|
keyword -> {
|
||||||
|
commandMap.put(keyword, processor);
|
||||||
|
});
|
||||||
|
return commandMap;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@Named("commandProcessor")
|
@Named("commandProcessor")
|
||||||
public Set<Processor> getCommandProcessor(Start start) {
|
public Map<String, Processor> getCommandProcessor(Start start, Help help) {
|
||||||
return Set.of(start);
|
final HashMap<String, Processor> commandMap = new HashMap<>();
|
||||||
|
commandMap.putAll(mapForProcessor(start));
|
||||||
|
commandMap.putAll(mapForProcessor(help));
|
||||||
|
|
||||||
|
return commandMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.github.polpetta.mezzotre.telegram.model;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.i18n.LocalizedMessageFactory;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class Help {
|
||||||
|
|
||||||
|
private final LocalizedMessageFactory localizedMessageFactory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public Help(LocalizedMessageFactory localizedMessageFactory) {
|
||||||
|
this.localizedMessageFactory = localizedMessageFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generateErrorMessage() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,16 @@
|
||||||
start.helloFirstName=Hello {0}! \ud83d\udc4b
|
start.helloFirstName=Hello {0}! \ud83d\udc4b
|
||||||
start.description=This is {0}, a simple bot focused on DnD content management! Please start by choosing a language down below \ud83d\udc47
|
start.description=This is {0}, a simple bot focused on DnD content management! Please start by choosing a language down below \ud83d\udc47
|
||||||
|
start.cmdDescription=Trigger this very bot
|
||||||
selectLanguageTutorial.drinkAction=*Proceeds to drink a potion with a strange, multicolor liquid*
|
selectLanguageTutorial.drinkAction=*Proceeds to drink a potion with a strange, multicolor liquid*
|
||||||
selectLanguageTutorial.setLanguage=Thanks! Now that I drank this modified potion of {0} that I''ve found at the "Crystal Fermentary" magic potion shop yesterday I can speak with you in the language that you prefer!
|
selectLanguageTutorial.setLanguage=Thanks! Now that I drank this modified potion of {0} that I''ve found at the "Crystal Fermentary" magic potion shop yesterday I can speak with you in the language that you prefer!
|
||||||
selectLanguageTutorial.instructions=You can always change your language settings by typing /selectLanguageTutorial in the chat.
|
selectLanguageTutorial.instructions=You can always change your language settings by typing /selectLanguageTutorial in the chat.
|
||||||
changeLanguage.english=English
|
changeLanguage.english=English
|
||||||
changeLanguage.italian=Italian
|
changeLanguage.italian=Italian
|
||||||
|
changeLanguage.cmdDescription=Select the new language I will use to speak to you
|
||||||
spell.speakWithAnimals=Speak with animals
|
spell.speakWithAnimals=Speak with animals
|
||||||
button.showMeTutorial=Show me what you can do!
|
button.showMeTutorial=Show me what you can do!
|
||||||
help.notShownYet=It seems you haven''t checked out what I can do yet! To have a complete list of my abilities, type /help in chat at any time!
|
help.notShownYet=It seems you haven''t checked out what I can do yet! To have a complete list of my abilities, type /help in chat at any time!
|
||||||
help.buttonBelow=Alternatively, you can click the button down below.
|
help.buttonBelow=Alternatively, you can click the button down below.
|
||||||
|
help.description=Here is a list of what I can do
|
||||||
|
help.buttonsToo=You can do the same operations you''d do with the commands aforementioned by selecting the corresponding button below \ud83d\udc47
|
||||||
|
help.cmdDescription=Print the help message
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
${i18n.help.description}:
|
||||||
|
|
||||||
|
#foreach(${command} in ${commands})
|
||||||
|
*#foreach(${key} in ${command.left}) ${key}#end: ${i18n.get(${command.right})}
|
||||||
|
#end
|
||||||
|
|
||||||
|
${i18n.help.buttonsToo}
|
|
@ -6,6 +6,7 @@ import static org.mockito.Mockito.*;
|
||||||
import com.github.polpetta.mezzotre.helper.Loader;
|
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.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;
|
||||||
|
@ -67,7 +68,8 @@ class SelectLanguageTutorialIntegrationTest {
|
||||||
selectLanguageTutorial =
|
selectLanguageTutorial =
|
||||||
new SelectLanguageTutorial(
|
new SelectLanguageTutorial(
|
||||||
Executors.newSingleThreadExecutor(),
|
Executors.newSingleThreadExecutor(),
|
||||||
new LocalizedMessageFactory(Loader.defaultVelocityEngine()),
|
new TemplateContentGenerator(
|
||||||
|
new LocalizedMessageFactory(Loader.defaultVelocityEngine())),
|
||||||
LoggerFactory.getLogger(SelectLanguageTutorial.class),
|
LoggerFactory.getLogger(SelectLanguageTutorial.class),
|
||||||
fakeUUIDGenerator);
|
fakeUUIDGenerator);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
package com.github.polpetta.mezzotre.telegram.command;
|
||||||
|
|
||||||
|
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.model.TgChat;
|
||||||
|
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.google.gson.Gson;
|
||||||
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
import com.pengrad.telegrambot.request.BaseRequest;
|
||||||
|
import com.pengrad.telegrambot.request.SendMessage;
|
||||||
|
import io.ebean.Database;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import org.apache.velocity.app.VelocityEngine;
|
||||||
|
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.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 HelpIntegrationTest {
|
||||||
|
private static Gson gson;
|
||||||
|
|
||||||
|
@Container
|
||||||
|
private final PostgreSQLContainer<?> postgresServer =
|
||||||
|
new PostgreSQLContainer<>(TestConfig.POSTGRES_DOCKER_IMAGE);
|
||||||
|
|
||||||
|
private Database database;
|
||||||
|
private VelocityEngine velocityEngine;
|
||||||
|
private Clock fakeClock;
|
||||||
|
private Help help;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void beforeAll() {
|
||||||
|
gson = new Gson();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws Exception {
|
||||||
|
database =
|
||||||
|
Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer));
|
||||||
|
velocityEngine = Loader.defaultVelocityEngine();
|
||||||
|
|
||||||
|
final Logger log = LoggerFactory.getLogger(Start.class);
|
||||||
|
fakeClock = mock(Clock.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldProvideMessageWithButtons() throws Exception {
|
||||||
|
when(fakeClock.now()).thenReturn(42L);
|
||||||
|
|
||||||
|
final TgChat tgChat = new TgChat(1111111L, new ChatContext(), "en-US", false);
|
||||||
|
tgChat.save();
|
||||||
|
|
||||||
|
final HashMap<String, Processor> commands = new HashMap<>();
|
||||||
|
final Processor dummy1 =
|
||||||
|
new 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 Processor dummy2 =
|
||||||
|
new 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);
|
||||||
|
|
||||||
|
help =
|
||||||
|
new Help(
|
||||||
|
new TemplateContentGenerator(new LocalizedMessageFactory(velocityEngine)),
|
||||||
|
Executors.newSingleThreadExecutor(),
|
||||||
|
fakeClock,
|
||||||
|
commands);
|
||||||
|
|
||||||
|
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<?, ?>>> gotFuture = help.process(tgChat, update);
|
||||||
|
final Optional<BaseRequest<?, ?>> gotResponseOpt = gotFuture.get();
|
||||||
|
final BaseRequest<?, ?> gotResponse = gotResponseOpt.get();
|
||||||
|
assertInstanceOf(SendMessage.class, 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);
|
||||||
|
|
||||||
|
// TODO InputKeyboard assertions
|
||||||
|
fail("Add inputkeyboard assertions too");
|
||||||
|
|
||||||
|
final TgChat gotChat = new QTgChat().id.eq(1111111L).findOne();
|
||||||
|
assertNotNull(gotChat);
|
||||||
|
assertTrue(gotChat.getHasHelpBeenShown());
|
||||||
|
final ChatContext gotChatChatContext = gotChat.getChatContext();
|
||||||
|
assertEquals(0, gotChatChatContext.getStep());
|
||||||
|
assertEquals("/help", gotChatChatContext.getStage());
|
||||||
|
assertEquals(42, gotChatChatContext.getPreviousMessageUnixTimestampInSeconds());
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import com.google.gson.Gson;
|
||||||
import com.pengrad.telegrambot.model.Update;
|
import com.pengrad.telegrambot.model.Update;
|
||||||
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.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
@ -32,12 +33,12 @@ class RouterTest {
|
||||||
gson = new Gson();
|
gson = new Gson();
|
||||||
|
|
||||||
dummyEmptyExampleProcessor = mock(Processor.class);
|
dummyEmptyExampleProcessor = mock(Processor.class);
|
||||||
when(dummyEmptyExampleProcessor.getTriggerKeyword()).thenReturn("/example");
|
when(dummyEmptyExampleProcessor.getTriggerKeywords()).thenReturn(Set.of("/example"));
|
||||||
when(dummyEmptyExampleProcessor.process(any(), any()))
|
when(dummyEmptyExampleProcessor.process(any(), any()))
|
||||||
.thenReturn(CompletableFuture.completedFuture(Optional.empty()));
|
.thenReturn(CompletableFuture.completedFuture(Optional.empty()));
|
||||||
|
|
||||||
anotherKeyWithResultProcessor = mock(Processor.class);
|
anotherKeyWithResultProcessor = mock(Processor.class);
|
||||||
when(anotherKeyWithResultProcessor.getTriggerKeyword()).thenReturn("/anotherExample");
|
when(anotherKeyWithResultProcessor.getTriggerKeywords()).thenReturn(Set.of("/anotherExample"));
|
||||||
when(anotherKeyWithResultProcessor.process(any(), any()))
|
when(anotherKeyWithResultProcessor.process(any(), any()))
|
||||||
.thenReturn(
|
.thenReturn(
|
||||||
CompletableFuture.completedFuture(Optional.of(new SendMessage(1234L, "hello world"))));
|
CompletableFuture.completedFuture(Optional.of(new SendMessage(1234L, "hello world"))));
|
||||||
|
@ -46,7 +47,10 @@ class RouterTest {
|
||||||
@Test
|
@Test
|
||||||
void shouldMessageExampleMessageAndGetEmptyOptional() throws Exception {
|
void shouldMessageExampleMessageAndGetEmptyOptional() throws Exception {
|
||||||
final Router router =
|
final Router router =
|
||||||
new Router(Set.of(dummyEmptyExampleProcessor), Executors.newSingleThreadExecutor());
|
new Router(
|
||||||
|
Map.of("/example", dummyEmptyExampleProcessor),
|
||||||
|
Executors.newSingleThreadExecutor(),
|
||||||
|
mock(NotFoundFactory.class));
|
||||||
final TgChat fakeChat = mock(TgChat.class);
|
final TgChat fakeChat = mock(TgChat.class);
|
||||||
when(fakeChat.getChatContext()).thenReturn(new ChatContext());
|
when(fakeChat.getChatContext()).thenReturn(new ChatContext());
|
||||||
final Update update =
|
final Update update =
|
||||||
|
@ -85,8 +89,13 @@ class RouterTest {
|
||||||
void shouldSelectRightExecutorAndReturnResult() throws Exception {
|
void shouldSelectRightExecutorAndReturnResult() throws Exception {
|
||||||
final Router router =
|
final Router router =
|
||||||
new Router(
|
new Router(
|
||||||
Set.of(dummyEmptyExampleProcessor, anotherKeyWithResultProcessor),
|
Map.of(
|
||||||
Executors.newSingleThreadExecutor());
|
"/example",
|
||||||
|
dummyEmptyExampleProcessor,
|
||||||
|
"/anotherExample",
|
||||||
|
anotherKeyWithResultProcessor),
|
||||||
|
Executors.newSingleThreadExecutor(),
|
||||||
|
mock(NotFoundFactory.class));
|
||||||
final TgChat fakeChat = mock(TgChat.class);
|
final TgChat fakeChat = mock(TgChat.class);
|
||||||
when(fakeChat.getChatContext()).thenReturn(new ChatContext());
|
when(fakeChat.getChatContext()).thenReturn(new ChatContext());
|
||||||
final Update update =
|
final Update update =
|
||||||
|
|
|
@ -6,6 +6,7 @@ import static org.mockito.Mockito.*;
|
||||||
import com.github.polpetta.mezzotre.helper.Loader;
|
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.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;
|
||||||
|
@ -22,8 +23,6 @@ import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import org.apache.velocity.app.VelocityEngine;
|
import org.apache.velocity.app.VelocityEngine;
|
||||||
import org.apache.velocity.runtime.RuntimeConstants;
|
|
||||||
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
|
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Tag;
|
import org.junit.jupiter.api.Tag;
|
||||||
|
@ -62,12 +61,7 @@ class StartIntegrationTest {
|
||||||
void setUp() throws Exception {
|
void setUp() throws Exception {
|
||||||
database =
|
database =
|
||||||
Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer));
|
Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer));
|
||||||
velocityEngine = new VelocityEngine();
|
velocityEngine = Loader.defaultVelocityEngine();
|
||||||
velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADERS, "classpath");
|
|
||||||
velocityEngine.setProperty(
|
|
||||||
"resource.loader.classpath.class", ClasspathResourceLoader.class.getName());
|
|
||||||
velocityEngine.init();
|
|
||||||
localizedMessageFactory = new LocalizedMessageFactory(velocityEngine);
|
|
||||||
|
|
||||||
final Logger log = LoggerFactory.getLogger(Start.class);
|
final Logger log = LoggerFactory.getLogger(Start.class);
|
||||||
|
|
||||||
|
@ -76,11 +70,12 @@ class StartIntegrationTest {
|
||||||
|
|
||||||
start =
|
start =
|
||||||
new Start(
|
new Start(
|
||||||
localizedMessageFactory,
|
new TemplateContentGenerator(new LocalizedMessageFactory(velocityEngine)),
|
||||||
Executors.newSingleThreadExecutor(),
|
Executors.newSingleThreadExecutor(),
|
||||||
log,
|
log,
|
||||||
fakeUUIDGenerator,
|
fakeUUIDGenerator,
|
||||||
fakeClock);
|
fakeClock,
|
||||||
|
"Mezzotre");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue