From 1e6d60f4c313496835a0af3ede36d30e37ab4ab8 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Mon, 20 Feb 2023 00:20:46 -0500 Subject: [PATCH] Working MySQL Storage system - testing delete --- commands/add.js | 20 +++-- commands/help.js | 53 +++++++----- commands/remove.js | 31 +++++++ index.js | 5 +- libFlayer.js | 24 +++--- libStorage.js | 208 +++++++++++++++++++++++++++++++++++---------- mysqlHandler.js | 134 ----------------------------- 7 files changed, 253 insertions(+), 222 deletions(-) create mode 100644 commands/remove.js delete mode 100644 mysqlHandler.js diff --git a/commands/add.js b/commands/add.js index 14a00a8..a5a5e48 100644 --- a/commands/add.js +++ b/commands/add.js @@ -13,16 +13,20 @@ module.exports = { var link = args[1]; var category = args[2]; - var result = libFlayer.addSource(title, link, category); - if (result) { - message.reply(`Adding ${title} to the list of RSS sources`); - } else { - message.reply(`${title} already exists in the list of RSS sources`); - } + libFlayer.addSource(title, link, category, (err, result) => { + console.log("Result from adding entry", result); - var sources = libFlayer.getSources(); - libFlayer.loadFeeds(); + if (result) { + message.reply(`Adding ${title} to the list of RSS sources`); + } else { + message.reply(`${title} already exists in the list of RSS sources`); + } + + var sources = libFlayer.getSources(); + libFlayer.loadFeeds(); + }); } catch (err) { + console.log(err); message.reply(err.toString()); } } diff --git a/commands/help.js b/commands/help.js index 3f4e9fb..3c36909 100644 --- a/commands/help.js +++ b/commands/help.js @@ -1,31 +1,42 @@ var libFlayer = require("../libFlayer.js"); +const prefix = process.env.prefix; + module.exports = { name: 'help', description: 'Help', execute(message) { message.reply( - `**!help** - *Lists the available commands* - **!chat** - Queries OpenAI: *!chat what is a pizza* - **!key** - Testing remote Airtable: *!key url* - **!categories** - Displays Categories: *!categories* - **!find** - Searches the RSS Sources: *!find google* - **!get** - Retrieves Search By Index: *!get 25* - **!add** - Add a new RSS Source: *!add http://www.engadget.com/rss.xml* - **!update** - Updates all current RSS Feeds: *!update* - **!quote** - Selects a random quote: *!quote* - **!random** - Selects a random article: *!random* - **!random category** - Selects a random article by category: *!random sports* - **!search** - Instant Live Search: *!search salesforce* - **!slang** - Urban Dictionary Search: *!slang slang* - **!stock** - AlphaVantage Stock Search: *!stock IBM* - **!play** - Plays a trivia game question: *!play* - **!answer** - Answers for a question above: *!answer 1* - **!npm** - Gets NPM info from repository: *!npm axios* - **!alert** - Gets weather alerts for an area: *!alert TX* - **!calc** - Do math: *!calc 2 + 2* - **!food** - Selects a random recipe: *!food* - **!code** - Searches for code snippets: *!code python loop* + `**${prefix}help** - *Lists the available commands* + + **${prefix}add** - Add a new RSS Source: *${prefix}add http://www.engadget.com/rss.xml* + **${prefix}alert** - Gets weather alerts for an area: *${prefix}alert TX* + **${prefix}answer** - Answers for a question above: *${prefix}answer 1* + + **${prefix}chat** - Queries OpenAI: *${prefix}chat what is a pizza* + **${prefix}calc** - Do math: *${prefix}calc 2 + 2* + **${prefix}categories** - Displays Categories: *${prefix}categories* + **${prefix}code** - Searches for code snippets: *${prefix}code python loop* + + **${prefix}find** - Searches the RSS Sources: *${prefix}find google* + **${prefix}food** - Selects a random recipe: *${prefix}food* + + **${prefix}get** - Retrieves Search By Index: *${prefix}get 25* + + **${prefix}key** - Testing key from storage: *${prefix}key keyName* + + **${prefix}npm** - Gets NPM info from repository: *${prefix}npm axios* + + **${prefix}quote** - Selects a random quote: *${prefix}quote* + + **${prefix}random** - Selects a random article: *${prefix}random* + **${prefix}random category** - Selects a random article by category: *${prefix}random sports* + + **${prefix}search** - Instant Live Search: *${prefix}search salesforce* + **${prefix}slang** - Urban Dictionary Search: *${prefix}slang slang* + **${prefix}stock** - AlphaVantage Stock Search: *${prefix}stock IBM* + + **${prefix}update** - Updates all current RSS Feeds: *${prefix}update* ` ); } diff --git a/commands/remove.js b/commands/remove.js new file mode 100644 index 0000000..d240b98 --- /dev/null +++ b/commands/remove.js @@ -0,0 +1,31 @@ +var libFlayer = require("../libFlayer.js"); + +module.exports = { + name: 'add', + description: 'Add RSS Source', + execute(message, args) { + try { + if (args.length < 1) { + message.reply(`Please use in !add [title] format`); + return; + } + var title = args[0]; + + libFlayer.deleteSource(title, link, category, (err, result) => { + console.log("Result from adding entry", result); + + if (result) { + message.reply(`Adding ${title} to the list of RSS sources`); + } else { + message.reply(`${title} already exists in the list of RSS sources`); + } + + var sources = libFlayer.getSources(); + libFlayer.loadFeeds(); + }); + } catch (err) { + console.log(err); + message.reply(err.toString()); + } + } +}; \ No newline at end of file diff --git a/index.js b/index.js index e4c1493..b6f7d98 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,7 @@ const fs = require('fs'); const path = require('node:path'); -const { - prefix -} = require('./config.json'); require('dotenv').config(); +prefix = process.env.PREFIX token = process.env.TOKEN; const { Routes @@ -128,6 +126,7 @@ console.log("Link Flayer Bot Activating"); keepAlive(); client.login(token); //Load Client Discord Token try { + console.log("Loading initial startup feeds"); libFlayer.loadFeeds(); } catch (error) { console.log(error); diff --git a/libFlayer.js b/libFlayer.js index 0242ba7..3879c72 100644 --- a/libFlayer.js +++ b/libFlayer.js @@ -50,7 +50,7 @@ const { * @param {string} link - URL of RSS feed. * @param {string} category - Category of RSS feed. */ -exports.addSource = function (title, link, category) { +exports.addSource = function (title, link, category, callback) { for (i = 0; i < feeds.length; i++) { if (feeds[i].link == link) { @@ -58,7 +58,7 @@ exports.addSource = function (title, link, category) { } } - base(userTable).create([{ + base.create([{ "fields": { "title": title, "link": link, @@ -66,11 +66,11 @@ exports.addSource = function (title, link, category) { } }], function (err, record) { if (err) { - console.error(err); - return; + console.error("Error in create:", err); + callback(err, undefined); } - console.log(record.getId()); + console.log("Record ID:", record.getId()); var linkData = { title: `${title}`, @@ -79,8 +79,12 @@ exports.addSource = function (title, link, category) { id: record.getId() } + console.log("Link Data:", linkData); + feeds.push(linkData); + console.log("pushed item to feeds"); + callback(undefined, true); }); } @@ -97,7 +101,7 @@ exports.deleteSource = function (title) { deleteRecord = feeds[i].id; } } - base(userTable).destroy(deleteRecord, function (err, deletedRecord) { + base.destroy(deleteRecord, function (err, deletedRecord) { if (err) { console.error(err); return; @@ -131,15 +135,13 @@ exports.loadFeeds = function () { linkFlayerMap = []; linkFlayerCats = []; - base(userTable) - .select().firstPage(function (err, records) { + base.getAllRecords(function (err, records) { records.forEach(function (record) { - try { + try { console.log('Retrieved title: ', record.get('title')); console.log('Retrieved link:', record.get('link')); console.log('Retrieved category:', record.get('category')); - var feedData = { title: `${unescape(record.get('title'))}`, link: `${unescape(record.get('link'))}`, @@ -147,6 +149,8 @@ exports.loadFeeds = function () { id: record.getId() } + console.log("Feed Data:", feedData); + var foundMatch = false; feeds.forEach(feedBlock => { if (feedBlock.link == feedData.link) { diff --git a/libStorage.js b/libStorage.js index 6f469a8..c85267b 100644 --- a/libStorage.js +++ b/libStorage.js @@ -5,7 +5,6 @@ // Storage Specific Modules // MySQL -const e = require("express"); const mysql = require("mysql"); @@ -16,20 +15,49 @@ function runSQL(sqlQuery, connection, callback = (err, rows) => { throw err; }) { connection.query(sqlQuery, (err, rows) => { - if (err) return callback(err, undefined); - //console.log('The rows are:', rows); - return callback(undefined, rows); + if (err) { + console.log("SQL Error:", err) + callback(err, undefined); + } + console.log("RunSQL Returned Rows:", rows); + callback(undefined, rows); }) } +class RSSRecord { + /** + * + * @param {*} _id + * @param {*} _title + * @param {*} _link + * @param {*} _category + */ + constructor(_id, _title, _link, _category) { + this.id = _id; + this.title = _title; + this.link= _link; + this.category = _category; + } + + getId() { + return this.id; + } + + get(key) { + if (!Object.keys(this).includes(key)) throw new Error("Key is invalid", key); + return this[key] + } +} + +exports.RSSRecord = RSSRecord; exports.Storage = class Storage { constructor(_dbTable) { this.connection = mysql.createConnection({ host: process.env.DB_HOST, - user: databaseConfig.DB_USER, - password: databaseConfig.DB_PASS, - database: databaseConfig.DB_NAME + user: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME }); // Start the MySQL Connection @@ -41,20 +69,29 @@ exports.Storage = class Storage { /** * Wrapper to save a new entry using the storage method configured - * @param {*} toBeSaved Entry or Entries to be added + * @param {Array} toBeSaved Entry or Entries to be added * @param {function} callback The callback function to be called with the record when saved */ create(toBeSaved, callback) { + console.log("To be saved:", toBeSaved); + console.log("to be saved length:", toBeSaved.length); if (!toBeSaved[0].fields?.title) callback(Error("No title given"), undefined); - - for (const entry of toBeSaved) { - if(!this.checkForTitleSync(entry.title)) { - this.saveEntry(entry, (err, rows) => { - if (err) callback(err, undefined); - if (rows?.affectedRows) callback(undefined, rows[0]); - }) - } + let newRecords = [] + for (var entry of toBeSaved) { + entry = entry.fields + console.log("Entry:", entry); + this.returnRecord(undefined, entry.title, entry.link, entry.category, (err, record) => { + if (err) callback(err, undefined); + newRecords.push(record); + if (toBeSaved.length === 1) { + console.log("One record to callback with:", record); + callback(undefined, record); + } + }) } + if (!toBeSaved.length === 1) { + callback(undefined, newRecords); + } } /** @@ -65,12 +102,12 @@ exports.Storage = class Storage { destroy(entryID, callback) { if (!entryID) callback(Error("No entry ID given"), undefined); - const entryRecord = this.getRecordBy('id', entryID); - - this.removeEntry(entryRecord.id, (err, results) => { - if (err) callback(err, undefined); - callback(undefined, results); - }); + this.getRecordBy('id', entryID, (err, entryRecord) => { + this.removeEntry(entryRecord.id, (err, results) => { + if (err) callback(err, undefined); + callback(undefined, results); + }); + }); } /** @@ -78,14 +115,14 @@ exports.Storage = class Storage { * @param {*} title The title of the entry to check if it exists * @returns {true|false|*} */ - checkForTitleSync(title) { - if (!title) throw new Error("No title given when checking for title"); - const sqlQuery = `SELECT * FROM ${this.dbTable} WHERE title = ${title}`; + checkForTitle(title, callback) { + if (!title) callback(new Error("No title given when checking for title"), undefined); + const sqlQuery = `SELECT * FROM ${this.dbTable} WHERE title = '${title}'`; - runSQL(this.connection, sqlQuery, (err, rows) => { - if (err) throw err; - if (rows[0]?.title) return true; - else return false; + runSQL(sqlQuery, this.connection, (err, rows) => { + if (err) callback(err, undefined); + if (rows[0]?.title) callback(undefined, true); + else callback(undefined, false); }) } @@ -94,16 +131,16 @@ exports.Storage = class Storage { * @param {*} key The key to search for * @param {*} keyValue The value of the key to search for */ - getRecordBy(key, keyValue) { + getRecordBy(key, keyValue, callback) { const validKeys = ["link", "title", "category", "id"]; - if (!Array(validKeys).includes(key)) throw new Error("Given key not valid"); + if (!validKeys.includes(key)) callback(new Error("Given key not valid"), undefined); - const sqlQuery = `SELECT * FROM ${this.dbTable} WHERE ${key} = ${keyValue}`; + const sqlQuery = `SELECT * FROM ${this.dbTable} WHERE ${key} = '${keyValue}'`; - runSQL(this.connection, sqlQuery, (err, rows) => { - if (err) throw err; - if (rows[0]?.title) return rows[0]; - else return false; + runSQL(sqlQuery, this.connection, (err, rows) => { + if (err) callback(err, undefined); + if (rows[0]?.title) callback(undefined, rows[0]); + else callback(undefined, false); }) } @@ -113,13 +150,16 @@ exports.Storage = class Storage { * @param {function} callback The callback to be called with either an error or undefined if successful */ saveEntry(entryObject, callback) { - if (!entryObject?.table || entryObject?.link || entryObject?.category) { + console.log("Saving entry:", entryObject); + if (!entryObject?.title || !entryObject?.link || !entryObject?.category) { callback(new Error("Entry object malformed, check the object before saving it"), undefined) } - const sqlQuery = `INSERT INTO ${this.dbTable} (title, link, category) VALUES ('${entryObject.table}', '${entryObject.link}', '${entryObject.category}')`; + const sqlQuery = `INSERT INTO ${this.dbTable} (title, link, category) VALUES ('${entryObject.title}', '${entryObject.link}', '${entryObject.category}');`; - runSQL(this.connection, sqlQuery, (err, rows) => { + console.log(`Adding new entry with SQL query: '${sqlQuery}'`) + + runSQL(sqlQuery, this.connection, (err, rows) => { if (err) callback(err, undefined); callback(undefined, rows); }) @@ -130,13 +170,13 @@ exports.Storage = class Storage { * @param {Object} entryObject The entry object to be saved * @param {function} callback The callback to be called with either an error or undefined if successful */ - updateEntry(entryObject, callback) { + updateEntry(entryObject, callback) { let queryParams = []; if (!entryObject.title) callback(new Error("No title given before updating"), undefined); - queryParams.push(`title = '${entryObject.title}`); + queryParams.push(`title = '${entryObject.title}'`); if (!entryObject.link) callback(new Error("No link given before updating"), undefined); - queryParams.push(`link = '${entryObject.link}`); - if (entryObject.category) queryParams.push(`category = '${entryObject.category}`); + queryParams.push(`link = '${entryObject.link}'`); + if (entryObject.category) queryParams.push(`category = '${entryObject.category}'`); let sqlQuery = `UPDATE ${this.dbTable} SET`; @@ -154,7 +194,9 @@ exports.Storage = class Storage { sqlQuery = `${sqlQuery} WHERE title = '${entryObject.title}';` - runSQL(this.connection, sqlQuery, (err, rows) => { + console.log(`Updating entry with SQL query: '${sqlQuery}'`) + + runSQL(sqlQuery, this.connection, (err, rows) => { if (err) callback(err, undefined); callback(undefined, rows); }) @@ -172,9 +214,83 @@ exports.Storage = class Storage { const sqlQuery = `DELETE FROM ${this.dbTable} WHERE title = '${title}'`; - runSQL(this.connection, sqlQuery, (err, rows) => { + runSQL(sqlQuery, this.connection, (err, rows) => { if (err) callback(err, undefined); callback(undefined, rows); }) } -} \ No newline at end of file + + /** + * Returns a record class for the given information, if there's no ID, it will create it + * @param {*} _id The ID / line number of the record in the storage medium (OPT) + * @param {*} _title The title of the record + * @param {*} _link The link to the RSS feed + * @param {*} _category The category of the record + * @param {*} callback Callback function to return an error or the record + */ + returnRecord(_id, _title, _link, _category, callback) { + console.log(`Return record for these values: ID: '${_id}', Title: '${_title}', Category: '${_category}', Link: '${_link}'`) + if (!_link && !_title) callback(new Error("No link or title given when creating a record"), undefined); + let entryObject = { + "title": _title, + "link": _link + } + if (_category) entryObject.category = _category; + + if (_id) { + entryObject.id = _id; + this.updateEntry(entryObject, (err, rows) => { + if (err) callback(err, undefined); + this.getRecordBy('id', entryObject.id, (err, record) => { + if (err) callback(err, undefined); + callback(undefined, new RSSRecord(record.id, record.title, record.link, record.category)); + }) + }) + } + else { + this.checkForTitle(_title, (err, titleExists) => { + if (!titleExists) { + console.log("Entry doesn't exist, making one now", entryObject); + this.saveEntry(entryObject, (err, rows) => { + if (err) callback(err, undefined); + this.getRecordBy("title", entryObject.title, (err, record) => { + if (err) callback(err, undefined); + callback(undefined, new RSSRecord(record.id, record.title, record.link, record.category)); + }) + }); + } + else{ + this.updateEntry(entryObject, (err, rows) => { + if (err) callback(err, undefined); + this.getRecordBy('title', entryObject.title, (err, record) => { + if (err) callback(err, undefined); + callback(undefined, new RSSRecord(record.id, record.title, record.link, record.category)); + }) + }) + } + }) + } + } + + /** + * Get all records stored + * @param {function} callback + */ + getAllRecords(callback) { + console.log("Getting all records"); + const sqlQuery = `SELECT * FROM ${this.dbTable}` + + let rssRecords = []; + + runSQL(sqlQuery, this.connection, (err, rows) => { + if (err) callback(err, undefined); + for (const row of rows) { + console.log("Row from SQL query:", row); + rssRecords.push(new RSSRecord(row.id, row.title, row.link, row.category)); + } + console.log("All records:", rssRecords); + callback(undefined, rssRecords); + }); + } +} + diff --git a/mysqlHandler.js b/mysqlHandler.js deleted file mode 100644 index 7a1225b..0000000 --- a/mysqlHandler.js +++ /dev/null @@ -1,134 +0,0 @@ -const mysql = require('mysql'); -const databaseConfig = require('../config/databaseConfig'); -const utils = require('./utils'); - -const connection = mysql.createConnection({ - host: databaseConfig.database_host, - user: databaseConfig.database_user, - password: databaseConfig.database_password, - database: databaseConfig.database_database -}); - -const nodesTable = `${databaseConfig.database_database}.nodes`; - -connection.connect() - -/** Get all nodes the server knows about regardless of status - * @param {*} callback Callback function - */ -exports.getAllNodes = (callback) => { - const sqlQuery = `SELECT * FROM ${nodesTable}` - runSQL(sqlQuery, (rows) => { - callback(rows); - }) -} - -/** Get all nodes that have the online status set true (are online) - * @param callback Callback function - */ -exports.getOnlineNodes = (callback) => { - const sqlQuery = `SELECT * FROM ${nodesTable} WHERE online = 1;` - runSQL(sqlQuery, (rows) => { - callback(rows); - }) -} - -/** Get info on a node based on ID - * @param nodeId The ID of the node - * @param callback Callback function - */ -exports.getNodeInfoFromId = (nodeId, callback) => { - const sqlQuery = `SELECT * FROM ${nodesTable} WHERE id = ${nodeId}` - runSQL(sqlQuery, (rows) => { - // Call back the first (and theoretically only) row - // Specify 0 so downstream functions don't have to worry about it - callback(rows[0]); - }) -} - -/** Add a new node to the DB - * @param nodeObject Node information object - * @param callback Callback function - */ -exports.addNewNode = (nodeObject, callback) => { - if (!nodeObject.name) throw new Error("No name provided"); - const name = nodeObject.name, - ip = nodeObject.ip, - port = nodeObject.port, - location = nodeObject.location, - nearbySystems = utils.JsonToBuffer(nodeObject.nearbySystems), - online = nodeObject.online; - const sqlQuery = `INSERT INTO ${nodesTable} (name, ip, port, location, nearbySystems, online) VALUES ('${name}', '${ip}', ${port}, '${location}', '${nearbySystems}', ${online})`; - - runSQL(sqlQuery, (rows) => { - callback(rows); - }) -} - -/** Update the known info on a node - * @param nodeObject Node information object - * @param callback Callback function - */ -exports.updateNodeInfo = (nodeObject, callback) => { - const name = nodeObject.name, - ip = nodeObject.ip, - port = nodeObject.port, - location = nodeObject.location, - online = nodeObject.online; - let queryParams = [], - nearbySystems = nodeObject.nearbySystems; - - if (name) queryParams.push(`name = '${name}'`); - if (ip) queryParams.push(`ip = '${ip}'`); - if (port) queryParams.push(`port = ${port}`); - if (location) queryParams.push(`location = '${location}'`); - if (nearbySystems) { - nearbySystems = utils.JsonToBuffer(nearbySystems) - queryParams.push(`nearbySystems = '${nearbySystems}'`); - } - if (typeof online === "boolean") { - if (online) queryParams.push(`online = 1`); - else queryParams.push(`online = 0`); - } - - let sqlQuery = `UPDATE ${nodesTable} SET` - if (!queryParams || queryParams.length === 0) return callback(undefined); - if (queryParams.length === 1) { - sqlQuery = `${sqlQuery} ${queryParams[0]}` - } else { - let i = 0; - for (const param of queryParams) { - if (i === queryParams.length-1) { - sqlQuery = `${sqlQuery} ${param}` - i += 1; - } - else { - sqlQuery = `${sqlQuery} ${param},` - i += 1; - } - } - } - - sqlQuery = `${sqlQuery} WHERE id = ${nodeObject.id};` - - runSQL(sqlQuery, (rows) => { - if (rows.affectedRows === 1) callback(true); - else callback(rows); - }) -} - -// Function to run and handle SQL errors -function runSQL(sqlQuery, callback, error = (err) => { - console.log(err); - throw err; -}) { - connection.query(sqlQuery, (err, rows) => { - if (err) return error(err); - //console.log('The rows are:', rows); - return callback(rows); - }) -} - -exports.closeConnection = () => { - connection.end() -} \ No newline at end of file