タイトル「未定」

自分の好きなことを書く

DiscordからLINEへのメッセージ転送Bot

今年はコロナとかでいろいろあって、友達とDiscordを使ってゲームをする機会が多かった。

なかでも流行ったのが、Among Us!!

このゲームは、Discordのボイスチャット機能を使いながらプレイするとかなり盛り上がる。

その過程でDIscordを普段使ってない友達も参加し始め、Discordの通知に気づかないこともしばしば…

そこで、DiscordのメッセージをLINEへ転送するBotを勉強がてら作ってみた。

必要な材料

  • メッセージ転送を行いたいDiscordサーバー
  • Glitchアカウント
  • Googleアカウント
  • LINEアカウント

1. LINEbotの作成

まずは、Discordからのメッセージを受信し、表示させるためのLINEアカウントを用意します。

LINE Developersにアクセスし、開発者登録を行います。 右上のボタンでLINEアカウントにログインし、フォームに名前やメールアドレスなどを入力します。

f:id:torikurosu:20201230230556p:plain
LINE Developers

登録が完了すると、下のような画面になります。 ここで、プロバイダーの作成を行います。

f:id:torikurosu:20201230231322p:plain
Providers画面

プロバイダーを作成するとこのような画面になります。

今回はメッセージを転送するBotを作成したいので、「Create a Messaging API channel」を選択します。

f:id:torikurosu:20201230231606p:plain
プロバイダー作成後

次に、いろいろと設定が出てきますが、良しなに設定してください。 カテゴリはこんな感じがいいらしいです。*1

f:id:torikurosu:20201230234709p:plain
カテゴリ設定

最後にチャンネルアクセストークンを発行し、控えておきます。 作成した、Massaging APIを選択し、

f:id:torikurosu:20201230235128p:plain
作成したMassaging API
Massaging APIを選択します。 すると、下部に「Channel access token」の設定欄があるので、アクセストークンを発行し、控えておきましょう。 f:id:torikurosu:20201230235231p:plain

2. GoogleAppScript(GAS)の構築

つぎに、Google App Scriptを用意します。 GASの役割は大きく2つあります。

  • Glitchサーバーの起動
  • Glitchサーバーからメッセージを受け取り、LINEBotへ転送

1つめのGlitchサーバーの起動ですが、Glitchは5分以上リクエストがない場合にスリープしてしまいます。 これを回避するために、トリガーを設定し定期的にGlitchに対してリクエストを送ります。 コードは以下のような感じになります。

// Messaging APIのチャネルアクセストークン
var CHANNEL_ACCESS_TOKEN = 

var GLITCH_URL =

// Glitchサーバーを起動させる
function wakeGlitch(){
 var json = {
   'type':'wake'
 };
 sendGlitch(GLITCH_URL, json);
}

function sendGlitch(uri, json){
 var params = {
   'contentType' : 'application/json; charset=utf-8',
   'method' : 'post',
   'payload' : json,
   'muteHttpExceptions': true
 };
 response = UrlFetchApp.fetch(uri, params);
}

/*
 * ボットイベント処理
 */
function doPost(e) {
  var events = JSON.parse(e.postData.contents).events;
  events.forEach(function(event) {
    if(event.type == 'discord') {
      sendLineMessage(event);
    }
 });
}

/*
 * LINEBotへメッセージを送信処理
 */
function sendLineMessage(e) {
  // メッセージの内容(送信先と内容)
  var message = {
    "messages" : [
      {
        "type" : "text",
        "text" : e.name + "「"+e.message+"」"
      }
    ]
  };
  // LINEにpostするメッセージデータ
  var replyData = {
    "method" : "post",
    "headers" : {
      "Content-Type" : "application/json",
      "Authorization" : "Bearer " + CHANNEL_ACCESS_TOKEN
    },
    "payload" : JSON.stringify(message)
  };
  // LINEにデータを投げる
  var response = UrlFetchApp.fetch("https://api.line.me/v2/bot/message/broadcast", replyData);
  // LINEにステータスコード200を返す
  return response.getResponseCode();
}

CHANNEL_ACCESS_TOKENには、先ほど発行したLINEBotのトークンを使用してください。

GLITCH_URLについては、後ほどで説明します。

プロジェクト名やファイル名は良しなに設定しましょう。

とりあえず、この状態でデプロイします。

f:id:torikurosu:20201231005057p:plain
GASデプロイ設定

こんな感じの設定でとりあえずデプロイしてください。

デプロイが完了すれば、ウェブアプリのURLが出てきます。これは後程、Glitchで使用します。

3. Discord Botの設定

Discord Botを作成します。 こいつは、Discordのメッセージを拾ってGlitchに投げつけます。

Discord Developer Portalにアクセスし、Botの作成を行います。 f:id:torikurosu:20201231005714p:plain

Applicationを選択し、「New Application」を選択します。 次に、Botを選択し「Add Bot」を選択します。

f:id:torikurosu:20201231005942p:plain
Bot作成

ここで、Discord Botトークンを取得しておきます。 これは後程、Glitchで使用します。

f:id:torikurosu:20201231005942p:plain
Discord Botトークンを取得

4. Glitchの構築

Glitchでウェブアプリを作成しますが、1から作成すると結構めんどくさいので、テンプレートを使用します。 https://glitch.com/~pumped-chopper

作成後、「.env」ファイルを編集します。 f:id:torikurosu:20201231010303p:plain 先ほど取得した、Discord botトークンや、GASのウェブアプリURLを設定しておきます。

次にメイン部分を書き上げます。 main.jsは下のような感じです。

// Response for GAS
const http = require("http");
const querystring = require('querystring');

http.createServer(function(req, res){
 if (req.method == 'POST'){
   var data = "";
   req.on('data', function(chunk){
     data += chunk;
   });
   req.on('end', function(){
     if(!data){
        console.log("No post data");
        res.end();
        return;
     }
     var dataObject = querystring.parse(data);
     console.log("post:" + dataObject.type);
     if(dataObject.type == "wake"){
       console.log("Woke up in post");
       res.end();
       return;
     }
     res.end();
   });
 }
 else if (req.method == 'GET'){
   res.writeHead(200, {'Content-Type': 'text/plain'});
   res.end('Discord Bot is active now\n');
 }
}).listen(3000);

// Discord bot implements
const discord = require("discord.js");
const client = new discord.Client();

client.on("ready", message => {
  // botのステータス表示
  client.user.setPresence({ game: { name: "with discord.js" } });
  console.log("bot is ready!");
});

client.on("message", message => {
  if (message.author.bot) {
    return;
  }
  // DMには応答しない
  if (message.channel.type == "dm") {
    return;
  }

  var msg = message;

  // botへのリプライは無視
  if (msg.mentions.has(client.user)) {
    return;
  } else {
    //GASにメッセージを送信
    sendGAS(msg);
    return;
  }

  function sendGAS(msg) {
    var jsonData = {
      events: [
        {
          type: "discord",
          name: msg.author.username,
          message: msg.content
        }
      ]
    };
    //GAS URLに送る
    console.log(msg.author.username);
    console.log(msg.content);
    post(process.env.GAS_URL, jsonData);
  }

  function post(url, data) {
    //requestモジュールを使う
    var request = require("request");
    var options = {
      uri: url,
      headers: { "Content-type": "application/json" },
      json: data,
      followAllRedirects: true
    };
    // postする
    request.post(options, function(error, response, body) {
      if (error != null) {
        msg.reply("更新に失敗しました");
        console.log("更新に失敗しました");
        return;
      }

      var userid = response.body.userid;
      var channelid = response.body.channelid;
      var message = response.body.message;
      if (
        userid != undefined &&
        channelid != undefined &&
        message != undefined
      ) {
        var channel = client.channels.get(channelid);
        if (channel != null) {
          channel.send(message);
        }
      }
    });
  }
});

if (process.env.DISCORD_BOT_TOKEN == undefined) {
  console.log("please set ENV: DISCORD_BOT_TOKEN");
  process.exit(0);
}

client.login(process.env.DISCORD_BOT_TOKEN);

最後に、package.jsonを開き、「Add Package」を選択し、requestモジュールをインポートしておきます。

f:id:torikurosu:20201231011126p:plain
requestモジュールの追加

5. GASへのGlitchURL設定

先ほどの手順2で空けておいたGlitchURLの設定をしておきます。 これは、Glitchのプロジェクト名に関連付けられます。

https://{Glitchプロジェクト名}.glitch.me/

設定後、もう一度デプロイしておきましょう。 おそらくこのデプロイでは、ウェブアプリURLは変わらないはずですが、変わってしまったらGlitchへ再度設定しましょう。

6. GASのトリガー設定

Glitchへ定期的にリクエストを送信するために、GASのトリガーを設定しておきます。

https://script.google.com/homeにアクセスし、先ほど作成したプロジェクトを右クリックで選択し、トリガーを選びます。

f:id:torikurosu:20201231012554p:plain
トリガー設定

先ほどのwakeGlitchを選択し、5分おきに実行するようにトリガーを設定してください。

7. Discord Botの招待

最後に、作成したDiscord Botを招待しましょう。

Discord Developer Portalにアクセスし、先ほどのBotを選択し、OAuth2のページを開きます。 このページでbotを選択し、生成されたURLを開くことで好きなDiscordサーバーに参加させることができます。 f:id:torikurosu:20201231012956p:plain

結果は、こんな感じ

f:id:torikurosu:20201231014704p:plain
Discord上のBot
f:id:torikurosu:20201231014844p:plain
Discord側
f:id:torikurosu:20201231015126p:plain
LINE側

まとめ

ということで、DiscordメッセージをLINEへ転送するBotを作成してみました。

逆のLINEメッセージをDiscordへ転送するBotを作ることもできますが、必要なかったので特にやっていません。

記事を書きながらもう一度作成したのですが、結構いろいろ設定があってややこしいですね。

そもそも、javascript自体あまり書いたことがないので、わからないことが多いし…ウェブアプリもほとんど書いたことないし…

いろいろ荒いつくりなので参考までに…

「もっと、こうしたらよくなるぞ!!」という方がいれば、教えていただけると幸いです。

そもそも、このBot作ったんですが、だれも使ってくれてないという…(泣)

まあ、いい勉強になったので、ここに残しておきます。

しかし、この手の技術記事って書くの大変なんだなぁ…いつもお世話になってるだけに感謝が増しました。

自分も発信できる側に回りたいですね。*2


参考サイト様:

qiita.com

*1:要出典

*2:技術力ないのに発信してええんか?