まあ表題で完結してるんですが、一応事細かに書いて行きたいと思います。
先に言っておくと運用コストとかルールブックの著作権だとかでいろいろ問題があるのでBotの公開はありません。
開発動機
ダイスロールをしてくれるDiscordのBotといえば Sidekick などが有名で、多くのTRPGサーバーで使用されています。
例えば /r 1d100
と打てば100面ダイスのダイスロールをしてくれます。便利ですね。
でもやっぱりそれだけでは(機能が)足りないし、可愛げがない
TRPGといえば有名所で言えばSW2.0や、DX3rdですね。僕の参加しているDiscordグループでもやはりこの2つがメジャーです(本当はネクロニカとかアリアンロッドもしたいんですけど)。
そしてSW2.0(ソード・ワールド2.0)でよくやる処理と言えば
- 成長
- 経歴表
などがあります。
DX3rd(ダブルクロス The 3rd Edition)でよくやる処理といえば
- 経験表
- 覚醒表
- 衝動表
などがあります。
これを通常のダイスロールで行うのは面倒です。
まあ、サイコロを1つ1つ転がして一喜一憂する楽しみも否定されるべきではないと思いますが、それなら(Botを)使わなければいいだけの話なので僕は作ります。
可愛げ?
必要です。
ほしい機能
開発言語
C#です。今回Botを制作するにあたり
を使っています。
実施詳細
まずDiscordのBotをサクッと作ります。
これについてはググれば ■ここ とか ■ここ とか ■ここ とかが出てくるので紹介しません。
埋め込みオブジェクトで自己紹介させる
いわゆる--help
コマンドですね。これはなんてことはなくて、一部抜粋すれば
public override async Task<bool> Run(SocketMessage msg)
{
var asm = Assembly.GetExecutingAssembly();
//AssemblyCopyrightの取得
var copyright = ((AssemblyCopyrightAttribute)Attribute.GetCustomAttribute(asm, typeof(AssemblyCopyrightAttribute))).Copyright;
//バージョンの取得
var version = asm.GetName().Version.ToString();
var abuilder = new EmbedAuthorBuilder();
abuilder.WithName("[Botの名前]");
abuilder.WithIconUrl("[アイコンのURL]");
abuilder.WithUrl("[マニュアルのURL]");
var builder = new EmbedBuilder();
builder.WithTitle("[タイトル]");
builder.WithThumbnailUrl("[アイコンのURL]");
builder.WithAuthor(abuilder);
builder.WithColor(Color.DarkRed);
builder.WithDescription("[Botの説明]");
builder.AddInlineField("マニュアル", "[マニュアルのURL]");
builder.AddInlineField("名前", "[Botの名前]");
builder.AddInlineField("特別協賛", "[特別協賛のリスト(実際は関数でJoinしています)]");
builder.AddInlineField("バージョン", version);
builder.AddInlineField("コピーライト", copyright);
await msg.Channel.SendMessageAsync(string.Empty, false, builder); //ここらへんは実際はWrapしたFactoryクラスに任せてる
return true;
}
みたいに書けばいいだけです。楽でいいわ。ありがとうDiscord.NETの人。
SSMSと連携するコマンド
これも楽で、App.config
に<connectionStrings>
を書いていろいろ書いてたらできます。
public static string ConnectionString => ConfigurationManager.ConnectionStrings["Database"].ConnectionString;
public static SqlConnection CreateConnection()
{
return new SqlConnection(ConnectionString);
}
public static void RunSql(Action<SqlConnection> action)
{
using (var con = CreateConnection())
{
action(con);
}
}
とか書いてコマンドの方からRunSql
を呼び出してあげれば楽でいいです。
public override async Task<bool> Run(SocketMessage msg)
{
var 好感度 = default(int);
SqlConnectionFactory.RunSql(con =>
{
var cmd = con.CreateCommand();
cmd.CommandText = "SELECT 好感度 FROM UserData WHERE ID=@id";
cmd.Parameters.AddWithValue("@id", msg.Author.Id);
con.Open();
var sdr = cmd.ExecuteReader();
if(sdr.HasRows)
{
if (sdr.Read())//コマンドによってwhileだったりifだったりする
{
好感度 = (int)sdr["好感度"];
}
}
});
//ここに処理を書く
return true;
}
Botに喋らせる
仕組みを考える
Discord -> 拙作プログラム1 -> 葵ちゃん -> 拙作プログラム2 -> Discord
でいけると思います。
- 拙作プログラム1
- Discordからメッセージを受け取り葵ちゃんに読ませる( UIAutomation )
- 葵ちゃんのウィンドウをSpyでSpySpyするとWPFでできているのがわかる(らしい)のでこの方法を採用(したい)
- 拙作プログラム2
- 葵ちゃんの生音声をDiscordにリダイレクト
- 難関
最初からUIAutomationに挑むのは難関なので、試しにRemoteTalkという便利なものが用意されている棒読みちゃんに喋らせます。
つまり
Discord -> 拙作プログラム1 -> RemoteTalk.exe -> 棒読みちゃん -> 拙作プログラム2 -> Discord
でやります。
拙作プログラム1
Processで呼んであげるだけなので楽です。
private static Process VoiceProxy { set; get; }
public static void Start()
{
VoiceProxy = Process.Start(@"[パス]\Discord Audio Stream Bot.exe");
}
public static void Exit()
{
VoiceProxy.WaitForExit();
}
public static void TalkBouyomi(string message)
{
Process.Start(@"[棒読みちゃんのパス]\RemoteTalk\RemoteTalk.exe", $"/Talk {message}");
}
Nullチェックとか各種検査(例えばmessageに棒読みちゃんに有効なパラメータとかが入ってた場合とか、| rm -rfとか)をしてないのが気になりますが、いいんです。コストがかかるだけです。YAGNIだよ兄貴。
拙作プログラム2
これが問題で、まず1つ思いついたのはNET DUETTOの出力を入力にしてくれるデバイスを拝借して、葵ちゃんの出力をマイクとして使用する方法です。
葵ちゃん -> 《NET DUETTO(出力) -> NET DUETTO(入力)》 -> 拙作プログラム2 -> Discord
で、多分これで問題ないと思うんですが、どうやってコード上でBotのマイクを設定すればいいんでしょうか……?
そんなニッチな需要のものググっても流石に出てこないだろうしな~~~出ました。神か。
…………
(ソースコード)読めない……
という訳でバイナリをありがたく拝借し、この項目についてはひとまず完成となりました。詳しく知りたいならDiscord Audio Stream Botのリポジトリを見るといいぞ。僕は諦めました。
ボイチャのユーザーを移動させたい
卓をやっていると、その時間に普段のGeneralなボイチャから、特定のシステムのボイチャに集まりたいことがあります。
ググってもあまり出てこなくて地味にハマったところなので一応。ソースコード中のClient
は接続済みのDiscordSocketClient
です。
var guild = Bot.Client.GetGuild([サーバーのID]);
var vcID = [ボイチャのID];
var channel = guild.GetVoiceChannel(vcID);
var user = guild.GetUser((ulong)[移動させたいユーザーのID]);
await user.ModifyAsync(e => e.Channel = channel);
でできます。
参考
- VOICEROID2にプログラムから喋らせる https://hgotoh.jp/wiki/doku.php/documents/voiceroid/voiceroid-008
- DiscordにVOICEROID2の音声を出力するためのデバイス(NET DUETTO)を用意 http://ch.nicovideo.jp/ironia/blomaga/ar1468999
- ボイチャユーザーを移動させる https://github.com/RogueException/Discord.Net/issues/899
- 埋め込みオブジェクト https://discordapp.com/developers/docs/resources/channel#embed-object
- デバイスからDiscordに音声を出力してくれるBot https://github.com/BinkanSalaryman/Discord-Audio-Stream-Bot