Node.js 與 BOT 月牙

來,哩來!!!!

簡報支援

github 支援

Simon Sun

程式中一個迷途的小書僮

Hackathon Taiwan 合作講師

Node.js Party 籌辦人

JSDC 2015 總召集人

當然包括我 Orz

我不是說你

為什麼要叫做「月牙天衝」?

剛好最近又在看死神XD

因為我想不到一個帥氣的題目

 如果我最近在看一拳超人

 題目應該會是「Node.js One Punch Bot」XD

Node.js One Punch Bot

進入主題

事前準備

Line BOT

Facebook BOT

Q & A

事前準備

請相信我...準備那些 #$@% 才是花最多時間的...

事前準備

Server

LINE developer

Domain

SSL

Faceboook developer

事前準備 - Server

我是用 DigitalOcean

事前準備 - Server

用好了大概是這樣XD

事前準備 - Domain

我用 godaddy

事前準備 - Domain

獲得新的 domain

事前準備 - Domain

設定 domain 指向哪一台主機 ip

事前準備 - SSL

我是用某位大大建議的 gogetssl

有一批 ssl 好便宜啊 !! 

事前準備 - SSL

先在 server 上產生 key 與 csr 

第一次操作可參考熱血漢誌的資料唷 

server 上先安裝 openssl

simon@simon:~$ openssl req -new -newkey rsa:2048 -nodes 
-sha256 -days 730 -keyout ~/XXX.key -out ~/XXX.csr

事前準備 - SSL

然後到你的設定頁面,把 csr 貼上去

事前準備 - SSL

認證過後,他們會寄 crt 到你的信箱

事前準備 - SSL

下載完整的檔案

事前準備 - SSL

解開檔案後會出現這些資料

事前準備 - SSL

把以下這些資料串聯在一起

產生出來的 real.crt 是真正要給 nginx 用的

simon@simon:~$ cat COMODORSADomainValidationSecureServerCA.crt 
COMODORSAAddTrustCA.crt XXX.crt >> real.crt

事前準備 - SSL

XXX.key 與產生的 real.crt 擺放到 server 去

事前準備 - SSL

設定 nginx 並重啟

server {
	listen 443 ssl;
	root /usr/share/nginx/html;
	index index.html index.htm;
	server_name xxx.com;
	ssl_certificate /etc/nginx/ssl/real.crt;
        ssl_certificate_key /etc/nginx/ssl/XXX.key;

    	location / {
	      	proxy_pass https://localhost:9898;
   	}
}

準備動作終於告一段落了

Line BOT

可是我不知道他現在還有沒有開放申請XD

Line BOT

先註冊一個新的 channel

Line BOT

這個頻道的 QR code

Line BOT

/*
 * 處理 ssl 相關事務
 */
let config = require('./config.js');
let fs = require('fs');
let https = require('https');
let sslKey = fs.readFileSync(config.ssl.key, 'utf8');
let sslCrt = fs.readFileSync(config.ssl.cert, 'utf8');
let sslOptions = {
    key: sslKey,
    cert: sslCrt
};

Line BOT

/*
 * server
 */
let express = require('express');
let app = express();

/*
 * body parser 很重要,我卡了半天 Orz
 */
let bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

Line BOT

/*
 * Line Bot 會用到的相關端點
 */
let lineBot = require('./lineBot');

/*
 * Routers
 */
app.post('/line/callback', lineBot.callback);

Line BOT

/*
 * 啟動 server
 */
let httpsServer = https.createServer(sslOptions, app);
httpsServer.listen(3000);
console.log('Listen https server at port 3000');

Line BOT

let request = require('request-promise');
let config = require('./config');

/*
 * 這兩個值是固定的,一定要固定唷
 */
const eventType = '138311608800106203';
const toChannel = '1383378250';

module.exports = {

    /*
     * Line channal 傳過來的資料
     */
    callback: function(req, res, next) {
        // 1. 處理 line 傳過來的訊息
        // 2. 分辨不同的內容
        // 3. 組成回傳的物件
        // 4. 回傳的訊息
    }
};

Line BOT

// 1. 處理 line 傳過來的訊息
let data = req.body.result[0]; // 從 channels post 過來的資料
let contentType = data.content.contentType; // 傳過來的資料類型
let fromWho = data.content.from; // 誰傳過來的

Line BOT

// 2. 分辨不同的內容
let content;
switch(contentType) {
    case 1:
        content = {
            toType: 1,
            contentType: 1,
            text: '你傳文字訊息給我'
        };
        break;
    case 2:
        // 照片處理
        break;
    case 8:
        // 貼圖處理
        break;
    default:
        content = {
            toType: 1,
            contentType: 1,
            text: '我現在分別不出來你傳什麼碗糕給我'
        };
}

Line BOT

// 3. 組成回傳的物件
let options = {
    method: 'POST',
    uri: 'https://trialbot-api.line.me/v1/events',
    body: {
        to: [fromWho],
        toChannel: toChannel,
        eventType: eventType,
        content: content
    },
    headers: {
        'Content-Type': 'application/json; charser=UTF-8',
        'X-Line-ChannelID': config.line.channelId,
        'X-Line-ChannelSecret': config.line.channelSecret,
        'X-Line-Trusted-User-With-ACL': config.line.channelMID
    },
    json: true
};

Line BOT

// 4. 回傳的訊息
request(options)
    .then(function (body) {
        return res.status(200).send();
    })
    .catch(function (err) {
        console.log(err);
        return res.status(400).send();
    });

Line BOT

It's Work!!!

Line BOT

參考資料

Line BOT

小心得

Line 的文件可以再更加強XD

Facebook BOT

Facebook BOT

創立粉絲專頁

Facebook BOT

新增應用程式

Facebook BOT

新增應用程式

Facebook BOT

產生粉絲專頁的 access_token

Facebook BOT

綁定 webhook

Facebook BOT

/*
 * Facebook Bot 會用到的相關端點
 */
let facebookBot = require('./facebookBot');

app.get('/facebook/callback', facebookBot.verify);
app.post('/facebook/callback', facebookBot.callback);

多增加兩個 facebook 端點

Facebook BOT

module.exports = {
    /*
     * 驗證 Facebook message webhook
     */
    verify: function(req, res, next) {
        // 驗證這個 url 是否正確
        if (req.query['hub.verify_token'] === '阿不就好棒棒') {
            return res.send(req.query['hub.challenge']);
        } else {
            return res.send('Error, wrong validation token');    
        }
    }
};

驗證 webhook 正確性

Facebook BOT

認證完成

Facebook BOT

module.exports = {

    /*
     * 驗證 Facebook message webhook
     */
    verify: function(req, res, next) {
        ......
    },

    /*
     * Facebook message
     */
    callback: function(req, res, next) {
        // 1. 處理傳進來的訊息
        // 2. 處理發訊息,收訊息,訊息的內容
        // 3. 要送出去的訊息格式
        // 4. 訊息選項
        // 5. 送出訊息
    }
};

Facebook BOT

// 1. 處理傳進來的訊息
let messaging_events = req.body.entry[0].messaging; // 訊息的內容
let sender; // 送出訊息的人
let recipient; // 收到訊息的人

// 2. 處理發訊息,收訊息,訊息的內容
for(let i = 0; i < messaging_events.length; i++) {
    let event = req.body.entry[0].messaging[i];
    sender = event.sender.id;
    recipient = event.recipient.id;
    if (event.message && event.message.text) {
        let text = event.message.text;
    }
}

Facebook BOT

// 3. 要送出去的訊息格式
let messageData = {
    text: 'JavaScript 好棒棒'
};

// 4. 訊息選項
let options = {
    method: 'POST',
    uri: 'https://graph.facebook.com/v2.6/me/messages',
    qs: {
        access_token: config.facebook.accessToken
    },
    body: {
        message: messageData,
        recipient: {
            id: sender
        }
    },
    json: true
};

Facebook BOT

// 5. 送出訊息
request(options)
    .then(function (body) {
        return res.status(200).send();
    })
    .catch(function (err) {
        console.log(err);
        return res.status(400).send();
    });

Facebook BOT

It's work!!!!

參考資料

Facebook BOT

小心得

反應速度較慢

Facebook BOT

文件比 Line 清楚

官方 demo code 是用 Node.js

我不小心趴在桌子上睡著了.....

我夢到 vue.js

我夢到 Netflix

我夢到 Angular.js 2.0

我夢到 universal

啊....夢醒了,怎麼這麼真實!!!!

Q & A