Google formに送信した画像をDiscord botで送る

前回創作用サーバー向けに作った実質匿名でメッセージ送信できるdiscord botに、画像送信機能をつけてほしいとのお達しがあった。実装したのでメモがてらの書き散らし


前回作ったやつはこんなの

・GASを使ってGoogle Formの回答をjsonにし、Glitchのサイトにpostリクエストで送る

・Glitchに送られてきたものをNode.jsを使ってDiscordに送る


うむ。それはそうなんだけど画像をそのまま送るはできない。送り方としてぱっと思いついたものは

・Google Formで画像を上げるとgoogle Driveに残るので、ファイルのIDかurlかをGlitchに送り、Google Driveからフェッチしてくる

・一回Base64にエンコードしたものを送って、Glitch側でデコードする


一つ目のやつは認証周りがめんどくさそう?だったのでとりあえず二つ目のやつでやってみた。

まずはGASでの実装

JavaScript
if(response.length==4){
      const fileIDs = response[1].getResponse().toString().split(',');

      var data = {0 : IDtoBase64(fileIDs[0])};
      json.image = data;

      for(let i=1; i<fileIDs.length; i++){
        json.image[i] = IDtoBase64(fileIDs[i]);
      }
}

前回のjsonにimageのキーを追加して、それぞれの画像をIDtoBase64でbase64に変換したものを送るようにしてます。

回答はGoogle Driveにある画像のIDになります。複数画像だと,で区切ったIDが送られてくるので分割してどうこうすることで対応してます

Imageのキーを追加するかどうかは、画像があるかないかで回答の数が変わるのでそれで判別してます…うーん…

IDtoBase64の実装がこれ

JavaScript
function IDtoBase64(file){
      //IDからファイル取得
      var fileImage = DriveApp.getFileById(file);

      //ユーザー名を切り取り
      var fileName = fileImage.getName().toString();
      console.log(fileName);
      var name_splited = fileName.split('-');
      var dot_splited = fileName.split('.');
      var newName = name_splited[0].toString().split('.')[0].trim()+"." + dot_splited[dot_splited.length-1].toString();
      fileImage.setName(newName);

      //jpegに変換
      const jpg = fileImage.getAs(MimeType.JPEG);  
      const jpgFile =  DriveApp.getFolderById(folderId).createFile(jpg); 

      //変換前のものは削除
      fileImage.setTrashed(true);

      //base64に変換
      blob = jpgFile.getBlob();
      var base64Data = Utilities.base64Encode(blob.getBytes());
      console.log("base64 convert completed")

      return base64Data;
}

Google DriveからファイルのIDで画像を拾ってきます。

Google Formからあげた画像はアンケートと同じ階層に生成されるフォルダ下に保存されていくのですが、ファイル名が「元のファイル名 – 投稿者名 . jpeg 」とかになっちゃうんですよね。
まあフォルダは私しか見れないのですが、せっかく匿名にしてる意味がなくなっちゃうので一応投稿者名を消してリネームするようにしてます。

あとはエンコード/デコードする際に元の拡張子に合わせて逐一処理を分けるのは面倒なのでjpegファイルに変換して、それをエンコードして返してます。

これをGlitch側で受け取って、前回のserver.jsでimageのキーが含まれているときはそれぞれの画像のbase64を取り出してデコードの処理をしてぢすこ送ります。

JavaScript
if(dataObject.image!==undefined){
//送られてきたものを分割してとりだしてるだけ 
      var imageStr = dataObject.image.substr(1,dataObject.image.length-2);
      var imageStrSplited =imageStr.split(',');

      for(var i =0;i<imageStrSplited.length;i++){
         var file =imageStrSplited[i].split("=")[1].trim();
         sendImage(msgChannelId,file);
      }
}

デコードしたやつは一時ファイルに書き込むことにしました

一時ファイルのモジュールにはtmpを使いました。Glitchのターミナルでコマンドを実行してモジュールをダウンロードしときます

$ npm install tmp

取り出したbase64をデコードして送るsendImageはこれ。tmpモジュールを使って一時ファイルを作成し、base64からデコードしたやつを書き出します。一時ファイルのパスがpathに入るので、それを指定してぢすこに送ります。

JavaScript
function sendImage(channelID, imageFile, option = {}) {
    tmp.file(function _tempFileCreated(err, path, fd, cleanupCallback) {
        if (err) throw err
    
        try {
            fs.writeFile(path, imageFile, { encoding: "base64" }, err => { 
                if (err) throw err
            });
          
          console.log(path);
          

          client.channels.get(channelID).send({
                  //embedしないと画像がプレビューされない
                  embed : {
                    image : {
                      url : "attachment://image.jpg"
                    }
                  },
                  files : [{ attachment : path, name : "image.jpg"},]                  
                })
                .then(console.log("画像送信"))
                .catch(console.error);;
        } finally {
            // ファイル削除
            cleanupCallback() 
        }
    }) 
}


これでgoogle formから画像も送れるようになりました やったね

ところで、Glitchは金を払わないと環境変数以外公開されるのであんまり良い方法じゃない気がします。一時ファイルだからすぐ消えるだろうけどめっちゃ良いタイミングでクローンすると画像が見える可能性がある…?一応一時ファイル作ってawaitしてその間にターミナルで確認した分には見えなかったけれど(クローンも同様)

botのサーバー、どっかに移行したい…

プログラミング

guest
0 コメント
Inline Feedbacks
全てのコメントを見る