ぐうたら備忘録

趣味で作ったものなどをぼちぼち書いていきます。

Discordの通知を棒読みちゃんに読み上げてもらおう!~Discord.Netを添えて~

この記事はSLP KBIT Advent Calendar 2017 - Adventarの19日目の記事となります。

いい感じの導入

生粋のゲーマーである私は、「壁を隔てたお友達」と遊ぶ時に
Discordというナウいゲーマー熱いアプリを公開当初から通話に使っています。
そこで通話するとき、聞き専(「リビングゲーマー」、「ママが怖い」等の理由で、
ボイスチャットに声で参加できない人)のチャットを見るために、
ゲーム画面から目を離せないときがあります。そこで棒読みちゃんに読んでもらおうと思いました。
(Discord自体に標準で読み上げ機能はあるけどもゆっくりボイスがいい)
そこで爆誕したDiscordと棒読みちゃんの仲介人である「Yomisen」を紹介したいと思います。

用意したもの

アプローチの手順

  1. Discordにログイン
  2. チャットの通知を受け取る
  3. 棒読みちゃんに指示を送る
  4. ゆっくりしていってね

実は超簡単な棒読みちゃんへの指示出し

棒読みちゃんのサブフォルダ「SampleSrc\IpcClientChannelで読み上げ指示を送る(ローカル専用・.NET専用)\Src\BouyomiChanSample」にあるBouyomiChanClient.csをプロジェクトにそのまま追加します。
これは作者様が開発用に提供してくださっているものです。
BouyomiChanClientクラスを使用すると

var bc = new BouyomiChanClient();
bc.AddTalkTask("読み上げたい内容");
bc.Dispose();

または、

using (var bc = new BouyomiChanClient())
{
    bc.AddTalkTask("読み上げたい内容");
}

のように簡単に棒読みちゃんに読み上げたい内容を送信できます。(棒読みちゃんを事前に起動しておく)
また、using構文はリソース解放の記述をしなくてもブロックを抜けると自動的にリソースを開放してくれます。
なので、下のほうがリソース解放の書き忘れの心配がないので安心できると思います。

ソースコード

概要

  • Program.cs (メイン)
  • DiscordApiHelper.cs (メールアドレスとパスワードでトークンを取得してくれるヘルパークラス)
  • BouyomiChanClient.cs (棒読みちゃんとの接続を簡単にしてくれるクラス)

Program.cs

一部省略

using Discord;
using Discord.WebSocket;
using FNF.Utility;
using System;
using System.Threading.Tasks;

namespace Yomisen
{
    class Program
    {
        static void Main(string[] args) => MainAsync().GetAwaiter().GetResult();

        /// <summary>
        /// 非同期Mainメソッド
        /// </summary>
        static async Task MainAsync()
        {
            // トークンのチェック
            await CheckTokenAsync();
            // ログイン処理
            Console.WriteLine("ログイン中…");
            var client = new DiscordSocketClient();
            await client.LoginAsync(TokenType.User, Properties.Settings.Default.Token);
            await client.StartAsync();
            Console.WriteLine("ログイン完了");
            // 始めのあいさつ(大事)
            var task = Task.Run(() =>
            {
                using (var start = new BouyomiChanClient())
                {
                    start.AddTalkTask("棒読みちゃん起動~!");
                }
            });
            // メッセージ受信時のイベントを追加
            client.MessageReceived += Talk;
            // 各種コマンド
            InputCommand();
            // 終わりのあいさつ(大事)
            task = Task.Run(() =>
            {
                using (var end = new BouyomiChanClient())
                {
                    end.AddTalkTask("棒読みちゃん終了~!");
                }
            });
            Console.WriteLine("キー入力で終了");
            Console.ReadLine();
        }

        /// <summary>
        /// メッセージを受け取った時の処理
        /// </summary>
        static async Task Talk(SocketMessage arg)
        {
            await Task.Run(() =>
            {
                // チャンネル一覧にあるなら
                if (Properties.Settings.Default.TextChannels.IndexOf(arg.Channel.Id.ToString()) >= 0)
                {
                    // 読み上げ
                    using (var bc = new BouyomiChanClient())
                    {
                        bc.AddTalkTask(arg.Content);
                    }
                }
            });
        }

    ~以下省略~

    }
}

メッセージの通知を受信したときのイベントで、棒読みちゃんへの指示を行っています。
受信したメッセージのチャンネルIDが読み上げたいチャンネルIDとして登録されていれば読み上げが行われるようにします。

~省略~
// メッセージ受信時のイベントを追加
client.MessageReceived += Talk;
~省略~

// 読み上げ
static async Task Talk(SocketMessage arg)
{
    await Task.Run(() =>
    {
        // チャンネル一覧にあるなら
        if (Properties.Settings.Default.TextChannels.IndexOf(arg.Channel.Id.ToString()) >= 0)
        {
            // 読み上げ
            using (var bc = new BouyomiChanClient())
            {
                bc.AddTalkTask(arg.Content);
            }
        }
    });
}

Properties.Settings.Default.~~はアプリの設定ファイルで、設定値等を保存しておくことができるものです。
今回は、チャンネルIDの配列を保存しています。
f:id:gootalife:20171212174558p:plain
機能として、コマンド入力によって読み上げたいテキストチャンネルのIDを登録・削除・列挙することができます。
トークンのリセットをした場合は一度終了となり、再度ログイン作業が必要となります。
f:id:gootalife:20171212172653p:plain
テキストチャンネルのIDを調べる方法

  1. 設定>テーマ>開発者モード をオン
  2. テキストチャンネルの名前の上で右クリック
  3. IDをコピー でクリップボードにコピーされる

f:id:gootalife:20171212172943p:plain

> add 012345678901234567

のように18桁のIDを正しく入力することでテキストチャンネルのIDを登録できます。
(適当に登録しても、自身のアカウントの知れる範囲のものでないと通知は来ません)

DiscordApiHelper.cs

Discord.Netではサポートされていないメールアドレスとパスワードによる
トークンの取得を実現するためのヘルパークラスです。

using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace Yomisen
{
    public static class DiscordApiHelper
    {
        /// <summary>
        /// EmailとPasswordでトークンを取得します
        /// </summary>
        public static async Task<string> LogInAsync(string mail, string password)
        {
            var baseUrl = "https://discordapp.com/api/";
            var appName = "Yomisen";
            var discordAccount = new { email = mail, password = password };
            var json = JsonConvert.SerializeObject(discordAccount);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var res = await GetClient(appName).PostAsync(new Uri($@"{baseUrl}/auth/login"), content);

            if (res.IsSuccessStatusCode == true)
            {
                var resJson = await res.Content.ReadAsStringAsync();
                var deserializedJson = JsonConvert.DeserializeAnonymousType(resJson, new { token = "" });
                return deserializedJson.token;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// ヘッダーの追加
        /// </summary>
        public static HttpClient GetClient(string appName)
        {
            var client = new HttpClient();
            client.DefaultRequestHeaders.Add("User-Agent", appName);
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            return client;
        }
    }
}

discordapp.com
このあたりの書式に沿ってヘッダーの編集などをしてJSON形式でトークンを貰います。
このトークンは、
Discord上で「ctrl+shift+i」>Appliacationタブ>Local Storage>https://discordapp.com>token
の値と同値になると思います。
f:id:gootalife:20171212182844p:plain

BouyomiChanClient.cs

特に手を加えてないので省略しますが、中を覗くと
IPCChannelを使って通信しているみたいです。興味のある方は、
棒読みちゃんをDL、または下記の私のGitHubリポジトリから閲覧してみて下さい。

使い方

  1. 棒読みちゃんを起動(音量・速度等はこちらで設定)
  2. Yomisenを起動
  3. Discordのメールアドレスとパスワードを入力(初回・リセット時のみ)
  4. テキストチャンネルIDの登録
  5. ゆっくりしていってね

GitHub

この記事では、ソースコードは一部省略していましたが、以下に公開しています。
github.com

最後に

Botを使わない方針でやってみましたが、うまく動作してくれてよかったです。
棒読みちゃんの扱いが簡単すぎて驚きました。
ユーザー指定機能もできれば追加したいですね。
あと、SkypeはアンインストールしてDiscordを使いましょう。(Discord過激派)

2017/12/20 追記
Win8以上でないと動作しません。

2018/05/22 追記
ユーザ指定機能追加しました