今天我們來完成透過Cahtbot 輸入經緯度搜尋附近藥局口罩數量的功能
首先要處理來自Line的 Location Webhook Event
{
"destination": "xxxxxxxxxx",
"events": [
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "message",
"mode": "active",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
},
"message": {
"id": "325708",
"type": "location",
"title": "my location",
"address": "〒150-0002 東京都渋谷区渋谷2丁目21−1",
"latitude": 35.65910807942215,
"longitude": 139.70372892916203
}
}
]
}
首先修改index.ts
import { AzureFunction, Context, HttpRequest } from "@azure/functions"
import { WebhookEvent, validateSignature } from "@line/bot-sdk"
const intercept = require('azure-function-log-intercept');
import { LINE } from "./config"
import * as Dispatcher from './Chatbot/Dispatcher'
const lineWebhook: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
intercept(context);// console.log works now!
const signature: string = req.headers["x-line-signature"]
if (validateSignature(JSON.stringify(req.body), LINE.channelSecret, signature)) {
const events = req.body.events as Array<WebhookEvent>
for (const event of events) {
await Dispatcher.eventDispatcher(event)
}
}
context.res = { status: 200, body: 'ok' };
};
export default lineWebhook;
我們新增一支Dispatcher.ts程式來解析Webhook Event,並且根據文字,座標等不同的輸入進行不同處理
import { WebhookEvent, EventMessage } from "@line/bot-sdk"
import * as lineService from "./lineService"
import * as workflow from "./Workflow"
export const eventDispatcher = async(event: WebhookEvent) => {
let replyToken: string
let userId: string
switch (event.type) {
case "follow":
const replyMessage = "歡迎加入口罩查詢,你可以輸入定位地點查詢附近健保特約機構口罩剩餘數量"
replyToken = event.replyToken
await lineService.replyMessage(replyMessage, replyToken)
break;
case "message":
const message: EventMessage = event.message
replyToken = event.replyToken
userId = event.source.userId
await messageDispatcher(message, replyToken, userId)
break
default:
break
}
}
const messageDispatcher = async(message: EventMessage, replyToken: string, userId: string) => {
switch (message.type) {
case "text":
const phrase = message.text
workflow.textUnderstanding(phrase, replyToken)
break
case "location":
console.log('location')
const GPS = {
lat: message.latitude,
lon: message.longitude
}
await workflow.findMaskStore(GPS, userId)
break
default:
break
}
}
想做到上述功能的Chatbot 我們可以使用政府公開資料集-健保特約機構口罩剩餘數量明細清單來取得藥局口罩剩餘數量的資料
使用postman取得資料測試:
再使用Google Map API轉換經緯度(但這要收費)
這邊要感謝口罩供需資訊平台 共筆上kiang提供轉好經緯度的資料:https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json
使用postman取得資料測試:
我們新增一支Workflow.ts程式來計算最接近用戶的藥局資料
import * as lineService from "./lineService"
import axios from "axios";
export const findMaskStore = async (GPS: any, userId: string) => {
let nearMaskStore = []
const maskSnapshot = await axios.get('https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json')
let maskData = maskSnapshot.data.features
for (let element of maskData) {
const storeGPS = element.geometry.coordinates
let R = 6371 // Radius of the earth in km
let dLon = (storeGPS[0] - GPS.lon) * (Math.PI / 180)
let dLat = (storeGPS[1] - GPS.lat) * (Math.PI / 180) // deg2rad below
let a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos((GPS.lat) * (Math.PI / 180)) * Math.cos((GPS.lat) * (Math.PI / 180)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2)
let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
let d = R * c // Distance in km
if (d < 0.4) {
nearMaskStore.push(element)
}
}
for (const store of nearMaskStore) {
const maskMessage = lineService.toMaskMessage(store)
lineService.pushMessage(maskMessage, userId)
}
}
找出距離輸入經緯度最接近的幾組資料推播給使用者,推播方式可參考Day [9] Azure Functions-Line Chatbot實作(ㄧ)
並且為了方便轉換口罩查詢結果的回應訊息我們在lineService.ts程式中新增一隻function
export function toMaskMessage(nearMaskStore: any) {
const maskMessage =
`${nearMaskStore.properties.name}\n` +
`${nearMaskStore.properties.phone}\n` +
`${nearMaskStore.properties.address}\n` +
`成人口罩:${nearMaskStore.properties.mask_adult}\n` +
`兒童口罩:${nearMaskStore.properties.mask_child}`
return maskMessage
}
修改後記得部署Azure Functions就完成嘍
完成後的成品: