Line Bot 簡單範例

  • 12434
  • 0
  • bot
  • 2016-09-26

針對初學者,利用 BOT API Trial 製作簡單的機器人

申請 Line Bot Api Trail

1. 進入 Line Business Center,點選「BOT API Trial Account」,點擊下方的開始使用。

BOT API Trial Account每人只能申請1組。將可能不經預告即結束服務

2. 登入Line帳號,填寫基本訊息,建立好自己的 BOT 帳號,進入Basic Information頁面。記下Channel ID、 Channel Secret 和 MID 。

3. 頁面下方有一個QR code,請拿手機加自己的BOT為好友。

目前 BOT API Trial Account 只能由 QR code 或 朋友邀請 加入好友

您也可以先和這個範例bot聊天看看結果:

https://qr-official.line.me/sid/L/tre1838w.png

Azure Web 應用程式建立

Line Bot api 要求伺服器必須為支援SSL,這個範例中我們將使用Azure web app,您可以申請試用或是其他方案。

若您是學生,您可參考免費的 DreamSpark 方案

1. 在入口網站中,依序點選「新增」、「web + 行動」、「web 應用程式」。

2. 輸入應用程式名稱,選擇定用帳戶、資源群組及方案後按下建立,並等待部屬完成。

應用程式名稱為您的網域名稱,不可以和其他應用程式重複。

使用 Node js 撰寫 Bot

這篇文章中我們使用 Node.js 來撰寫程式,您必須安裝好Visual StudioNode.js 工具

1. 開啟Visual Studio,點選「新增專案」。

2. 選擇 Node.js 中的 Blank Azure Node.js Web Application 並按下確定。

 

在「位置」選項中,請盡量避免中文路徑或是太長的名子。

3. 在右上方「方案總管」中,npm上案右鍵,選擇「Install New npm Packages」。

4. 搜尋 Express 並安裝,並依照同樣方法安裝 body-parserrequest crypto,完成後您的方案總管應該像下方右圖所示。

5. 在 server.ts 中替換下列程式碼

import http = require('http');
var bodyParser = require('body-parser');
var express = require('express');

var port = process.env.port || 1337
var app = express();
app.use(bodyParser.json());

// 接聽來自Line伺服器中的訊息,交由Function receiver處理
app.post('/callback', function (req, res) {
    receiver(req, res);
});

// 開啟伺服器
http.createServer(app).listen(port);

 

驗證伺服器

1. Line 在發送訊息給你的伺服器時,會加入一個 header,叫做 Signature validation。用來驗證傳送訊息過來的是Line伺服器,而不是其他人,計算這組字串需要 Channel secret傳送內容,所以每次都不一樣,詳細的資料請看官方文件
我們可以利用下列程式計算Signature

const secret = "";
function getSign(event) {
    var crypto = require('crypto');
    var body = new Buffer(JSON.stringify(event.body), 'utf8');
    // secret 為您的 Channel secret     
    var hash = crypto.createHmac('sha256', secret).update(body).digest('base64');
    return hash
}
您也可以選擇忽略認證,接收傳送過來的所有訊息。

2. 接著撰寫 receiver

function receiver(req, res) {
    var data = req.body;
    if (getSign(req) == req.get("X-LINE-ChannelSignature")) {
        // ChannelSignature 正確,處理訊息
        res.sendStatus(200);
    }
    else
        res.sendStatus(403); //ChannelSignature錯誤,回傳403

}

3. 到此步驟您的server.ts應該長這樣(server 2.ts),在專案上案右鍵,選擇「發行」。

4. 選擇「Microsoft Azure Web 應用程式」,並選擇剛剛建立的應用程式,確定後按下發行。

5. 前往您的Line bot設定頁面,在Basic information下方點選 EDIT。

6. 在 Callback URL 中輸入 https://[應用程式名稱].azurewebsites.net:443/callback,例如https://goofyline.azurewebsites.net:443/callback,點選Save。

7. 點選Callback URL後面的 VERIFY,應該要出現success字樣。

接收/傳送訊息

1. 修改 receiver function 如下

function receiver(req, res) {
    var data = req.body;
    if (getSign(req) == req.get("X-LINE-ChannelSignature")) {
        // ChannelSignature 正確,處理訊息
        data.result.forEach(function (result) {
            var type = result.content.contentType;
            if (type == "1") {
                sendTextMessage(result.content.from, "傳送您的位置來獲得天氣訊息");
            }
            else if (type == "8") {
                sendSticker(result.content.from, 4, getRandom(260, 289));
                //console.log(result.content);
            }
            else if (type == "7")//location
            {
                sendWeather(result.content.from, result.content.location.latitude, result.content.location.longitude)
            }

        });
        res.sendStatus(200);
    }
    else
        res.sendStatus(403); //ChannelSignature錯誤,回傳403

}

在驗證伺服器後,我們開始接受訊息,其中 result.content.contentType 為訊息類別,為1~10的數字(訊息分類),在這個範例中,我們要處理的有

訊息代號 訊息格式 處理
1 純文字訊息 傳送「傳送您的位置來獲得天氣訊息」給使用者
7 地點 傳送該地點的天氣訊息和一張貼圖
8 貼圖 隨機傳送一張貼圖給使用者(可使用的貼圖列表)

2. 撰寫其他Function,server.ts :

const secret = ""; // 您的 Channel Secret
const id = ""; // 您的 Channel ID
const mid = ""; // 您的 MID 
const weather_key = ""; // openweathermap API key

var http = require('http');
var bodyParser = require('body-parser');
var express = require('express');
var request = require('request');

var port = process.env.port || 1337
var app = express();
app.use(bodyParser.json());

// 接聽來自Line伺服器中的訊息,交由Function receiver處理
app.post('/callback', function (req, res) {
    receiver(req, res);
});

// 開啟伺服器
http.createServer(app).listen(port);

function getSign(event) {
    var crypto = require('crypto');
    var body = new Buffer(JSON.stringify(event.body), 'utf8');
    // secret 為您的 Channel secret     
    var hash = crypto.createHmac('sha256', secret).update(body).digest('base64');
    return hash
}
function receiver(req, res) {
    var data = req.body;
    if (getSign(req) == req.get("X-LINE-ChannelSignature")) {
        // ChannelSignature 正確,處理訊息
        data.result.forEach(function (result) {
            var type = result.content.contentType;
            if (type == "1") {
                sendTextMessage(result.content.from, "傳送您的位置來獲得天氣訊息");
            }
            else if (type == "8") {
                // 傳送一張隨機貼圖
                sendSticker(result.content.from, 4, getRandom(260, 289));
            }
            else if (type == "7")//location
            {
                // 傳送天氣訊息
                sendWeather(result.content.from, result.content.location.latitude, result.content.location.longitude)
            }

        });
        res.sendStatus(200);
    }
    else
        res.sendStatus(403); //ChannelSignature錯誤,回傳403

}
function sendWeather(recipientId, lat, lng) {
    // 查詢天氣,設定語言為繁體中文,溫度單位為攝氏溫度
    request({
        uri: 'http://api.openweathermap.org/data/2.5/weather',
        qs: {
            appid: weather_key,
            lat: lat,
            lon: lng,
            lang: "zh_tw",
            units: "metric"
        },
        method: 'GET',
    },

        function (error, response, body) {
            //Check for error
            if (error) {
                return console.log('Error:', error);
            }

            //Check for right status code
            if (response.statusCode !== 200) {
                return console.log('Invalid Status Code Returned:', response.statusCode, response.statusMessage);
            }
            var data = JSON.parse(body);
            // 傳送 城市名稱 天氣狀況 溫度
            sendTextMessage(recipientId, data.name + " " + data.weather[0].description + " 溫度:" + data.main.temp)
            // 傳送和天氣有關的貼圖
            var icon = data.weather[0].icon[0] + data.weather[0].icon[1];
            if (icon == "01" || icon == "02") //晴天
                sendSticker(recipientId, 4, 263);
            else if (icon == "03" || icon == "04") //多雲
                sendSticker(recipientId, 4, 264);
            else //雨天
                sendSticker(recipientId, 4, 266);

        }


    );
}
function getRandom(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
}

function sendSticker(recipientId, s_pack, s_id) {
    var messageData = {
        to: [recipientId],
        toChannel: 1383378250,
        eventType: "138311608800106203",
        content: {
            contentType: 8,
            toType: 1,
            contentMetadata: {
                STKID: s_id + '',
                STKPKGID: s_pack + ''
            }
        }
    };

    toLine(messageData);
}
function sendTextMessage(recipientId, messageText) {
    var messageData = {
        to: [recipientId],
        toChannel: 1383378250,
        eventType: "138311608800106203",
        content: {
            contentType: 1,
            toType: 1,
            text: messageText
        }
    };
    toLine(messageData);
}
function toLine(messageData) {
    request({
        uri: 'https://trialbot-api.line.me/v1/events',
        headers: {
            "Content-type": "application/json; charser=UTF-8",
            "X-Line-ChannelID": id,
            "X-Line-ChannelSecret": secret,
            "X-Line-Trusted-User-With-ACL": mid
        },
        method: 'POST',
        json: messageData
    },
        function (error, response, body) {
            //Check for error
            if (error) {
                return console.log('Error:', error);
            }

            //Check for right status code
            if (response.statusCode !== 200) {
                return console.log('Invalid Status Code Returned:', response.statusCode, response.statusMessage);
            }

            //All is good. Print the body
            console.log(body); // Show the HTML for the Modulus homepage.

        }


    );
}

這個範例中查詢天氣我們使用OpeWeatherMap來查詢,您可以在這裡註冊,並在API管理頁面獲取API key

最終的程式碼也可以在Github上找到(server final.ts)

3. 在專案上方按下右鍵,發行,直接按下發行即可,發行完成後,試著傳送訊息、貼圖、地點您的BOT,應該就可以看到回應了。

結論

1. 若要使用其他服務的BOT (Facebook messenger、Skype等服務),可以使用 Microsoft Bot Framework ,他提供了整合的服務 ,只要寫一次程式就可以應用在支援的服務上。

2. 可以搭配自然語言學習的服務,LUIS 來讓機器人能更人性化。

後繼

2016/9/26
Line bot 帳戶是有期限的,必須重新認證Email確定妳是活躍帳戶。