Sonntag, 11. Oktober 2020

Bleun-Sales veröffentlich

 Black-Garlic-Powder innovativ hergestellt und im Vertrieb

Bleun-Logo

Die "Bleun® Engineering" hilft im Rahmen seines Beratungsauftrags jetzt auch im Vertrieb, so scheint es. Sprecher der Technikschmiede betonen jedoch: "Das soll die Ausnahme bleiben. Frau Bunprasoet ist nicht nur eine Kundin, sondern auch eine liebe Freundin unseres Hause, daher haben wir den online Auftritt übernommen."

Wir als "wjk-Software®", als beauftragtes Unternehmen, freut es natürlich, daher helfen wir auch beim Auftritt in den Social Media und in weiteren Aufbau des Vertriebs. Unser Haus war ja schon in früheren Jahren für bei Unternehmen tätig.

Sieht man sich die Site genauer an, kommt auch zum Ausdruck, daß der Vertrieb in den vertrauten Händen von "Nan's Online Shop" verbleibt und die Technik, die dahinter steckt, bei "Bleun" entwickelt wurde. So bleibt alles beim alten: Jeder macht, was er am besten kann.

Wir, die wir die Site entwickelt haben, hatten nur die Vorgabe, daß das Projekt flexibel eingesetzt werden kann. So haben wir es bei HTML und CSS3 belassen. Ein wenig JavaScript war nötig, um den Bestellprozeß anzustoßen und andere kleine Funktionalität umzusetzen-

Jetzt freuen wir uns, daß unsere Kunden zufrieden sind und wünschen viel Erfolg.

Diw Web-Site finden sie hier: Bleun-Sales





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/


Montag, 27. Juli 2020

ASP.net WebForms und "Googles" ReCaptcha

Nach verschiedenen Ausflügen in andere Bereiche meines Berufes (Desktop-, Mobile, IoT und reine HTML-Entwicklung) bin ich wieder bei einem Lieblinge angekommen den WebForms in ASP.net. Dort habe ich schon verschiedentlich "Captchas" eingebaut, auch das "ReCaptcha" von "Google", allerdings die Version 1.0, die nicht mehr unterstütz wird. Da gab es eine kleine DLL, die einem die meiste Arbeit abnahm, auf die wollte und konnte ich mich jetzt nicht verlassen.

Also habe ich ein wenig herumgesucht und bin, wie zumeist, bei "Stackoverflow" fündig geworden. Wie immer mußte man verschiedene Lösungsansätze zusammenbringen, um ein Ergebnis zu erzielen. Da es diesbezüglich Interesse zu geben scheint, will ich meinen Ansatz hier veröffentlichen.


Zunächst bauen wir den Zugriff auf die "Google-API" in den "Header" in der "Master.aspx" oder, wenn Ihr ohne "Master" arbeitet, in den Header der ".aspx"-Page ein, wo das "ReCaptcha" erscheinen soll:
Ich habe das so gelöst, daß ich in der "Master" einen "Content-Bereich für "JavaScript" in den Header geschrieben habe, benötige ich jetzt auf einer Seite spezielles "JavaScript", dann kann ich das auf den Pages bequem im Content plazieren. Dort kommt z.B. auch der HTML-Editor für die User rein, den benötige ich ja auch nicht auf allen Seiten.
Das sieht dann so aus:

Master.aspx

(...)
<html lang="en">
<head runat="server">
(...)
    <asp:ContentPlaceHolder ID="ScriptContent" runat="server">
    </asp:ContentPlaceHolder>
(...)
</head>
(...)


bei mir ist das die "Contact.aspx", da kommt rein:

(...)
<asp:Content ID="Content0" ContentPlaceHolderID="ScriptContent" runat="server">
(...)
    <script type="text/javascript" src="https://www.google.com/recaptcha/api.js" async            defer></script>
</asp:Content>
(...)


Ansonsten baut den Teil irgendwie in den "Header" ein:
<script type="text/javascript" src="https://www.google.com/recaptcha/api.js" async defer></script>

und anschließend den DIV-Tag, wo das ReCptcha-Widget gerendert werden soll:

<div id="dvCaptcha" class="g-recaptcha" data-sitekey="<%: ReCaptcha_Key %>"></div>


Den "ReCaptcha_Key" machen wir uns im Quellcode zugänglich (ich arbeite ausschließlich mit C#. solltet Ihr VB oder etwas ähnliches verwenden, müßt Ihr das leicht anpassen).

Bei mir heißt dann die "CodeBehind": Contact.aspx.cs und die startet mit:

{
    public partial class Contact : Page
    {
        (...)
        protected static string ReCaptcha_Key = "<RECaptcha Site Key>";

        protected static string ReCaptcha_Secret = "<RECaptcha Secret Key>";
(...)


Diese Declaration macht die Variabeln für das "ReCaptch" pagewide verfügbar und wir konnten den "ReCaptcha_Key" auf der "aspx" einfügen.

Jetzt müssen wir unserem "ReCaptcha" Leben einhauchen (Funktionalität geben), sonst sieht es zwar schön aus, wird aber keinen Einfluß auf die Validierung unseres Formulars haben. Da ich grundsätzlich alle Daten validiere, habe ich diese Funktion einfach um einen Methodeaufruf erweitert:

        protected void btnSendMessage_Click(object sender, EventArgs e)
        {
(...)
            try
            {
(...)
                if (!IsValidCaptcha()) throw new ValidationException("The ReCaptcha is wrong!");
(...)
            catch (Exception ex)
            {
                lblResult.Text = ex.Message;
                lblResult.ForeColor = System.Drawing.Color.Red;
            }
        }


Jetzt müssen wir nur noch das "ReCaptcha" validieren lassen:

        public bool IsValidCaptcha()
        {
            string resp = Request["g-recaptcha-response"];
            string quest = string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", ReCaptcha_Secret, resp);
            var req = (HttpWebRequest)WebRequest.Create
                      (quest);
            using (WebResponse wResponse = req.GetResponse())
            {
                using (StreamReader readStream = new StreamReader(wResponse.GetResponseStream()))
                {
                    string jsonResponse = readStream.ReadToEnd();
                    JavaScriptSerializer js = new JavaScriptSerializer();
                    // Deserialize Json
                    CaptchaResult data = js.Deserialize<CaptchaResult>(jsonResponse);
                    if (Convert.ToBoolean(data.success))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        public class CaptchaResult
        {
            public string success { get; set; }
        }


Da es jetzt haufenweise Fehlermeldungen hagelt, fügt bitte an entsprechender Stelle noch die "Using-Anweisungen" ein:

using System.Net;
using System.IO;
using System.Web.Script.Serialization;
using System.ComponentModel.DataAnnotations;


Hier noch einige Links, die Ihr ggf. brauchen könnt.

ReCaptcha Homepage: https://www.google.com/recaptcha/intro/v3.html

Falls Du schon einen Account bei Google hast, kommst Du hier zur richtigen Admin-Console:
https://www.google.com/recaptcha/admin/site/xxxxxxx

Und hier habe ich die Lösung ursprünglich gefunden:
https://stackoverflow.com/questions/27764692/validating-recaptcha-2-no-captcha-recaptcha-in-asp-nets-server-side

und ein herzliches Danke schön an Tabish Usman.