diff --git a/background.js b/background.js index 2165940..0cc171c 100644 --- a/background.js +++ b/background.js @@ -10,7 +10,12 @@ new cron.CronJob({ // eslint-disable-line onTick: async () => { console.log('run cron') let db = await DB.connect() - let text = `select "pageid", "groupid", "lastpost", "notify", "notify_tmpl" from "public"."page_group_rt"` + let text = `select line."notify" as notify, fb."id" as id, fb."groupid" as group, fb."pageid" as page, fb."tmpl" as tmpl, fb."lastpost" as post + from "public"."facebook_page" fb + left join "public"."line_group" line + on line."id" = fb."groupid" + where + line."notify" = true` let res = await db.query({ text }) @@ -19,25 +24,25 @@ new cron.CronJob({ // eslint-disable-line await new Promise(resolve => { let count = res.rowCount res.rows.forEach(t => { - fbParser.getLastPost(t.pageid) + fbParser.getLastPost(t.page) .then((data) => { let n = Math.floor(Date.now() / 1000) - if (t.lastpost === data.id || data.time < (n - 10 * 60)) { + if (t.post === data.id || data.time < (n - 10 * 60)) { if (!--count) resolve(null) return } - t.lastpost = data.id - let msg = t.notify_tmpl || '' + t.post = data.id + let msg = t.tmpl || '' if (typeof msg !== 'string' || msg.trim().length === 0) { msg = `${data.txt}\n${data.link}` } else { msg = msg.replace(/{link}/, data.link).replace(/{txt}/, data.txt).replace(/\\n/, '\n') } if (t.notify) { - pushMessage(t.groupid, msg).then(() => {}).catch(() => {}) + pushMessage(t.group, msg).then(() => {}).catch(() => {}) } - let text = `update "public"."page_group_rt" set "lastpost" = $1, "mtime" = now() where "pageid" = $2 and "groupid" = $3` - let values = [data.id, t.pageid, t.groupid] + let text = `update "public"."facebook_page" set "lastpost" = $1, "mtime" = now() where "id" = $2` + let values = [data.id, t.id] db.query({ text, values @@ -52,6 +57,18 @@ new cron.CronJob({ // eslint-disable-line db.release() }, + runOnInit: true, + start: true, + timeZone: 'Asia/Taipei' +}) + +// register twitch streaming webhook +new cron.CronJob({ //eslint-disable-line + cronTime: '00 00 0,6,12,18 * * *', + onTick: async () => { + + }, + runOnInit: true, start: true, timeZone: 'Asia/Taipei' }) diff --git a/libs/facebook-pageparse/index.js b/libs/facebook-pageparse/index.js index da8ccea..80dfbc1 100644 --- a/libs/facebook-pageparse/index.js +++ b/libs/facebook-pageparse/index.js @@ -51,8 +51,9 @@ const getLastPost = async (pageid = '') => { let time = timeEl.attr('data-utime') let link = timeEl.parent().attr('href') let p = t('div.userContent') - let txt = p.first().text() + let txt = p.text() || '' let id = p.first().attr('id') + if (!id) { if (/[\?|&]id\=(\d+)/.test(link)) { // eslint-disable-line let m = link.match(/[\?|&]story_fbid\=(\d+)/) // eslint-disable-line @@ -64,9 +65,15 @@ const getLastPost = async (pageid = '') => { if (m !== null && m.length > 1) { id = m[1] } + } else if (/\/photos\/.+?\/(\d+)/.test(link)) { + let m = link.match(/\/photos\/.+?\/(\d+)/) + if (m !== null && m.length > 1) { + id = m[1] + } } } - if (!time || !link || !txt || !id) return null + console.log(time, link, txt, id) + if (!time || !link || !id) return null let tmp = { txt, id, diff --git a/libs/line-message/commands/group.js b/libs/line-message/commands/group.js new file mode 100644 index 0000000..ad04729 --- /dev/null +++ b/libs/line-message/commands/group.js @@ -0,0 +1,110 @@ +/** + * add group to database + * @param {string} txt command body format => groupName notifyEnable(0,1) + * @param {object} source + * @param {object} db + */ +const addGroup = async (txt, source = {}, db) => { + if (!db) return null + if (!('type' in source) || !('groupId' in source) || !('userId' in source)) return null + let {groupId, userId} = source + let arr = txt.split(' ') + if (arr.length < 2 || !isFinite(arr[1])) return null + let name = arr[0] + let notify = arr[1] + if (typeof notify === 'string') notify = parseInt(notify) + if (notify !== 0 && notify !== 1) return null + let text = `select "id" from "public"."line_group" where "id" = $1` + let values = [groupId] + let result = await db.query({ + text, + values + }) + if (result.rowCount > 0) { + let reply = 'group exists' + return { reply } + } + text = `insert into "public"."line_group" ("id", "name", "notify", "owner", "ctime", "mtime") values ($1, $2, $3, $4, now(), now())` + values = [groupId, name, (notify === 1), userId] + await db.query({ + text, + values + }) + let reply = 'add success' + return { reply } +} + +/** + * add facebook page notify to group + * @param {string} txt command body format => pageid name tmpl + * @param {object} source + * @param {object} db + */ +const addPage = async (txt, source = {}, db) => { + if (!db) return null + if (!('type' in source) || !('groupId' in source) || !('userId' in source)) return null + let {groupId, userId} = source + let arr = txt.split(' ') + if (arr.length < 3) return null + let page = arr[0] + let name = arr[1] + let tmpl = arr.slice(2).join(' ') + let text = `select "id","owner" from "public"."line_group" where "id" = $1` + let values = [groupId] + let result = await db.query({ + text, + values + }) + if (result.rowCount === 0) { + let reply = 'group not register' + return { reply } + } + if (result.rows[0].owner !== userId) { + let reply = 'not owner' + return { reply } + } + // check pageid in group + text = `select fb."id" from "public"."facebook_page" fb + left join "public"."line_group" line + on fb."groupid" = line."id" + where + fb."pageid" = $1` + values = [page] + result = await db.query({ + text, + values + }) + if (result.rowCount > 0) { + let reply = 'page exists' + return { reply } + } + // check page name in group + text = `select fb."id" from "public"."facebook_page" fb + left join "public"."line_group" line + on fb."groupid" = line."id" + where + fb."name" = $1` + values = [name] + result = await db.query({ + text, + values + }) + if (result.rowCount > 0) { + let reply = 'page name exists' + return { reply } + } + text = `insert into "public"."facebook_page" ("groupid", "pageid", "name", "tmpl", "ctime", "mtime") values + ($1, $2, $3, $4, now(), now())` + values = [groupId, page, name, tmpl] + await db.query({ + text, + values + }) + let reply = 'add page success' + return { reply } +} + +module.exports = { + addgroup: addGroup, + addpage: addPage +} diff --git a/libs/line-message/commands/index.js b/libs/line-message/commands/index.js new file mode 100644 index 0000000..8be22c0 --- /dev/null +++ b/libs/line-message/commands/index.js @@ -0,0 +1,31 @@ +const DB = require('../../database') +const groupCMD = require('./group') + +const cmds = {} + +for (let i in groupCMD) { + cmds[i] = groupCMD[i] +} + +const parseCMD = async (text = '', source = {}) => { + if (typeof text !== 'string' || text.trim().length === 0) return null + if (!source || typeof source !== 'object' || !('type' in source)) return null + 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 cmd = arr[0].replace(/^!/, '') + if (!(cmd in cmds)) return null + let db = await DB.connect() + let result = null + try { + result = await cmds[cmd](arr.slice(1).join(' '), source, db) + } catch (err) { + console.log(err) + } + // if (result === null) return null + db.release() + return result +} + +module.exports = parseCMD diff --git a/libs/line-message/index.js b/libs/line-message/index.js index fa24f4a..5726b8b 100644 --- a/libs/line-message/index.js +++ b/libs/line-message/index.js @@ -1,6 +1,6 @@ const axios = require('axios') const config = require('../../config') -const DB = require('../database') +const commands = require('./commands') const client = axios.create({ baseURL: 'https://api.line.me/v2/bot', @@ -33,31 +33,34 @@ const pushMessage = async (target, message = '') => { } const textMessage = async (evt) => { - let replyURL = '/message/reply' + let url = '/message/reply' let {replyToken, source, message} = evt + if (!source || !('type' in source) || source.type !== 'group') return if (!message || message.type !== 'text') return let {text} = message if (typeof text !== 'string') return text = text.trim() if (text.length === 0) return - let db = await DB.connect() - // let opts = { - // method: 'post', - // url: replyURL, - // data: { - // replyToken, - // messages: [ - // { - // type: 'text', - // text: 'test message' - // } - // ] - // } - // } + let result = await commands(text, source) + if (result === null) return + if (typeof result === 'object' && 'reply' in result) { + let opts = { + method: 'post', + url, + data: { + replyToken, + messages: [ + { + type: 'text', + text: result.reply + } + ] + } + } - // await client(opts) - db.release() + await client(opts) + } } module.exports = { diff --git a/schema/main.sql b/schema/main.sql index 21a4894..f434c25 100644 --- a/schema/main.sql +++ b/schema/main.sql @@ -2,23 +2,33 @@ -- PostgreSQL database dump -- --- Dumped from database version 10.2 (Debian 10.2-1.pgdg90+1) --- Dumped by pg_dump version 10.2 (Debian 10.2-1.pgdg90+1) +-- Dumped from database version 9.6.8 +-- Dumped by pg_dump version 9.6.8 SET statement_timeout = 0; SET lock_timeout = 0; SET idle_in_transaction_session_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); SET check_function_bodies = false; SET client_min_messages = warning; SET row_security = off; -SET search_path = public, pg_catalog; - -ALTER TABLE IF EXISTS ONLY public.page_group_rt DROP CONSTRAINT IF EXISTS page_group_rt_pageid_groupid_pk; +ALTER TABLE IF EXISTS ONLY public.twitch_channel DROP CONSTRAINT IF EXISTS twitch_channel_line_group_id_fk; +ALTER TABLE IF EXISTS ONLY public.facebook_page DROP CONSTRAINT IF EXISTS facebook_page_line_group_id_fk; +DROP INDEX IF EXISTS public.line_group_name_uindex; +ALTER TABLE IF EXISTS ONLY public.twitch_channel DROP CONSTRAINT IF EXISTS twitch_channel_pkey; +ALTER TABLE IF EXISTS ONLY public.line_group DROP CONSTRAINT IF EXISTS line_group_pkey; +ALTER TABLE IF EXISTS ONLY public.facebook_page DROP CONSTRAINT IF EXISTS facebook_page_pkey; +ALTER TABLE IF EXISTS public.twitch_channel ALTER COLUMN id DROP DEFAULT; +ALTER TABLE IF EXISTS public.facebook_page ALTER COLUMN id DROP DEFAULT; DROP TABLE IF EXISTS public.version_ctrl; -DROP TABLE IF EXISTS public.page_group_rt; +DROP SEQUENCE IF EXISTS public.twitch_channel_id_seq; +DROP TABLE IF EXISTS public.twitch_channel; +DROP TABLE IF EXISTS public.line_group; +DROP SEQUENCE IF EXISTS public.facebook_page_id_seq; +DROP TABLE IF EXISTS public.facebook_page; DROP EXTENSION IF EXISTS plpgsql; DROP SCHEMA IF EXISTS public; -- @@ -49,44 +59,163 @@ CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; -SET search_path = public, pg_catalog; - SET default_tablespace = ''; SET default_with_oids = false; -- --- Name: page_group_rt; Type: TABLE; Schema: public; Owner: - +-- Name: facebook_page; Type: TABLE; Schema: public; Owner: - -- -CREATE TABLE page_group_rt ( +CREATE TABLE public.facebook_page ( + id integer NOT NULL, + groupid character varying(100) NOT NULL, pageid character varying(200) NOT NULL, - groupid character varying(200) NOT NULL, - notify boolean DEFAULT false NOT NULL, - lastpost character varying(200) DEFAULT ''::character varying NOT NULL, - ctime timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - mtime timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - notify_tmpl character varying(500) DEFAULT ''::character varying NOT NULL + name character varying(100) NOT NULL, + lastpost character varying(100) DEFAULT ''::character varying NOT NULL, + tmpl character varying(200) DEFAULT ''::character varying NOT NULL, + ctime timestamp with time zone DEFAULT now() NOT NULL, + mtime timestamp with time zone DEFAULT now() NOT NULL ); +-- +-- Name: facebook_page_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.facebook_page_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: facebook_page_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.facebook_page_id_seq OWNED BY public.facebook_page.id; + + +-- +-- Name: line_group; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.line_group ( + id character varying(100) NOT NULL, + name character varying(200) NOT NULL, + notify boolean DEFAULT false NOT NULL, + ctime timestamp with time zone DEFAULT now() NOT NULL, + mtime timestamp with time zone DEFAULT now() NOT NULL, + owner character varying(100) DEFAULT ''::character varying NOT NULL +); + + +-- +-- Name: twitch_channel; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.twitch_channel ( + id integer NOT NULL, + twitchid character varying(100) NOT NULL, + name character varying(200) NOT NULL, + type character varying(100) DEFAULT ''::character varying NOT NULL, + ctime timestamp with time zone DEFAULT now() NOT NULL, + mtime timestamp with time zone DEFAULT now() NOT NULL, + groupid character varying(100) NOT NULL +); + + +-- +-- Name: twitch_channel_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.twitch_channel_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: twitch_channel_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.twitch_channel_id_seq OWNED BY public.twitch_channel.id; + + -- -- Name: version_ctrl; Type: TABLE; Schema: public; Owner: - -- -CREATE TABLE version_ctrl ( +CREATE TABLE public.version_ctrl ( version integer NOT NULL, - ctime timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - querystr character varying(5000) DEFAULT ''::character varying NOT NULL + ctime timestamp with time zone DEFAULT now() NOT NULL, + querystr text DEFAULT ''::character varying NOT NULL ); -- --- Name: page_group_rt page_group_rt_pageid_groupid_pk; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: facebook_page id; Type: DEFAULT; Schema: public; Owner: - -- -ALTER TABLE ONLY page_group_rt - ADD CONSTRAINT page_group_rt_pageid_groupid_pk PRIMARY KEY (pageid, groupid); +ALTER TABLE ONLY public.facebook_page ALTER COLUMN id SET DEFAULT nextval('public.facebook_page_id_seq'::regclass); + + +-- +-- Name: twitch_channel id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twitch_channel ALTER COLUMN id SET DEFAULT nextval('public.twitch_channel_id_seq'::regclass); + + +-- +-- Name: facebook_page facebook_page_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.facebook_page + ADD CONSTRAINT facebook_page_pkey PRIMARY KEY (id); + + +-- +-- Name: line_group line_group_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.line_group + ADD CONSTRAINT line_group_pkey PRIMARY KEY (id); + + +-- +-- Name: twitch_channel twitch_channel_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twitch_channel + ADD CONSTRAINT twitch_channel_pkey PRIMARY KEY (id); + + +-- +-- Name: line_group_name_uindex; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX line_group_name_uindex ON public.line_group USING btree (name); + + +-- +-- Name: facebook_page facebook_page_line_group_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.facebook_page + ADD CONSTRAINT facebook_page_line_group_id_fk FOREIGN KEY (groupid) REFERENCES public.line_group(id) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: twitch_channel twitch_channel_line_group_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twitch_channel + ADD CONSTRAINT twitch_channel_line_group_id_fk FOREIGN KEY (groupid) REFERENCES public.line_group(id); --