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を作成している情報って結構少ないんですよね・・・
javascriptやPythonで作ってる方が多いので・・・
では,自分が実装した部分ですが,
observeコマンドを受け取ったら,サーバーの状態を取得します.
サーバーの状態はWebClientクラスを使って,URLにアクセスして,文字列で取得しています.
ちなみにMapleRoyalsっていうMMORPGのサーバーの状態を監視しているんですよね.
取得した文字列を処理して,見やすい形に整理しているのが,Web.csです.
表示する内容は,Process.csで処理しています.
最後に,Processクラスのprocessingで処理された文字列を
await ReplyAsync
によって,Discord上に表示させます.
これに常駐機能を追加したのがautoobsコマンドです.
もっと綺麗に書けるんだろうけど,今の自分にはこれが限界でした・・・
改善点などありましたら,遠慮なくご指摘頂ければ幸いです.