Sonntag, 2. August 2020

Discord-Bots mit der ASP.net-Web-Site hosten

Beim arbeiten am Projekt "Thai-Service.net", einer Web-Applikation im "BtoC"-Umfeld, stehen Kommunikationskanäle ganz oben an. So bin ich auf "Discord" aufmerksam geworden. Eigentlich ein Instand-Messaging-Dienst der ans Spieleumfeld angelegt ist. Mich haben die API-Schnittstellen begeistert.

Man kann da zum Beispiel "Bots" einrichten, die sehr vielfältige Aufgaben des Administrators automatisch erledigen (davon gibt es sehr viele "out of the box") oder, und da wird es interessant, man greift zu einer der beiden "C#"-Libraries und baut seine eigenen Bots. "Discord" hat eine APi, die allerdings nicht so schön ".net"-optimiert ist. Beide Libraries scheinen auch die gleichen Väter zu haben, den sie ähneln sich sehr. Der eine Anbieter nennt sein Produkt "Discord.net", der zweite seines "DSharpPlus". Warum ich mich bei der Auswahl auf zweiteres Produkt festgelegt habe, weiß ich nicht mehr, es muß mit der Dokumentation zusammen gehangen haben.

Da ich mit "Discord" erst seit zwei Tagen zu tun habe, kann ich nicht sehr viel erzählen darüber. Ich habe die Zeit genutzt, einiges auszuprobieren und vor allen Dingen ein Problem zu lösen, daß mir den Einsatz fast unmöglich gemacht hätte. Die Dokumentation beruht ausschließlich auf einer Konsolenanwendung basierend auf dem ASP.net-Framework "Core". Das würde bedeuten, daß ich neben dem Web-Space auch Server-Ressourcen anmieten müßte (so schlagen es auch die Dokumentationen vor). Das wäre es mir nicht wert gewesen.

Daher habe ich mir das ganze noch einmal angesehen und bin zum Schluß gekommen, das in die Web-Site zu integrieren. Die ist, wie wir wissen, dafür jedoch nicht gemacht. Das richtige wäre ein Dienst gewesen, aber auch das scheitert an den Vorgaben fast aller Anbieter von Web-Space 

Als erstes muß der "Bot" zum Leben erweckt werden und zwar 24 x 7, ohne daß die Web-Site beeinflußt wird (das könnte man auch anders lösen, aber mein Anspruch war die Integration)- So bin ich darauf verfallen den "Bot" in einen "generischen Handler" zu packen. Er sollte ja nur angestoßen werden und ab dann die Events des "Bots" aufzunehmen. Ich hatte auch andere Lösungen im Sinn ("Signal R" zum Beispiel), aber das wäre einfach zu aufwändig gewesen, auch wenn es den "saubereren" Ansatz darstellte.

Okay, gehen wir den unsauberen Weg. Als erstes sehen wir uns den "Handler" an, "BotResponse.ashx.cs":

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using DSharpPlus;
using DSharpPlus.Interactivity;
using DSharpPlus.CommandsNext;
using System.Threading.Tasks;

namespace ThaiService.Content
{
    /// <summary>
    /// Here the Discord-Bot is activated and the messages in the channel are intercepted and analyzed.
    /// </summary>
    public class BotResponse : IHttpHandler
    {
        static DiscordClient discord;
        static CommandsNextModule commands;
        static InteractivityModule interactivity;
        Dictionary<string, string> settings = new Models.DiscordBot().ReadSetting();

        public async void ProcessRequest(HttpContext context)
        {
            discord = new DiscordClient(new DiscordConfiguration
            {
                Token = (settings.TryGetValue("BasicBotToken", out string value)) ? value : "",
                TokenType = TokenType.Bot,
                UseInternalLogHandler = true,    // Here we must work on
                LogLevel = LogLevel.Debug
            });

            discord.GuildMemberAdded += async e =>
            {
                // In the future this messages are set in a database.
                await e.Member.SendMessageAsync("Hello, welcome on board!");
            };

            discord.MessageCreated += async e =>
            {
                if (e.Message.Content.ToLower().StartsWith("thai-service"))
                {
                    new Models.DiscordBot().AddMessage(e.Message);
                    await e.Message.RespondAsync("Thank you! The message is send to us...");
                }
            };

            discord.MessageCreated += async e =>
            {
                if (e.Message.Content.ToLower().StartsWith("ping"))
                    await e.Message.RespondAsync("pong!");
            };

            commands = discord.UseCommandsNext(new CommandsNextConfiguration
            {
                StringPrefix = (settings.TryGetValue("BasicBotPrefix", out value)) ? value : ""
            });

            await discord.ConnectAsync();
            await Task.Delay(-1);
        }



        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
    }


Im Source der "BotResponse.ashx.cs" habe ich einen Aufruf gelb gemarkert. Das wäre ein Beispiel dafür wie ein Event aus "Discord" heraus, an eine Funktion weiter geleitet und verarbeitet wird.
Ihr müsst Euch noch eine Classe basteln, die ggf. Commands enthält und Euch auch die Settings, Ins Besonderen das Token Eures Bots zurück gibt. Das ist in beiden Libraries sehr gut dokumentiert.

Der Aufruf in der "Default.aspx.cs" kreiert ein "Warning", da das "await" fehlt, Überseht es einfach einmal... (Wir können keinen "Awaiter" einfügen, da ansonsten unsere Web-Site nicht geladen würde. Da gibt es bessere Lösungen aber ich will ja sehen, das es läuft.)


    public partial class _Default : Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            lblBot.Text = startBot().Result;
        }

        private async Task<string> startBot()
        {
            // Test if the task is alrady started
            if (Session["task"] == null)
            {
                HttpContext context = HttpContext.Current;
                Session["task"] = Task.Run(() => new BotResponse().ProcessRequest(context));
                return "bot started!";
            }
            else
            {
// Hier kann ich ggf. feststellen, ob mein Bot noch läuft...
                return "bot is " + ((Task)Session["task"]).Status;
            }
        }
    }


In "Discord" kann man jetzt "ping" oder "thai-service" aufrufen und löst damit eine Aktion aus:



Der Aufruf "thai-service" am Beginn einer Nachricht führt dazu, daß eine Hinweis in unserem Messaging-System von "Thai-Service.net" geposted wird. Der Response in "Discord" ist ein schlichtes "Danke, die Nachricht wurde an uns gesendet..."




Achtung bitte:
Die Site "Thai-Service.net" ist noch nicht online. Daher könnt Ihr einen Live-Test nur ausprobieren, wenn ich sie an meinem Entwicklungsrechner gestartet habe....

Ihr könnt es gerne probieren, der Einladungslink ist: https://discord.gg/ETUKs8f






Wichtige Links:
"Discord" Web-Site: https://discord.com/new
"Discord" for Developer: https://discord.com/developers/


Keine Kommentare:

Kommentar posten