Skip to content

Getting started

Mikhail edited this page Jan 11, 2021 · 12 revisions

Getting started

Dependency Injection

Framework uses Autofac as common Dependency Injection container, so whole services should be reigstered with it. If you're not familiar with it, please, check its documentation.

Processing updates

Framework processes not a whole Update, only it's part (e.g. Message, CallbackQuery, InlineQuery), which called Entity and that's means you could register handlers for each type of update.

IHandlerContext<TEntity>, where TEntity is Message, CallbackQuery, or any available type, which Update object has, - is the general interface, which holds common information about entity, such as update object, this entity taken from, update sender (User who send this entity, could be null, if entity doesn't have From field), Container, which is Autofac's IContainer interface, which is using for resolving types and services, if built-in service injection doesn't suit you.

Declaring an entity handler

Entity handler structure should be like this:

[YourCustomHandlerAttribute(...)]
public ReturnType MyCoolEntityHandler(IHandlerContext<TEntity> handlerContext, TService1 requiredService1, TService2 requiredService2)
{
    //Your handler logic here
}

Where:

  • YourCustomHandlerAttribute - attribute, which allows current method to be registered in entity processing pipeline. Inherited from UpdateEntityHandlerAttributeBase
  • ReturnType is bool, or Task<bool>, or ValueTask<bool>.
  • TService1, TService2 - services you needed in, which are contained in Autofac's IContainer. Otherwise, if services aren't represented in dependency injection container, requiredService1 and requiredService2 will equals to null.

Handler return value indicates, should be next entity handler be invoked, or not, so, to continue handling, you should write return true, otherwise, logically, return false.

Examples

Here we will use classes, attributes, and extension methods from here.

Firstly, let's define some constant, which we will use as an identifier for entity handlers and entity builder of which is related to Message processing:

public string const MessagesEntityProcessorId = "Messages";

Then, let's define our first entity handler, which will process Messages.

[MessageEntityHandlerAttribute(position: 1)] //Marks this handler to be invoked first.
public async bool CheckMessageTextForHelloHandler(IHandlerContext<Message> messageHandlerContext)
{
    string text = messageHandlerContext.Entity.Text; //Entity - Message object
    if(text.Contains("hello"))
    {
        return true;
    }
    return false;
}

Where MessageEntityHandlerAttribute is:

public class MessageEntityHandlerAttribute : PositionedEntityHandlerAttribute
{
    public MessageEntityHandlerAttribute(int position) : base(position, MessagesEntityProcessorId, UpdateType.Message)
    {

    }
}

Example handler with using services from container

[MessageEntityHandlerAttribute(position: 1)]
public Task<bool> CheckMessageTextForHelloHandler(IHandlerContext<Message> messageHandlerContext, ITextMatcherService textMatcherService)
{
    string text = messageHandlerContext.Entity.Text; //Entity - Message object
    
    return textMatcherService.CheckThatMessageTextMatchSomeCriteria(text);
}

Where ITextMatcherService is:

public interface ITextMatcherService
{
    Task<bool> CheckThatMessageTextMatchSomeCriteria(string messageText);
}

And realization of this interface:

public class TextMatcherService : ITextMatcherService
{
    private const string RequiredTextInMessage = "hello";

    public async Task<bool> CheckThatMessageTextMatchSomeCriteria(string messageText)
    {
        return messageText.Contains(RequiredTextInMessage);
    }
}

Register service with Autofac's ContainerBuilder:

ContainerBuilder containerBuilder = new ContainerBuilder();

containerBuilder.RegisterType<TextMatcherService>().As<ITextMatcherService>();

Configuring bot

To configure and build bot instance, you should use BotBuilder class:

string botName = "Spire Example Bot"; //Defining bot name.

ITelegramBotClient telegramBotClient = new TelegramBotClient("Your Bot Api Token here"); //Creating telegram bot client instance.

IContainer container = containerBuilder.Build(); //Building previously configured ContainerBuilder.

IBotBuilder botBuilder = new BotBuilder(botName, telegramBotClient, container);

Then, lets assume our CheckMessageTextForHelloHandler is container in CheckMessageTextForHelloHandlerSource type:

public class CheckMessageTextForHelloHandlerSource
{

    [MessageEntityHandlerAttribute(position: 1)]
    public Task<bool> CheckMessageTextForHelloHandler(IHandlerContext<Message> messageHandlerContext, ITextMatcherService textMatcherService)
    {
        string text = messageHandlerContext.Entity.Text; //Entity - Message object
     
        return textMatcherService.CheckThatMessageTextMatchSomeCriteria(text);
    }

}

Then, registration of this handler will be like this:

botBuilder.WithPositionedUpdateEntityProcessorBuilder<Message>(
    MessagesEntityProcessorId,
    UpdateType.Message,
    updateEntityProcessorBuilder => updateEntityProcessorBuilder
        .WithUpdateEntityHandlersFromType(typeof(CheckMessageTextForHelloHandlerSource))

WithUpdateEntityHandlersFromType will scan type for methods, available for registering in entity processing pipeline.

Also, there is WithUpdateEntityHandlersFromAssembly methos, which scans specified assembly for exported types, and calls WithUpdateEntityHandlersFromType for each of them.

Putting everything together

Note, we will use console hosting framework part here.

After all steps, specified below, our project will be like this:

Program.cs:

using Autofac;
using Spire;
using Spire.Core.Abstractions;
using Spire.Core.Abstractions.Builders;
using Spire.Core.Builders;
using Spire.Hosting;
using Spire.Hosting.Console;
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums; 
using ExampleBot.Services; //TextMatcherService and ITextMatcherService definitions.

namespace ExampleBot
{
    class Program
    {
        static void Main(string[] args)
        {
            ConsoleBotHost consoleBotHost = ConsoleBotHost
                .CreateDefault(args)
                .WithBot(BuildExampleBot);

            consoleBotHost.Run(); //Runs bot in LongPolling mode.
            while(true) { }
        }

        static IBot BuildExampleBot(BotConfigurationOptions consoleBotConfigurationOptions)
        {
            ContainerBuilder containerBuilder = new ContainerBuilder();

            containerBuilder.RegisterType<TextMatcherService>().As<ITextMatcherService>();

            IContainer container = containerBuilder.Build();

            ITelegramBotClient telegramBotClient = new TelegramBotClient(consoleBotConfigurationOptions.ApiToken);
            
            IBotBuilder botBuilder = new BotBuilder(consoleBotConfigurationOptions.Name, telegramBotClient, container);

            botBuilder.WithPositionedUpdateEntityProcessorBuilder<Message>(
                DefaultProcessorId,
                UpdateType.Message,
                messageEntityProcessorBuilder => messageEntityProcessorBuilder
                    .WithUpdateEntityHandlersFromType(typeof(CheckMessageTextForHelloHandlerSource)));

            return botBuilder.Build();
        }
    }
}

Services/ITextMatcherService.cs:

using System.Threading.Tasks;

namespace ExampleBot.Services
{
    
    public interface ITextMatcherService
    {
        Task<bool> CheckThatMessageTextMatchSomeCriteria(string messageText);
    }

}

Services/TextMatcherService.cs:

using System.Threading.Tasks;

namespace ExampleBot.Services
{
    public class TextMatcherService : ITextMatcherService
    {
        private const string RequiredTextInMessage = "hello";

        public async Task<bool> CheckThatMessageTextMatchSomeCriteria(string messageText)
        {
             return messageText.Contains(RequiredTextInMessage);
        }
    }
}

MessageEntityHandlerAttribute.cs:

using Spire;

namespace ExampleBot
{

    public class MessageEntityHandlerAttribute : PositionedEntityHandlerAttribute
    {
        public MessageEntityHandlerAttribute(int position) : base(position, MessagesEntityProcessorId, UpdateType.Message)
        {

        }
    }

}

CheckMessageTextForHelloHandlerSource.cs:

using System.Threading.Tasks;
using Spire.Core.Abstractions.Processing.Contexts;
using Telegram.Bot.Types;
using ExampleBot.Services;

namespace ExampleBot
{
    public class CheckMessageTextForHelloHandlerSource
    {
        [MessageEntityHandlerAttribute(position: 1)]
        public Task<bool> CheckMessageTextForHelloHandler(IHandlerContext<Message> messageHandlerContext,
            ITextMatcherService textMatcherService)
        {
            string text = messageHandlerContext.Entity.Text;

            return textMatcherService.CheckThatMessageTextMatchSomeCriteria(text);
        }
    }
}

Clone this wiki locally