Skip to content

Commit

Permalink
Merge pull request linedevth#3 from linedevth/master
Browse files Browse the repository at this point in the history
Organize multiple functions following by modularization concept
  • Loading branch information
jirawatee authored Mar 4, 2021
2 parents 98a372a + 101bb67 commit cb5f8af
Show file tree
Hide file tree
Showing 10 changed files with 661 additions and 97 deletions.
51 changes: 48 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ logs
*/npm-debug.log
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
firebase-debug.log*
firebase-debug.*.log*
**/firebase-debug.log
**/ui-debug.log
**/database-debug.log
**/pubsub-debug.log
lerna-debug.log

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Firebase cache
**/.firebase


# Firebase config
**/.firebaserc

Expand All @@ -30,6 +32,7 @@ lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output
Expand All @@ -48,13 +51,26 @@ build/Release

# Dependency directories
**/node_modules/*
**/jspm_packages/*

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

Expand All @@ -67,6 +83,35 @@ yarn.lock

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

.idea
**/.runtimeconfig.json
Expand All @@ -76,4 +121,4 @@ service-account.json
service-account-credentials.json

.DS_Store
Thumbs.db
Thumbs.db
6 changes: 6 additions & 0 deletions database.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"rules": {
".read": true,
".write": true
}
}
24 changes: 23 additions & 1 deletion firebase.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
{
"functions": {
"predeploy": ["npm --prefix \"$RESOURCE_DIR\" run lint"],
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
],
"source": "functions"
},
"database": {
"rules": "database.rules"
},
"firestore": {
"rules": "firestore.rules"
},
"emulators": {
"functions": {
"port": 5001
},
"firestore": {
"port": 8080
},
"database": {
"port": 9000
},
"ui": {
"enabled": true
}
}
}
7 changes: 7 additions & 0 deletions firestore.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
2 changes: 1 addition & 1 deletion functions/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"parserOptions": {
// Required for certain syntax usages
"ecmaVersion": 2017
"ecmaVersion": 2018
},
"plugins": [
"promise"
Expand Down
106 changes: 22 additions & 84 deletions functions/index.js
Original file line number Diff line number Diff line change
@@ -1,91 +1,29 @@
const request = require("request-promise")
const functions = require("firebase-functions")
const LINE_MESSAGING_API = "https://api.line.me/v2/bot"
const LINE_USER_ID = "YOUR-LINE-USER-ID"
const LINE_HEADER = {
"Content-Type": "application/json",
Authorization: "Bearer YOUR-LINE-CHANNEL-ACCESS-TOKEN"
}
const OPENWEATHER_API = "https://api.openweathermap.org/data/2.5/weather/"
const OPENWEATHER_APPID = "YOUR-OPEN-WEATHER-APP-ID"
const util = require('./util')
const runtimeOpts = { timeoutSeconds: 8, memory: "4GB" }
const region = "asia-southeast2"

exports.LineBotReply = functions.https.onRequest((req, res) => {
exports.LineProxy = functions.region(region).runWith(runtimeOpts).https.onRequest(async (req, res) => {
if (req.method === "POST") {
reply(req.body)
if (!util.verifySignature(req.headers['x-line-signature'], req.body)) {
return res.status(401).send('Unauthorized')
}

let event = req.body.events[0]

if (event.message !== undefined) {
if (event.message.type !== "text") {
util.reply(event.replyToken, { type: "text", text: JSON.stringify(event) })
} else {
util.postToDialogflow(req)
}
}
}
return res.status(200).send(req.method)
})

const reply = bodyResponse => {
return request.post({
uri: `${LINE_MESSAGING_API}/message/reply`,
headers: LINE_HEADER,
body: JSON.stringify({
replyToken: bodyResponse.events[0].replyToken,
messages: [{ type: "text", text: JSON.stringify(bodyResponse) }]
})
})
}

exports.LineBotPush = functions.https.onRequest(async (req, res) => {
let response = await request.get({
uri: `${OPENWEATHER_API}?appid=${OPENWEATHER_APPID}&units=metric&type=accurate&zip=10330,th`,
json: true
})
const message = `City: ${response.name}\nWeather: ${response.weather[0].description}\nTemperature: ${response.main.temp}`
return push(LINE_USER_ID, message)
})

const push = async (targetId, msg) => {
await request.post({
uri: `${LINE_MESSAGING_API}/message/push`,
headers: LINE_HEADER,
body: JSON.stringify({
to: targetId,
messages: [{ type: "text", text: msg }]
})
})
return res.status(200).send({ message: `Push: ${msg}` })
}

exports.LineBotMulticast = functions.https.onRequest((req, res) => {
const text = req.query.text
if (text !== undefined && text.trim() !== "") {
return multicast(res, text)
} else {
return res.status(400).send({ message: "Text not found" })
}
})

const multicast = async (res, msg) => {
await request.post({
uri: `${LINE_MESSAGING_API}/message/multicast`,
headers: LINE_HEADER,
body: JSON.stringify({
to: [LINE_USER_ID, "Ua0e8dd654eeb56790bc0e342bfd46e1c"],
messages: [{ type: "text", text: msg }]
})
})
return res.status(200).send({ message: `Multicast: ${msg}` })
}

exports.LineBotBroadcast = functions.https.onRequest((req, res) => {
const text = req.query.text
if (text !== undefined && text.trim() !== "") {
return broadcast(res, text)
} else {
const ret = { message: "Text not found" }
return res.status(400).send(ret)
}
})

const broadcast = async (res, msg) => {
await request.post({
uri: `${LINE_MESSAGING_API}/message/broadcast`,
headers: LINE_HEADER,
body: JSON.stringify({
messages: [{ type: "text", text: msg }]
})
})
return res.status(200).send({ message: `Broadcast: ${msg}` })
}
const line = require('./line')
exports.lineBotReply = line.botReply
exports.lineBotPush = line.botPush
exports.lineBotMulticast = line.botMulticast
exports.lineBotBroadcast = line.botBroadcast
52 changes: 52 additions & 0 deletions functions/line.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use strict"

const functions = require("firebase-functions")
const axios = require('axios')
const util = require('./util')

const runtimeOpts = { timeoutSeconds: 8, memory: "4GB" }
const region = "asia-southeast2"
const LINE_USER_ID = functions.config().jirawatee.userid
const OPENWEATHER_API = "https://api.openweathermap.org/data/2.5/weather/"
const OPENWEATHER_APPID = functions.config().jirawatee.openweather_appid

exports.botReply = functions.region(region).runWith(runtimeOpts).https.onRequest((req, res) => {
if (req.method === "POST") {
util.reply(
req.body.events[0].replyToken,
{ type: "text", text: JSON.stringify(req.body) }
)
}
return res.status(200).send(req.method)
})

exports.botPush = functions.region(region).runWith(runtimeOpts).https.onRequest(async (req, res) => {
const response = await axios({
method: 'get',
url: `${OPENWEATHER_API}?appid=${OPENWEATHER_APPID}&units=metric&type=accurate&zip=10330,th`
})
const msg = `City: ${response.data.name}\nWeather: ${response.data.weather[0].description}\nTemperature: ${response.data.main.temp}`
util.push(LINE_USER_ID, { type: "text", text: msg })
return res.status(200).send({ message: `Push: ${msg}` })
})

exports.botMulticast = functions.region(region).runWith(runtimeOpts).https.onRequest((req, res) => {
const msg = req.query.msg
if (msg !== undefined && msg.trim() !== "") {
util.multicast({ type: "text", text: msg })
return res.status(200).send({ message: `Multicast: ${msg}` })
} else {
return res.status(400).send({ message: "Text not found" })
}
})

exports.botBroadcast = functions.region(region).runWith(runtimeOpts).https.onRequest((req, res) => {
const msg = req.query.msg
if (msg !== undefined && msg.trim() !== "") {
util.broadcast({ type: "text", text: msg })
return res.status(200).send({ message: `Broadcast: ${msg}` })
} else {
const ret = { message: "Text not found" }
return res.status(400).send(ret)
}
})
15 changes: 7 additions & 8 deletions functions/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"description": "LINE Bot 101 by Messaging API and Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase serve --only functions",
Expand All @@ -10,17 +10,16 @@
"logs": "firebase functions:log"
},
"dependencies": {
"firebase-admin": "^8.9.2",
"firebase-functions": "^3.3.0",
"request": "^2.88.2",
"request-promise": "^4.2.5"
"axios": "^0.21.1",
"firebase-admin": "^9.5.0",
"firebase-functions": "^3.13.2"
},
"devDependencies": {
"eslint": "^6.8.0",
"eslint-plugin-promise": "^4.2.1"
"eslint": "^7.21.0",
"eslint-plugin-promise": "^4.3.1"
},
"engines": {
"node": "8"
"node": "14"
},
"private": true
}
Loading

0 comments on commit cb5f8af

Please sign in to comment.