diff --git a/Dockerfile b/Dockerfile index ce40035..dfd81c0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,9 @@ RUN mkdir -p /data WORKDIR /data COPY . . RUN rm -f .env +RUN apt-get update -qqy +RUN apt-get install -qqy imagemagick graphicsmagick +RUN apt-get clean RUN npm install EXPOSE ${NODE_PORT} CMD ["npm", "run", "dbrun"] \ No newline at end of file diff --git a/bin/dbVersion.json b/bin/dbVersion.json index 28bacc1..627f9be 100644 --- a/bin/dbVersion.json +++ b/bin/dbVersion.json @@ -6,7 +6,8 @@ {"file": "20180702-1.sql", "version": 4}, {"file": "20180706-1.sql", "version": 5}, {"file": "20180710-1.sql", "version": 6}, - {"file": "20180711-1.sql", "version": 7} + {"file": "20180711-1.sql", "version": 7}, + {"file": "20180712-1.sql", "version": 8} ], "test": [] } \ No newline at end of file diff --git a/bin/dbtool.js b/bin/dbtool.js index 8c4f388..3bddfa1 100644 --- a/bin/dbtool.js +++ b/bin/dbtool.js @@ -1,6 +1,7 @@ /* eslint-disable no-unused-expressions */ +require('module-alias/register') const pg = require('pg') -const config = require('../config') +const config = require('@config/index') const path = require('path') const fs = require('fs') diff --git a/config/index.js b/config/index.js index 477b073..81842ba 100644 --- a/config/index.js +++ b/config/index.js @@ -1,6 +1,7 @@ module.exports = { port: process.env.NODE_PORT || 10230, url: process.env.HOST_URL || '', + image_root: process.env.IMAGE_ROOT || '/image', line: { secret: process.env.LINE_SECRET || '', access: process.env.LINE_ACCESS || '' diff --git a/index.js b/index.js index 4781824..7210190 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,4 @@ +require('module-alias/register') const fs = require('fs') const path = require('path') try { diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..c2ae2a2 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "baseUrl": ".", + "paths": { + "@libs/*": ["libs/*"], + "@config/*": ["config/*"], + "@route/*": ["route/*"] + } + } +} \ No newline at end of file diff --git a/libs/api-action/google.js b/libs/api-action/google.js index 22ae02a..ad76d19 100644 --- a/libs/api-action/google.js +++ b/libs/api-action/google.js @@ -1,5 +1,5 @@ const axios = require('axios') -const config = require('../../config') +const config = require('@config/index') const qs = require('querystring') const queryYoutubeName = async (id = '') => { diff --git a/libs/api-action/line.js b/libs/api-action/line.js index 26001f8..16f4883 100644 --- a/libs/api-action/line.js +++ b/libs/api-action/line.js @@ -1,5 +1,5 @@ const axios = require('axios') -const config = require('../../config') +const config = require('@config/index') const client = axios.create({ baseURL: 'https://api.line.me/v2/bot', @@ -44,6 +44,16 @@ const textObject = (txt = '') => { } } +const imageObject = (txt = '') => { + if (typeof txt !== 'string' || txt.trim().length === 0) return null + txt = txt.split(';') + return { + type: 'image', + originalContentUrl: txt[0], + previewImageUrl: txt[1] + } +} + /** * send reply message * @param {string} replyToken line message reply token @@ -59,6 +69,7 @@ const replyMessage = async (replyToken, message) => { if (m !== null && m.length > 1) { switch (m[1]) { case 'image': + obj = imageObject(message) break case 'text': obj = textObject(message) @@ -66,6 +77,8 @@ const replyMessage = async (replyToken, message) => { default: obj = textObject(message) } + } else { + obj = textObject(message) } if (obj !== null) { diff --git a/libs/api-action/twitch.js b/libs/api-action/twitch.js index 08140cd..7deff8b 100644 --- a/libs/api-action/twitch.js +++ b/libs/api-action/twitch.js @@ -1,5 +1,5 @@ const axios = require('axios') -const config = require('../../config') +const config = require('@config/index') const client = axios.create({ baseURL: 'https://api.twitch.tv/helix', headers: { diff --git a/libs/database.js b/libs/database.js index 9fba19e..c9fb2c3 100644 --- a/libs/database.js +++ b/libs/database.js @@ -1,5 +1,5 @@ const pg = require('pg') -const config = require('../config') +const config = require('@config/index') const pool = new pg.Pool({ user: config.database.user, @@ -10,4 +10,4 @@ const pool = new pg.Pool({ database: config.database.dbname }) -module.exports = pool \ No newline at end of file +module.exports = pool diff --git a/libs/line-message/commands/actions/group.js b/libs/line-message/commands/actions/group.js index f7c4806..b5d1024 100644 --- a/libs/line-message/commands/actions/group.js +++ b/libs/line-message/commands/actions/group.js @@ -1,5 +1,5 @@ -const api = require('../../../api-action') -const DB = require('../../../database') +const api = require('@libs/api-action') +const DB = require('@libs/database') const axios = require('axios') async function run (fn = null, txt, source) { diff --git a/libs/line-message/commands/index.js b/libs/line-message/commands/index.js index 0cbb219..820f28d 100644 --- a/libs/line-message/commands/index.js +++ b/libs/line-message/commands/index.js @@ -1,4 +1,4 @@ -const DB = require('../../database') +const DB = require('@libs/database') const actions = require('./actions') const parseCMD = async (text = '', source = {}) => { @@ -7,7 +7,8 @@ const parseCMD = async (text = '', source = {}) => { let txt = text.trim() let arr = txt.split(' ').map(t => t.trim()) if (arr.length === 0) return null - if (arr[0][0] !== '!') return null + let cmdMsg = false + if (arr[0][0] === '!') cmdMsg = true let cmd = arr[0].replace(/^!/, '') cmd = cmd.toLowerCase() @@ -16,56 +17,70 @@ const parseCMD = async (text = '', source = {}) => { try { // query normal command - let result = await db.query({ - text: `select "message", "group" from "public"."commands" where "cmd" = $1 and ("group" = '' or "group" = $2)`, - values: [cmd, source.groupId] - }) - if (result.rowCount > 0) { - let obj = result.rows.filter(t => t.group === source.groupId) - if (obj.length === 0) obj = result.rows[0] - else obj = obj[0] - let content = obj.message - let m = content.match(/{{(.+?)}}/g) - if (m !== null && m.length > 0) { - for (let i = 0; i < m.length; i++) { - let c = m[i].replace(/^{{/, '').replace(/}}$/, '') - let carr = c.split('=') - if (carr.length > 1) c = carr - let res = await actions(c, arr.slice(1).join(' '), source) - content = content.replace(m[i], res || '') + if (cmdMsg) { + let result = await db.query({ + text: `select "message", "group" from "public"."commands" where "cmd" = $1 and ("group" = '' or "group" = $2)`, + values: [cmd, source.groupId] + }) + if (result.rowCount > 0) { + let obj = result.rows.filter(t => t.group === source.groupId) + if (obj.length === 0) obj = result.rows[0] + else obj = obj[0] + let content = obj.message + let m = content.match(/{{(.+?)}}/g) + if (m !== null && m.length > 0) { + for (let i = 0; i < m.length; i++) { + let c = m[i].replace(/^{{/, '').replace(/}}$/, '') + let carr = c.split('=') + if (carr.length > 1) c = carr + let res = await actions(c, arr.slice(1).join(' '), source) + content = content.replace(m[i], res || '') + } } - } - if (content.trim().length > 0) { - reply = { - reply: content + if (content.trim().length > 0) { + reply = { + reply: content + } } } } if (reply === null) { + console.log('enter key command') // query keyword commands let keyCMD = await db.query({ - text: `select "message", "group", "cmd" from "public"."key_commands" where ("group" = '' or "group" = $1)`, + text: `select "message", "group", "key" from "public"."key_commands" where ("group" = '' or "group" = $1)`, values: [source.groupId] }) + console.log(keyCMD.rows) if (keyCMD.rowCount > 0) { let obj = keyCMD.rows.filter(t => t.group === '') let obj2 = keyCMD.rows.filter(t => t.group === source.groupId) obj = obj.map(t => { for (let i of obj2) { - if (i.cmd === t.cmd && i.group !== '') return i + if (i.key === t.key && i.group !== '') return i } return t }) + let tmp = obj2.filter(t => { + for (let i of obj) { + if (i.key === t.key) return false + } + return true + }) + obj = [...obj, ...tmp] let regex = null let txt = '' + console.log('obj ::: ', obj) for (let i of obj) { - txt += (txt.length > 0 ? '|' : '') + i.cmd + txt += (txt.length > 0 ? '|' : '') + i.key } regex = new RegExp(`(${txt})`) - let m = arr.slice(1).join(' ').match(regex) + console.log(regex) + let m = text.match(regex) + console.log('match :::: ', m) if (m !== null && m.length > 0) { - let key = obj.filter(t => t.cmd === m[0]) + let key = obj.filter(t => t.key === m[0]) if (key.length > 0) { let content = key[0].message let m = content.match(/{{(.+?)}}/g) @@ -74,7 +89,7 @@ const parseCMD = async (text = '', source = {}) => { let c = m[i].replace(/^{{/, '').replace(/}}$/, '') let carr = c.split('=') if (carr.length > 1) c = carr - let res = await actions(c, arr.slice(1).join(' '), source) + let res = await actions(c, text, source) content = content.replace(m[i], res || '') } } diff --git a/libs/line-message/index.js b/libs/line-message/index.js index 61d6730..4288077 100644 --- a/libs/line-message/index.js +++ b/libs/line-message/index.js @@ -1,6 +1,6 @@ const commands = require('./commands') -const api = require('../api-action') const DB = require('@libs/database') +const api = require('@libs/api-action') /** * parse text message object diff --git a/libs/middleware/index.js b/libs/middleware/index.js index bb6d776..7a9a80f 100644 --- a/libs/middleware/index.js +++ b/libs/middleware/index.js @@ -1,4 +1,4 @@ -const config = require('../../config') +const config = require('@config/index') const rawBody = require('raw-body') const crypto = require('crypto') diff --git a/package.json b/package.json index 1c55d5f..e2c7bad 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,12 @@ "cheerio": "^1.0.0-rc.2", "cron": "^1.3.0", "dotenv": "^6.0.0", + "gm": "^1.23.1", "koa": "^2.5.1", "koa-body": "^4.0.3", "koa-logger": "^3.2.0", "koa-router": "^7.4.0", + "module-alias": "^2.1.0", "pg": "^7.4.3", "raw-body": "^2.3.3", "request": "^2.87.0", @@ -28,5 +30,10 @@ }, "devDependencies": { "standard": "^11.0.1" + }, + "_moduleAliases": { + "@libs": "libs", + "@config": "config", + "@route": "route" } } diff --git a/route/google/index.js b/route/google/index.js index b58f12b..7fd4488 100644 --- a/route/google/index.js +++ b/route/google/index.js @@ -4,9 +4,9 @@ const r = new Router() // const koaBody = require('koa-body') const { getRaw -} = require('../../libs/middleware') -const api = require('../../libs/api-action') -const DB = require('../../libs/database') +} = require('@libs/middleware') +const api = require('@libs/api-action') +const DB = require('@libs/database') r.get('/youtube/webhook', async (c, n) => { let db = await DB.connect() diff --git a/route/index.js b/route/index.js index 930270b..4be1dc0 100644 --- a/route/index.js +++ b/route/index.js @@ -1,5 +1,103 @@ const Router = require('koa-router') const r = new Router() +const gm = require('gm') +const config = require('@config/index') +const fs = require('fs') +const path = require('path') + +const getImageToRes = async (c, n) => { + let filepath = path.resolve(config.image_root, c.state.file) + console.log(filepath) + try { + fs.accessSync(filepath) + } catch (err) { + c.throw(404, 'image not found') + } + + let buf = null + + let imgSize = await getImageSize(filepath) + if (imgSize === null) c.throw(500) + if (c.state.ori === true) { + // max 1024x1024 + if (imgSize.width > 1024 || imgSize.height > 1024) { + buf = await resizeImage(filepath, 1024) + if (buf === null) c.throw(500) + } + } else { + // max 240x240 + if (imgSize.width > 240 || imgSize.height > 240) { + buf = await resizeImage(filepath, 240) + if (buf === null) c.throw(500) + } + } + + if (buf === null) { + let format = await getImageFormat(filepath) + if (format === null) c.throw(500) + if (format !== 'JPEG') { + buf = await convertToJPEG(filepath) + if (buf === null) c.throw(500) + } else { + buf = fs.readFileSync(filepath) + } + } + + c.type = 'image/jpeg' + c.body = buf +} + +const convertToJPEG = async (file = null) => { + let buf = await new Promise((resolve) => { + gm(file).toBuffer('JPEG', (err, buf) => { + resolve(err ? null : buf) + }) + }) + return buf +} + +const getImageFormat = async (file = null) => { + let format = await new Promise((resolve) => { + gm(file).format((err, format) => { + resolve(err ? null : format) + }) + }) + return format +} + +const getImageSize = async (file = null) => { + let size = await new Promise((resolve) => { + gm(file).size((err, val) => { + resolve(err ? null : val) + }) + }) + return size +} + +const resizeImage = async (file = null, size = 1024) => { + let buf = await new Promise((resolve) => { + gm(file).resize(size, size).toBuffer('JPEG', (err, buf) => { + resolve(err ? null : buf) + }) + }) + return buf +} + +r.get('/image/origin/:name', async (c, n) => { + c.state.ori = true + let name = c.params.name || '' + if (typeof name !== 'string' || name.trim().length === 0) c.throw('image name not valid', 400) + c.state.file = name + return n() +}, getImageToRes) + +r.get('/image/thumbnail/:name', async (c, n) => { + c.state.ori = false + let name = c.params.name || '' + if (typeof name !== 'string' || name.trim().length === 0) c.throw('image name not valid', 400) + c.state.file = name + return n() +}, getImageToRes) r.use('/line', require('./line').routes()) r.use('/google', require('./google').routes()) diff --git a/route/line/index.js b/route/line/index.js index 4fa23e2..1f14fdd 100644 --- a/route/line/index.js +++ b/route/line/index.js @@ -4,10 +4,10 @@ const r = new Router() const { verifyLine, getRaw -} = require('../../libs/middleware') +} = require('@libs/middleware') const { textMessage -} = require('../../libs/line-message') +} = require('@libs/line-message') r.post('/', getRaw, verifyLine, async (c, n) => { console.log(JSON.stringify(c.request.body, null, 2)) diff --git a/route/twitch/index.js b/route/twitch/index.js index 6b7b2d0..516fb54 100644 --- a/route/twitch/index.js +++ b/route/twitch/index.js @@ -2,9 +2,9 @@ const Router = require('koa-router') const r = new Router() const { getRaw -} = require('../../libs/middleware') -const DB = require('../../libs/database') -const api = require('../../libs/api-action') +} = require('@libs/middleware') +const DB = require('@libs/database') +const api = require('@libs/api-action') // const config = require('../../config') r.get('/webhook', async (c, n) => {