3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Slack bot に画像を表示してみた

Posted at

なんで作ろうと思った?

前回作ったSlack bot にセリフと一緒にスライムの画像も表示したいと思ったから。

使った技術は?

Node.js

前提

・Node.js インストール済
・Yarn インストール済
・Slack アカウント作成済

どうやって作ったの?

一つの画像を表示する

1. images ディレクトリ作成

mkdir scripts/images

2. サンプル画像をダウンロードして保存する

サンプル画像
command + s
ダウンロードフォルダにいったん保存する

3. ダウンロードした画像を images ディレクトリに移動させる

4. 参考にした記事の coffeescript のコードを JavaScript に変換する

変換前

nekobot.coffee
module.exports = (robot) ->
    robot.hear /()$/i, (msg) ->
      room = msg.envelope.room
      if room == "image"
        text = msg.match[1]
        msg.send  "#{text} の画像アップするよ"
        # 下記を追加
        filename = 'image/nya-o.png'
        channel = msg.message.rawMessage.channel
        exec "curl -F file=@#{filename} -F channels=#{channel} -F token=#{process.env.HUBOT_SLACK_TOKEN} https://slack.com/api/files.upload", (err, stdout, stderr) ->

変換後

nekobot.js
module.exports = function(robot) {
  return robot.hear(/()$/i, function(msg) {
    var channel, filename, room, text;
    room = msg.envelope.room;
    if (room === "image") {
      text = msg.match[1];
      msg.send(text + " の画像アップするよ");
      filename = 'image/nya-o.png';
      channel = msg.message.rawMessage.channel;
      return exec("curl -F file=@" + filename + " -F channels=" + channel + " -F token=" + process.env.HUBOT_SLACK_TOKEN + " https://slack.com/api/files.upload", function(err, stdout, stderr) {});
    }
  });
};

5. JavaScript に変換後のコードを既存のコードに合うように編集する

編集前

hello.js
'use strict';
const data = require('./data.json');
const words = data.slimelife;
module.exports = (robot) => {
  robot.hear(/スライム/i, (msg) => {
    const user_id = msg.message.user.id;
    const word = words[Math.floor(Math.random() * words.length)];
    msg.send(`<@${user_id}> ${word}`);
  });

varconst にする。
room は使用しないので消す。(チャンネルはすでにSlackの設定の時にひとつ指定しているから)
text は使用しないので消す。(セリフのJSONファイルがあるから)

編集後

hello.js
  'use strict';
  const data = require('./data.json');
  const words = data.slimelife;
  module.exports = (robot) => {
    robot.hear(/スライム/i, (msg) => {
      const user_id = msg.message.user.id;
      const word = words[Math.floor(Math.random() * words.length)];
+     msg.send(`<@${user_id}> ${word}`);
+     const filename = `images/pose_pien_uruuru_woman.png`;
+     const channel = msg.message.rawMessage.channel;
+     return exec("curl -F file=@" + filename + " -F channels=" + channel + " -F token=" + process.env.HUBOT_SLACK_TOKEN + " https://slack.com/api/files.upload", function(err, stdout, stderr) {
+       if (err) {
+         console.error(err);
+        }
+      });
    });
  };

実行すると以下のエラーメッセージがでた。

ERROR ReferenceError: exec is not defined`

こちらの記事によると、exec の読み込みできてないのが原因だったので、以下のコードを1行目に追記した。

hello.js
const { exec } = require('child_process');

実行すると今度は別のエラーメッセージがでた。

Failed to open/read local data from file/application

画像ファイルのパスが合ってないのが原因だった。

$ pwd
/Users/maiamea/git/maiamea/slimelife-bot

現在位置が slimelife-bot ディレクトリ。images ディレクトリは scripts ディレクトリ配下だから、slimelife-bot ディレクトリ直下ではないのでエラーになった。

ファイルパスを以下に修正

hello.js
const filename = `scripts/images/pose_pien_uruuru_woman.png`;

6. ボットを Slack のアダプターなしで動かす

bin/hubot

実行すると以下のエラーメッセージがでた。

ERROR TypeError: Cannot read property 'channel' of undefined

<検証1>

bin/hubotの時、messageに何が入ってるのか。channelのオブジェクトであるrawMessageに何が入ってるのか確認

hello.js
'use strict';
const { exec } = require('child_process');
const data = require('./data.json');
const words = data.slimelife;
module.exports = (robot) => {
  robot.hear(/スライム/i, (msg) => {
    const user_id = msg.message.user.id;
    const word = words[Math.floor(Math.random() * words.length)];
    msg.send(`<@${user_id}> ${word}`);
    const filename = `scripts/images/pose_pien_uruuru_woman.png`;

    console.log('ーーーーーーーーーーー');
    console.log(msg.message);
    console.log('ーーーーーーーーーーー');
    console.log(msg.message.rawMessage);
    console.log('ーーーーーーーーーーー');

    const channel = msg.message.rawMessage.channel;
    return exec("curl -F file=@" + filename + " -F channels=" + channel + " -F token=" + process.env.HUBOT_SLACK_TOKEN + " https://slack.com/api/files.upload", function(err, stdout, stderr) {
      if (err) {
        console.error(err);
      }
    });
  });
};
bin/hubot

 TextMessage {
  user:
   User { id: '1', _getRobot: [Function], name: 'Shell', room: 'Shell' },
  done: false,
  room: 'Shell',
  text: 'スライム',
  id: 'messageId' }
ーーーーーーーーーーー
undefined
ーーーーーーーーーーー

ERROR TypeError: Cannot read property 'channel' of undefined

bin/hubotで実行する場合、messageにはTextMessageが入ってる。
rawMessageundefinedのためchannelに何も入ってない状態になりエラーとなった。

<検証2>

・Slackに繋がないで実行した時と、繋げて実行した時とで何が違うか確認

参考にした記事で元々const room = msg.envelope.room;があったので、roomのオブジェクトのmsg.envelopeに何が入っているのか確認

hello.js
'use strict';
const { exec } = require('child_process');
const data = require('./data.json');
const words = data.slimelife;
module.exports = (robot) => {
  robot.hear(/スライム/i, (msg) => {
    const user_id = msg.message.user.id;
    const word = words[Math.floor(Math.random() * words.length)];
    msg.send(`<@${user_id}> ${word}`);
    const filename = `scripts/images/pose_pien_uruuru_woman.png`;

    console.log('ーーーーーーーーーーー');
    console.log(msg.message);
    console.log('ーーーーーーーーーーー');
    console.log(msg.message.rawMessage);
    console.log('ーーーーーーーーーーー');
    console.log(msg.envelope);
    console.log('ーーーーーーーーーーー');

    const channel = msg.message.rawMessage.channel;
    return exec("curl -F file=@" + filename + " -F channels=" + channel + " -F token=" + process.env.HUBOT_SLACK_TOKEN + " https://slack.com/api/files.upload", function(err, stdout, stderr) {
      if (err){
        console.error(err);
      }
    });
  });
};

【Slackに繋がないで実行した時】

bin/hubot

ーーーーーーーーーーー
TextMessage {
  user:
   User { id: '1', _getRobot: [Function], name: 'Shell', room: 'Shell' },
  done: false,
  room: 'Shell',
  text: 'スライム',
  id: 'messageId' }
ーーーーーーーーーーー
undefined
ーーーーーーーーーーー
{ room: 'Shell',
  user:
   User { ... },
  message:
   TextMessage { ... } 
}
ーーーーーーーーーーー
ERROR TypeError: Cannot read property 'channel' of undefined

messageにはTextMessageが入ってる。
rawMessageundefined
roomShell

【Slackに繋げて実行した時】

env HUBOT_SLACK_TOKEN=控えておいた文字列 bin/hubot --adapter slack

ーーーーーーーーーーー
SlackTextMessage {
  user:
   User { ... },
  text: 'スライム',
  rawMessage:
    { client_msg_id: 'ee2210fa-39ee-4ffc-bfdb-5337d946b1ce',
     ...
     channel: 'CHCUY01CK',
     ... 
    },
  _channel_id: 'CHCUY01CK',
  _robot_name: 'slimelife_bot',
  _robot_alias: false,
  rawText: 'スライム',
  mentions: [],
  done: false,
  room: 'CHCUY01CK',
  id: '1597567494.002100',
  constructor:
   { ... },
  buildText: [Function],
  replaceLinks: [Function],
  replaceUser: [Function],
  replaceConversation: [Function] }
ーーーーーーーーーーー
{ client_msg_id: 'ee2210fa-39ee-4ffc-bfdb-5337d946b1ce',
  ...
  channel: 'CHCUY01CK',
  ...
}
ーーーーーーーーーーー
{ room: 'CHCUY01CK',
  user:
   User { ... },
  message:
   SlackTextMessage { ... }
ーーーーーーーーーーー

messageにはSlackTextMessageが入ってる。
rawMessageはちゃんと存在していて、channelが入ってる。
roomchannel

Slack に繋げて実行すると、ちゃんと Slack の方でセリフと画像が返ってきた。
スクリーンショット 2020-08-16 2.16.47.jpg

【解決策】

bin/hubotでエラーにならずにセリフと同様に画像もちゃんと返ってくることを確認するために、roomShellかどうかで場合分けする。roomShellの時はrawMessageundefinedなので、channelの定義はせずそのまま結果を返す。
・ターミナルでは画像表示はできないので、画像のファイル名を返すことで確認する。

hello.js
'use strict';
const { exec } = require('child_process');
const data = require('./data.json');
const words = data.slimelife;
module.exports = (robot) => {
  robot.hear(/スライム/i, (msg) => {
    const user_id = msg.message.user.id;
    const word = words[Math.floor(Math.random() * words.length)];
    msg.send(`<@${user_id}> ${word}`);
    const filename = `scripts/images/pose_pien_uruuru_woman.png`;

    console.log('ーーーーーーーーーーー');
    console.log(msg.message);
    console.log('ーーーーーーーーーーー');
    console.log(msg.message.rawMessage);
    console.log('ーーーーーーーーーーー');
    console.log(msg.envelope);
    console.log('ーーーーーーーーーーー');

    const room = msg.envelope.room;
    if (room === "Shell") {
      msg.send(`表示される画像ファイルは、pose_pien_uruuru_woman.png です。`);
    } else {
      const channel = msg.message.rawMessage.channel;
      return exec("curl -F file=@" + filename + " -F channels=" + channel + " -F token=" + process.env.HUBOT_SLACK_TOKEN + " https://slack.com/api/files.upload", function (err, stdout, stderr) {
        if (err) {
          console.error(err);
        }
      });
    }
  });
};
bin/hubot

ーーーーーーーーーーー
TextMessage {
  user:
   User { id: '1', _getRobot: [Function], name: 'Shell', room: 'Shell' },
  done: false,
  room: 'Shell',
  text: 'スライム',
  id: 'messageId' }
ーーーーーーーーーーー
undefined
ーーーーーーーーーーー
{ room: 'Shell',
  user:
   User { ... },
  message:
   TextMessage { ... } 
}
ーーーーーーーーーーー
<@1> 一行読めた!やったぁ!!
表示される画像ファイルは、pose_pien_uruuru_woman.png です。

ターミナル上で、セリフと画像ファイル名どちらも返ってきた。

複数の画像をランダムに表示する

1. 画像をリサイズする

画像のファイルサイズが大きいとアップロードに時間がかかってしまう。
こちらの記事を参考にしました。

2. 画像ファイルを scripts/images に移動

3. 画像ファイルのパスをまとめる json ファイルを作成

touch scripts/path.json
scripts/path.json
{
  "imagePaths": [
    "IMG_8952.jpeg",
    "IMG_8953.jpeg",
    "IMG_8955.jpeg"
  ]
} 

4. 画像がランダム表示されるようにコードを追記した。

hello.js
  'use strict';
  const { exec } = require('child_process');
  const data = require('./data.json');
  const words = data.slimelife;
+ const paths = require('./path.json');
+ const imagePaths = paths.imagePaths;
  module.exports = (robot) => {
    robot.hear(/スライム/i, (msg) => {
      const user_id = msg.message.user.id;
      const word = words[Math.floor(Math.random() * words.length)];
+     const imagePath = imagePaths[Math.floor(Math.random() * imagePaths.length)];  
      msg.send(`<@${user_id}> ${word}`);
+     const filename = `scripts/images/${imagePath}`;
      const room = msg.envelope.room;
      if (room === "Shell") {
+       msg.send(`表示される画像ファイルは、${filename}です。`);
      } else {
        const channel = msg.message.rawMessage.channel;
        return exec("curl -F file=@" + filename + " -F channels=" + channel + " -F token=" + process.env.HUBOT_SLACK_TOKEN + " https://slack.com/api/files.upload", function(err, stdout, stderr) {
          if (err) {
            console.error(err);
          }
        });
      }
    });
  };

5. 実装

【Slackに繋がないで実行した時】

bin/hubot

 ーーーーーーーーーーー
TextMessage {
  user:
   User { id: '1', _getRobot: [Function], name: 'Shell', room: 'Shell' },
  done: false,
  room: 'Shell',
  text: 'スライム',
  id: 'messageId' }
ーーーーーーーーーーー
undefined
ーーーーーーーーーーー
{ room: 'Shell',
  user:
   User { ... },
  message:
   TextMessage { ... } 
}
ーーーーーーーーーーー
<@1> 今回のダンジョンでなにをしたか思い出しなさい。まず美味しいカレーを作ったでしょ?お化けも倒して闇の試練も乗り越えてフェンリルとも友達になったでしょ。この経験が今後に活きない訳ないじゃん。近づいてるよなんでも技師
表示される画像ファイルは、scripts/images/IMG_8955.jpegです。

【Slackに繋げて実行した時】

env HUBOT_SLACK_TOKEN=控えておいた文字列 bin/hubot --adapter slack

スクリーンショット 2020-08-15 10.25.06.jpg

無事 Slack でランダムに可愛いスライムの画像を表示できた。

参考記事

Hubotからslackに画像を投稿してみる - Qiita
javascriptのcoffeescript、cssとscssの変換ならこのサービスで - Qiita
Node.js|シェルコマンドを実行する方法(child_process) - わくわくBank
Macだけでできる!画像をリサイズして、ファイルサイズを軽量化しよう![プレビュー.app編] - アイデアマンズブログ

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?