Micronaut Chatbots
Ease the creation of ChatBots with the Micronaut Framework
Version: 1.3.0
1 Introduction
Improve this doc
2 Release History
Improve this doc
For this project, you can find a list of releases (with release notes) here:
3 Telegram
Improve this doc
You can develop a Telegram Bot quickly with Micronaut Chatbots.
Create a Telegram Bot by talking with the @BotFather .
To see what actions the BotFather can perform for you, send /help
in the chat.
First send the command /newbot
to the BotFather and follow the instructions to create a new bot.
At the end of this process, you will receive a token to access the HTTP API.
3.1 Telegram Bot Configuration
Improve this doc
micronaut.chatbots.telegram.bots.mnexample.token=xxxyyyzzz
micronaut.chatbots.telegram.bots.mnexample.at-username=@MicronautExampleBot
micronaut:
chatbots:
telegram:
bots:
mnexample:
token: 'xxxyyyzzz'
at-username: '@MicronautExampleBot'
[micronaut]
[micronaut.chatbots]
[micronaut.chatbots.telegram]
[micronaut.chatbots.telegram.bots]
[micronaut.chatbots.telegram.bots.mnexample]
token="xxxyyyzzz"
at-username="@MicronautExampleBot"
micronaut {
chatbots {
telegram {
bots {
mnexample {
token = "xxxyyyzzz"
atUsername = "@MicronautExampleBot"
}
}
}
}
}
{
micronaut {
chatbots {
telegram {
bots {
mnexample {
token = "xxxyyyzzz"
at-username = "@MicronautExampleBot"
}
}
}
}
}
}
{
"micronaut": {
"chatbots": {
"telegram": {
"bots": {
"mnexample": {
"token": "xxxyyyzzz",
"at-username": "@MicronautExampleBot"
}
}
}
}
}
}
micronaut.chabots.telegram.bots.*.token
matches the value specified as secret_token
while setting the webhook.
3.2 Telegram Webhook
Improve this doc
curl -X "POST" "https://api.telegram.org/bot{httpApiToken}/setWebhook?url={webhookUrl}&secret_token={secretToken}"
httpApiToken
You obtain this value via the @BotFather
webhookUrl
is the endpoint of your Micronaut application.
{secretToken}
- A secret token, Telegram sends in a header X-Telegram-Bot-Api-Secret-Token
in every webhook request.
3.3 Telegram Bot Handlers
Improve this doc
@Singleton
class HelloWorldHandler implements TelegramHandler<SendMessage> {
private final SpaceParser<Update, Chat> spaceParser;
HelloWorldHandler(SpaceParser<Update, Chat> spaceParser) {
this.spaceParser = spaceParser;
}
@Override
public boolean canHandle(@Nullable TelegramBotConfiguration bot, @NotNull Update input) {
return input.getMessage().getText().contains("hello");
}
@Override
public Optional<SendMessage> handle(@Nullable TelegramBotConfiguration bot, @NotNull Update input) {
return SendMessageUtils.compose(spaceParser, input, "Hello World");
}
}
@Singleton
class HelloWorldHandler(private val spaceParser: SpaceParser<Update, Chat>) : TelegramHandler<SendMessage> {
override fun canHandle(bot: TelegramBotConfiguration?, input: Update): Boolean = input.message.text.contains("hello")
override fun handle(bot: TelegramBotConfiguration?, input: Update): Optional<SendMessage> =
SendMessageUtils.compose(spaceParser, input, "Hello World")
}
To respond to Telegram Bot commands , with static content, you can extend from CommandHandler .
For example to respond to an /about
command with a static message, you create a CommandHandler
bean:
@Singleton
class AboutCommandHandler extends CommandHandler {
private static final String COMMAND_ABOUT = "/about";
AboutCommandHandler(
TelegramSlashCommandParser slashCommandParser,
TextResourceLoader textResourceLoader,
SpaceParser<Update, Chat> spaceParser
) {
super(slashCommandParser, textResourceLoader, spaceParser);
}
@Override
@NonNull
public String getCommand() {
return COMMAND_ABOUT;
}
}
@Singleton
class AboutCommandHandler extends CommandHandler {
private static final String COMMAND_ABOUT = "/about"
AboutCommandHandler(
TelegramSlashCommandParser slashCommandParser,
TextResourceLoader textResourceLoader,
SpaceParser<Update, Chat> spaceParser
) {
super(slashCommandParser, textResourceLoader, spaceParser)
}
@Override
@NonNull
String getCommand() {
COMMAND_ABOUT
}
}
@Singleton
class AboutCommandHandler(
slashCommandParser: TelegramSlashCommandParser,
textResourceLoader: TextResourceLoader,
spaceParser: SpaceParser<Update, Chat>
) : CommandHandler(slashCommandParser, textResourceLoader, spaceParser) {
override fun getCommand() = COMMAND_ABOUT
companion object {
private const val COMMAND_ABOUT = "/about"
}
}
And then create some content with a matching name in the botcommands
directory:
resources/botcommands/about.md
Bot developed with 💙 using [Micronaut](https://micronaut.io)
The botcommands
directory may be configured via the micronaut.chatbots.folder
configuration property.
🔗
Table 1. Configuration Properties for ChatbotsConfigurationProperties
Property
Type
Description
micronaut.chatbots.enabled
boolean
Whether chatbots is enabled. Default value (true).
micronaut.chatbots.folder
java.lang.String
The folder to look for bot commands.
micronaut.chatbots.possible-static-command-extensions
java.util.List
Possible static command file extensions. Default values MARKDOWN, HTML, TXT
3.4 Ordering Handlers
Improve this doc
Chatbot handlers have order, and the first handler to support a command is the one used.
To change the order of handlers, you can override the getOrder
method in your handler.
@Singleton
class UnknownCommandHandler implements TelegramHandler<SendMessage> {
private final SpaceParser<Update, Chat> spaceParser;
UnknownCommandHandler(SpaceParser<Update, Chat> spaceParser) {
this.spaceParser = spaceParser;
}
@Override
public boolean canHandle(@Nullable TelegramBotConfiguration bot, @NotNull Update input) {
return true; // (1)
}
@Override
public Optional<SendMessage> handle(@Nullable TelegramBotConfiguration bot, @NotNull Update input) {
return SendMessageUtils.compose(spaceParser, input, "I don't know how to handle your query: %s".formatted(input.getMessage().getText()));
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE; // (2)
}
}
@Singleton
class UnknownCommandHandler implements TelegramHandler<SendMessage> {
private final SpaceParser<Update, Chat> spaceParser
UnknownCommandHandler(SpaceParser<Update, Chat> spaceParser) {
this.spaceParser = spaceParser
}
@Override
boolean canHandle(@Nullable TelegramBotConfiguration bot, @NotNull Update input) {
true // (1)
}
@Override
Optional<SendMessage> handle(@Nullable TelegramBotConfiguration bot, @NotNull Update input) {
return SendMessageUtils.compose(spaceParser, input, "I don't how to handle your query: ${input.message.text}")
}
@Override
int getOrder() {
Ordered.LOWEST_PRECEDENCE // (2)
}
}
@Singleton
class UnknownCommandHandler(private val spaceParser: SpaceParser<Update, Chat>) : TelegramHandler<SendMessage> {
override fun canHandle(bot: TelegramBotConfiguration?, input: Update) = true // (1)
override fun handle(bot: TelegramBotConfiguration?, input: Update): Optional<SendMessage> =
SendMessageUtils.compose(
spaceParser,
input,
"I don't how to handle your query: ${input.message.text}"
)
override fun getOrder() = Ordered.LOWEST_PRECEDENCE // (2)
}
1
This handler will handle any message.
2
Ensure this handler is last in the list of handlers.
3.5 Telegram Chatbots as an AWS Lambda Function
Improve this doc
If you want to deploy to AWS Lambda , you can use the following dependency:
implementation("io.micronaut.chatbots:micronaut-chatbots-telegram-lambda")
<dependency>
<groupId>io.micronaut.chatbots</groupId>
<artifactId>micronaut-chatbots-telegram-lambda</artifactId>
</dependency>
3.6 Telegram Chatbots as a Google Cloud Function
Improve this doc
implementation("io.micronaut.chatbots:micronaut-chatbots-telegram-gcp-function")
<dependency>
<groupId>io.micronaut.chatbots</groupId>
<artifactId>micronaut-chatbots-telegram-gcp-function</artifactId>
</dependency>
You can use the following entry point:
3.7 Telegram Chatbots as an Azure Function
Improve this doc
If you want to deploy to an Azure function, you can use the following dependency:
implementation("io.micronaut.chatbots:micronaut-chatbots-telegram-azure-function")
<dependency>
<groupId>io.micronaut.chatbots</groupId>
<artifactId>micronaut-chatbots-telegram-azure-function</artifactId>
</dependency>
This adds an AzureFunction
with the name TelegramTrigger
via the class io.micronaut.chatbots.telegram.azurefunction.Handler
3.8 Telegram Chatbots Controller
Improve this doc
If you want to configure one endpoint as the Telegram chatbot webhook while using a runtime such as Netty or Servlet, you can include the following dependency:
implementation("io.micronaut.chatbots:micronaut-mironaut-chatbots-telegram-http")
<dependency>
<groupId>io.micronaut.chatbots</groupId>
<artifactId>micronaut-mironaut-chatbots-telegram-http</artifactId>
</dependency>
You can configure the path with:
🔗
Table 1. Configuration Properties for TelegramControllerConfiguration
Property
Type
Description
micronaut.chatbots.telegram.endpoint.enabled
boolean
Enables the controller. Default value true .
micronaut.chatbots.telegram.endpoint.path
java.lang.String
Path to the controller. Default value "/telegram" .
4 Basecamp
Improve this doc
4.1 Basecamp Webhook
Improve this doc
curl -s \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"service_name":"${BOTNAME}","command_url":"${YOUR_HTTP_TRIGGER_URL}"}' \
https://3.basecampapi.com/${APP_ID}/buckets/${BUCKET_ID}/chats/${CHAT_ID}/integrations.json
BOTNAME
is the name of your bot
ACCESS_TOKEN
is your Oauth2 Basecamp access token
YOUR_HTTP_TRIGGER_URL
is the https address of your URL of your controller or function
APP_ID
, BUCKET_ID
and CHAT_ID
are the IDs of your Basecamp application, bucket and chat respectively
4.2 Basecamp Bot Handlers
Improve this doc
@Singleton
class HelloWorldHandler implements BasecampHandler {
@Override
public boolean canHandle(BasecampBotConfiguration bot, @NonNull @NotNull Query input) {
return input.getCommand().contains("hello");
}
@NonNull
@Override
public Optional<String> handle(BasecampBotConfiguration bot, @NonNull @NotNull Query input) {
return Optional.of("Hello World");
}
}
@Singleton
class HelloWorldHandler : BasecampHandler {
override fun canHandle(bot: BasecampBotConfiguration?, input: Query) =
input.command.contains("hello")
override fun handle(bot: BasecampBotConfiguration?, input: Query) =
Optional.of("Hello World")
}
For example to respond to an /about
command with a static message, you create a bean:
@Singleton
class AboutCommandHandler implements BasecampHandler {
public static final String ABOUT = "about";
private final TextResourceLoader textResourceLoader;
AboutCommandHandler(TextResourceLoader textResourceLoader) {
this.textResourceLoader = textResourceLoader;
}
@Override
public boolean canHandle(@Nullable BasecampBotConfiguration bot, @NonNull @NotNull Query input) {
return input.getCommand().equalsIgnoreCase("/" + ABOUT);
}
@Override
public @NonNull Optional<String> handle(@Nullable BasecampBotConfiguration bot, @NonNull @NotNull Query input) {
return textResourceLoader
.composeCommandResponse(ABOUT)
.map(CommandResponse::text);
}
}
@Singleton
class AboutCommandHandler implements BasecampHandler {
static final String ABOUT = "about"
private final TextResourceLoader textResourceLoader
AboutCommandHandler(TextResourceLoader textResourceLoader) {
this.textResourceLoader = textResourceLoader
}
@Override
boolean canHandle(@Nullable BasecampBotConfiguration bot, @NonNull @NotNull Query input) {
input.command.equalsIgnoreCase("/$ABOUT")
}
@Override
@NonNull Optional<String> handle(@Nullable BasecampBotConfiguration bot, @NonNull @NotNull Query input) {
textResourceLoader
.composeCommandResponse(ABOUT)
.map(CommandResponse::text)
}
}
@Singleton
class AboutCommandHandler(
private val textResourceLoader: TextResourceLoader
) : BasecampHandler {
override fun canHandle(bot: BasecampBotConfiguration?, input: Query) =
input.command.equals("/$ABOUT", ignoreCase = true)
override fun handle(bot: BasecampBotConfiguration?, input: Query) =
textResourceLoader.composeCommandResponse(ABOUT).map(CommandResponse::text)
companion object {
const val ABOUT: String = "about"
}
}
And then create some content with a matching name in the botcommands
directory:
resources/botcommands/about.md
Bot developed with 💙 using [Micronaut](https://micronaut.io)
The botcommands
directory may be configured via the micronaut.chatbots.folder
configuration property.
🔗
Table 1. Configuration Properties for ChatbotsConfigurationProperties
Property
Type
Description
micronaut.chatbots.enabled
boolean
Whether chatbots is enabled. Default value (true).
micronaut.chatbots.folder
java.lang.String
The folder to look for bot commands.
micronaut.chatbots.possible-static-command-extensions
java.util.List
Possible static command file extensions. Default values MARKDOWN, HTML, TXT
4.3 Ordering Handlers
Improve this doc
Chatbot handlers have order, and the first handler to support a command is the one used.
To change the order of handlers, you can override the getOrder
method in your handler.
@Singleton
class UnknownCommandHandler implements BasecampHandler {
@Override
public boolean canHandle(@Nullable BasecampBotConfiguration bot, @NonNull @NotNull Query input) {
return true; // (1)
}
@Override
public @NonNull Optional<String> handle(@Nullable BasecampBotConfiguration bot, @NonNull @NotNull Query input) {
return Optional.of("I don't know how to handle your query: %s".formatted(input.getCommand()));
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE; // (2)
}
}
@Singleton
class UnknownCommandHandler implements BasecampHandler {
@Override
boolean canHandle(@Nullable BasecampBotConfiguration bot, @NonNull @NotNull Query input) {
true // (1)
}
@Override
@NonNull Optional<String> handle(@Nullable BasecampBotConfiguration bot, @NonNull @NotNull Query input) {
Optional.of("I don't know how to handle your query: $input.command".toString())
}
@Override
public int getOrder() {
Ordered.LOWEST_PRECEDENCE // (2)
}
}
@Singleton
class UnknownCommandHandler : BasecampHandler {
override fun canHandle(bot: BasecampBotConfiguration?, input: Query) = true // (1)
override fun handle(bot: BasecampBotConfiguration?, input: Query) =
Optional.of("I don't know how to handle your query: ${input.command}")
override fun getOrder() = Ordered.LOWEST_PRECEDENCE // (2)
1
This handler will handle any message.
2
Ensure this handler is last in the list of handlers.
4.4 Basecamp Chatbots as an AWS Lambda Function
Improve this doc
If you want to deploy to AWS Lambda , you can use the following dependency:
implementation("io.micronaut.chatbots:micronaut-chatbots-basecamp-lambda")
<dependency>
<groupId>io.micronaut.chatbots</groupId>
<artifactId>micronaut-chatbots-basecamp-lambda</artifactId>
</dependency>
4.5 Basecamp Chatbots as a Google Cloud Function
Improve this doc
implementation("io.micronaut.chatbots:micronaut-chatbots-basecamp-gcp-function")
<dependency>
<groupId>io.micronaut.chatbots</groupId>
<artifactId>micronaut-chatbots-basecamp-gcp-function</artifactId>
</dependency>
You can use the following entry point:
4.6 Basecamp Chatbots as an Azure Function
Improve this doc
If you want to deploy to an Azure function, you can use the following dependency:
implementation("io.micronaut.chatbots:micronaut-chatbots-basecamp-azure-function")
<dependency>
<groupId>io.micronaut.chatbots</groupId>
<artifactId>micronaut-chatbots-basecamp-azure-function</artifactId>
</dependency>
This adds an AzureFunction
with the name BasecampTrigger
via the class io.micronaut.chatbots.basecamp.azurefunction.Handler
4.7 Basecamp Chatbots Controller
Improve this doc
If you want to configure one endpoint as the Basecamp chatbot webhook while using a runtime such as Netty or Servlet, you can include the following dependency:
implementation("io.micronaut.chatbots:micronaut-chatbots-basecamp-http")
<dependency>
<groupId>io.micronaut.chatbots</groupId>
<artifactId>micronaut-chatbots-basecamp-http</artifactId>
</dependency>
You can configure the path with:
🔗
Table 1. Configuration Properties for BasecampControllerConfiguration
Property
Type
Description
micronaut.chatbots.basecamp.endpoint.enabled
boolean
Enables the controller. Default value true.
micronaut.chatbots.basecamp.endpoint.path
java.lang.String
Path to the controller. Default value "/basecamp".
5 Breaking Changes
Improve this doc
This section documents breaking changes between Micronaut Chatbots versions:
Micronaut Chatbots 2.0.0
Deprecations
The private class field io.micronaut.chatbots.google.api.Space.type
deprecated previously has been removed.
As a consequence the corresponding accessor/mutator methods getType()
and setType(Type)
have also been removed,
even though they were not deprecated explicitly.
Use the accessor/mutator methods for singleUserBotDm
or spaceType
(developer preview) instead.
6 Repository
Improve this doc
You can find the source code of this project in this repository: