/* ---------------------------------------------------------------------------- Question Server GitLab: This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ------------------------------------------------------------------------- */ module.exports = { ProcessIncomingRequest: ProcessIncomingRequest, CheckData: CheckData, NLoad: NLoad, LoadJSON: LoadJSON }; var recievedFile = "stats/recieved"; var staticFile = "public/data/static"; var manFile = "public/man.html"; var dataFile = "public/data.json"; var motdFile = "public/motd"; var versionFile = "public/currVersion"; var logger = require('./logger.js'); var utils = require('./utils.js'); class Question { constructor(q, a, i) { this.Q = q; this.A = a; this.I = i; } toString() { var r = "?" + this.Q + "\n!" + this.A; if (this.I) r += "\n>" + this.I; return r; } HasQuestion() { return this.Q != undefined; } HasAnswer() { return this.A != undefined; } HasImage() { return this.I != undefined; } IsComplete() { return this.HasQuestion() && this.HasAnswer(); } Compare(q2, i) { if (typeof q2 == 'string') { var qmatchpercent = Question.CompareString(this.Q, q2); if (i == undefined || i.length == 0) return qmatchpercent; else { if (this.HasImage()) { const imatchpercent = this.HasImage() ? Question.CompareString(this.I.join(" "), i.join(" ")) : 0; return (qmatchpercent + imatchpercent) / 2; } else { qmatchpercent -= 30; if (qmatchpercent < 0) return 0; else return qmatchpercent; } } } else { const qmatchpercent = Question.CompareString(this.Q, q2.Q); const amatchpercent = Question.CompareString(this.A, q2.A); if (this.I != undefined) { const imatchpercent = this.I == undefined ? Question.CompareString(this.I.join(" "), q2.I.join( " ")) : 0; return (qmatchpercent + amatchpercent + imatchpercent) / 3; } else { return (qmatchpercent + amatchpercent) / 2; } } } static CompareString(s1, s2) { //if (s1 == undefined || s2 == undefined) // return 0; s1 = SimplifyStringForComparison(s1).split(" "); s2 = SimplifyStringForComparison(s2).split(" "); var match = 0; for (var i = 0; i < s1.length; i++) if (s2.includes(s1[i])) match++; var percent = Math.round(((match / s1.length) * 100).toFixed(2)); // matched words percent var lengthDifference = Math.abs(s2.length - s1.length); percent -= lengthDifference * 3; if (percent < 0) percent = 0; return percent; } } class Subject { constructor(n) { this.Name = n; this.Questions = []; } get length() { return this.Questions.length; } AddQuestion(q) { this.Questions.push(q); } Search(q, img) { var r = []; for (var i = 0; i < this.length; i++) { let percent = this.Questions[i].Compare(q, img); if (percent > minMatchAmmount) r.push({ q: this.Questions[i], match: percent }); } for (var i = 0; i < r.length; i++) for (var j = i; j < r.length; j++) if (r[i].match < r[j].match) { var tmp = r[i]; r[i] = r[j]; r[j] = tmp; } return r; } toString() { var r = []; for (var i = 0; i < this.Questions.length; i++) r.push(this.Questions[i].toString()); return "+" + this.Name + "\n" + r.join("\n"); } } class QuestionDB { constructor() { this.Subjects = []; } get length() { return this.Subjects.length; } get activeIndexes() { var r = []; for (var i = 0; i < this.length; i++) { if (GM_getValue("Is" + i + "Active")) { r.push(i); } } return r; } GetIfActive(ind) { return GM_getValue("Is" + ind + "Active"); } ChangeActive(i, value) { GM_setValue("Is" + i + "Active", !!value); } AddQuestion(subj, q) { var i = 0; while (i < this.Subjects.length && this.Subjects[i].Name != subj) i++; if (i < this.Subjects.length) this.Subjects[i].AddQuestion(q); else { const n = new Subject(subj); n.AddQuestion(q); this.Subjects.push(n); } } Search(q, img) { var r = []; for (var i = 0; i < this.length; i++) if (this.GetIfActive(i)) r = r.concat(this.Subjects[i].Search(q, img)); for (var i = 0; i < r.length; i++) for (var j = i; j < r.length; j++) if (r[i].match < r[j].match) { var tmp = r[i]; r[i] = r[j]; r[j] = tmp; } return r; } AddSubject(subj) { var i = 0; while (i < this.length && subj.Name != this.Subjects[i].Name) i++; if (i < this.length) { this.Subjects.concat(subj.Questions); } else { this.Subjects.push(subj); } } toString() { var r = []; for (var i = 0; i < this.Subjects.length; i++) r.push(this.Subjects[i].toString()); return r.join("\n\n"); } } function Process(d, file) { try { logger.Log("[PCES]:\tFile: " + file); if (d.data.split("\n").length > 1) { var oldFile = utils.ReadFile(file); var newFile = oldFile + "\n"; if (d.data[0] == '+') newFile += d.data; else newFile += "+" + d.data; var newRes = CheckData(newFile); var oldRes = CheckData(oldFile); if (oldRes.count > 0) logger.Log("[NLOD]:\t\told public result: " + oldRes.count, logger.GetColor("blue")); else logger.Log("[NLOD]:\t\told public NLOD error, " + oldRes.log, logger.GetColor("redbg"), true); if (newRes.count > 0) logger.Log("[NLOD]:\t\tnew file result: " + newRes.count, logger.GetColor("blue")); else logger.Log("[NLOD]:\t\tnew file NLOD error, " + newRes.log, logger.GetColor("redbg"), true); utils.WriteFile(newFile, file); logger.Log("[NLOD]:\t\tNew data written to: " + file); return newRes.count - oldRes.count; } else logger.Log("[PCES]:\t\tNo new data"); } catch (e) { Beep(); logger.Log("[ERR ]:\tError at processing data! File: " + file, logger.GetColor("redbg")); logger.Log(e.toString(), logger.GetColor("redbg")); } return -1; } function ProcessIncomingRequest(data) { if (data == undefined) { logger.Log("[PCES]:\tRecieved data is undefined!", logger.GetColor("redbg")); return; } try { var d = JSON.parse(data); var dfile = utils.ReadFile(dataFile); var data = LoadJSON(dfile); var allQuestions = []; for ( var i = 0; i < d.allData.length; i++){ allQuestions.push(new Question(d.allData[i].Q, d.allData[i].A, d.allData[i].I)); } var questions = []; for ( var i = 0; i < d.data.length; i++){ let q = new Question(d.data[i].Q, d.data[i].A, d.data[i].I); questions.push(q); data.AddQuestion(d.subj, q); } var motd = utils.ReadFile(motdFile); var version = utils.ReadFile(versionFile); data.motd = motd; data.version = version; logger.Log("[PCES]:\t" + d.subj); var msg = "All / new count: " + allQuestions.length + " / " + questions.length; if (d.version != undefined) msg += ". Version: " + d.version; var color = logger.GetColor("green"); if (data != undefined && d.data.length > 0){ utils.WriteBackup(); utils.WriteFile(JSON.stringify(data), dataFile); msg += " - Data file written!"; var color = logger.GetColor("blue"); } logger.Log("[PCES]:\t" + msg, color); } catch (e) { console.log(e); logger.Log("[PCES]: Couldnt parse JSON data, trying old format..."); var d = SetupData(data); var qcount = -1; try { var splitted = d.alldata.split("\n"); var count = 0; for (var i = 0; i < splitted.length; i++) if (splitted[i][0] == '?') count ++; qcount = count; } catch (e) {console.log("Error :c"); console.log(e);} logger.Log("[PCES]:\tProcessing data: " + d.subj + " (" + d.type + "), count: " + qcount, logger.GetColor("green")); if (d.subj == undefined){ logger.Log(JSON.stringify(d), logger.GetColor("red")); return; } var newItems = 0; var newStatItems = Process(d, staticFile); utils.AppendToFile(logger.GetDateString() + "\n" + d.data, recievedFile); PrintNewCount(d, newStatItems, staticFile); } } function PrintNewCount(d, newItems, file) { if (newItems > 0) { var count = 0; var splitted = d.alldata.split("\n"); for (var i = 0; i < splitted.length; i++) if (splitted[i].startsWith("?")) count++; logger.Log("[NEW ]:\t" + file + " All / New: " + count + " / " + newItems, logger.GetColor("cyan")); } } function SetupData(data) { var pdata = data.split("<#>"); if (pdata.length <= 0){ logger.Log("[SUPD]: Data length is zero !", logger.GetColor("redbg")); throw "No data recieved!"; } var d = {}; // parsed data for (var i = 0; i < pdata.length; i++) { var td = pdata[i].split("<=>"); if (td.length == 2) d[td[0]] = td[1]; else { logger.Log("[SUPD]: Invalid parameter!", logger.GetColor("redbg")); throw "Invalid parameter recieved!"; } } return d; } function CheckData(data) { try { var presult = NLoad(data); return presult; } catch (e) { logger.Log("[NLOD]: NLOD error, " + e.toString(), logger.GetColor("redbg"), true); return { count: -1, log: [e.toString()] }; } } function NLoad(resource) { var resultLog = []; var count = -1; var allCount = 0; if (resource == undefined) throw "A megadott adat undefined!"; resource = resource.split("\n"); // splitting by enters if (resource.length == 1) throw "A megadott adat nem sorokból áll!"; var data = []; // initializing data declared at the begining function AddNewSubj(name) // adds a new subject to the data { data.push({ "questions": [] }); // ads aa new object, with an empty array in it GetCurrSubj().name = name; // sets the name for it GetCurrSubj().count = 0; // setting count to default // setting if its active only if its undefined, otherwise previous user setting shouldt be overwritten } function AddItem() // adds an item to the last subjects questions { GetCurrSubj().count++; allCount++; // incrementing all count GetCurrSubj().questions.push({}); // adding a new empty object to the last item in the data } function GetLastItem() // returns the last item of the last item of data { var q = GetCurrSubj().questions.length; // questions length return GetCurrSubj().questions[q - 1]; } function GetCurrSubj() { return data[data.length - 1]; } // ? : question // ! : answer // > : image JSON data // + : subject name // checking for name for (var j = 0; j < resource.length; j++) // goes through resources { if (resource[j][0] == '+') // if there is a name identifier { break; // breaks, couse there will be a name } if (resource[j][0] == '?' || resource[j][0] == '!' || resource[j][0] == '>') // if it begins with another identifier: { AddNewSubj("NONAME"); // there is no name (for the first question at least), so setting it noname. break; } // else it does nothing, continues to check } var jumped = 0; // the amount of lines it processed for (var i = 0; i < resource.length; i += jumped) // gouing through the resource { jumped = 0; // resetting it to 0 var currRawDataQ = resource[i]; // current question var currRawDataA = resource[i + 1]; // current answer var currRawDataI = resource[i + 2]; // current image if (currRawDataQ != undefined && currRawDataQ[0] == '?' && currRawDataA != undefined && currRawDataA[0] == '!') // if the current line is ? and the next is ! its a data { AddItem(); GetLastItem().q = currRawDataQ.substr(1); GetLastItem().a = currRawDataA.substr(1); jumped += 2; if (currRawDataI != undefined && currRawDataI[0] == '>') { GetLastItem().i = currRawDataI.substr(1); jumped++; } } else if (currRawDataQ[0] == '+') // if its a new subject { AddNewSubj(currRawDataQ.substr(1)); jumped++; } else { // this should be invalid question order resultLog.push("Warning @ line " + i + ":" + currRawDataQ + " " + currRawDataA + " " + currRawDataI); jumped++; } } // end of parsing all data return { count: allCount, log: resultLog }; } // loading stuff function LoadJSON(resource) { var count = -1; try { var d = JSON.parse(resource); var r = new QuestionDB(); var rt = []; var allCount = -1; for (var i = 0; i < d.Subjects.length; i++) { let s = new Subject(d.Subjects[i].Name); var j = 0; for (j = 0; j < d.Subjects[i].Questions.length; j++) { var currQ = d.Subjects[i].Questions[j]; s.AddQuestion(new Question(currQ.Q, currQ.A, currQ.I)); } rt.push({ name: d.Subjects[i].Name, count: j }); allCount += j; r.AddSubject(s); } return r; } catch (e) { logger.Log("[LOAD]:Error loading sutff", logger.GetColor("redbg"), true); } }