My Calendar

2018年6月23日 星期六

Node.js 開發Slack Bot - 3

      Slack API 可以設定當某些事件觸發的時候才執行該方法,如之前使用到的"connected"事件,當Bot成功連接Slack才會觸發。相關文件可以到http://slackapi.github.io/node-slack-sdk/rtm_api查看。

如果希望在Bot登入但連接到頻道之前可以用AUTHENTICATED。
slack.on("authenticated",(connectData)=>{
  console.log(`Logged in as ${connectData.self.name} of team ${connectData.team.name}, but not yet connected to a channel`);
});

當等你將會看到以下的訊息。
Logged in as hellobot of team Deikhoong, but not yet connected to a channel

Bot要接收使用者的訊息可以使用MESSAGE EVENT. 只要每次將訊息發送到Bot所在的頻道或向Bot發送Direct Message時,這個Event都會被觸發。當我們發送"Hi Bot"或"Hello Bot",Bot會回覆給發送者的頻道。

slack.on('message', (message) => {

  (async() =>{
      let user= {"user":message.user};
      let userInfo = await getUserInfo(user);
      console.log(userInfo.user.is_bot);
      if (userInfo && userInfo.user.is_bot) {
          return;
      }
      
      let channel = message.channel;
      if (message.text) {
          console.log(`Incoming message: ${message.text}`);
          let msg = message.text.toLowerCase();
          if (/(hello|hi) bot/g.test(msg)) {
            slack.sendMessage(`Hello to you too, ${userInfo.user.name}!`,channel);
          }
      }
  })();
     
});


結果如下










傳送Direct Message
Direct Message (DM) 是一個僅在兩個用戶之間運行的頻道。根據設計,它不能有多於或少於兩個用戶,並且用於私人通信。 發送DM與向頻道發送消息非常相似。

在index.js新增getDM() function和片段程式
slack.on('message', (message) => {

  (async() =>{
      let user= {"user":message.user};
      let userInfo = await getUserInfo(user);
      let dmInfo = await getDM(user);
      if (userInfo && userInfo.user.is_bot) {
          return;
      }
      
      let channel = message.channel;
      if (message.text) {
          console.log(`Incoming message: ${message.text}`);
          let msg = message.text.toLowerCase();

          if(/uptime/g.test(msg))
          {
            let uptime = process.uptime();
            let minutes = parseInt(uptime / 60, 10),
            hours = parseInt(minutes / 60, 10),
            seconds = parseInt(uptime % 60, 10);
            slack.sendMessage(`I have been running for: ${hours} hours,${minutes} minutes and ${seconds} seconds.`, dmInfo.channel.id);
          }else if (/(hello|hi) bot/g.test(msg)) {
            slack.sendMessage(`Hello to you too, ${userInfo.user.name}!`,channel);
          }
      }
  })();
     
});

function getDM(user) 
{
   return web.im.open(user).then((resp)=>{
      return resp;
   }).catch((error) => {console.log(error);});   
}

在這個例子中,我們的Bot會將當前正常運行時間透過DM發送給任何發送"uptime"關鍵字的用戶。









































限制訪問
偶爾,我們會希望Bot某些指令只允許Admin才能使用。我們使用之前的例子uptime 指令只能讓Admin才能使用,其他user使用將會回傳warning 訊息。只要透過userInfo判斷該user是否admin即可。
if(/uptime/g.test(msg)){
     if (!userInfo.user.is_admin) {
          slack.sendMessage(`Sorry ${userInfo.user.name}, but that functionality is only for admins.`, dmInfo.channel.id);
          return;
     }
            
     let uptime = process.uptime();
     let minutes = parseInt(uptime / 60, 10),
     hours = parseInt(minutes / 60, 10),
     seconds = parseInt(uptime % 60, 10);
     slack.sendMessage(`I have been running for: ${hours} hours,${minutes} minutes and ${seconds} seconds.`, dmInfo.channel.id);  
}




2018年6月21日 星期四

Node.js 開發Slack Bot - 2

       Node.js開發Slack Bot - 1已經有一個連接到Slack的Chat Bot, 現在實作當Bot上線後會發送"Hello"給有邀請Bot的全部頻道。

在原本的index.js新增getAllChannels() function
function getAllChannels()
{
   return web.channels.list().then((resp)=>{
      return resp;
   }).catch((error) => {console.log(error);});
}

在connected event呼叫getAllChannels和判斷Bot在哪一個頻道。

slack.on('connected', () => {

  let user= {"user":slack.activeUserId};

  (async() =>{
             
    let userInfo = await getUserInfo(user);
    let teamInfo = await getTeamInfo();
    let allChannels = await getAllChannels();
    let channels = [];
    for (let id in allChannels.channels) {
          let channel = allChannels.channels[id];
          if(channel.is_member)
            channels.push(channel);
    }  

    let channelNames = channels.map((channel) => {
                          return channel.name;
                        }).join(', ');         

    console.log(`Connected to ${teamInfo.team.name} as ${userInfo.user.name}`);
    console.log(`Currently in: ${channelNames}`);
  })();

});

切換Terminal會看到以下類似的輸出,這時候已經知道Bot在哪一個頻道,開始發送"Hello"給這些頻道的所有人。

Connected to Deikhoong as helloBot
Currently in : bot-test

獲取頻道中的全部人
我們已經有了channels這個物件,那需要得到頻道中的人就變得非常簡單。
channels.forEach((channel) => {
     console.log('Members of this channel: ', channel.members);
});

結果如下,
Connected to Deikhoong as hellobot
Currently in: bot-test
Members of this channel:  [ 'U1GUZL79C', 'UBGN5T9K2' ]

發現channels.members回傳的不是member的物件而是member id, 這會讓我們很難辨識.我們在改寫這段程式碼使用直接的getUserInfo()獲得 user的名稱。
let members=[];
channels.forEach(async (channel) => {
     for (let id in channel.members) {
        let memberId = channel.members[id];      
        user= {"user":memberId};
        let member= await getUserInfo(user);
        members.push(member);
     }
     let memberNames = members.map((member) => {
              return member.user.name;
      }).join(', ');
     console.log('Members of this channel: ', memberNames);
});

已經可以看到user的名稱
Connected to Deikhoong as hellobot
Currently in: bot-test
Members of this channel: tan.deik.hoong, hellobot

發送訊息到頻道
透過RTMClient.sendMessage發送訊息給頻道。

slack.on('connected', () => {

  let user= {"user":slack.activeUserId};

  (async() =>{
             
    let userInfo = await getUserInfo(user);
    let teamInfo = await getTeamInfo();
    let allChannels = await getAllChannels();
    let channels = [];
    for (let id in allChannels.channels) {
          let channel = allChannels.channels[id];
          if(channel.is_member)
            channels.push(channel);
    }  

    let channelNames = channels.map((channel) => {
                          return channel.name;
                        }).join(', '); 


    console.log(`Connected to ${teamInfo.team.name} as ${userInfo.user.name}`);
    console.log(`Currently in: ${channelNames}`);

    let members=[];
    channels.forEach(async (channel) => {
         for (let id in channel.members) {
            let memberId = channel.members[id];      
            user= {"user":memberId};
            let member= await getUserInfo(user);
            members.push(member);
         }
         let memberNames = members.map((member) => {
              return member.user.name;
          }).join(', ');
        console.log('Members of this channel: ', memberNames);
        //send message to channel
        slack.sendMessage(`Hello ${memberNames}!`, channel.id);
    });

  })();

});

Bot每次上線的時候都會發送訊息給全部頻道的人。

2018年6月20日 星期三

Node.js 開發Slack Bot -1

在開發Slack Bot之前需要準備環境和套件。

1) 安裝Node.js
2) 安裝nodemon 套件
  • npm install -g nodemon
3) 創建新Project和安裝slack client 套件
  • mkdir botTest && cd botTest
  • npm init
  • npm install @slack/client --save  (目前版本是4.0++)
4) 創建Slack API Token

     開啟瀏覽器輸入以下網址(my.slack.com 修改成自己的網址)                     
     https://my.slack.com/apps/build/customintegration.

按照以下步驟:

1) 選擇 Custom Integrations


2)選擇 Add Configuration 和設定Bots的名稱

3) 獲取API Token 
環境準備好,那就開始我們第一支程式。

新增檔案index.js, 程式碼如下:
'use strict';

// Import the Real Time Messaging (RTM) client
const RtmClient = require('@slack/client').RTMClient;

// Import the Web Client
const WebClient = require('@slack/client').WebClient;

//Slack API Token
const token = "";

const web = new WebClient(token);

let slack = new RtmClient(token);

// When the bot connects success
slack.on('connected', () => {

  let user= {"user":slack.activeUserId};

   (async() =>{
             
    let userInfo = await getUserInfo(user);
    let teamInfo = await getTeamInfo();
    console.log(`Connected to ${teamInfo.team.name} as ${userInfo.user.name}`);
  })();

});

// Start the login process
slack.start();


function getUserInfo(user)
{
    return web.users.info(user).then((resp) => {
        return resp;
    }).catch((error) => {console.log(error);});;
}

function getTeamInfo()
{
    return  web.team.info().then((resp) => {
         return resp;
    }).catch((error) => {console.log(error);});
}

當你在Terminal 看到類似的內容, 代表你的Chat Bot已經成功連結到Slack.

Connected to "Deikhoong" as "hellobot"





















加入頻道
Bot 不能透過編程的方式加入頻道,這是防止Bot在沒有被邀請的情況下加入私人頻道。當Bot加入了頻道,頻道中的全部活動都會被Bot 監控。Bot相關可以執行和不可執行的操作在 https://api.slack.com/bot-users 有完整的文件說明。

要將Bot加入頻道可以通過Slack Client中的invite指令. 輸入後將會受到Bot加入頻道的確認訊息。要將Bot從頻道移除一樣通過Slack Client中的remove指令即可。