半人前プログラマーの技術談議

開発したもの紹介していくブログ

C# 非同期処理編 Discord BOT part1-☆2

前回紹介したDiscordBotについて紹介します!

 

ソースコード

Program.cs


using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.DependencyInjection;

using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace NoticeBot
{
    class Program
    {
        public static DiscordSocketClient client;
        public static CommandService commands;
        public static IServiceProvider services;
        private const ulong chanelid = チャンネルID;  // 通知用チャンネルID
        public static bool observe = false; // 自動通知制御用

        static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult();

        /// 
        /// 起動時処理
        /// 
        /// 
        public async Task MainAsync()
        {
            client = new DiscordSocketClient();
            commands = new CommandService();
            services = new ServiceCollection().BuildServiceProvider();
            client.MessageReceived += CommandRecieved;

            client.Log += Log;
            string token = "トークン";
            await commands.AddModulesAsync(Assembly.GetEntryAssembly());
            await client.LoginAsync(TokenType.Bot, token);
            await client.StartAsync();

            await Task.Delay(-1);
        }

        /// 
        /// メッセージの受信処理
        /// 
        ///
        /// 
        private async Task CommandRecieved(SocketMessage messageParam)
        {
            var message = messageParam as SocketUserMessage;
            Console.WriteLine("{0} {1}:{2}", message.Channel.Name, message.Author.Username, message);

            if (message == null) { return; }
            // コメントがユーザーかBotかの判定
            if (message.Author.IsBot) { return; }

            int argPos = 0;

            // コマンドかどうか判定(今回は、「?」で判定)
            if (!(message.HasCharPrefix('\\', ref argPos) || message.HasMentionPrefix(client.CurrentUser, ref argPos))) { return; }

            var context = new CommandContext(client, message);

            // 実行
            var result = await commands.ExecuteAsync(context, argPos, services);

            //実行できなかった場合
            if (!result.IsSuccess) { await context.Channel.SendMessageAsync(result.ErrorReason); }

        }

        /// 
        /// コンソール表示処理
        /// 
        ///
        /// 
        private Task Log(LogMessage msg)
        {
            Console.WriteLine(msg.ToString());
            return Task.FromResult(msg);
        }


        // 常駐処理
        public async Task autoNotif()
        {
            var task = Task.Run(() =>
            {
                Process pros = new Process();
                var chatchannnel = client.GetChannel(chanelid) as SocketTextChannel;
                int ct = 0;

                while (observe == true) // 自動通知がオンの場合のみ
                {
                    Console.WriteLine("オンライン情報を取得します");
                    string str = pros.processing();

                    if (pros.online == true) // オンライン状態の場合
                    {
                        if (ct % 60 == 0) // 60分に1回,Discord上に通知する
                        {
                            ct = 0;
                            chatchannnel.SendMessageAsync(str);
                        }
                        else
                        {
                            Console.WriteLine(str);
                        }

                        ct++;

                        Console.WriteLine("1分間待機します");
                        Thread.Sleep(1 * 60 * 1000);
                    }
                    else // オフライン状態の場合
                    {
                        chatchannnel.SendMessageAsync(str);
                        ct = 0; // オンラインに切り替わった時に即通知するように,0に設定
                        Console.WriteLine("3分間待機します");
                        Thread.Sleep(3 * 60 * 1000);
                    }
                }
            });

            await task;
        }


    }
}

 

Message.cs


using Discord.Commands;
using System;
using System.Threading.Tasks;

namespace NoticeBot
{
    public class Message : ModuleBase
    {

        /// 
        /// [ping]というコメントが来た際の処理
        /// 
        /// Botのコメント
        [Command("ping")]
        public async Task ping()
        {
            await ReplyAsync("pong");
        }

        /// 
        /// [observe]というコメントが来た際の処理
        /// 
        /// Botのコメント
        [Command("observe")]
        public async Task observe()
        {
            Process pros = new Process();
            string str = pros.processing();
            await ReplyAsync(str);
        }

        /// 
        /// [autoobs]というコメントが来た際の処理
        /// 
        /// Botのコメント
        [Command("autoobs")]
        public async Task autoobs()
        {
            string msg = "";
            if (Program.observe == false)
            {
                msg = "自動通知機能を有効にしました。";
                Program.observe = true;
                Console.WriteLine(Program.observe);
                await ReplyAsync(msg);
                new Program().autoNotif();
            }
            else
            {
                msg = "自動通知機能を無効にしました。";
                Program.observe = false;
                Console.WriteLine(Program.observe);
                await ReplyAsync(msg);
            }
        }
    }
}

 

Web.cs


using System;
using System.Net;

namespace NoticeBot
{
    public class Web
    {
        private bool mode = false;
        private string population = "";

        public void Extraction()
        {
            processing();
        }

        private string GetWeb()
        {
            WebClient client = new WebClient();
            string str = client.DownloadString("https://mapleroyals.com/api/v1/get_status");
            client.Dispose();
            return str;
        }

        private string[] StrProcess()
        {
            String str = GetWeb();
            str = str.Substring(1, str.Length - 2);
            string[] arr = str.Split(',');
            arr[0] = arr[0].Substring(14);
            arr[1] = arr[1].Substring(9);
            return arr;
        }

        private void processing()
        {
            String[] arr = StrProcess();

            if (arr[1] != "false")
                mode = true;
            else
                mode = false;

            population = arr[0];
        }

        public bool isOnline()
        {
            return mode;
        }

        public string isPopulation()
        {
            return population;
        }


    }
}

 

Process.cs


namespace NoticeBot
{
    public class Process
    {
        public bool online = false;

        public string processing()
        {
            Web web = new Web();
            web.Extraction();
            online = web.isOnline();

            string msg = "";

            if (web.isOnline() == true)
            {
                msg += "MapleRoyals:**ONLINE**";
                msg += "\n現在のプレイヤー数は**" + web.isPopulation() + "人**です。\n";
            }
            else
            {
                msg += "MapleRoyals:**OFFLINE**";
            }

            return msg;
        }
    }
}

 

実はこれ作成する為に,非同期処理について勉強していました.

起動時の処理などは,

https://discord.foxbot.me/docs/guides/getting_started/installing.html

こちらの情報を参考にさせて頂いたんですが,

情報が全て英語なので解読が結構大変でした.

 

前回紹介したこちらの記事も参考にしました.

https://qiita.com/HAGITAKO/items/fff2e029064ea38ff13a

 

C#でDiscordのBOTを作成している情報って結構少ないんですよね・・・

javascriptPythonで作ってる方が多いので・・・

 

では,自分が実装した部分ですが,

 

observeコマンドを受け取ったら,サーバーの状態を取得します.

 

サーバーの状態はWebClientクラスを使って,URLにアクセスして,文字列で取得しています.

ちなみにMapleRoyalsっていうMMORPGのサーバーの状態を監視しているんですよね.

取得した文字列を処理して,見やすい形に整理しているのが,Web.csです.

 

表示する内容は,Process.csで処理しています.

 

最後に,Processクラスのprocessingで処理された文字列を

await ReplyAsync

によって,Discord上に表示させます.

 

 

これに常駐機能を追加したのがautoobsコマンドです.

 

 

もっと綺麗に書けるんだろうけど,今の自分にはこれが限界でした・・・

改善点などありましたら,遠慮なくご指摘頂ければ幸いです.