通知イメージ
root
package.json
{
"name": "redticket",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"serve": "firebase emulators:start",
"deploy": "firebase deploy",
"logs": "firebase functions:log"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-eslint": "^10.1.0",
"eslint": "^7.0.0",
"eslint-plugin-promise": "^4.2.1",
"firebase-tools": "^8.2.0",
"prettier": "^2.0.5"
}
}
firebase.json
{
"hosting": {
"public": "public",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"function": "api"
}
]
}
}
.prettierrc
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"printWidth": 100,
"arrowParens": "avoid"
}
.eslintrc
{
"extends": ["eslint:recommended"],
"plugins": [],
"parser": "babel-eslint",
"parserOptions": {},
"env": {
"es6": true,
"node": true
},
"globals": {},
"rules": {
"semi": ["error", "never"],
"arrow-parens": "off"
}
}
functions
package,json
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {},
"engines": {
"node": "8"
},
"dependencies": {
"@slack/web-api": "^5.8.1",
"axios": "^0.19.2",
"express": "^4.17.1",
"firebase-admin": "^8.10.0",
"firebase-functions": "^3.6.1"
},
"devDependencies": {
"firebase-functions-test": "^0.2.0"
},
"private": true
}
index.js
const admin = require('firebase-admin')
const axios = require('axios')
const serviceAccount = require('./serviceAccount.json') // Firestoreの秘密鍵
const Express = require('express')
const functions = require('firebase-functions')
const { WebClient } = require('@slack/web-api')
const client = new WebClient('[Bot User OAuth Access Token]')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: 'https://xxxxxxx-xxxxx.firebaseio.com',
})
const usersCollection = admin.firestore().collection('users')
const redmineBaseURL = 'http://redmine.url'
const api = Express()
api.use(Express.json())
api.use(Express.urlencoded({ extended: true }))
api.post('/redmine', async (req, res) => {
try {
const ticketUrl = req.body.payload.url
const ticket = req.body.payload.issue
const comment = req.body.payload.journal
const watchers = [...ticket.watchers]
const query = usersCollection.where(
'redmine_mail',
'in',
watchers.map(watcher => watcher.mail)
)
query.get().then(querySnapshot => {
if (!querySnapshot.empty) {
querySnapshot.docs.forEach(doc => {
client.chat.postMessage({
channel: doc.data().channel_id,
text: ``,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*Subject:*\n${ticket.subject}\n\n*URL:*\n${ticketUrl}\n\n*Description:*\n${ticket.description}`,
},
},
{
type: 'divider',
},
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*Latest comment:*\n${comment.notes}\n\n*Comment by:*\n${comment.author.firstname} ${comment.author.lastname}`,
},
},
],
})
})
}
})
} catch (err) {
console.error(err)
} finally {
res.status(200).send()
}
})
api.post('/redticket', async (req, res) => {
try {
res.send('') // Error avoid
await client.chat.postMessage({
channel: req.body.channel_id,
text: `🏃: Start /redticket ${req.body.text}`,
})
const mode = req.body.text.trim() !== '' ? req.body.text.trim().split(' ')[0] : ''
switch (mode) {
case '':
case 'help': {
await client.chat.postMessage({
channel: req.body.channel_id,
text: '',
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text:
"Hello👋 I will explain how to use RedTicket. The first step is to initialize the user data. The command is `/redticket init` 💻\n\nThe next step is to register your redmine account! You will need redmine's API token to register your account. The command is `/redticket verify [API Token]`. Let's sign up for an account right away 🏃 You can check your registered user data at any time at `/redticket check` 👌\n\nIf you need help, try send the `/redticket` or `/redticket help` 📖",
},
},
{
type: 'divider',
},
],
})
break
}
case 'init': {
await client.chat.postMessage({
channel: req.body.channel_id,
text: `🏃: Checking the existence of the user data`,
})
const userInfo = await usersCollection.doc(`${req.body.team_id}${req.body.user_id}`).get()
const setDocument = async () => {
const data = {
team_id: req.body.team_id,
team_domain: req.body.team_domain,
user_id: req.body.user_id,
channel_id: req.body.channel_id,
user_name: req.body.user_name,
}
await client.chat.postMessage({
channel: req.body.channel_id,
text: `🏃: Creating your user data.`,
})
return await usersCollection
.doc(`${req.body.team_id}${req.body.user_id}`)
.set(data)
.then(async () => {
const userInfo = await usersCollection
.doc(`${req.body.team_id}${req.body.user_id}`)
.get()
return userInfo.data()
})
}
const docRef = userInfo.data() === undefined ? await setDocument() : userInfo.data()
await client.chat.postMessage({
channel: req.body.channel_id,
text: `🏃: Your user data.\n\n${JSON.stringify(docRef, null, '\t')}`,
})
break
}
case 'check': {
await client.chat.postMessage({
channel: req.body.channel_id,
text: `🏃: Reading your user data`,
})
const userInfo = await usersCollection.doc(`${req.body.team_id}${req.body.user_id}`).get()
const docRef = userInfo.data()
await client.chat.postMessage({
channel: req.body.channel_id,
text: `🏃: Your user data.\n\n${JSON.stringify(docRef, null, '\t')}`,
})
break
}
case 'verify': {
if (req.body.text.trim().split(' ').length <= 1) {
await client.chat.postMessage({
channel: req.body.channel_id,
text: `🙅: Need Redmine API Token.`,
})
break
}
await client.chat.postMessage({
channel: req.body.channel_id,
text: `🏃: Verify the Redmine API Token.`,
})
const redmineUserInfo = await axios.get(`${redmineBaseURL}/my/account.json`, {
params: {
key: req.body.text.trim().split(' ')[1],
},
})
await client.chat.postMessage({
channel: req.body.channel_id,
text: `🏃: Your user data in Redmine.\n\n${JSON.stringify(
redmineUserInfo.data.user,
null,
'\t'
)}`,
})
const userInfo = await usersCollection.doc(`${req.body.team_id}${req.body.user_id}`).get()
const docRef = userInfo.data()
if (!userInfo.exists) {
throw new Error('No such your data')
}
docRef.redmine_login = redmineUserInfo.data.user.login
docRef.redmine_mail = redmineUserInfo.data.user.mail
const updatedUserData = await usersCollection
.doc(`${req.body.team_id}${req.body.user_id}`)
.update(docRef)
.then(async () => {
const userInfo = await usersCollection
.doc(`${req.body.team_id}${req.body.user_id}`)
.get()
return userInfo.data()
})
await client.chat.postMessage({
channel: req.body.channel_id,
text: `🏃: Your user data.\n\n${JSON.stringify(updatedUserData, null, '\t')}`,
})
break
}
case 'project': {
await client.chat.postMessage({
channel: req.body.channel_id,
text: `🏃: Verify the Redmine API Token.`,
})
break
}
default:
throw new Error('Invalid parameters.')
}
} catch (error) {
await client.chat.postMessage({ channel: req.body.channel_id, text: `🙅: ${error}` })
} finally {
await client.chat.postMessage({
channel: req.body.channel_id,
text: `🎉: Done /redticket ${req.body.text}`,
})
}
})
exports.api = functions.https.onRequest(api)
リファクタしないと。