From a7930baabd98a22372de283a0c306c9431991d74 Mon Sep 17 00:00:00 2001 From: Stuart John Watson Date: Sat, 31 Dec 2016 18:18:10 +0000 Subject: [PATCH 01/12] Add Initial MySql Connection --- DataAccess/.jshintrc | 14 ++++++++++++++ DataAccess/Meets.js | 8 ++++++++ DataAccess/MySql.js | 28 ++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 DataAccess/.jshintrc create mode 100644 DataAccess/Meets.js create mode 100644 DataAccess/MySql.js diff --git a/DataAccess/.jshintrc b/DataAccess/.jshintrc new file mode 100644 index 0000000..a6f83c4 --- /dev/null +++ b/DataAccess/.jshintrc @@ -0,0 +1,14 @@ +{ + "curly":true, + "eqeqeq":true, + "notypeof":true, + "undef":false, + "unused":true, + "varstmt":true, + "esnext":true, + "globals":{ + "module":true, + "require":false, + "console":false + } +} diff --git a/DataAccess/Meets.js b/DataAccess/Meets.js new file mode 100644 index 0000000..5d46701 --- /dev/null +++ b/DataAccess/Meets.js @@ -0,0 +1,8 @@ +const MySql = require("./MySql.js"); + +function parseResults() + +function getAllMeets(){ + MySql.query(`SELECT * FROM wp_posts WHERE post_type = 'meet'`).then(result=> + ) +} diff --git a/DataAccess/MySql.js b/DataAccess/MySql.js new file mode 100644 index 0000000..ae36071 --- /dev/null +++ b/DataAccess/MySql.js @@ -0,0 +1,28 @@ +const MySql = require("mysql"); +const config = require("./config.json"); + +const pool = MySql.createPool(config.mysql); + +function getConnection(){ + return new Promise(good=>pool.getConnection((err,con)=>{ + if (err){ + throw err; + } + good(con); + })); +} + +function query(sql,values){ + return new Promise(good=>pool.query(sql,values,(err,results,fields)=>{ + if (err){ + throw err; + } + good({results,fields}); + })); +} + +function queryStream(sql,values){ + return pool.query(sql,values).stream({highWaterMark:10}); +} + +module.exports = {getConnection,query,queryStream,pool}; From 99cadfcc54d6555e8cd343627906a2c2f9c8066f Mon Sep 17 00:00:00 2001 From: Stuart John Watson Date: Sat, 31 Dec 2016 19:17:02 +0000 Subject: [PATCH 02/12] WIP Add dataAccess to meets --- DataAccess/Meets.js | 28 +++++++++++++++++++++++++--- DataAccess/MySql.js | 2 +- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/DataAccess/Meets.js b/DataAccess/Meets.js index 5d46701..8427831 100644 --- a/DataAccess/Meets.js +++ b/DataAccess/Meets.js @@ -1,8 +1,30 @@ const MySql = require("./MySql.js"); -function parseResults() +class Meet{ + constructor(obj){ + Object.keys(obj).forEach(k=>this[k]=obj[k]); + } + + asMarkdown(){ + return `*${this.post_title}* ${this.meet_start_time}\n_${this.guid}_`; + } + +} +Meet.fromObjArray = function(objArray){ + return objArray.map(o=>new Meet(o)); +}; function getAllMeets(){ - MySql.query(`SELECT * FROM wp_posts WHERE post_type = 'meet'`).then(result=> - ) + return MySql.query(`SELECT * FROM wp_posts WHERE post_type = 'meet'`) + .then(results=>Meet.fromObjArray(results.results)); } + +function getUpcomingMeets(){ + return MySql.query(` + SELECT meta_value AS meet_start_time, wp_posts.* + FROM wp_postmeta LEFT JOIN wp_posts ON post_id=ID + WHERE meta_key = 'meet_start_time' AND CAST(meta_value AS DATE) > CURDATE() + `).then(results=>Meet.fromObjArray(results.results)); +} + +module.exports = {getAllMeets,getUpcomingMeets}; diff --git a/DataAccess/MySql.js b/DataAccess/MySql.js index ae36071..d498723 100644 --- a/DataAccess/MySql.js +++ b/DataAccess/MySql.js @@ -1,5 +1,5 @@ const MySql = require("mysql"); -const config = require("./config.json"); +const config = require("../config.json"); const pool = MySql.createPool(config.mysql); From 405f53b0ccfa92d93f8e828c09131699985e7ccc Mon Sep 17 00:00:00 2001 From: Stuart John Watson Date: Sat, 31 Dec 2016 19:33:46 +0000 Subject: [PATCH 03/12] Add unhandled promise rejection handler --- index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.js b/index.js index a740808..08260a1 100644 --- a/index.js +++ b/index.js @@ -24,3 +24,7 @@ connection.getMe().then(me=>{ console.log("> Starting message source."); source.start(); }); + +process.on('unhandledRejection', err => { + console.log(`${new Date()} WARN: Unandled error in promise:`,err); +}); From cf1b6a0f7df449b834d1fecca64a774a6f768593 Mon Sep 17 00:00:00 2001 From: Stuart John Watson Date: Sat, 31 Dec 2016 19:54:31 +0000 Subject: [PATCH 04/12] FIX broken markdown and entities --- DataAccess/Meets.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/DataAccess/Meets.js b/DataAccess/Meets.js index 8427831..8c9b3ad 100644 --- a/DataAccess/Meets.js +++ b/DataAccess/Meets.js @@ -1,12 +1,13 @@ const MySql = require("./MySql.js"); +const entities = require("entities"); class Meet{ constructor(obj){ - Object.keys(obj).forEach(k=>this[k]=obj[k]); + Object.keys(obj).forEach(k=>this[k]=entities.decodeHTML(obj[k])); } asMarkdown(){ - return `*${this.post_title}* ${this.meet_start_time}\n_${this.guid}_`; + return `*${this.post_title}* ${this.meet_start_time}\n${this.guid}`; } } @@ -21,7 +22,7 @@ function getAllMeets(){ function getUpcomingMeets(){ return MySql.query(` - SELECT meta_value AS meet_start_time, wp_posts.* + SELECT CAST(meta_value AS DATE) AS meet_start_time, wp_posts.* FROM wp_postmeta LEFT JOIN wp_posts ON post_id=ID WHERE meta_key = 'meet_start_time' AND CAST(meta_value AS DATE) > CURDATE() `).then(results=>Meet.fromObjArray(results.results)); From 2c71605f9a6fae04d2bdd6cceafc59aa949a8e7c Mon Sep 17 00:00:00 2001 From: Stuart John Watson Date: Sat, 31 Dec 2016 20:00:56 +0000 Subject: [PATCH 05/12] FIX formatting on meet markdown --- DataAccess/Meets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataAccess/Meets.js b/DataAccess/Meets.js index 8c9b3ad..49f8420 100644 --- a/DataAccess/Meets.js +++ b/DataAccess/Meets.js @@ -7,7 +7,7 @@ class Meet{ } asMarkdown(){ - return `*${this.post_title}* ${this.meet_start_time}\n${this.guid}`; + return `[${this.post_title}](${this.guid}) _${this.meet_start_time}_`; } } From 535212be6c375877c9b5a670d77d227618675eb5 Mon Sep 17 00:00:00 2001 From: Stuart John Watson Date: Sat, 31 Dec 2016 19:16:43 +0000 Subject: [PATCH 06/12] WIP Add upcoming command --- Commands.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Commands.js b/Commands.js index 099ed5e..57d4fa1 100644 --- a/Commands.js +++ b/Commands.js @@ -1,4 +1,6 @@ const Command = require("./Ship/Command.js"); +const Meets = require("./DataAccess/Meets.js"); + const START_WELCOME_MESSAGE = ` Hello, I am SailBot, the helpbot for the severnbronies chat. From f503e1c99128dbc01e006dfd495f9c97cffbd96f Mon Sep 17 00:00:00 2001 From: Stuart John Watson Date: Sat, 31 Dec 2016 19:29:06 +0000 Subject: [PATCH 07/12] Fix `/upcoming` when no meets are upcoming --- Commands.js | 9 ++++++++- index.js | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Commands.js b/Commands.js index 57d4fa1..2c0efb8 100644 --- a/Commands.js +++ b/Commands.js @@ -27,7 +27,14 @@ module.exports = function(commandSource){ //Upcoming command new Command("upcoming",commandSource) - .addHandler("","Posts the next upcoming meet",notImplemented,Command.nArgs(0)) + .addHandler("","Posts the next upcoming meet",function(parmaters,message){ + Meets.getUpcomingMeets().then(upcoming=>{ + let reply = upcoming.length? + upcoming.map(m=>m.asMarkdown()).join("\n"): + "They are no announced meets coming up"; + Command.basicReply(reply,message); + }); + },Command.nArgs(0)) .addHandler("_location(s)_","Filters the meets to the given locations",notImplemented,Command.atleastArgs(1)); //Easter egg diff --git a/index.js b/index.js index 08260a1..fa1b43a 100644 --- a/index.js +++ b/index.js @@ -25,6 +25,7 @@ connection.getMe().then(me=>{ source.start(); }); + process.on('unhandledRejection', err => { console.log(`${new Date()} WARN: Unandled error in promise:`,err); }); From 12142f18db42a684a29464b82b34dc1b4b999e3b Mon Sep 17 00:00:00 2001 From: Stuart John Watson Date: Sat, 31 Dec 2016 20:03:49 +0000 Subject: [PATCH 08/12] Removed web preview and notifcation from upcoming response --- Commands.js | 9 ++++++++- package.json | 6 +++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Commands.js b/Commands.js index 2c0efb8..3c78d14 100644 --- a/Commands.js +++ b/Commands.js @@ -32,7 +32,14 @@ module.exports = function(commandSource){ let reply = upcoming.length? upcoming.map(m=>m.asMarkdown()).join("\n"): "They are no announced meets coming up"; - Command.basicReply(reply,message); + message.connection.sendMessage({ + chat_id:message.chat.id, + //reply_to_message_id:message.message_id, + parse_mode:"Markdown", + text:reply, + disable_web_page_preview:true, + display_notification:true + }); }); },Command.nArgs(0)) .addHandler("_location(s)_","Filters the meets to the given locations",notImplemented,Command.atleastArgs(1)); diff --git a/package.json b/package.json index 58c9be2..e7bfe9d 100644 --- a/package.json +++ b/package.json @@ -7,5 +7,9 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", - "license": "ISC" + "license": "ISC", + "dependencies": { + "entities": "^1.1.1", + "mysql": "^2.12.0" + } } From c08989e384b95df68e6617775e5477945baa31f3 Mon Sep 17 00:00:00 2001 From: Stuart John Watson Date: Sun, 1 Jan 2017 17:16:56 +0000 Subject: [PATCH 09/12] Change date formatting in `/upcoming` to a relative format --- DataAccess/Meets.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DataAccess/Meets.js b/DataAccess/Meets.js index 49f8420..341a904 100644 --- a/DataAccess/Meets.js +++ b/DataAccess/Meets.js @@ -1,5 +1,6 @@ const MySql = require("./MySql.js"); const entities = require("entities"); +const moment = require("moment"); class Meet{ constructor(obj){ @@ -7,7 +8,7 @@ class Meet{ } asMarkdown(){ - return `[${this.post_title}](${this.guid}) _${this.meet_start_time}_`; + return `[${this.post_title}](${this.guid}) _${moment(this.meet_start_time).calendar()}_`; } } From 62b0bc23502bb6d5b2e1ab1273a86432d4fe62cf Mon Sep 17 00:00:00 2001 From: Stuart John Watson Date: Sun, 1 Jan 2017 17:18:05 +0000 Subject: [PATCH 10/12] Fix `/upcoming` getting drafts and unpulished meets --- DataAccess/Meets.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/DataAccess/Meets.js b/DataAccess/Meets.js index 341a904..ebcdc09 100644 --- a/DataAccess/Meets.js +++ b/DataAccess/Meets.js @@ -23,9 +23,12 @@ function getAllMeets(){ function getUpcomingMeets(){ return MySql.query(` - SELECT CAST(meta_value AS DATE) AS meet_start_time, wp_posts.* + SELECT meta_value AS meet_start_time, wp_posts.* FROM wp_postmeta LEFT JOIN wp_posts ON post_id=ID - WHERE meta_key = 'meet_start_time' AND CAST(meta_value AS DATE) > CURDATE() + WHERE meta_key = 'meet_start_time' + AND post_type = 'meet' + AND post_status = 'publish' + AND CAST(meta_value AS DATE) >= CURDATE(); `).then(results=>Meet.fromObjArray(results.results)); } From de628e23f9b820e9483bb20d176cb6ad6c633736 Mon Sep 17 00:00:00 2001 From: Stuart John Watson Date: Mon, 2 Jan 2017 15:17:26 +0000 Subject: [PATCH 11/12] Add location data to meets --- DataAccess/Meets.js | 76 +++++++++++++++++++++++++++++++++++++++++---- package.json | 3 +- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/DataAccess/Meets.js b/DataAccess/Meets.js index ebcdc09..bb73b16 100644 --- a/DataAccess/Meets.js +++ b/DataAccess/Meets.js @@ -1,24 +1,65 @@ const MySql = require("./MySql.js"); const entities = require("entities"); const moment = require("moment"); +const phpUnserialize = require('php-unserialize').unserialize; class Meet{ constructor(obj){ Object.keys(obj).forEach(k=>this[k]=entities.decodeHTML(obj[k])); } + loadMetaData(){ + let meta = {}; + return MySql.query(` + SELECT meta_key,meta_value + FROM wp_postmeta + WHERE post_id=? + AND meta_key NOT LIKE "\\_%" + `,[this.ID]).then(results=>{ + results.results.forEach(row=>meta[row.meta_key]=row.meta_value); + + this.meet_start_time = meta.meet_start_time; + this.meet_end_time = meta.meet_end_time; + return getLocationTable(); + }).then(locationTable => { + let usedLocations = {}; + let locData = phpUnserialize(meta.meet_location); + Object.keys(locData).forEach(k=> + usedLocations[locationTable[locData[k]]] = 1 + ); + + this.meet_locations = Object.keys(usedLocations); + console.log(this.meet_locations); + if(this.meet_locations.length === 0){ + this.meet_location = "no specified location"; + } else if (this.meet_locations.length === 1){ + this.meet_location = this.meet_locations[0]; + } else { + let body = this.meet_locations.slice(0,-1); + let tail = this.meet_locations[this.meet_locations.length-1]; + this.meet_location = `${body.join(", ")}, and ${tail}`; + } + }); + } + asMarkdown(){ - return `[${this.post_title}](${this.guid}) _${moment(this.meet_start_time).calendar()}_`; + return `[${this.post_title}](${this.guid}) ${moment(this.meet_start_time).calendar()} at ${this.meet_location}`; } } Meet.fromObjArray = function(objArray){ - return objArray.map(o=>new Meet(o)); + let meetArray = objArray.map(o=>new Meet(o)); + return Promise.all(meetArray.map(o=>o.loadMetaData())).then(()=>meetArray); }; -function getAllMeets(){ - return MySql.query(`SELECT * FROM wp_posts WHERE post_type = 'meet'`) - .then(results=>Meet.fromObjArray(results.results)); +function intercepts(as,bs){ + bs = bs.map(b=>b.toLowerCase()); + as = as.map(a=>a.toLowerCase()); + return as.some(a=>bs.includes(a)); +} + +function getUpcomingMeetsAt(locations){ + return getUpcomingMeets().then(meets=>meets.filter(m=>intercepts(locations,m.meet_locations))); } function getUpcomingMeets(){ @@ -32,4 +73,27 @@ function getUpcomingMeets(){ `).then(results=>Meet.fromObjArray(results.results)); } -module.exports = {getAllMeets,getUpcomingMeets}; + +let locationTable; +function updateLocationTable(){ + return MySql.query(` + SELECT post_id, meta_value + FROM wp_postmeta + WHERE meta_key = 'location_locality' + `).then(results=>{ + locationTable = {}; + results.results.forEach(row=>locationTable[row.post_id]=row.meta_value); + return locationTable; + }); +} +updateLocationTable(); + +function getLocationTable(update){ + if(locationTable === undefined || update){ + return updateLocationTable(); + } else { + return Promise.resolve(locationTable); + } +} + +module.exports = {getUpcomingMeets,getUpcomingMeetsAt,getLocationTable}; diff --git a/package.json b/package.json index e7bfe9d..c910165 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "entities": "^1.1.1", - "mysql": "^2.12.0" + "mysql": "^2.12.0", + "php-serialization": "0.0.4" } } From c02d6456990817cb1654a7390fc3a7c121129461 Mon Sep 17 00:00:00 2001 From: Stuart John Watson Date: Mon, 2 Jan 2017 15:38:40 +0000 Subject: [PATCH 12/12] Add location specific version of /upcoming --- Commands.js | 16 +++++++++++++++- DataAccess/Meets.js | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Commands.js b/Commands.js index 3c78d14..09b252f 100644 --- a/Commands.js +++ b/Commands.js @@ -42,7 +42,21 @@ module.exports = function(commandSource){ }); }); },Command.nArgs(0)) - .addHandler("_location(s)_","Filters the meets to the given locations",notImplemented,Command.atleastArgs(1)); + .addHandler("_location(s)_","Filters the meets to the given locations",function(paramaters,message){ + Meets.getUpcomingMeetsAt(paramaters).then(upcoming=>{ + let reply = upcoming.length? + upcoming.map(m=>m.asMarkdown()).join("\n"): + "They are no announced meets coming up at given locations"; + message.connection.sendMessage({ + chat_id:message.chat.id, + //reply_to_message_id:message.message_id, + parse_mode:"Markdown", + text:reply, + disable_web_page_preview:true, + display_notification:true + }); + }); + },Command.atleastArgs(1)); //Easter egg new Command("⬆⬆⬇⬇⬅➡⬅➡ba",commandSource) diff --git a/DataAccess/Meets.js b/DataAccess/Meets.js index bb73b16..b4a83c3 100644 --- a/DataAccess/Meets.js +++ b/DataAccess/Meets.js @@ -76,6 +76,7 @@ function getUpcomingMeets(){ let locationTable; function updateLocationTable(){ + console.log("Updating meet location table..."); return MySql.query(` SELECT post_id, meta_value FROM wp_postmeta @@ -83,6 +84,7 @@ function updateLocationTable(){ `).then(results=>{ locationTable = {}; results.results.forEach(row=>locationTable[row.post_id]=row.meta_value); + console.log("Meet location table updated..."); return locationTable; }); }