📤

LINEの画像をGyazoに保存して画像オウム返しBotをつくる

2023/02/10に公開

挨拶

こんにちは。予想外に記事の反応がよくてビビっているマグロです。
前回、動画のオウム返しを作りました。
今回は画像保存サービスであるGyazoを使って、LINEの画像オウム返しBotを作ります。

https://zenn.dev/maguro_alterna/articles/00083367dd0184

また、以下の知識があることが前提です。

  • Google Apps ScriptでLINEBotを動かしたことがある。
  • APIのリクエストとレスポンスについて大体理解している。

経緯

LINEBotの基礎としてテキストのオウム返しがあります。
データの取り出し、リクエストの方法を学べて入門に最適だと思います。

ただ画像のオウム返しってなかなか聞かないと思いますが、どうしてでしょうか?
ちょっとLINEの公式リファレンスを覗いてみましょう。

https://developers.line.biz/ja/reference/messaging-api/#wh-image

上記のリンクはLINEBotが画像を受け取った際のjsonの中身を説明してます。
するとこんな記述が

contentProvider.type
String
画像ファイルの提供元。

line:LINEユーザーが画像ファイルを送信しました。画像ファイルのバイナリデータは、メッセージIDを指定してAPIを呼び出すことで取得できます。詳しくは、「コンテンツを取得する」を参照してください。
external:LIFFのliff.sendMessages()メソッドで画像ファイルが送信されました。詳しくは、『LIFF APIリファレンス』の「liff.sendMessages()」を参照してください。

ほうほう、ここから送信元が判別できるようです。
LINEユーザーからの送信なので、lineと返されますね。

で、画像の取得方法は、、、ん?

画像ファイルのバイナリデータは、メッセージIDを指定してAPIを呼び出すことで取得できます。

どうやらバイナリデータとして扱われるようです。
うーんちょっとめんどくさい。

https://developers.line.biz/ja/reference/messaging-api/#get-content

バイナリデータを取得する際、messageIdをURLに含めてリクエストする必要があるそうな。

GET https://api-data.line.me/v2/bot/message/{messageId}/content

そこから画像ファイルに起こして、Botに送信させればよさそうですね。
思ってたより簡単かも???
よし、送信だ!!

https://developers.line.biz/ja/reference/messaging-api/#image-message

type
String 必須
image

originalContentUrl
String 必須
画像のURL(最大文字数:2000)
HTTPS(TLS 1.2以降)
JPEGまたはPNG
最大ファイルサイズ:10MB

URLはUTF-8を用いてパーセントエンコードしてください。詳しくは、「リクエストボディのプロパティに指定するURLのエンコードについて」を参照してください。

previewImageUrl
String 必須
プレビュー画像のURL(最大文字数:2000)
HTTPS(TLS 1.2以降)
JPEGまたはPNG
最大ファイルサイズ:1MB

URLはUTF-8を用いてパーセントエンコードしてください。詳しくは、「リクエストボディのプロパティに指定するURLのエンコードについて」を参照してください。

な、なにぃーーーーーーーー!!!????
画像送信の際にはhttps形式の画像URLを送付しろだとぉぉぉぉぉぉっ!!

真面目に経緯

取り乱しました、すいません。
上記の通り、LINEBotで画像を送信する際、https形式の画像URLで送信する必要があります。

これの何が面倒なのかというと、

  • 画像を保存してURLで共有させる必要がある。
  • しかしLINE側では画像をバイナリデータとして扱い、一定時間で削除してしまう。(httpsで参照できない)

画像をどこかのストレージサービスにAPIで保存し、共有させる工程を加えないといけません。
TwitterとかDiscordとかは普通にサーバー側で保存してくれるので参照しやすいんですけどね、、、

じゃあどうする?

以下の点を考慮し、画像保存サービスを利用します。

  • プライバシー重視(画像が第三者に見られないようにする)
  • 容量無制限
  • 無料
  • 投稿用のAPIアリ

どんなものでも長期間運用することを想定した方がいい(と個人的には思っている)ので、容量によってアップロードできない点や運用コストを考慮してます。

でもそんな都合のいいサービスなんてあるのでしょうか...

image.png
プライバシー確保できて、
image.png
アップロード用のAPIがあって、
image.png
image.png
できればバイナリデータを送信できて、
image.png
容量無制限だなんて、、、

あった!!!!
しかもバイナリデータを直前アップロードできるじゃん!!!

というわけで上記の要件を満たすGyazoを利用します。

設計

使うサービスは以下の通りです。

  • LINE Messaging API
  • Gyazo
  • Google Apps Script

Google Apps Script(通称GAS)でサーバーレスでLINEbotを稼働させます。

処理の流れは以下の通りです。
1.LINEbotに画像を送信する。
2.受け取ったメッセージidから画像のバイナリデータを取得する。
3.バイナリデータごとGyazoに送信する。
4.レスポンスに含まれている画像のURLをリプライで返す。

バイナリデータごと送信する点に結構驚きました。
これでファイルに起こす手間がなくなるので便利です。

コードを書く前に、Gyazoのトークンを取得しておきましょう。

https://gyazo.com/api

コーディング

const TOKEN = ""; //LINEのトークン
const LINE_ENDPOINT = 'https://api.line.me/v2/bot/message/reply';

function doPost(e) {
  try{
    // 受け取ったイベントを展開
    const json = JSON.parse(e.postData.contents);
    
    // イベントの中身とリプライトークンを取得
    const event = json.events[0];
    const reply_token= event.replyToken;

    //画像メッセージの場合
    if(event.message.type === 'image'){
      // 画像バイナリデータを取得
      const image = getImage('https://api-data.line.me/v2/bot/message/'+event.message.id+'/content');
      const gyazo = JSON.parse(gyazoup(image.getBlob()));

      // メッセージを返信    
      UrlFetchApp.fetch(LINE_ENDPOINT, {
        'headers': {
          'Content-Type': 'application/json; charset=UTF-8',
          'Authorization': 'Bearer ' + TOKEN,
        },
        'method': 'post',
        'payload': JSON.stringify({
          'replyToken': reply_token,
          "messages" : [
            {
              'type': 'image',
              'originalContentUrl': gyazo.url,
              'previewImageUrl': gyazo.url
            }
          ]
        })
      })
    }
    return ContentService.createTextOutput(
        JSON.stringify(
            {'content': 'post ok'}
        )
    ).setMimeType(ContentService.MimeType.JSON);
  }catch{
    return
  }

}

//lineから送信された画像をバイナリデータで取得
function getImage(url) {
    return UrlFetchApp.fetch(url, {
        "method": "get",
        'headers': {
            'Content-Type': 'application/json; charset=UTF-8',
            'Authorization': 'Bearer ' + TOKEN
        }
    });
}
//バイナリデータの画像をgyazoに送信し、urlを取得
function gyazoup(image){
  const options  = {
    "method": "post",
    "payload": {
      "access_token": '', //gyazoのアクセストークン
      "imagedata": image
    }
  };
  
  return UrlFetchApp.fetch("https://upload.gyazo.com/api/upload",options);
}

やっていることは単純なので、コードの説明は省きます。

完成!!

試しに画像を送信しましょう!!
画像がオウム返しされれば成功です!!
image.png

最後に

いかがでしたでしょうか??
Gyazoの宣伝になってしまったように思いますが、プライバシーを考慮しながら画像を簡単に共有できるのは非常にありがたいと思います。
また、現在執筆中のDiscordとLINEの連携では、LINEの画像共有にこのギミックを利用します。
大まかな流れを理解していただければ幸いです。

Discussion