Skip to content

Core Module Code Snippets

SKitLs-dev edited this page Mar 29, 2024 · 16 revisions

Please note: the entire project is carried out by one person. Some examples may be outdated. If you find outdated code, please create a new issue. Up-to-dated code is contained in the project.

Contents

Create new bot

To review the architecture of handlers (IUpdateHandlerBase<TUpdate>), managers (LinearActionManager), and updates, visit this link.

More info about BotBuilder and available methods here.

        static async Task Main(string[] args)
        {
            try
            {
                var privateCommandsManager = new LinearActionManager<SignedMessageTextUpdate>("commands.private");
                var privateTextsHandler = new SignedMessageTextHandler(privateCommandsManager);
                var privateMessagesHandler = new SignedMessageBaseHandler(privateTextsHandler);

                var privateCallbacksManager = new LinearActionManager<SignedCallbackUpdate>("callbacks.private");
                var privateCallbacksHandler = new CallbackHandler(privateCallbacks);

                ChatDesigner privates = ChatDesigner.NewDesigner()
                    .UseMessageHandler(privateMessagesHandler)
                    .UseCallbackHandler(privateCallbacksHandler);

                var bot = BotBuilder.NewBuilder("your_api_key")
                    .EnablePrivates(privates)
                    .Build();

                BotBuilder.DebugSettings.LogExceptionTrace = true;
                await bot.Listen();
            }
            catch (Exception e)
            {
                BotBuilder.DebugSettings.LocalLogger.Log(e);
                return;
            }
        }

Last review: .Core v3.1.1 Back to contents

Add Actions

According to architecture IBotAction serves as the endpoint for received updates. Here are examples of default actions available.

If you require passing any data via action data, please refer to the link (TODO: add refer ArgedInteractions).

Bot Command

public static DefaultCommand StartCommand { get; } = new("start", Do_StartAsync);
private static async Task Do_StartAsync(SignedMessageTextUpdate update)
{
    // ...
}

Callback

public static DefaultCallback ClickMeCallback { get; } = new("ClickMeData", "Click Me Visible Text", Do_ClickMeAsync);
private static async Task Do_ClickMeAsync(SignedCallbackUpdate update)
{
    // ...
}

Last review: .Core v3.1.1 Back to contents

Coding Actions

General

When IBotAction body defined, main question is: what can I do?

Through the received ICastedUpdated you can access both: framework facilities and API methods.

BotManager and ICastedUpdate should cover all your needs, however if you need more fine-tuning or working with the API, you can access the Telegram.Bot project's classes (Update and ITelegramBotClient).

private static async Task Do_StartAsync(SignedMessageTextUpdate update)
{
    Update original = update.OriginalSource;
    BotManager owner = update.Owner;
    ITelegramBotClient bot = update.Owner.Bot;
}

Sending Messages

Strings

Send a text message to the chat using its ID via bot instance or IDeliveryService. Please note that this example based on the fact: Telegram users' IDs are equivalent to their chat IDs.

private static async Task Do_ActionAsync(SignedMessageTextUpdate update)
{
    // Using ITelegramBotClient
    await update.Owner.Bot.SendTextMessageAsync(update.Sender.TelegramId, "Hello World!");

    // Using IDeliveryService
    await update.Owner.DeliveryService.SendMessageToChatAsync(update.Sender.TelegramId, "Hello World!");
}

If you need to send a message to the same chat, then instead of explicitly declaring the ID (update.Sender.TelegramId), you can use .AnswerSenderAsync(..., ISignedUpdate) method.

Message class

Also, the basic functionality provides the ability to create a message class (TelegramTextMessage). This functionality is greatly expanded by the .AdvancedMessages module (WIP, temporarily refer to v3 Review (TODO)).

private static async Task Do_ActionAsync(SignedMessageTextUpdate update)
{
    var message = new TelegramTextMessage("Hello World Message!")
    {
        AllowSendingWithoutReply = true,
        ReplyToMessageId = update.TriggerMessageId,
    };
    await update.Owner.DeliveryService.AnswerSenderAsync(message, update);
}

Localized Messages

If are developing multi-lingual bot interface you can use localizations.

private static async Task Do_ActionAsync(SignedMessageTextUpdate update)
{
    var messageText = update.Owner.ResolveBotString("app.startUp");
    var message = new TelegramTextMessage(messageText);
    await update.Owner.DeliveryService.AnswerSenderAsync(message, update);
}

Ensuring you have declared "app.startUp" string in your localization pack:

{
  "app.startUp": "..."
}

Calling Services

You can access the service provided it has been previously declared. More information on creating new services here.

private static async Task Do_ActionAsync(SignedMessageTextUpdate update)
{
    var payments = await update.Owner.ResolveService<IPaymentsService>();
    await payments.InitPayment(update.Sender, update);
}

Last review: .Core v3.1.1 Back to contents

Using Applicants

IApplicant<T> allows you to localize all bot actions in one class, which can be automatically applied to handlers/managers/other T objects.

An example of the use of Applicant is the MenuService class from the .PageNavs module.

Here MenuService as applicant, when applied to the appropriate manager, automatically declares the callbacks necessary for operation ('Back' and 'Open Page'). This adds development flexibility and allows you to divide code into logical parts.

    public interface IMenuService : IBotService, IApplicant<IActionManager<SignedCallbackUpdate>>
    {
        // ...
    }

    public class MenuService : BotServiceBase, IMenuService
    {
        public void ApplyTo(IActionManager<SignedCallbackUpdate> callbackManager)
        {
            callbackManager.AddSafely(OpenPageCallback);
            callbackManager.AddSafely(BackCallback);
        }
    }

An example of use can also be found in the project. Recommended system for using a complex Applicant - partial class using a C# nesting file.

Example Applicant (Applicant.cs)

    internal partial class MainApplicant : IApplicant<IActionManager<SignedMessageTextUpdate>>,
    {
        public void ApplyTo(IActionManager<SignedMessageTextUpdate> entity)
        {
            EnableCommands(entity);
        }

        private void EnableCommands(IActionManager<SignedMessageTextUpdate> entity)
        {
            entity.AddSafely(StartCommand);
        }

        internal static DefaultCommand StartCommand => new("start", Do_StartAsync);
        private static async Task Do_StartAsync(SignedMessageTextUpdate update) { //... }
    }

Example Applicant usage (Program.Main())

var privateCommandsManager = new LinearActionManager<SignedMessageTextUpdate>("commands.private");
applicant.ApplyTo(privateCommandsManager);

Example nesting file (.filenesting.json)

{
  "help": "https://go.microsoft.com/fwlink/?linkid=866610",
  "root": true,

  "dependentFileProviders": {
    "add": {
      "fileToFile": {
        "add": {
          "iCallbacks.cs": [ "MainApplicant.cs" ],
          "iCommands.cs": [ "MainApplicant.cs" ],
          "iInputs.cs": [ "MainApplicant.cs" ],
          "iProcesses.cs": [ "MainApplicant.cs" ],
          "iDataSets.cs": [ "MainApplicant.cs" ],
          "iMenus.cs": [ "MainApplicant.cs" ]
        }
      }
    }
  }
}

Applicant Nesting Example

Last review: .Core v3.1.1 Back to contents

Create Services

Services require IBotService implementation (IBotService : IDebugNamed, IOwnerCompilable). Module provides OwnedObject (see Owning) and BotServiceBase classes.

public interface IPaymentsService : IBotService
{
    public Task InitPayment(IBotUser payer, ISignedUpdate update);
}
public interface PaymentsService : BotServiceBase, IPaymentsService
{
    public async Task InitPayment(IBotUser payer, ISignedUpdate update)
    {
        // You can access BotManager via inherited Owner property.
    }
}

On bot creating add service or ServiceNotDefinedException would be thrown on ResolveService<T>() attempt.

var bot = BotBuilder.NewBuilder("your_api_key")
    .EnablePrivates(privates)
    .AddService<IPaymentsService>(new PaymentsService())
    .Build();

Last review: .Core v3.1.1 Back to contents

Users Manager

To enable Users Manager in your project write custom UsersManager class, implementing IUsersManager. UM is used to collect and store user data during runtime (for ex. to save user state (TODO: add refer))

    internal class UsersManager : IUsersManager
    {
        public UserDataChanged<IBotUser>? SignedUpdateHandled { get; set; }

        private readonly List<DefaultBotUser> _users = [];

        public async Task<bool> CheckIfRegisteredAsync(long telegramId) => await GetUserByIdAsync(telegramId) is not null;

        public async Task<IBotUser?> GetUserByIdAsync(long telegramId) => await Task.FromResult(_users.Find(x => x.TelegramId == telegramId));

        public async Task<IBotUser?> RegisterNewUserAsync(ICastedUpdate update)
        {
            var user = TelegramHelper.GetSender(update.OriginalSource, this)!;
            var @new = new DefaultBotUser(user.Id, user.IsPremium.GetValueOrDefault(), user.LanguageCode ?? "en", user.FirstName);
            _users.Add(@new);
            return await Task.FromResult(@new);
        }
    }
ChatDesigner privates = ChatDesigner.NewDesigner()
    .UseUsersManager(usersManager)
    // ...

Last review: .Core v3.1.1 Back to contents