ardifx01 Profile Photo
Muhammad Nadhif

Membangun Bot di .NET dengan Bot Framework SDK

dhiff
dhiff
Membangun Bot di .NET dengan Bot Framework SDK

Artikel ini memandu kamu membangun bot berbasis HTTP di .NET menggunakan Bot Framework SDK: menyiapkan proyek, menulis adapter, controller, logika bot (ActivityHandler), menyimpan percakapan dengan state, membuat dialog, menambah NLU, menguji via Emulator, hingga gambaran deployment.

Visualisasi Arsitektur

Prasyarat

  • .NET 7/8 SDK dan VS Code atau Visual Studio.
  • Bot Framework Emulator untuk uji lokal.
  • Ngrok (opsional) jika ingin menerima callback publik.
  • Akun Azure (opsional) untuk publish dan channel.

Inisialisasi Proyek

# Buat Web API minimal
dotnet new webapi -n DotNetBot
cd DotNetBot

# Tambah paket Bot Framework
dotnet add package Microsoft.Bot.Builder
dotnet add package Microsoft.Bot.Builder.Integration.AspNet.Core

# (Opsional) dialog, LUIS/recognizer
dotnet add package Microsoft.Bot.Builder.Dialogs
dotnet add package Microsoft.Bot.Builder.AI.Luis

Adapter HTTP

using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder;
using Microsoft.Extensions.Logging;

public class AdapterWithErrorHandler : BotFrameworkHttpAdapter
{
    public AdapterWithErrorHandler(ILogger<BotFrameworkHttpAdapter> logger)
    {
        OnTurnError = async (turnContext, exception) =>
        {
            logger.LogError(exception, "Bot error");
            await turnContext.SendActivityAsync("Maaf, terjadi error. Coba lagi.");
        };
    }
}

Controller Endpoint

using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;

[ApiController]
[Route("api/messages")]
public class BotController : ControllerBase
{
    private readonly IBot _bot;
    private readonly BotFrameworkHttpAdapter _adapter;

    public BotController(BotFrameworkHttpAdapter adapter, IBot bot)
    {
        _adapter = adapter;
        _bot = bot;
    }

    [HttpPost, HttpGet]
    public async Task PostAsync()
        => await _adapter.ProcessAsync(Request, Response, _bot);
}

Bot Sederhana: Echo

using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;

public class EchoBot : ActivityHandler
{
    protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> context, CancellationToken ct)
    {
        var text = context.Activity.Text?.Trim();
        await context.SendActivityAsync(MessageFactory.Text($"Kamu berkata: {text}"), ct);
    }

    protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> context, CancellationToken ct)
    {
        foreach (var m in membersAdded)
            if (m.Id != context.Activity.Recipient?.Id)
                await context.SendActivityAsync("Halo! Kirim pesan apa saja dan akan saya echo.", ct: ct);
    }
}

Registrasi Service (Program.cs)

var builder = WebApplication.CreateBuilder(args);

// Adapter + Bot
builder.Services.AddSingleton<BotFrameworkHttpAdapter, AdapterWithErrorHandler>();
builder.Services.AddSingleton<IBot, EchoBot>();

// State (in-memory untuk dev)
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.MemoryStorage;
var storage = new MemoryStorage();
builder.Services.AddSingleton<IStorage>(storage);
builder.Services.AddSingleton<UserState>();
builder.Services.AddSingleton<ConversationState>();

var app = builder.Build();
app.MapControllers();
app.Run();

Menambah State di Bot

public class EchoBot : ActivityHandler
{
    private readonly UserState _userState;
    private readonly ConversationState _convState;
    private readonly IStatePropertyAccessor<string> _nameAccessor;

    public EchoBot(UserState userState, ConversationState convState)
    {
        _userState = userState;
        _convState = convState;
        _nameAccessor = _userState.CreateProperty<string>("name");
    }

    protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> context, CancellationToken ct)
    {
        var text = context.Activity.Text?.Trim().ToLowerInvariant();
        var name = await _nameAccessor.GetAsync(context, () => string.Empty, ct);

        if (text?.StartsWith("set nama ") == true)
        {
            name = text.Substring("set nama ".Length);
            await _nameAccessor.SetAsync(context, name, ct);
            await context.SendActivityAsync($"Nama disimpan: {name}", ct: ct);
        }
        else
        {
            await context.SendActivityAsync($"Hai {name or 'teman'} — kamu berkata: {context.Activity.Text}", ct: ct);
        }

        await _userState.SaveChangesAsync(context, false, ct);
        await _convState.SaveChangesAsync(context, false, ct);
    }
}

Dialog Waterfall (opsional)

using Microsoft.Bot.Builder.Dialogs;

public class ProfileDialog : ComponentDialog
{
    public ProfileDialog() : base(nameof(ProfileDialog))
    {
        var steps = new WaterfallStep[]
        {
            async (step, ct) => { return await step.PromptAsync("name", new PromptOptions{ Prompt = MessageFactory.Text("Siapa namamu?") }, ct); },
            async (step, ct) => { var name = (string)step.Result; await step.Context.SendActivityAsync($"Halo {name}!", ct: ct); return await step.EndDialogAsync(null, ct); }
        };
        AddDialog(new TextPrompt("name"));
        AddDialog(new WaterfallDialog(nameof(WaterfallDialog), steps));
        InitialDialogId = nameof(WaterfallDialog);
    }
}

Integrasi NLU (LUIS/CLU) singkat

// Pseudo: panggil recognizer lalu route intent
var (intent, score) = await RecognizeIntentAsync(context.Activity.Text);
if (intent == "Help") { await context.SendActivityAsync("Perintah: set nama <teks>, help, bye"); }
else if (intent == "Bye") { await context.SendActivityAsync("Sampai jumpa!"); }

Uji dengan Bot Framework Emulator

  • Jalankan: dotnet run.
  • Buka Emulator, Open Bot, endpoint: http://localhost:5000/api/messages (atau port proyekmu).
  • Kirim pesan dan pastikan respon sesuai.

Deployment singkat

  • Publish ke Azure App Service.
  • Buat Azure Bot resource (Channel Registration) dan arahkan Messaging endpoint ke https:///api/messages.
  • Aktifkan channel (Teams, Telegram, Web Chat) sesuai kebutuhan.

Keamanan dan Praktik Baik

  • Simpan secrets di Key Vault atau user-secrets, jangan di repo.
  • Batasi ukuran payload dan rate limit endpoint.
  • Aktifkan logging terstruktur dan correlation id.
  • Gunakan penyimpanan state persisten (Cosmos/Blob) untuk produksi.

Troubleshooting umum

  • 401 pada Emulator: kosongkan MicrosoftAppId/MicrosoftAppPassword saat uji lokal.
  • 404: pastikan route api/messages benar dan controller terdaftar.
  • Tidak ada balasan: cek OnTurnError dan log adapter, cek firewall/ngrok jika publik.

Kesimpulan

Komponen inti bot di .NET: adapter untuk HTTP, controller sebagai endpoint, ActivityHandler untuk logika, state untuk konteks, dialog untuk alur, dan NLU untuk pemahaman. Mulai sederhana, amankan endpoint, lalu orkestrasi dialog dan channel sesuai kasus penggunaan.