Micronaut Chatbots 
        
        Ease the creation of ChatBots with the Micronaut Framework
        Version:  1.4.0 
     
    
    
         
    
         
For this project, you can find a list of releases (with release notes) here:
 
    
         
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.
 
    
         
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.
 
    
         
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.
 
 
 
    
         
@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
 
 
    
         
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. 
 
 
    
         
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> 
 
 
    
         
        
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:
 
    
         
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
 
    
         
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" .
 
 
    
         
    
         
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
 
 
 
    
         
@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
 
 
    
         
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. 
 
 
    
         
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> 
 
 
    
         
        
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:
 
    
         
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
 
    
         
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".
 
 
    
         
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.
 
 
 
 
 
    
         
You can find the source code of this project in this repository: