RubyでLINEとDiscord連携するソフト作った話

## 作った理由
私ね、スマホのDiscord時間制限かかってるんですよ
LINEはかかってないし両方APIが公開されているから頑張ったら連携ソフト作れるんじゃね？って思って作ることにしました

## 必要ものとかもろもろ
今回使った言語はRubyです
なんとね、Disocrdrbって
```ruby
bot.run true
```
って書くだけでbotのもろもろの処理バックグラウンドで実行してくれるんですよ
サーバーを併用するときにこれくそ便利なんです
なんか最近Rubyオワコンとか言われてますがこの機能めちゃくそ便利なんでぜひやってみてください！！

あとは今回使うSinatra鯖を公開するための[ngrok](https://ngrok.com/)ですね
これもくそ便利です
ぜひ使ってみてください


### そんで今回使ったモジュール君たち

```bash
gem install discordrb
gem install line-bot-api
gem install sinatra
```

この3つですね
圧倒的少量で済むんですからめちゃくそ楽です

## 仕組み
まぁやってることはProxyみたいなもんです

### Discordrb
まず、Discordのほうから説明していきます
Discordrbでbotをログインさせます
ここから全部バックグラウンドでの処理です

ボットが参加しているサーバーのメッセージ受信イベントを検知したらLINEのAPIにリクエスト飛ばして度のサーバーのどのチャンネルでどのユーザーがどんなメッセージを送信したかを転送します

### Sinatra(LINEのWebhookサーバー側)
LINEにメッセージを送信したらこのSinatraの鯖にも同じリクエストが来るんでメッセージ内容を取得、そんでDiscordの指定チャンネルにメッセージを送信するといった感じです

## コード
```ruby
require 'sinatra'
require 'rack/protection'
require 'discordrb'
require './src/internal/config'
require './src/internal/client/line'
require './src/internal/client/discord'

# Config関係class作成
ConfigPath    = 'config.json'
ServerConfig  = Config::Server.new(config_file: ConfigPath)  # ConfigのServer項目を読み取るオブジェクトを作成
DiscordConfig = Config::Discord.new(config_file: ConfigPath) # ConfigのDiscord項目を読み取るオブジェクトを作成
LineConfig    = Config::Line.new(config_file: ConfigPath)    # ConfigのLine項目を読み取るオブジェクトを作成

# Config読み込み
server_config  = ServerConfig.load  # server関連の設定取得
discord_config = DiscordConfig.load # discord関連のtokenなどを取得
line_config    = LineConfig.load    # line関連のtokenなど取得

# SinatraServerの設定
# 以下は自家用サーバーで実行するとき
set :bind, server_config['host'].to_s             # hostを設定
set :port, server_config['port'].to_i             # 何番PORTでサーバーを開くか設定
# 以下はRenderでデプロイする場合
# set :port, ENV['PORT'] || 4567
set :environment, :production
set :protection, :except => [:host_authorization] # サーバーのhost制限を解除 セキュリティ的に非推奨

# その他の変数設定
set_mode   = false # チャンネルの送信モードか取得
send_mode  = true  # LINEのAPI使用制限節約のため、取得したメッセージをLINEに送るかモード選択
fetch_mode = false # サーバー情報取得モードか判別用

# LineBotの作成
line_client = LineBot::Client.new(
  access_token: line_config['access_token'],
  channel_secret: line_config['channel_secret'],
  send_user_id: line_config['send_user_id']
)

# DiscordBotの作成
bot = Discordrb::Bot.new(
  client_id: discord_config['client_id'],
  token: discord_config['token'],
)
discord_client = DiscordBot::Client.new(
  client: bot
)

bot.message do |event| # メッセージを受け取った時
  unless send_mode
    return nil
  end

  message_server  = event.server  # メッセージ送信元のサーバーを取得
  message_channel = event.channel # メッセージ送信元のチャンネルを取得
  message_author  = event.author  # メッセージ送信者を取得
  message_content = event.content # メッセージの内容を取得

  if message_author.bot_account? # メッセージ送信者がbotならそのままスルー
    puts "pass"
    return nil
  end

  line_send_content = discord_config['template']['message'].to_s

  replace_words = { # 置き換え文字一覧 ここから変数の追加、削除が可能
    "{guild_name}"          => message_server.name,
    "{guild_id}"            => message_server.id,
    "{channel_name}"        => message_channel.name,
    "{channel_id}"          => message_channel.id,
    "{author_name}"         => message_author.name,
    "{author_display_name}" => message_author.display_name,
    "{author_id}"           => message_author.id,
    "{content}"             => message_content,
  }

  replace_words.each do |id, value|
    line_send_content = line_send_content.sub(id, value.to_s)
  end

  res = line_client.send line_send_content # LINEに送信
  puts res.body
end

# サーバー指定のための変数など
discord_place = discord_config['template']

# SinatraServerの処理書き込み

post '/' do # LINEからメッセージを受信したら
  params = JSON.parse request.body.read
  event = params['events'][0]
  puts event
  unless event['message']['type'] == 'message'
    return nil
  end
  message = event['message']['text'].to_s

  if set_mode
    argv = message.split(" ") # 書式が`<server_id> <channel_id>`のはずなので、サーバーとチャンネルで分ける

    discord_place['guild'] = argv[0].to_i
    discord_place['channel'] = argv[1].to_i

    line_client.reply(event, "サーバーを変更しました\n詳しくは!infoコマンドで確認してください")

    set_mode = false
    return
  end

  if fetch_mode
    guild_id = message.to_i
    guild_data = discord_client.get_guild(guild_id)
    content = "Name: #{guild_data['name']}\nId: #{guild_data['id']}\nChannels\n"
    guild_data['channels'].each do |channel_id, channel|
      content += "Name: #{channel['name']}\nType: #{channel['type']}\nId: #{channel_id}\n\n"
    end
    line_client.reply(event, content)
    fetch_mode = false
    return
  end

  commands = %w[!ready !info !set !list !fetch !catch] # コマンドと認識する単語を追加 ここからコマンドの追加＆削除可能

  if commands.include? message # メッセージがコマンドと認識されたら
    case message
    when '!ready' then # ログインしているか送信
      content = 'Login!'

    when '!info' then  # 現在のサーバー、チャンネル情報などを送信
      guild   = bot.server(discord_place['guild'].to_i)
      channel = bot.channel(discord_place['channel'].to_i, guild)

      content = "GuildInfo\nGuildName: #{guild.name}\nGuildId: #{guild.id}\nChannelName: #{channel.name}\nChannelId: #{channel.id}"
    when '!set' then   # サーバ、チャンネルを設定するモードに移行
      set_mode = true
      content  = "サーバー指定モードへ移行します\n<サーバーID> <チャンネルID>\n上記のように送信してください"

    when '!list' then # サーバーの情報を取得
      content = "参加しているサーバーリスト\n"
      bot.servers.each do |server_id, server|
        content += "#{server.name}\nID: #{server.id}\n\n"
      end

    when '!fetch' then
      fetch_mode = true
      content    = "サーバー取得モードに移行します\nサーバーIDだけ送信してください"

    when '!catch' then
      send_mode = !send_mode
      content   = "メッセージ取得モードを#{send_mode.to_s}に設定しました\n" +
        if send_mode
          "メッセージを送信します"
        else
          "LINE Message API使用制限節約のため、メッセージの送信を控えます"
        end

    end

    line_client.send content
    return
  end

  discord_client.send_message(
    discord_place['guild'].to_i,
    discord_place['channel'].to_i,
    message
  )
end

bot.run true
```

こんなコードですね
完璧なコード（internal内のコードも含むやつ）は販売してるので公開しません！
でもこれさえあれば誰でも作れると思います

まぁこんなくそコードでも何人か買ってくれてるのでね
作るのめんどくさい！とか思う人はぜひご購入ください
[Twitter](https://twitter.com/tanahiro2010)のDMやDiscord(@tanahiro2010)のDMから受け付けます
値段は1500円！

# 読んでくれてありがとうって話
最後まで読んでいただき、ありがとうございました！
僕に依頼してくれたり売ってるプログラム購入してくれたりすると励みになります！
Qiitaではあんまり活動してないけどまぁ読んでくれる人が何人かいたら次の記事も出ると思うのでぜひ高評価（あるのかは知らん）おねがいします！
ではまた次の記事で！