From 334fce3b9b81999be6756277119d7a7545bdd3c8 Mon Sep 17 00:00:00 2001 From: Jay Date: Sun, 12 Aug 2018 23:31:09 +0800 Subject: [PATCH] add twitch oauth login --- Dockerfile | 6 +- app.js | 2 + background.js | 6 +- config/index.js | 1 + libs/api-action/index.js | 8 +- libs/api-action/twitch-new.js | 32 ++++ .../{twitch.js => twitch-public.js} | 0 libs/line-message/commands/actions/group.js | 2 +- package.json | 4 +- route/index.js | 2 +- route/twitch/index.js | 154 ++++++++++-------- 11 files changed, 140 insertions(+), 77 deletions(-) create mode 100644 libs/api-action/twitch-new.js rename libs/api-action/{twitch.js => twitch-public.js} (100%) diff --git a/Dockerfile b/Dockerfile index dfd81c0..51e1241 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,9 +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 apt-get update -qqy \ + && apt-get install -qqy imagemagick graphicsmagick \ + && apt-get clean RUN npm install EXPOSE ${NODE_PORT} CMD ["npm", "run", "dbrun"] \ No newline at end of file diff --git a/app.js b/app.js index ab9bc1e..4ead8a8 100644 --- a/app.js +++ b/app.js @@ -1,10 +1,12 @@ const config = require('./config') const Koa = require('koa') const koaLogger = require('koa-logger') +const koaSession = require('koa-session2') const router = require('./route') const app = new Koa() app.use(koaLogger()) +app.use(koaSession()) app.use(router.allowedMethods()) app.use(router.routes()) diff --git a/background.js b/background.js index da110bc..caf5739 100644 --- a/background.js +++ b/background.js @@ -106,7 +106,7 @@ new cron.CronJob({ //eslint-disable-line text }) let ids = twch.rows.map(t => t.id) - let streams = await api.twitch.getUserStream(ids) + let streams = await api.twitchPublic.getUserStream(ids) if (streams !== null && Array.isArray(streams)) { streams.forEach(t => { sendStreamNotify(t).then(() => {}).catch(() => {}) @@ -283,9 +283,9 @@ const checkDonate = async (loginName = null, opayid = null) => { }, method: 'post' }) - console.log(loginName, 'http response :::: ', JSON.stringify(result.data)) + // console.log(loginName, 'http response :::: ', JSON.stringify(result.data)) if ('data' in result && 'lstDonate' in result.data && Array.isArray(result.data.lstDonate) && result.data.lstDonate.length > 0) { - console.log(`${loginName} donate :::: ${JSON.stringify(result.data)}`) + // console.log(`${loginName} donate :::: ${JSON.stringify(result.data)}`) let ids = result.data.lstDonate.map(t => t.donateid) let inparams = ids.map((t, idx) => `$${(idx + 1)}`) let dbres = await db.query({ diff --git a/config/index.js b/config/index.js index 4a51cc2..3cbd297 100644 --- a/config/index.js +++ b/config/index.js @@ -8,6 +8,7 @@ module.exports = { }, twitch: { clientid: process.env.TWITCH_CLIENT_ID || '', + clientsecret: process.env.TWITCH_CLIENT_SECRET || '', subsecret: process.env.TWITCH_SUB_SECRET || '', chat_host: process.env.TWITCH_CHAT_HOST || '', bot_oauth: process.env.TWITCH_BOT_OAUTH || '' diff --git a/libs/api-action/index.js b/libs/api-action/index.js index b137360..8746588 100644 --- a/libs/api-action/index.js +++ b/libs/api-action/index.js @@ -1,9 +1,11 @@ -const twitch = require('./twitch') +const twitchPublic = require('./twitch-public') const line = require('./line') const google = require('./google') +const twitchNew = require('./twitch-new') module.exports = { - twitch, + twitchPublic, line, - google + google, + twitchNew } diff --git a/libs/api-action/twitch-new.js b/libs/api-action/twitch-new.js new file mode 100644 index 0000000..0261dd1 --- /dev/null +++ b/libs/api-action/twitch-new.js @@ -0,0 +1,32 @@ +const axios = require('axios') +const config = require('@config/index') +const baseURL = `https://api.twitch.tv/helix` +const client = axios.create({ + baseURL, + headers: { + 'Client-ID': config.twitch.clientid + } +}) + +const tokenHeader = (token) => { + if (typeof token !== 'string' || token.trim().length === 0) return null + return { + 'Authorization': `Bearer ${token}` + } +} + +module.exports.getUserData = async (token = '') => { + let headers = tokenHeader(token) + if (headers === null) return null + let url = '/users' + + let result = await client({ + method: 'get', + headers, + url + }) + + if (!('data' in result) || !('data' in result.data) || !Array.isArray(result.data.data)) return null + if (result.data.data.length === 0) return null + return result.data.data[0] +} diff --git a/libs/api-action/twitch.js b/libs/api-action/twitch-public.js similarity index 100% rename from libs/api-action/twitch.js rename to libs/api-action/twitch-public.js diff --git a/libs/line-message/commands/actions/group.js b/libs/line-message/commands/actions/group.js index 28d8bd1..ff8a7f4 100644 --- a/libs/line-message/commands/actions/group.js +++ b/libs/line-message/commands/actions/group.js @@ -236,7 +236,7 @@ const addTwitch = async (txt = '', source = {}, db) => { return { reply } } - let twitchUser = await api.twitch.getUserIDByName(name) + let twitchUser = await api.twitchPublic.getUserIDByName(name) if (twitchUser === null) return null // check channel count diff --git a/package.json b/package.json index 9011edc..9e5dac5 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "start": "node index.js 2>&1 | tee runtime.txt", "dbtool": "node bin/dbtool", "dbrun": "npm run dbtool && npm start", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "postinstall": "npm i otakukaze/koa-session2#master" }, "keywords": [], "author": "JayChen", @@ -23,6 +24,7 @@ "koa-body": "^4.0.3", "koa-logger": "^3.2.0", "koa-router": "^7.4.0", + "koa-session2": "github:otakukaze/koa-session2#master", "lodash": "^4.17.10", "module-alias": "^2.1.0", "pg": "^7.4.3", diff --git a/route/index.js b/route/index.js index 1837d12..751ad4d 100644 --- a/route/index.js +++ b/route/index.js @@ -103,6 +103,6 @@ r.use('/web', require('./web').routes()) r.use('/api', require('./api').routes()) r.use('/line', require('./line').routes()) r.use('/google', require('./google').routes()) -// r.use('/twitch', require('./twitch').routes()) +r.use('/twitch', require('./twitch').routes()) module.exports = r diff --git a/route/twitch/index.js b/route/twitch/index.js index 516fb54..4f70e72 100644 --- a/route/twitch/index.js +++ b/route/twitch/index.js @@ -1,81 +1,105 @@ 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') -// const config = require('../../config') +const qs = require('querystring') +const config = require('@config/index') +const axios = require('axios') +const _ = require('lodash') -r.get('/webhook', async (c, n) => { - let mode = c.query['hub.mode'] - let token = c.query['hub.secret'] - let challenge = c.query['hub.challenge'] - console.log(mode, token, challenge) - console.log(c.headers) - if (mode) { - if (mode === 'subscribe') { - c.status = 200 - c.body = challenge - } else { - c.status = 403 - c.body = '' - } +r.get('/login', async (c, n) => { + let redirectUrl = `${config.url.replace(/\/$/, '')}/twitch/oauth` + let qsObj = { + client_id: config.twitch.clientid, + redirect_uri: redirectUrl, + response_type: 'code', + scope: 'user:read:email' } + let qsStr = qs.stringify(qsObj) + let toUrl = `https://id.twitch.tv/oauth2/authorize?${qsStr}` + + let backUrl = c.query['tourl'] || '' + if (typeof backUrl === 'string' && backUrl.trim().length > 0) { + c.session.backUrl = backUrl + } + + c.redirect(toUrl) }) -r.post('/webhook', getRaw, async (c, n) => { - // middleware - c.db = await DB.connect() +r.get('/oauth', async (c, n) => { + let url = `https://id.twitch.tv/oauth2/token` + let redirectUrl = `${config.url.replace(/\/$/, '')}/twitch/oauth` + let code = c.query.code || '' + if (!code) { + c.body = 'oauth login fail' + return + } + + let qsObj = { + client_id: config.twitch.clientid, + client_secret: config.twitch.clientsecret, + code, + grant_type: 'authorization_code', + redirect_uri: redirectUrl + } + + let data = { + 'access_token': '', + 'refresh_token': '', + 'expires_in': 0, + 'scope': '', + 'token_type': 'bearer' + } try { - await n() + let tokenResult = await axios({ + method: 'post', + url, + params: qsObj + }) + if (!('data' in tokenResult)) { + c.body = 'read token fail' + return + } + data = _.assign({}, data, tokenResult.data) + } catch (err) { + console.log('request token fail ', err) + c.body = 'get token fail' + return + } + + c.session.token = data + try { + let user = await api.twitchNew.getUserData(data.access_token) + c.session.user = user + } catch (err) { + console.log('get user data fail ::: ', err) + c.body = 'get user data fail' + return + } + let db = await DB.connect() + try { + let count = await db.query({ + text: `select id from "public"."twitch_channel" where id = $1`, + values: [c.session.user.id] + }) + if (count.rowCount === 0) { + await db.query({ + text: `insert into "public"."twitch_channel" ("id", "name") values ($1, $2)`, + values: [c.session.user.id, c.session.user.login] + }) + } } catch (err) { console.log(err) } - - c.db.release() - c.body = 'success' - c.status = 200 -}, async (c, n) => { - console.log(JSON.stringify(c.request.body, null, 2)) - if (!('data' in c.request.body) || !Array.isArray(c.request.body.data)) return - if (c.request.body.data.length === 0) return // length > 0 live, length === 0 down - let data = c.request.body.data[0] - let uid = c.query['uid'] || '' - let type = c.query['type'] || '' - - if (!uid || !type) return - - let text = `select rt."line" as group, rt."twitch" as twitch, rt."tmpl" as tmpl, twitch."name" as user from "public"."line_twitch_rt" rt - left join "public"."twitch_channel" twitch - on twitch."id" = rt."twitch" - left join "public"."line_group" line - on line."id" = rt."line" - where - line."notify" = true - and twitch."id" = $1 - and rt."type" = $2` - let values = [uid, type] - let twch = await c.db.query({ - text, - values - }) - if (twch.rowCount === 0) return - - let chLink = `https://twitch.tv/` - - for (let i in twch.rows) { - let tmp = twch.rows[i] - let msg = tmp.tmpl || '' - let link = chLink + tmp.user - if (typeof msg !== 'string' || msg.trim().length === 0) { - msg = `${data.title}\n${link}` - } else { - msg = msg.replace(/{link}/, link).replace(/{txt}/, data.title).replace(/\\n/, '\n') - } - - await api.line.pushMessage(tmp.group, msg).then(() => { }).catch(() => { }) + db.release() + c.session.loginType = 'twitch' + if (typeof c.session.backUrl === 'string' && c.session.backUrl.length > 0) { + let url = c.session.backUrl + delete c.session.backUrl + c.redirect(url) + } else { + c.redirect(`${config.url.replace(/\/$/, '')}/web`) } })