Working MySQL Storage system

- testing delete
This commit is contained in:
Logan Cusano
2023-02-20 00:20:46 -05:00
parent 18b960231d
commit 1e6d60f4c3
7 changed files with 253 additions and 222 deletions

View File

@@ -13,16 +13,20 @@ module.exports = {
var link = args[1]; var link = args[1];
var category = args[2]; var category = args[2];
var result = libFlayer.addSource(title, link, category); libFlayer.addSource(title, link, category, (err, result) => {
if (result) { console.log("Result from adding entry", 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(); if (result) {
libFlayer.loadFeeds(); 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) { } catch (err) {
console.log(err);
message.reply(err.toString()); message.reply(err.toString());
} }
} }

View File

@@ -1,31 +1,42 @@
var libFlayer = require("../libFlayer.js"); var libFlayer = require("../libFlayer.js");
const prefix = process.env.prefix;
module.exports = { module.exports = {
name: 'help', name: 'help',
description: 'Help', description: 'Help',
execute(message) { execute(message) {
message.reply( message.reply(
`**!help** - *Lists the available commands* `**${prefix}help** - *Lists the available commands*
**!chat** - Queries OpenAI: *!chat what is a pizza*
**!key** - Testing remote Airtable: *!key url* **${prefix}add** - Add a new RSS Source: *${prefix}add http://www.engadget.com/rss.xml*
**!categories** - Displays Categories: *!categories* **${prefix}alert** - Gets weather alerts for an area: *${prefix}alert TX*
**!find** - Searches the RSS Sources: *!find google* **${prefix}answer** - Answers for a question above: *${prefix}answer 1*
**!get** - Retrieves Search By Index: *!get 25*
**!add** - Add a new RSS Source: *!add http://www.engadget.com/rss.xml* **${prefix}chat** - Queries OpenAI: *${prefix}chat what is a pizza*
**!update** - Updates all current RSS Feeds: *!update* **${prefix}calc** - Do math: *${prefix}calc 2 + 2*
**!quote** - Selects a random quote: *!quote* **${prefix}categories** - Displays Categories: *${prefix}categories*
**!random** - Selects a random article: *!random* **${prefix}code** - Searches for code snippets: *${prefix}code python loop*
**!random category** - Selects a random article by category: *!random sports*
**!search** - Instant Live Search: *!search salesforce* **${prefix}find** - Searches the RSS Sources: *${prefix}find google*
**!slang** - Urban Dictionary Search: *!slang slang* **${prefix}food** - Selects a random recipe: *${prefix}food*
**!stock** - AlphaVantage Stock Search: *!stock IBM*
**!play** - Plays a trivia game question: *!play* **${prefix}get** - Retrieves Search By Index: *${prefix}get 25*
**!answer** - Answers for a question above: *!answer 1*
**!npm** - Gets NPM info from repository: *!npm axios* **${prefix}key** - Testing key from storage: *${prefix}key keyName*
**!alert** - Gets weather alerts for an area: *!alert TX*
**!calc** - Do math: *!calc 2 + 2* **${prefix}npm** - Gets NPM info from repository: *${prefix}npm axios*
**!food** - Selects a random recipe: *!food*
**!code** - Searches for code snippets: *!code python loop* **${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*
` `
); );
} }

31
commands/remove.js Normal file
View File

@@ -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());
}
}
};

View File

@@ -1,9 +1,7 @@
const fs = require('fs'); const fs = require('fs');
const path = require('node:path'); const path = require('node:path');
const {
prefix
} = require('./config.json');
require('dotenv').config(); require('dotenv').config();
prefix = process.env.PREFIX
token = process.env.TOKEN; token = process.env.TOKEN;
const { const {
Routes Routes
@@ -128,6 +126,7 @@ console.log("Link Flayer Bot Activating");
keepAlive(); keepAlive();
client.login(token); //Load Client Discord Token client.login(token); //Load Client Discord Token
try { try {
console.log("Loading initial startup feeds");
libFlayer.loadFeeds(); libFlayer.loadFeeds();
} catch (error) { } catch (error) {
console.log(error); console.log(error);

View File

@@ -50,7 +50,7 @@ const {
* @param {string} link - URL of RSS feed. * @param {string} link - URL of RSS feed.
* @param {string} category - Category 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++) { for (i = 0; i < feeds.length; i++) {
if (feeds[i].link == link) { if (feeds[i].link == link) {
@@ -58,7 +58,7 @@ exports.addSource = function (title, link, category) {
} }
} }
base(userTable).create([{ base.create([{
"fields": { "fields": {
"title": title, "title": title,
"link": link, "link": link,
@@ -66,11 +66,11 @@ exports.addSource = function (title, link, category) {
} }
}], function (err, record) { }], function (err, record) {
if (err) { if (err) {
console.error(err); console.error("Error in create:", err);
return; callback(err, undefined);
} }
console.log(record.getId()); console.log("Record ID:", record.getId());
var linkData = { var linkData = {
title: `${title}`, title: `${title}`,
@@ -79,8 +79,12 @@ exports.addSource = function (title, link, category) {
id: record.getId() id: record.getId()
} }
console.log("Link Data:", linkData);
feeds.push(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; deleteRecord = feeds[i].id;
} }
} }
base(userTable).destroy(deleteRecord, function (err, deletedRecord) { base.destroy(deleteRecord, function (err, deletedRecord) {
if (err) { if (err) {
console.error(err); console.error(err);
return; return;
@@ -131,15 +135,13 @@ exports.loadFeeds = function () {
linkFlayerMap = []; linkFlayerMap = [];
linkFlayerCats = []; linkFlayerCats = [];
base(userTable) base.getAllRecords(function (err, records) {
.select().firstPage(function (err, records) {
records.forEach(function (record) { records.forEach(function (record) {
try { try {
console.log('Retrieved title: ', record.get('title')); console.log('Retrieved title: ', record.get('title'));
console.log('Retrieved link:', record.get('link')); console.log('Retrieved link:', record.get('link'));
console.log('Retrieved category:', record.get('category')); console.log('Retrieved category:', record.get('category'));
var feedData = { var feedData = {
title: `${unescape(record.get('title'))}`, title: `${unescape(record.get('title'))}`,
link: `${unescape(record.get('link'))}`, link: `${unescape(record.get('link'))}`,
@@ -147,6 +149,8 @@ exports.loadFeeds = function () {
id: record.getId() id: record.getId()
} }
console.log("Feed Data:", feedData);
var foundMatch = false; var foundMatch = false;
feeds.forEach(feedBlock => { feeds.forEach(feedBlock => {
if (feedBlock.link == feedData.link) { if (feedBlock.link == feedData.link) {

View File

@@ -5,7 +5,6 @@
// Storage Specific Modules // Storage Specific Modules
// MySQL // MySQL
const e = require("express");
const mysql = require("mysql"); const mysql = require("mysql");
@@ -16,20 +15,49 @@ function runSQL(sqlQuery, connection, callback = (err, rows) => {
throw err; throw err;
}) { }) {
connection.query(sqlQuery, (err, rows) => { connection.query(sqlQuery, (err, rows) => {
if (err) return callback(err, undefined); if (err) {
//console.log('The rows are:', rows); console.log("SQL Error:", err)
return callback(undefined, rows); 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 { exports.Storage = class Storage {
constructor(_dbTable) { constructor(_dbTable) {
this.connection = mysql.createConnection({ this.connection = mysql.createConnection({
host: process.env.DB_HOST, host: process.env.DB_HOST,
user: databaseConfig.DB_USER, user: process.env.DB_USER,
password: databaseConfig.DB_PASS, password: process.env.DB_PASS,
database: databaseConfig.DB_NAME database: process.env.DB_NAME
}); });
// Start the MySQL Connection // Start the MySQL Connection
@@ -41,20 +69,29 @@ exports.Storage = class Storage {
/** /**
* Wrapper to save a new entry using the storage method configured * 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 * @param {function} callback The callback function to be called with the record when saved
*/ */
create(toBeSaved, callback) { 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); if (!toBeSaved[0].fields?.title) callback(Error("No title given"), undefined);
let newRecords = []
for (const entry of toBeSaved) { for (var entry of toBeSaved) {
if(!this.checkForTitleSync(entry.title)) { entry = entry.fields
this.saveEntry(entry, (err, rows) => { console.log("Entry:", entry);
if (err) callback(err, undefined); this.returnRecord(undefined, entry.title, entry.link, entry.category, (err, record) => {
if (rows?.affectedRows) callback(undefined, rows[0]); 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) { destroy(entryID, callback) {
if (!entryID) callback(Error("No entry ID given"), undefined); if (!entryID) callback(Error("No entry ID given"), undefined);
const entryRecord = this.getRecordBy('id', entryID); this.getRecordBy('id', entryID, (err, entryRecord) => {
this.removeEntry(entryRecord.id, (err, results) => {
this.removeEntry(entryRecord.id, (err, results) => { if (err) callback(err, undefined);
if (err) callback(err, undefined); callback(undefined, results);
callback(undefined, results); });
}); });
} }
/** /**
@@ -78,14 +115,14 @@ exports.Storage = class Storage {
* @param {*} title The title of the entry to check if it exists * @param {*} title The title of the entry to check if it exists
* @returns {true|false|*} * @returns {true|false|*}
*/ */
checkForTitleSync(title) { checkForTitle(title, callback) {
if (!title) throw new Error("No title given when checking for title"); if (!title) callback(new Error("No title given when checking for title"), undefined);
const sqlQuery = `SELECT * FROM ${this.dbTable} WHERE title = ${title}`; const sqlQuery = `SELECT * FROM ${this.dbTable} WHERE title = '${title}'`;
runSQL(this.connection, sqlQuery, (err, rows) => { runSQL(sqlQuery, this.connection, (err, rows) => {
if (err) throw err; if (err) callback(err, undefined);
if (rows[0]?.title) return true; if (rows[0]?.title) callback(undefined, true);
else return false; else callback(undefined, false);
}) })
} }
@@ -94,16 +131,16 @@ exports.Storage = class Storage {
* @param {*} key The key to search for * @param {*} key The key to search for
* @param {*} keyValue The value of 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"]; 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) => { runSQL(sqlQuery, this.connection, (err, rows) => {
if (err) throw err; if (err) callback(err, undefined);
if (rows[0]?.title) return rows[0]; if (rows[0]?.title) callback(undefined, rows[0]);
else return false; 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 * @param {function} callback The callback to be called with either an error or undefined if successful
*/ */
saveEntry(entryObject, callback) { 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) 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); if (err) callback(err, undefined);
callback(undefined, rows); callback(undefined, rows);
}) })
@@ -130,13 +170,13 @@ exports.Storage = class Storage {
* @param {Object} entryObject The entry object to be saved * @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 * @param {function} callback The callback to be called with either an error or undefined if successful
*/ */
updateEntry(entryObject, callback) { updateEntry(entryObject, callback) {
let queryParams = []; let queryParams = [];
if (!entryObject.title) callback(new Error("No title given before updating"), undefined); 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); if (!entryObject.link) callback(new Error("No link given before updating"), undefined);
queryParams.push(`link = '${entryObject.link}`); queryParams.push(`link = '${entryObject.link}'`);
if (entryObject.category) queryParams.push(`category = '${entryObject.category}`); if (entryObject.category) queryParams.push(`category = '${entryObject.category}'`);
let sqlQuery = `UPDATE ${this.dbTable} SET`; let sqlQuery = `UPDATE ${this.dbTable} SET`;
@@ -154,7 +194,9 @@ exports.Storage = class Storage {
sqlQuery = `${sqlQuery} WHERE title = '${entryObject.title}';` 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); if (err) callback(err, undefined);
callback(undefined, rows); callback(undefined, rows);
}) })
@@ -172,9 +214,83 @@ exports.Storage = class Storage {
const sqlQuery = `DELETE FROM ${this.dbTable} WHERE title = '${title}'`; 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); if (err) callback(err, undefined);
callback(undefined, rows); callback(undefined, rows);
}) })
} }
}
/**
* 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);
});
}
}

View File

@@ -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()
}