diff --git a/README.md b/README.md
index 7c7de34..27ee895 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ Install:
Requires node.js.
-`npm install express connect-busboy ejs express-layout querystring express`
+`npm install`
then
diff --git a/actions.js b/actions.js
deleted file mode 100644
index 74b2f7f..0000000
--- a/actions.js
+++ /dev/null
@@ -1,533 +0,0 @@
-/* ----------------------------------------------------------------------------
-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,
- ProcessQA: ProcessQA
-};
-
-var staticFile = "public/data/static";
-var manFile = "public/man.html";
-var dataFile = "public/data.json";
-const recDataFile = "stats/recdata";
-const versionFile = "public/version";
-const motdFile = "public/motd";
-const qaFile = "public/qa";
-
-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("File: " + 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("\t\told public result: " + oldRes.count, logger.GetColor("blue"));
- else
- logger.Log("\t\told public NLOD error, " + oldRes.log, logger.GetColor("redbg"), true);
-
- if (newRes.count > 0)
- logger.Log("\t\tnew file result: " + newRes.count, logger.GetColor("blue"));
- else
- logger.Log("\t\tnew file NLOD error, " + newRes.log, logger.GetColor("redbg"), true);
-
- utils.WriteFile(newFile, file);
- logger.Log("\t\tNew data written to: " + file);
-
- return newRes.count - oldRes.count;
- } else
- logger.Log("\t\tNo new data");
-
-
- } catch (e) {
- Beep();
- logger.Log("\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("\tRecieved data is undefined!", logger.GetColor("redbg"));
- return;
- }
-
- try {
- let towrite = logger.GetDateString() + "\n";
- towrite += "------------------------------------------------------------------------------\n";
- towrite += data
- towrite += "\n------------------------------------------------------------------------------\n";
- utils.AppendToFile(towrite, recDataFile);
- } catch (e) {
- logger.log("Error writing recieved data.");
- }
-
- 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);
- }
-
- logger.Log("\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");
-
- try {
- data.version = utils.ReadFile(versionFile);
- data.motd = utils.ReadFile(motdFile);
- } catch (e) {
- Log("MOTD/Version writing/reading error!");
- }
-
- 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("\t" + msg, color);
-
- } catch (e) {
- logger.Log("Couldnt parse JSON data, trying old format...", logger.GetColor("redbg"));
- 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("\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);
-
- 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("\t" + file + " All / New: " + count + " / " + newItems, logger.GetColor("cyan"));
- }
-}
-
-function SetupData(data) {
- var pdata = data.split("<#>");
- if (pdata.length <= 0){
- logger.Log("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("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("Load 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("Error loading sutff", logger.GetColor("redbg"), true);
- }
-}
-
-function ProcessQA() {
- if (!utils.FileExists(qaFile))
- utils.WriteFile("", qaFile);
-
- let a = utils.ReadFile(qaFile).split("\n");
- let r = [];
- let ind = 0;
- for (let i = 0; i < a.length; i++) {
- if ( a[i] == "#")
- ind ++;
- else {
- if (r[ind] == undefined)
- r[ind] = {};
-
- if (r[ind].q == undefined) {
- r[ind].q = a[i];
- } else {
- if (r[ind].a == undefined)
- r[ind].a = [];
-
- r[ind].a.push(a[i]);
- }
- }
- }
-
- return r;
-}
diff --git a/changedataversion.js b/changedataversion.js
deleted file mode 100644
index 2b5052a..0000000
--- a/changedataversion.js
+++ /dev/null
@@ -1,32 +0,0 @@
-const utils = require('./utils.js');
-const dataFile = "public/data.json";
-const versionFile = "public/version";
-
-var p = GetParams();
-if (p.length <= 0) {
- console.log("no params!");
- process.exit(0);
-}
-
-
-var param = p.join(" ");
-
-console.log("param: " + param);
-
-var d = utils.ReadFile(dataFile);
-var parsed = JSON.parse(d);
-
-console.log("Old version:");
-console.log(parsed.version);
-
-parsed.version = param;
-
-console.log("New version:");
-console.log(parsed.version);
-
-utils.WriteFile(JSON.stringify(parsed), dataFile);
-utils.WriteFile(parsed.version, versionFile);
-
-function GetParams() {
- return process.argv.splice(2);
-}
diff --git a/logger.js b/logger.js
deleted file mode 100644
index bd848f0..0000000
--- a/logger.js
+++ /dev/null
@@ -1,104 +0,0 @@
-/* ----------------------------------------------------------------------------
-
- 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 = {
- GetDateString: GetDateString,
- Log: Log,
- GetColor: GetColor,
- LogReq: LogReq
-};
-
-const DELIM = "|";
-
-var utils = require('./utils.js');
-const nlogFile = "stats/nlogs";
-const logFile = "/nlogs/nlogs";
-const locLogFile = "stats/logs";
-const allLogFile = "/nlogs/log";
-
-function GetDateString() {
- var m = new Date();
- return m.getFullYear() + "/" +
- ("0" + (m.getMonth() + 1)).slice(-2) + "/" +
- ("0" + m.getDate()).slice(-2) + " " +
- ("0" + m.getHours()).slice(-2) + ":" +
- ("0" + m.getMinutes()).slice(-2) + ":" +
- ("0" + m.getSeconds()).slice(-2);
-}
-
-function Log(s, c, b) {
- if (c != undefined)
- console.log(c, GetDateString() + DELIM + s);
- else
- console.log(GetDateString() + DELIM + s);
-
- if (b)
- utils.Beep();
-
- utils.AppendToFile(GetDateString() + DELIM + s, nlogFile);
- utils.AppendToFile(GetDateString() + DELIM + s, logFile);
-}
-
-function LogReq(req, toFile, sc) {
- try {
- var ip = req.headers['cf-connecting-ip'] || req.connection.remoteAddress;
- var logEntry = ip + DELIM + req.hostname + DELIM + req.headers['user-agent'] +
- DELIM + req.method + DELIM;
-
- logEntry += req.url;
-
- if (sc != undefined && sc == 404)
- logEntry += DELIM + sc;
- var color = GetColor("green");
-
- if (req.url.toLowerCase().includes("isadding"))
- color = GetColor("yellow");
- if (!toFile) {
- Log(logEntry, color);
- } else {
- var defLogs = GetDateString() + DELIM + logEntry;
- var extraLogs = "\n\t" + JSON.stringify(req.headers) + "\n\t" + JSON.stringify(req.body) + "\n";
-
- utils.AppendToFile(defLogs, locLogFile);
- utils.AppendToFile(defLogs, allLogFile);
- }
-
- } catch (e) {
- console.log(e);
- Log("Error at logging lol", GetColor("redbg"), true);
- }
-}
-
-function GetColor(c) {
- if (c == "redbg")
- return '\x1b[41m%s\x1b[0m';
- if (c == "bluebg")
- return '\x1b[44m%s\x1b[0m';
- if (c == "red")
- return '\x1b[31m%s\x1b[0m';
- if (c == "green")
- return '\x1b[32m%s\x1b[0m';
- if (c == "yellow")
- return '\x1b[33m%s\x1b[0m';
- if (c == "blue")
- return '\x1b[34m%s\x1b[0m';
- if (c == "cyan")
- return '\x1b[36m%s\x1b[0m';
-}
diff --git a/merger.js b/merger.js
deleted file mode 100644
index 2833705..0000000
--- a/merger.js
+++ /dev/null
@@ -1,452 +0,0 @@
-/* ----------------------------------------------------------------------------
-
- Question Server question file merger
- 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 .
-
- ------------------------------------------------------------------------- */
-
-// TODO: handle flags
-// join json datas, or raw datas
-// or something else
-
-const minMatchAmmount = 55;
-const minResultMatchPercent = 99;
-const lengthDiffMultiplier = 10;
-
-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();
- }
- // TODO: TEST DIS
- 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) {
- 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 * lengthDiffMultiplier;
- 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);
- }
- 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;
- }
- 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);
- }
- }
- 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");
- }
-}
-var utils = require('./utils.js');
-var actions = require('./actions.js');
-
-Main();
-
-function Main() {
- console.clear();
- const params = GetParams();
- console.log(params);
- var dbs = [];
-
-
- for (var i = 0; i < params.length; i++) {
- PrintLN();
- console.log(params[i] + ": ");
- try {
- dbs.push(ParseJSONData(utils.ReadFile(params[i])));
- console.log("JSON data added");
- } catch (e) {
- console.log(e);
- console.log("Trying with old format...");
- dbs.push(ReadData(utils.ReadFile(params[i])).result);
- }
- }
- PrintLN();
-
- dbs.forEach((item) => {
- PrintDB(item);
- });
-
- var olds = [];
- if (dbs.length == 1) {
- for ( let i = 0; i < dbs[0].length; i++)
- olds.push(dbs[0].Subjects[i].length);
- }
-
- console.log("Parsed data count: " + dbs.length);
- PrintLN();
-
- console.log("Merging databases...");
- var db = MergeDatabases(dbs);
-
- console.log("Removing duplicates...");
- var r = RemoveDuplicates(db);
-
- console.log("RESULT:");
- PrintDB(r, olds);
-
- utils.WriteFile(JSON.stringify(r), "newData");
- console.log("File written!");
-}
-
-function PrintLN() {
- console.log("------------------------------------------------------");
-}
-
-function PrintDB(r, olds) {
- console.log("Data subject count: " + r.length);
- var maxLength = 0;
- for (var i = 0; i < r.length; i++) {
- if (maxLength < r.Subjects[i].Name.length)
- maxLength = r.Subjects[i].Name.length;
- }
-
- let qcount = 0;
-
- for (var i = 0; i < r.length; i++) {
- let line = i;
- if (line < 10)
- line += ' ';
-
- line += ": ";
- var currLength = line.length + maxLength + 4;
- line += r.Subjects[i].Name;
-
- while (line.length < currLength) {
- if (i % 4 == 0)
- line += ".";
- else
- line += " ";
- }
-
- if (olds && olds.length > 0) {
- // TODO: check if correct row! should be now, but well...
- if (olds[i] < 10)
- line += " ";
- if (olds[i] < 100)
- line += " ";
-
- line += olds[i];
- line += " -> ";
- }
-
- if (r.Subjects[i].length < 10)
- line += " ";
- if (r.Subjects[i].length < 100)
- line += " ";
-
- line += r.Subjects[i].length;
- qcount += r.Subjects[i].length;
-
- line += " db";
-
- console.log(line);
- }
-
- console.log("Total questions: " + qcount);
-
- PrintLN();
-}
-
-function GetParams() {
- return process.argv.splice(2);
-}
-
-function ParseJSONData(data) {
- var d = JSON.parse(data);
- var r = new QuestionDB();
- var rt = [];
-
- 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
- });
- r.AddSubject(s);
- }
- return r;
-}
-
-function MergeDatabases(dbs) {
- var db = new QuestionDB();
- for (var i = 0; i < dbs.length; i++)
- for (var j = 0; j < dbs[i].length; j++)
- db.AddSubject(dbs[i].Subjects[j]);
- return db;
-}
-
-/*
- * Returns a question database from the given data.
- * Parameter should be raw read file in string with "\n"-s
- * TODO: ??? -s are not listed as errors, tho works correctly
- * */
-function ReadData(data) {
-
- const d = data.split("\n");
- const r = new QuestionDB();
- var logs = [];
- var currSubj = ""; // the current subjects name
- var ExpectedIdentifier = ['+', '?'];
- let currQuestion = new Question();
-
- var i = -1;
- while (i < d.length) {
- let currIdentifier;
- let skipped = 0;
- do {
- if (skipped >= 1)
- logs.push(i + ": " + d[i]);
- i++;
- if (i >= d.length) {
- if (currQuestion.IsComplete())
- r.AddQuestion(currSubj, currQuestion);
- return {
- result: r,
- logs: logs
- };
- }
- currIdentifier = d[i][0];
- skipped++;
- } while (!ExpectedIdentifier.includes(currIdentifier) && i < d.length);
-
- let currData = d[i].substring(1).trim();
-
- if (currIdentifier == '+') {
- if (currQuestion.IsComplete())
- r.AddQuestion(currSubj, currQuestion);
- currQuestion = new Question();
- currSubj = currData;
- ExpectedIdentifier = ['?'];
- continue;
- }
-
- if (currIdentifier == '?') {
- if (currQuestion.IsComplete()) {
- r.AddQuestion(currSubj, currQuestion);
- currQuestion = new Question();
- }
- // overwriting is allowed here, bcus:
- // ?????!>
- currQuestion.Q = currData;
- ExpectedIdentifier = ['!', '?'];
- continue;
- }
-
- if (currIdentifier == '!') {
- // if dont have question continue
- if (!currQuestion.HasQuestion())
- throw "No question! (A)";
- // dont allow overwriting
- // ?!!!!
- if (!currQuestion.HasAnswer()) {
- currData = currData.replace("A helyes válaszok: ", "");
- currData = currData.replace("A helyes válasz: ", "");
-
- currQuestion.A = currData;
- }
- ExpectedIdentifier = ['?', '>', '+'];
- continue;
- }
-
- if (currIdentifier == '>') {
- // if dont have question or answer continue
- if (!currQuestion.HasQuestion())
- throw "No question! (I)";
- if (!currQuestion.HasAnswer())
- throw "No asnwer! (I)";
- // dont allow overwriting
- // ?!>>>
- if (!currQuestion.HasImage()) {
- try {
- currQuestion.I = JSON.parse(currData);
- } catch (e) {
- currQuestion.I = currData.split(',');
- }
- }
- ExpectedIdentifier = ['?', '+'];
- continue;
- }
- }
-
- return {
- result: r,
- logs: logs
- };
-}
-
-function RemoveDuplicates(dataObj) {
- for (var i = 0; i < dataObj.length; i++)
- RemoveDuplFromSubject(dataObj.Subjects[i]);
- return dataObj;
-}
-
-function RemoveDuplFromSubject(subj) {
- var cp = subj.Questions;
- subj.Questions = [];
- for (var i = 0; i < cp.length; i++) {
- var j = 0;
- // Only removes 100% match!
- while (j < subj.length && cp[i].Compare(subj.Questions[j]) != 100) {
- j++;
- }
- if (j < subj.length) {
- //console.log("----------------------------------------------------------");
- //console.log(cp[i].toString());
- //console.log(" VS ");
- //console.log(subj.Questions[j].toString());
- //console.log(cp[i].Compare(subj.Questions[j]));
- //console.log(j);
- //console.log("removed:");
- //console.log(subj.Questions.splice(j, 1).toString());
- //console.log("----------------------------------------------------------");
- } else {
- subj.AddQuestion(cp[i]);
- }
- }
-}
-
-function SimplifyStringForComparison(value) {
- value = RemoveUnnecesarySpaces(value).toLowerCase();
- var removableChars = [",", ".", ":", "!"];
- for (var i = 0; i < removableChars.length; i++) {
- var regex = new RegExp(removableChars[i], "g");
- value.replace(regex, "");
- }
- return value;
-}
-
-function RemoveUnnecesarySpaces(toremove) {
- toremove = NormalizeSpaces(toremove);
- while (toremove.includes(" ")) // while the text includes double spaces replaces all of them with a single one
- {
- toremove = toremove.replace(/ /g, " ");
- }
- return toremove.trim();
-}
-
-function NormalizeSpaces(input) {
- return input.replace(/\s/g, ' ');
-}
diff --git a/modules/main.js b/modules/main.js
new file mode 100644
index 0000000..831e0d5
--- /dev/null
+++ b/modules/main.js
@@ -0,0 +1,75 @@
+/* ----------------------------------------------------------------------------
+
+ 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 .
+
+ ------------------------------------------------------------------------- */
+
+const express = require('express')
+const bodyParser = require('body-parser')
+const busboy = require('connect-busboy')
+const app = express()
+
+const logger = require('../utils/logger.js')
+// const utils = require('../utils/utils.js')
+// const actions = require('../utils/actions.js')
+const stat = require('../utils/stat.js')
+stat.Load()
+
+app.set('view engine', 'ejs')
+app.use(function (req, res, next) {
+ res.on('finish', function () {
+ logger.LogReq(req, true, res.statusCode)
+ if (res.statusCode !== 404) { stat.LogStat(req.url) }
+ })
+ next()
+})
+app.use(express.static('public'))
+app.use(busboy({
+ limits: {
+ fileSize: 10000 * 1024 * 1024
+ }
+}))
+app.use(bodyParser.json())
+app.use(bodyParser.urlencoded({
+ limit: '5mb',
+ extended: true
+}))
+app.use(bodyParser.json({
+ limit: '5mb'
+}))
+
+// --------------------------------------------------------------
+
+app.get('/', function (req, res) {
+ // res.render()
+ res.end('henlo')
+})
+
+app.get('*', function (req, res) {
+ res.render('shared/404')
+ res.status(404)
+ // utils.AppendToFile(logger.GetDateString() + ": " + "404 GET", logFile);
+})
+
+app.post('*', function (req, res) {
+ res.status(404)
+ // utils.AppendToFile(logger.GetDateString() + ": " + "404 POST", logFile);
+})
+
+exports.app = app
+
+logger.Log('Main module started', logger.GetColor('yellow'))
diff --git a/modules/qmining.js b/modules/qmining.js
new file mode 100644
index 0000000..1ed14ca
--- /dev/null
+++ b/modules/qmining.js
@@ -0,0 +1,226 @@
+/* ----------------------------------------------------------------------------
+
+ 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 .
+
+ ------------------------------------------------------------------------- */
+
+const siteUrl = 'https://qmining.frylabs.net' // http(s)//asd.basd
+
+const express = require('express')
+const bodyParser = require('body-parser')
+const busboy = require('connect-busboy')
+const fs = require('fs')
+const app = express()
+// const http = require('http')
+// const https = require('https')
+
+const logger = require('../utils/logger.js')
+const utils = require('../utils/utils.js')
+const actions = require('../utils/actions.js')
+const stat = require('../utils/stat.js')
+stat.Load()
+
+const recivedFiles = 'public/recivedfiles'
+const uloadFiles = 'public/f'
+const staticFile = 'public/data/static'
+const dataFile = 'public/data.json'
+const msgFile = 'stats/msgs'
+
+app.set('view engine', 'ejs')
+app.use(function (req, res, next) {
+ res.on('finish', function () {
+ logger.LogReq(req, true, res.statusCode)
+ if (res.statusCode !== 404) { stat.LogStat(req.url) }
+ })
+ next()
+})
+app.use(express.static('public'))
+app.use(busboy({
+ limits: {
+ fileSize: 10000 * 1024 * 1024
+ }
+}))
+app.use(bodyParser.json())
+app.use(bodyParser.urlencoded({
+ limit: '5mb',
+ extended: true
+}))
+app.use(bodyParser.json({
+ limit: '5mb'
+}))
+
+// --------------------------------------------------------------
+
+app.get('/', function (req, res) {
+ // req.hostname
+
+ res.render('qmining/main', {
+ siteurl: siteUrl,
+ qa: actions.ProcessQA()
+ })
+ res.end()
+})
+
+app.get('/manual', function (req, res) {
+ res.render('qmining/man')
+ res.end()
+ logger.LogReq(req)
+})
+
+app.get('/public', function (req, res) {
+ var response = '@Plz update :)'
+ response += utils.ReadFile(staticFile)
+ res.write(response)
+ res.end()
+})
+
+app.get('/static', function (req, res) {
+ var response = '@Plz update :)'
+ response += utils.ReadFile(staticFile)
+ res.write(response)
+ res.end()
+})
+
+app.get('/legacy', function (req, res) {
+ var f = utils.ReadFile(dataFile)
+ var d = actions.LoadJSON(f)
+ let qcount = 0
+ for (let i = 0; i < d.length; i++) { qcount += d.Subjects[i].length }
+ let scount = d.length
+
+ res.render('qmining/alldata', {
+ data: d,
+ scount: scount,
+ qcount: qcount,
+ siteurl: siteUrl
+ })
+
+ logger.LogReq(req)
+})
+
+app.post('/postfeedback', function (req, res) {
+ res.redirect('back')
+ logger.Log('New feedback message', logger.GetColor('bluebg'), true)
+ utils.AppendToFile('\n\n' + logger.GetDateString() + ': ' + req.body.message_field, msgFile)
+})
+
+app.get('/postfeedback', function (req, res) {
+ // TODO: res.redirect("/"); or if needs this anyways, becouse /postfeedback post handler already
+ // redirects
+ res.render('qmining/main', {
+ sdata: utils.ReadFile(staticFile)
+ })
+})
+
+app.post('/isAdding', function (req, res) {
+ res.end('OK')
+ logger.LogReq(req)
+ actions.ProcessIncomingRequest(req.body.datatoadd)
+ utils.WriteBackup()
+})
+
+app.get('/lred', function (req, res) {
+ res.redirect('/legacy')
+ res.end()
+ logger.LogReq(req)
+})
+
+app.get('/menuClick', function (req, res) {
+ res.redirect('/')
+ res.end()
+ logger.LogReq(req)
+})
+
+// all questions readable
+app.get('/allqr', function (req, res) {
+ var f = utils.ReadFile(dataFile)
+ var d = actions.LoadJSON(f)
+
+ res.render('qmining/allqr', {
+ d: d.toString().split('\n')
+ })
+ logger.LogReq(req)
+})
+
+app.get('/greasy', function (req, res) {
+ res.redirect('https://greasyfork.org/en/scripts/38999-moodle-elearning-kmooc-test-help')
+ res.end()
+ logger.LogReq(req)
+})
+
+app.get('/scriptgit', function (req, res) {
+ res.redirect('https://gitlab.com/YourFriendlyNeighborhoodDealer/moodle-test-userscript')
+ res.end()
+ logger.LogReq(req)
+})
+
+app.get('/servergit', function (req, res) {
+ res.redirect('https://gitlab.com/YourFriendlyNeighborhoodDealer/question-node-server')
+ res.end()
+ logger.LogReq(req)
+})
+
+function UploadFile (req, res, path, next) {
+ var fstream
+ req.pipe(req.busboy)
+ req.busboy.on('file', function (fieldname, file, filename) {
+ logger.Log('Uploading: ' + filename, logger.GetColor('blue'))
+
+ utils.CreatePath(path, true)
+ let d = new Date()
+ let fn = d.getHours() + '' + d.getMinutes() + '' + d.getSeconds() + '_' + filename
+
+ fstream = fs.createWriteStream(path + '/' + fn)
+ file.pipe(fstream)
+ fstream.on('close', function () {
+ logger.Log('Upload Finished of ' + path + '/' + fn, logger.GetColor('blue'))
+ next(fn)
+ })
+ fstream.on('error', function (err) {
+ console.log(err)
+ res.end('something bad happened :s')
+ })
+ })
+}
+
+app.route('/fosuploader').post(function (req, res, next) {
+ UploadFile(req, res, uloadFiles, (fn) => {
+ res.redirect('/f/' + fn)
+ })
+})
+
+app.route('/badtestsender').post(function (req, res, next) {
+ UploadFile(req, res, recivedFiles, (fn) => {
+ res.render('qmining/uploaded')
+ })
+ logger.LogReq(req)
+})
+
+app.get('*', function (req, res) {
+ res.render('shared/404')
+ res.status(404)
+ // utils.AppendToFile(logger.GetDateString() + ": " + "404 GET", logFile);
+})
+
+app.post('*', function (req, res) {
+ res.status(404)
+ // utils.AppendToFile(logger.GetDateString() + ": " + "404 POST", logFile);
+})
+
+exports.app = app
+
+logger.Log('Qmining module started', logger.GetColor('yellow'))
diff --git a/modules/sio.js b/modules/sio.js
new file mode 100644
index 0000000..bd6fceb
--- /dev/null
+++ b/modules/sio.js
@@ -0,0 +1,107 @@
+/* ----------------------------------------------------------------------------
+
+ 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 .
+
+ ------------------------------------------------------------------------- */
+
+const express = require('express')
+const bodyParser = require('body-parser')
+const busboy = require('connect-busboy')
+const fs = require('fs')
+const app = express()
+// const http = require('http')
+// const https = require('https')
+
+const logger = require('../utils/logger.js')
+const utils = require('../utils/utils.js')
+const stat = require('../utils/stat.js')
+stat.Load()
+
+const uloadFiles = './public/f'
+
+app.set('view engine', 'ejs')
+app.use(function (req, res, next) {
+ res.on('finish', function () {
+ logger.LogReq(req, true, res.statusCode)
+ if (res.statusCode !== 404) { stat.LogStat(req.url) }
+ })
+ next()
+})
+app.use(express.static('public'))
+app.use(busboy({
+ limits: {
+ fileSize: 10000 * 1024 * 1024
+ }
+}))
+app.use(bodyParser.json())
+app.use(bodyParser.urlencoded({
+ limit: '5mb',
+ extended: true
+}))
+app.use(bodyParser.json({
+ limit: '5mb'
+}))
+
+// --------------------------------------------------------------
+
+app.get('/', function (req, res) {
+ res.render('sio/uload')
+ res.end()
+})
+
+function UploadFile (req, res, path, next) {
+ var fstream
+ req.pipe(req.busboy)
+ req.busboy.on('file', function (fieldname, file, filename) {
+ logger.Log('Uploading: ' + filename, logger.GetColor('blue'))
+
+ utils.CreatePath(path, true)
+ let d = new Date()
+ let fn = d.getHours() + '' + d.getMinutes() + '' + d.getSeconds() + '_' + filename
+
+ fstream = fs.createWriteStream(path + '/' + fn)
+ file.pipe(fstream)
+ fstream.on('close', function () {
+ logger.Log('Upload Finished of ' + path + '/' + fn, logger.GetColor('blue'))
+ next(fn)
+ })
+ fstream.on('error', function (err) {
+ console.log(err)
+ res.end('something bad happened :s')
+ })
+ })
+}
+
+app.route('/fosuploader').post(function (req, res, next) {
+ UploadFile(req, res, uloadFiles, (fn) => {
+ res.redirect('/f/' + fn)
+ })
+})
+app.get('*', function (req, res) {
+ res.render('shared/404')
+ res.status(404)
+ // utils.AppendToFile(logger.GetDateString() + ": " + "404 GET", logFile);
+})
+
+app.post('*', function (req, res) {
+ res.status(404)
+ // utils.AppendToFile(logger.GetDateString() + ": " + "404 POST", logFile);
+})
+
+exports.app = app
+
+logger.Log('Sio module started', logger.GetColor('yellow'))
diff --git a/modules/stuff.js b/modules/stuff.js
new file mode 100644
index 0000000..1321594
--- /dev/null
+++ b/modules/stuff.js
@@ -0,0 +1,124 @@
+/* ----------------------------------------------------------------------------
+
+ 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 .
+
+ ------------------------------------------------------------------------- */
+
+const express = require('express')
+const bodyParser = require('body-parser')
+const busboy = require('connect-busboy')
+const fs = require('fs')
+const app = express()
+
+const logger = require('../utils/logger.js')
+// const utils = require('../utils/utils.js')
+// const actions = require('../utils/actions.js')
+const stat = require('../utils/stat.js')
+stat.Load()
+
+const listedFiles = './public/files'
+
+app.set('view engine', 'ejs')
+app.use(function (req, res, next) {
+ res.on('finish', function () {
+ logger.LogReq(req, true, res.statusCode)
+ if (res.statusCode !== 404) { stat.LogStat(req.url) }
+ })
+ next()
+})
+app.use(express.static('public'))
+app.use(busboy({
+ limits: {
+ fileSize: 10000 * 1024 * 1024
+ }
+}))
+app.use(bodyParser.json())
+app.use(bodyParser.urlencoded({
+ limit: '5mb',
+ extended: true
+}))
+app.use(bodyParser.json({
+ limit: '5mb'
+}))
+
+// --------------------------------------------------------------
+
+app.get('/*', function (req, res) {
+ let curr = listedFiles + '/' + req.url.substring('/stuff/'.length, req.url.length).split('?')[0]
+ let relPath = curr.substring('./public/files'.length, curr.length)
+
+ if (relPath[relPath.length - 1] !== '/') { relPath += '/' }
+
+ let t = relPath.split('/')
+ let prevDir = ''
+ for (let i = 0; i < t.length - 2; i++) { prevDir += t[i] + '/' }
+
+ // curr = curr.replace(/\//g, "/");
+ // relPath = relPath.replace(/\//g, "/");
+
+ logger.LogReq(req)
+
+ if (fs.lstatSync(curr).isDirectory()) {
+ if (curr[curr.length - 1] !== '/') { curr += '/' }
+
+ let f = []
+
+ fs.readdirSync(curr).forEach((item) => {
+ if (item[0] !== '.') {
+ let res = { name: item }
+ let stats = fs.statSync(curr + '/' + item)
+
+ let fileSizeInBytes = stats['size']
+ res.size = Math.round(fileSizeInBytes / 1000000)
+
+ res.path = relPath
+ if (res.path[res.path.length - 1] !== '/') { res.path += '/' }
+ res.path += item
+
+ res.mtime = stats['mtime'].toLocaleString()
+
+ f.push(res)
+ }
+ })
+
+ res.render('stuff/folders', {
+ folders: f,
+ dirname: relPath,
+ prevDir
+ })
+ } else {
+ let fileStream = fs.createReadStream(curr)
+ fileStream.pipe(res)
+ }
+})
+
+// -----------------------------------------------------------------------------------------------
+
+app.get('*', function (req, res) {
+ res.render('shared/404')
+ res.status(404)
+ // utils.AppendToFile(logger.GetDateString() + ": " + "404 GET", logFile);
+})
+
+app.post('*', function (req, res) {
+ res.status(404)
+ // utils.AppendToFile(logger.GetDateString() + ": " + "404 POST", logFile);
+})
+
+exports.app = app
+
+logger.Log('Stuff module started', logger.GetColor('yellow'))
diff --git a/motd.js b/motd.js
deleted file mode 100644
index 6a8bcc9..0000000
--- a/motd.js
+++ /dev/null
@@ -1,24 +0,0 @@
-const utils = require('./utils.js');
-const dataFile = "public/data.json";
-const versionFile = "public/version";
-const motdFile = "public/motd";
-
-var p = GetParams();
-if (p.length <= 0) {
- console.log("no params!");
- process.exit(0);
-}
-
-
-var param = p.join(" ");
-console.log("param: " + param);
-var d = utils.ReadFile(dataFile);
-var parsed = JSON.parse(d);
-parsed.motd = param;
-utils.WriteFile(JSON.stringify(parsed), dataFile);
-
-utils.WriteFile(parsed.motd, motdFile);
-
-function GetParams() {
- return process.argv.splice(2);
-}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..3471a4b
--- /dev/null
+++ b/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "node-ejs",
+ "main": "server.js",
+ "dependencies": {
+ "connect-busboy": "0.0.2",
+ "ejs": "^1.0.0",
+ "express": "^4.6.1",
+ "express-ejs-layouts": "^1.1.0",
+ "vhost": "^3.0.2"
+ }
+}
diff --git a/server.js b/server.js
index 536957b..5b61971 100644
--- a/server.js
+++ b/server.js
@@ -18,314 +18,47 @@
------------------------------------------------------------------------- */
-const startHTTPS = true
-const siteUrl = 'https://qmining.tk' // http(s)//asd.basd
-const ircURL = 'https://kiwiirc.com/nextclient/irc.sub.fm/#qmining'
-
const express = require('express')
-const bodyParser = require('body-parser')
-const busboy = require('connect-busboy')
-const fs = require('fs')
-const app = express()
-const http = require('http')
-const https = require('https')
+const vhost = require('vhost')
+const logger = require('./utils/logger.js')
-const logger = require('./logger.js')
-const utils = require('./utils.js')
-const actions = require('./actions.js')
-const stat = require('./stat.js')
-
-const listedFiles = './public/files'
-const recivedFiles = 'public/recivedfiles'
-const uloadFiles = 'public/f'
-const staticFile = 'public/data/static'
-const dataFile = 'public/data.json'
-const msgFile = 'stats/msgs'
-
-// https://certbot.eff.org/
-const privkeyFile = '/etc/letsencrypt/live/qmining.tk/privkey.pem'
-const fullchainFile = '/etc/letsencrypt/live/qmining.tk/fullchain.pem'
-const chainFile = '/etc/letsencrypt/live/qmining.tk/chain.pem'
-
-var certsLoaded = false
-if (startHTTPS && utils.FileExists(privkeyFile) && utils.FileExists(fullchainFile) && utils.FileExists(
- chainFile)) {
- try {
- const key = fs.readFileSync(privkeyFile, 'utf8')
- const cert = fs.readFileSync(fullchainFile, 'utf8')
- const ca = fs.readFileSync(chainFile, 'utf8')
- var certs = {
- key: key,
- cert: cert,
- ca: ca
- }
- certsLoaded = true
- } catch (e) {
- logger.Log('Error loading cert files!', logger.GetColor('redbg'))
- }
-}
+const qmining = require('./modules/qmining.js').app
+const main = require('./modules/main.js').app
+const sio = require('./modules/sio.js').app
+const stuff = require('./modules/stuff.js').app
const port = 8080
-const httpsPort = 8443
-app.set('view engine', 'ejs')
-app.use(function (req, res, next) {
- res.on('finish', function () {
- logger.LogReq(req, true, res.statusCode)
- if (res.statusCode !== 404) { stat.LogStat(req.url) }
- })
- next()
-})
-app.use(express.static('public'))
-app.use(busboy({
- limits: {
- fileSize: 10000 * 1024 * 1024
- }
-}))
-app.use(bodyParser.json())
-app.use(bodyParser.urlencoded({
- limit: '5mb',
- extended: true
-}))
-app.use(bodyParser.json({
- limit: '5mb'
-}))
+// // https://certbot.eff.org/
+// const privkeyFile = '/etc/letsencrypt/live/qmining.tk/privkey.pem'
+// const fullchainFile = '/etc/letsencrypt/live/qmining.tk/fullchain.pem'
+// const chainFile = '/etc/letsencrypt/live/qmining.tk/chain.pem'
+//
+// var certsLoaded = false
+// if (startHTTPS && utils.FileExists(privkeyFile) && utils.FileExists(fullchainFile) && utils.FileExists(
+// chainFile)) {
+// try {
+// const key = fs.readFileSync(privkeyFile, 'utf8')
+// const cert = fs.readFileSync(fullchainFile, 'utf8')
+// const ca = fs.readFileSync(chainFile, 'utf8')
+// var certs = {
+// key: key,
+// cert: cert,
+// ca: ca
+// }
+// certsLoaded = true
+// } catch (e) {
+// logger.Log('Error loading cert files!', logger.GetColor('redbg'))
+// }
+// }
-// --------------------------------------------------------------
+express()
+ .use(vhost('qmining.frylabs.net', qmining))
+ .use(vhost('sio.frylabs.net', sio))
+ .use(vhost('stuff.frylabs.net', stuff))
+ .use(vhost('frylabs.net', main))
+ .use(vhost('qmining.tk', qmining))
+ .listen(port)
-app.get('/', function (req, res) {
- // req.hostname
-
- res.render('main', {
- siteurl: siteUrl,
- qa: actions.ProcessQA()
- })
- res.end()
-})
-
-app.get('/sio', function (req, res) {
- res.render('uload')
- res.end()
-})
-
-app.get('/manual', function (req, res) {
- res.render('man')
- res.end()
- logger.LogReq(req)
-})
-
-app.get('/public', function (req, res) {
- var response = '@Plz update :)'
- response += utils.ReadFile(staticFile)
- res.write(response)
- res.end()
-})
-
-app.get('/static', function (req, res) {
- var response = '@Plz update :)'
- response += utils.ReadFile(staticFile)
- res.write(response)
- res.end()
-})
-
-app.get('/legacy', function (req, res) {
- var f = utils.ReadFile(dataFile)
- var d = actions.LoadJSON(f)
- let qcount = 0
- for (let i = 0; i < d.length; i++) { qcount += d.Subjects[i].length }
- let scount = d.length
-
- res.render('alldata', {
- data: d,
- scount: scount,
- qcount: qcount,
- siteurl: siteUrl
- })
-
- logger.LogReq(req)
-})
-
-app.post('/postfeedback', function (req, res) {
- res.redirect('back')
- logger.Log('New feedback message', logger.GetColor('bluebg'), true)
- utils.AppendToFile('\n\n' + logger.GetDateString() + ': ' + req.body.message_field, msgFile)
-})
-
-app.get('/postfeedback', function (req, res) {
- // TODO: res.redirect("/"); or if needs this anyways, becouse /postfeedback post handler already
- // redirects
- res.render('main', {
- sdata: utils.ReadFile(staticFile)
- })
-})
-
-app.post('/isAdding', function (req, res) {
- res.end('OK')
- logger.LogReq(req)
- actions.ProcessIncomingRequest(req.body.datatoadd)
- utils.WriteBackup()
-})
-
-app.get('/lred', function (req, res) {
- res.redirect('/legacy')
- res.end()
- logger.LogReq(req)
-})
-
-app.get('/menuClick', function (req, res) {
- res.redirect('/')
- res.end()
- logger.LogReq(req)
-})
-
-app.get('/irc', function (req, res) {
- res.redirect(ircURL)
- res.end()
- logger.LogReq(req)
-})
-
-// all questions readable
-app.get('/allqr', function (req, res) {
- var f = utils.ReadFile(dataFile)
- var d = actions.LoadJSON(f)
-
- res.render('allqr', {
- d: d.toString().split('\n')
- })
- logger.LogReq(req)
-})
-
-app.get('/greasy', function (req, res) {
- res.redirect('https://greasyfork.org/en/scripts/38999-moodle-elearning-kmooc-test-help')
- res.end()
- logger.LogReq(req)
-})
-
-app.get('/scriptgit', function (req, res) {
- res.redirect('https://gitlab.com/YourFriendlyNeighborhoodDealer/moodle-test-userscript')
- res.end()
- logger.LogReq(req)
-})
-
-app.get('/servergit', function (req, res) {
- res.redirect('https://gitlab.com/YourFriendlyNeighborhoodDealer/question-node-server')
- res.end()
- logger.LogReq(req)
-})
-
-function UploadFile (req, res, path, next) {
- var fstream
- req.pipe(req.busboy)
- req.busboy.on('file', function (fieldname, file, filename) {
- logger.Log('Uploading: ' + filename, logger.GetColor('blue'))
-
- utils.CreatePath(path, true)
- let d = new Date()
- let fn = d.getHours() + '' + d.getMinutes() + '' + d.getSeconds() + '_' + filename
-
- fstream = fs.createWriteStream(path + '/' + fn)
- file.pipe(fstream)
- fstream.on('close', function () {
- logger.Log('Upload Finished of ' + path + '/' + fn, logger.GetColor('blue'))
- next(fn)
- })
- fstream.on('error', function (err) {
- console.log(err)
- res.end('something bad happened :s')
- })
- })
-}
-
-app.route('/fosuploader').post(function (req, res, next) {
- UploadFile(req, res, uloadFiles, (fn) => {
- res.redirect('/f/' + fn)
- })
-})
-
-app.route('/badtestsender').post(function (req, res, next) {
- UploadFile(req, res, recivedFiles, (fn) => {
- res.render('uploaded')
- })
- logger.LogReq(req)
-})
-
-// -----------------------------------------------------------------------------------------------
-
-app.get('/stuff*', function (req, res) {
- let curr = listedFiles + '/' + req.url.substring('/stuff/'.length, req.url.length).split('?')[0]
- let relPath = curr.substring('./public/files'.length, curr.length)
-
- if (relPath[relPath.length - 1] !== '/') { relPath += '/' }
-
- let t = relPath.split('/')
- let prevDir = ''
- for (let i = 0; i < t.length - 2; i++) { prevDir += t[i] + '/' }
-
- // curr = curr.replace(/\//g, "/");
- // relPath = relPath.replace(/\//g, "/");
-
- logger.LogReq(req)
-
- if (fs.lstatSync(curr).isDirectory()) {
- if (curr[curr.length - 1] !== '/') { curr += '/' }
-
- let f = []
-
- fs.readdirSync(curr).forEach((item) => {
- if (item[0] !== '.') {
- let res = { name: item }
- let stats = fs.statSync(curr + '/' + item)
-
- let fileSizeInBytes = stats['size']
- res.size = Math.round(fileSizeInBytes / 1000000)
-
- res.path = relPath
- if (res.path[res.path.length - 1] !== '/') { res.path += '/' }
- res.path += item
-
- res.mtime = stats['mtime'].toLocaleString()
-
- f.push(res)
- }
- })
-
- res.render('folders', {
- folders: f,
- dirname: relPath,
- prevDir
- })
- } else {
- let fileStream = fs.createReadStream(curr)
- fileStream.pipe(res)
- }
-})
-
-// -----------------------------------------------------------------------------------------------
-
-app.get('*', function (req, res) {
- res.render('404')
- res.status(404)
- // utils.AppendToFile(logger.GetDateString() + ": " + "404 GET", logFile);
-})
-
-app.post('*', function (req, res) {
- res.status(404)
- // utils.AppendToFile(logger.GetDateString() + ": " + "404 POST", logFile);
-})
-
-var msg = ''
-stat.Load()
-
-const httpServer = http.createServer(app)
-httpServer.listen(port)
-msg += 'Server listening on port ' + port + ' (http)'
-
-if (startHTTPS && certsLoaded) {
- const httpsServer = https.createServer(certs, app)
- httpsServer.listen(httpsPort)
- msg += ', and ' + httpsPort + ' (https)...'
-} else {
- logger.Log('Cert files does not exists, starting http only!', logger.GetColor('redbg'))
-}
-logger.Log(msg, logger.GetColor('yellow'))
logger.Log('Node version: ' + process.version)
+logger.Log('Listening on port: ' + port)
diff --git a/stat.js b/stat.js
deleted file mode 100644
index 96bc53c..0000000
--- a/stat.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/* ----------------------------------------------------------------------------
-
- 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 = {
- LogStat: LogStat,
- Load: Load
-};
-
-var utils = require('./utils.js');
-var logger = require('./logger.js');
-
-const statFile = "stats/stats";
-const vStatFile = "stats/vstats";
-const writeInterval = 10;
-
-var data = {};
-var vData = {};
-var writes = 0;
-
-function Load() {
- try {
- var prevData = utils.ReadFile(statFile);
- data = JSON.parse(prevData);
- } catch (e) {
- logger.Log("Error at loading logs! (@ first run its normal)", logger.GetColor("redbg"));
- console.log(e);
- }
-
- try {
- var prevVData = utils.ReadFile(vStatFile);
- vData = JSON.parse(prevVData);
- } catch (e) {
- logger.Log("Error at loading visit logs! (@ first run its normal)", logger.GetColor("redbg"));
- console.log(e);
- }
-}
-
-function LogStat(url) {
- Inc(url);
- AddVisitStat(url);
- Save();
-}
-
-function Inc(value) {
- if (value.startsWith("/?"))
- value = "/";
- if (data[value] == undefined)
- data[value] = 0;
- data[value]++;
-}
-
-function AddVisitStat(name) {
- var m = new Date();
- const now = m.getFullYear() + "/" +
- ("0" + (m.getMonth() + 1)).slice(-2) + "/" +
- ("0" + m.getDate()).slice(-2);
- if (vData[now] == undefined)
- vData[now] = {};
- if (vData[now][name] == undefined)
- vData[now][name] = 0;
- vData[now][name]++;
-}
-
-function AddVisitStat(name) {
- var m = new Date();
- const now = m.getFullYear() + "/" +
- ("0" + (m.getMonth() + 1)).slice(-2) + "/" +
- ("0" + m.getDate()).slice(-2);
- if (vData[now] == undefined)
- vData[now] = {};
- if (vData[now][name] == undefined)
- vData[now][name] = 0;
- vData[now][name]++;
-}
-
-function Save() {
- writes++;
- if (writes == writeInterval) {
- try {
- utils.WriteFile(JSON.stringify(data), statFile);
- // logger.Log("Stats wrote.");
- } catch (e) {
- logger.Log("Error at writing logs!", logger.GetColor("redbg"));
- console.log(e);
- }
- try {
- utils.WriteFile(JSON.stringify(vData), vStatFile);
- // logger.Log("Stats wrote.");
- } catch (e) {
- logger.Log("Error at writing visit logs!", logger.GetColor("redbg"));
- console.log(e);
- }
- writes = 0;
- }
-}
diff --git a/utils.js b/utils.js
deleted file mode 100644
index e3f736b..0000000
--- a/utils.js
+++ /dev/null
@@ -1,109 +0,0 @@
-module.exports = {
- ReadFile: ReadFile,
- WriteFile: WriteFile,
- writeFileAsync: WriteFileAsync,
- AppendToFile: AppendToFile,
- Beep: Beep,
- WriteBackup: WriteBackup,
- FileExists: FileExists,
- CreatePath: CreatePath,
- GetAllFilesFromFolder: GetAllFilesFromFolder
-};
-
-var fs = require('fs');
-
-var logger = require('./logger.js');
-
-const recievedFile = "stats/recieved";
-const manFile = "public/man.html";
-const logFile = "stats/logs";
-const dataFile = "public/data.json";
-
-function ReadFile(name) {
- if (!FileExists(name))
- throw "No such file: " + name;
- return fs.readFileSync(name, "utf8");
-}
-
-function FileExists(path) {
- return fs.existsSync(path);
-}
-
-function CreatePath(path, onlyPath) {
- if (FileExists(path))
- return;
-
- var p = path.split("/");
- var currDir = p[0];
- for (var i = 1; i < p.length; i++) {
- if (currDir != "" && !fs.existsSync(currDir)) {
- try {
- fs.mkdirSync(currDir);
- } catch (e) { console.log("Failed to make " + currDir + " directory... "); }
- }
- currDir += "/" + p[i];
- }
- if (onlyPath == undefined || onlyPath == false)
- fs.writeFileSync(path, "");
- else
- fs.mkdirSync(path);
-
-}
-
-function WriteFile(content, path) {
- CreatePath(path);
- fs.writeFileSync(path, content);
-}
-
-function WriteFileAsync(content, path) {
- CreatePath(path);
- fs.writeFile(path, content, function(err) {
- if (err) {
- logger.Log("Error writing file: " + path + " (sync)", logger.GetColor("redbg"));
- }
- });
-}
-
-function AppendToFile(data, file) {
- CreatePath(file);
- fs.appendFile(file, "\n" + data, function(err) {
- if (err)
- logger.Log("Error writing log file: " + file + " (sync)", logger.GetColor("redbg"));
- });
-}
-
-function Beep() {
- try {
- process.stdout.write('\x07');
- } catch (e) {
- console.log("error beepin");
- }
-}
-
-function WriteBackup() {
- try {
- WriteFileAsync(ReadFile(dataFile), 'public/backs/data_' + new Date().toString());
- } catch (e) {
- logger.Log("Error backing up data json file!", logger.GetColor("redbg"));
- console.log(e);
- }
-}
-
-function GetAllFilesFromFolder(dir) {
-
- var results = [];
-
- fs.readdirSync(dir).forEach(function(file) {
-
- file = dir + '/' + file;
- var stat = fs.statSync(file);
-
- if (stat && stat.isDirectory()) {
- results = results.concat(_getAllFilesFromFolder(file));
- } else results.push(file);
-
- });
-
- return results;
-}
-
diff --git a/utils/actions.js b/utils/actions.js
new file mode 100644
index 0000000..e691c7c
--- /dev/null
+++ b/utils/actions.js
@@ -0,0 +1,496 @@
+/* ----------------------------------------------------------------------------
+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,
+ ProcessQA: ProcessQA
+}
+
+const staticFile = './public/data/static'
+const dataFile = './public/data.json'
+const recDataFile = './stats/recdata'
+const versionFile = './public/version'
+const motdFile = './public/motd'
+const qaFile = './public/qa'
+
+var logger = require('../utils/logger.js')
+var utils = require('../utils/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) {
+ const minMatchAmmount = 90
+ var r = []
+ for (let 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 (let 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 (let i = 0; i < this.length; i++) {
+ if (this.GetIfActive(i)) { r = r.concat(this.Subjects[i].Search(q, img)) }
+ }
+
+ for (let 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('File: ' + 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('\t\told public result: ' + oldRes.count, logger.GetColor('blue')) } else { logger.Log('\t\told public NLOD error, ' + oldRes.log, logger.GetColor('redbg'), true) }
+
+ if (newRes.count > 0) { logger.Log('\t\tnew file result: ' + newRes.count, logger.GetColor('blue')) } else { logger.Log('\t\tnew file NLOD error, ' + newRes.log, logger.GetColor('redbg'), true) }
+
+ utils.WriteFile(newFile, file)
+ logger.Log('\t\tNew data written to: ' + file)
+
+ return newRes.count - oldRes.count
+ } else { logger.Log('\t\tNo new data') }
+ } catch (e) {
+ logger.Log('\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('\tRecieved data is undefined!', logger.GetColor('redbg'))
+ return
+ }
+
+ try {
+ let towrite = logger.GetDateString() + '\n'
+ towrite += '------------------------------------------------------------------------------\n'
+ towrite += data
+ towrite += '\n------------------------------------------------------------------------------\n'
+ utils.AppendToFile(towrite, recDataFile)
+ } catch (e) {
+ logger.log('Error writing recieved data.')
+ }
+
+ try {
+ var d = JSON.parse(data)
+ var dfile = utils.ReadFile(dataFile)
+ data = LoadJSON(dfile)
+ var allQuestions = []
+ for (let 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 (let 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)
+ }
+
+ logger.Log('\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')
+
+ try {
+ data.version = utils.ReadFile(versionFile)
+ data.motd = utils.ReadFile(motdFile)
+ } catch (e) {
+ logger.Log('MOTD/Version writing/reading error!')
+ }
+
+ if (data !== undefined && d.data.length > 0) {
+ utils.WriteBackup()
+ utils.WriteFile(JSON.stringify(data), dataFile)
+ msg += ' - Data file written!'
+ color = logger.GetColor('blue')
+ }
+ logger.Log('\t' + msg, color)
+ } catch (e) {
+ logger.Log('Couldnt parse JSON data, trying old format...', logger.GetColor('redbg'))
+ 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('\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 newStatItems = Process(d, staticFile)
+
+ 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('\t' + file + ' All / New: ' + count + ' / ' + newItems, logger.GetColor('cyan'))
+ }
+}
+
+function SetupData (data) {
+ var pdata = data.split('<#>')
+ if (pdata.length <= 0) {
+ logger.Log('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('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('Load error, ' + e.toString(), logger.GetColor('redbg'), true)
+ return {
+ count: -1,
+ log: [e.toString()]
+ }
+ }
+}
+
+function NLoad (resource) {
+ var resultLog = []
+ 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) {
+ 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('Error loading sutff', logger.GetColor('redbg'), true)
+ }
+}
+
+function ProcessQA () {
+ if (!utils.FileExists(qaFile)) { utils.WriteFile('', qaFile) }
+
+ let a = utils.ReadFile(qaFile).split('\n')
+ let r = []
+ let ind = 0
+ for (let i = 0; i < a.length; i++) {
+ if (a[i] == '#') { ind++ } else {
+ if (r[ind] == undefined) { r[ind] = {} }
+
+ if (r[ind].q == undefined) {
+ r[ind].q = a[i]
+ } else {
+ if (r[ind].a == undefined) { r[ind].a = [] }
+
+ r[ind].a.push(a[i])
+ }
+ }
+ }
+
+ return r
+}
diff --git a/utils/changedataversion.js b/utils/changedataversion.js
new file mode 100644
index 0000000..94582e2
--- /dev/null
+++ b/utils/changedataversion.js
@@ -0,0 +1,31 @@
+const utils = require('../utils/utils.js')
+const dataFile = '../public/data.json'
+const versionFile = '../public/version'
+
+var p = GetParams()
+if (p.length <= 0) {
+ console.log('no params!')
+ process.exit(0)
+}
+
+var param = p.join(' ')
+
+console.log('param: ' + param)
+
+var d = utils.ReadFile(dataFile)
+var parsed = JSON.parse(d)
+
+console.log('Old version:')
+console.log(parsed.version)
+
+parsed.version = param
+
+console.log('New version:')
+console.log(parsed.version)
+
+utils.WriteFile(JSON.stringify(parsed), dataFile)
+utils.WriteFile(parsed.version, versionFile)
+
+function GetParams () {
+ return process.argv.splice(2)
+}
diff --git a/utils/logger.js b/utils/logger.js
new file mode 100644
index 0000000..e8f7bc2
--- /dev/null
+++ b/utils/logger.js
@@ -0,0 +1,90 @@
+/* ----------------------------------------------------------------------------
+
+ 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 = {
+ GetDateString: GetDateString,
+ Log: Log,
+ GetColor: GetColor,
+ LogReq: LogReq
+}
+
+const DELIM = '|'
+
+var utils = require('../utils/utils.js')
+const nlogFile = './stats/nlogs'
+const locLogFile = './stats/logs'
+const logFile = '/nlogs/nlogs'
+const allLogFile = '/nlogs/log'
+
+function GetDateString () {
+ var m = new Date()
+ return m.getFullYear() + '/' +
+ ('0' + (m.getMonth() + 1)).slice(-2) + '/' +
+ ('0' + m.getDate()).slice(-2) + ' ' +
+ ('0' + m.getHours()).slice(-2) + ':' +
+ ('0' + m.getMinutes()).slice(-2) + ':' +
+ ('0' + m.getSeconds()).slice(-2)
+}
+
+function Log (s, c, b) {
+ if (c != undefined) { console.log(c, GetDateString() + DELIM + s) } else { console.log(GetDateString() + DELIM + s) }
+
+ if (b) { utils.Beep() }
+
+ utils.AppendToFile(GetDateString() + DELIM + s, nlogFile)
+ utils.AppendToFile(GetDateString() + DELIM + s, logFile)
+}
+
+function LogReq (req, toFile, sc) {
+ try {
+ var ip = req.headers['cf-connecting-ip'] || req.connection.remoteAddress
+ var logEntry = ip + DELIM + req.hostname + DELIM + req.headers['user-agent'] +
+ DELIM + req.method + DELIM
+
+ logEntry += req.url
+
+ if (sc != undefined && sc == 404) { logEntry += DELIM + sc }
+ var color = GetColor('green')
+
+ if (req.url.toLowerCase().includes('isadding')) { color = GetColor('yellow') }
+ if (!toFile) {
+ Log(logEntry, color)
+ } else {
+ var defLogs = GetDateString() + DELIM + logEntry
+ var extraLogs = '\n\t' + JSON.stringify(req.headers) + '\n\t' + JSON.stringify(req.body) + '\n'
+
+ utils.AppendToFile(defLogs, locLogFile)
+ utils.AppendToFile(defLogs, allLogFile)
+ }
+ } catch (e) {
+ console.log(e)
+ Log('Error at logging lol', GetColor('redbg'), true)
+ }
+}
+
+function GetColor (c) {
+ if (c == 'redbg') { return '\x1b[41m%s\x1b[0m' }
+ if (c == 'bluebg') { return '\x1b[44m%s\x1b[0m' }
+ if (c == 'red') { return '\x1b[31m%s\x1b[0m' }
+ if (c == 'green') { return '\x1b[32m%s\x1b[0m' }
+ if (c == 'yellow') { return '\x1b[33m%s\x1b[0m' }
+ if (c == 'blue') { return '\x1b[34m%s\x1b[0m' }
+ if (c == 'cyan') { return '\x1b[36m%s\x1b[0m' }
+}
diff --git a/utils/merger.js b/utils/merger.js
new file mode 100644
index 0000000..b3b0e07
--- /dev/null
+++ b/utils/merger.js
@@ -0,0 +1,420 @@
+/* ----------------------------------------------------------------------------
+
+ Question Server question file merger
+ 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 .
+
+ ------------------------------------------------------------------------- */
+
+// TODO: handle flags
+// join json datas, or raw datas
+// or something else
+
+const minMatchAmmount = 55
+const minResultMatchPercent = 99
+const lengthDiffMultiplier = 10
+
+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()
+ }
+ // TODO: TEST DIS
+ 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) {
+ 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 * lengthDiffMultiplier
+ 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)
+ }
+ 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
+ }
+ 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)
+ }
+ }
+ 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')
+ }
+}
+var utils = require('./utils.js')
+var actions = require('./actions.js')
+
+Main()
+
+function Main () {
+ console.clear()
+ const params = GetParams()
+ console.log(params)
+ var dbs = []
+
+ for (var i = 0; i < params.length; i++) {
+ PrintLN()
+ console.log(params[i] + ': ')
+ try {
+ dbs.push(ParseJSONData(utils.ReadFile(params[i])))
+ console.log('JSON data added')
+ } catch (e) {
+ console.log(e)
+ console.log('Trying with old format...')
+ dbs.push(ReadData(utils.ReadFile(params[i])).result)
+ }
+ }
+ PrintLN()
+
+ dbs.forEach((item) => {
+ PrintDB(item)
+ })
+
+ var olds = []
+ if (dbs.length == 1) {
+ for (let i = 0; i < dbs[0].length; i++) { olds.push(dbs[0].Subjects[i].length) }
+ }
+
+ console.log('Parsed data count: ' + dbs.length)
+ PrintLN()
+
+ console.log('Merging databases...')
+ var db = MergeDatabases(dbs)
+
+ console.log('Removing duplicates...')
+ var r = RemoveDuplicates(db)
+
+ console.log('RESULT:')
+ PrintDB(r, olds)
+
+ utils.WriteFile(JSON.stringify(r), 'newData')
+ console.log('File written!')
+}
+
+function PrintLN () {
+ console.log('------------------------------------------------------')
+}
+
+function PrintDB (r, olds) {
+ console.log('Data subject count: ' + r.length)
+ var maxLength = 0
+ for (var i = 0; i < r.length; i++) {
+ if (maxLength < r.Subjects[i].Name.length) { maxLength = r.Subjects[i].Name.length }
+ }
+
+ let qcount = 0
+
+ for (var i = 0; i < r.length; i++) {
+ let line = i
+ if (line < 10) { line += ' ' }
+
+ line += ': '
+ var currLength = line.length + maxLength + 4
+ line += r.Subjects[i].Name
+
+ while (line.length < currLength) {
+ if (i % 4 == 0) { line += '.' } else { line += ' ' }
+ }
+
+ if (olds && olds.length > 0) {
+ // TODO: check if correct row! should be now, but well...
+ if (olds[i] < 10) { line += ' ' }
+ if (olds[i] < 100) { line += ' ' }
+
+ line += olds[i]
+ line += ' -> '
+ }
+
+ if (r.Subjects[i].length < 10) { line += ' ' }
+ if (r.Subjects[i].length < 100) { line += ' ' }
+
+ line += r.Subjects[i].length
+ qcount += r.Subjects[i].length
+
+ line += ' db'
+
+ console.log(line)
+ }
+
+ console.log('Total questions: ' + qcount)
+
+ PrintLN()
+}
+
+function GetParams () {
+ return process.argv.splice(2)
+}
+
+function ParseJSONData (data) {
+ var d = JSON.parse(data)
+ var r = new QuestionDB()
+ var rt = []
+
+ 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
+ })
+ r.AddSubject(s)
+ }
+ return r
+}
+
+function MergeDatabases (dbs) {
+ var db = new QuestionDB()
+ for (var i = 0; i < dbs.length; i++) {
+ for (var j = 0; j < dbs[i].length; j++) { db.AddSubject(dbs[i].Subjects[j]) }
+ }
+ return db
+}
+
+/*
+ * Returns a question database from the given data.
+ * Parameter should be raw read file in string with "\n"-s
+ * TODO: ??? -s are not listed as errors, tho works correctly
+ * */
+function ReadData (data) {
+ const d = data.split('\n')
+ const r = new QuestionDB()
+ var logs = []
+ var currSubj = '' // the current subjects name
+ var ExpectedIdentifier = ['+', '?']
+ let currQuestion = new Question()
+
+ var i = -1
+ while (i < d.length) {
+ let currIdentifier
+ let skipped = 0
+ do {
+ if (skipped >= 1) { logs.push(i + ': ' + d[i]) }
+ i++
+ if (i >= d.length) {
+ if (currQuestion.IsComplete()) { r.AddQuestion(currSubj, currQuestion) }
+ return {
+ result: r,
+ logs: logs
+ }
+ }
+ currIdentifier = d[i][0]
+ skipped++
+ } while (!ExpectedIdentifier.includes(currIdentifier) && i < d.length)
+
+ let currData = d[i].substring(1).trim()
+
+ if (currIdentifier == '+') {
+ if (currQuestion.IsComplete()) { r.AddQuestion(currSubj, currQuestion) }
+ currQuestion = new Question()
+ currSubj = currData
+ ExpectedIdentifier = ['?']
+ continue
+ }
+
+ if (currIdentifier == '?') {
+ if (currQuestion.IsComplete()) {
+ r.AddQuestion(currSubj, currQuestion)
+ currQuestion = new Question()
+ }
+ // overwriting is allowed here, bcus:
+ // ?????!>
+ currQuestion.Q = currData
+ ExpectedIdentifier = ['!', '?']
+ continue
+ }
+
+ if (currIdentifier == '!') {
+ // if dont have question continue
+ if (!currQuestion.HasQuestion()) { throw 'No question! (A)' }
+ // dont allow overwriting
+ // ?!!!!
+ if (!currQuestion.HasAnswer()) {
+ currData = currData.replace('A helyes válaszok: ', '')
+ currData = currData.replace('A helyes válasz: ', '')
+
+ currQuestion.A = currData
+ }
+ ExpectedIdentifier = ['?', '>', '+']
+ continue
+ }
+
+ if (currIdentifier == '>') {
+ // if dont have question or answer continue
+ if (!currQuestion.HasQuestion()) { throw 'No question! (I)' }
+ if (!currQuestion.HasAnswer()) { throw 'No asnwer! (I)' }
+ // dont allow overwriting
+ // ?!>>>
+ if (!currQuestion.HasImage()) {
+ try {
+ currQuestion.I = JSON.parse(currData)
+ } catch (e) {
+ currQuestion.I = currData.split(',')
+ }
+ }
+ ExpectedIdentifier = ['?', '+']
+ continue
+ }
+ }
+
+ return {
+ result: r,
+ logs: logs
+ }
+}
+
+function RemoveDuplicates (dataObj) {
+ for (var i = 0; i < dataObj.length; i++) { RemoveDuplFromSubject(dataObj.Subjects[i]) }
+ return dataObj
+}
+
+function RemoveDuplFromSubject (subj) {
+ var cp = subj.Questions
+ subj.Questions = []
+ for (var i = 0; i < cp.length; i++) {
+ var j = 0
+ // Only removes 100% match!
+ while (j < subj.length && cp[i].Compare(subj.Questions[j]) != 100) {
+ j++
+ }
+ if (j < subj.length) {
+ // console.log("----------------------------------------------------------");
+ // console.log(cp[i].toString());
+ // console.log(" VS ");
+ // console.log(subj.Questions[j].toString());
+ // console.log(cp[i].Compare(subj.Questions[j]));
+ // console.log(j);
+ // console.log("removed:");
+ // console.log(subj.Questions.splice(j, 1).toString());
+ // console.log("----------------------------------------------------------");
+ } else {
+ subj.AddQuestion(cp[i])
+ }
+ }
+}
+
+function SimplifyStringForComparison (value) {
+ value = RemoveUnnecesarySpaces(value).toLowerCase()
+ var removableChars = [',', '.', ':', '!']
+ for (var i = 0; i < removableChars.length; i++) {
+ var regex = new RegExp(removableChars[i], 'g')
+ value.replace(regex, '')
+ }
+ return value
+}
+
+function RemoveUnnecesarySpaces (toremove) {
+ toremove = NormalizeSpaces(toremove)
+ while (toremove.includes(' ')) // while the text includes double spaces replaces all of them with a single one
+ {
+ toremove = toremove.replace(/ {2}/g, ' ')
+ }
+ return toremove.trim()
+}
+
+function NormalizeSpaces (input) {
+ return input.replace(/\s/g, ' ')
+}
diff --git a/utils/motd.js b/utils/motd.js
new file mode 100644
index 0000000..6f50a6a
--- /dev/null
+++ b/utils/motd.js
@@ -0,0 +1,23 @@
+const utils = require('../utils/utils.js')
+const dataFile = '../public/data.json'
+const versionFile = '../public/version'
+const motdFile = '../public/motd'
+
+var p = GetParams()
+if (p.length <= 0) {
+ console.log('no params!')
+ process.exit(0)
+}
+
+var param = p.join(' ')
+console.log('param: ' + param)
+var d = utils.ReadFile(dataFile)
+var parsed = JSON.parse(d)
+parsed.motd = param
+utils.WriteFile(JSON.stringify(parsed), dataFile)
+
+utils.WriteFile(parsed.motd, motdFile)
+
+function GetParams () {
+ return process.argv.splice(2)
+}
diff --git a/utils/stat.js b/utils/stat.js
new file mode 100644
index 0000000..2539ec7
--- /dev/null
+++ b/utils/stat.js
@@ -0,0 +1,96 @@
+/* ----------------------------------------------------------------------------
+
+ 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 = {
+ LogStat: LogStat,
+ Load: Load
+}
+
+var utils = require('../utils/utils.js')
+var logger = require('../utils/logger.js')
+
+const statFile = 'stats/stats'
+const vStatFile = 'stats/vstats'
+const writeInterval = 10
+
+var data = {}
+var vData = {}
+var writes = 0
+
+function Load () {
+ try {
+ var prevData = utils.ReadFile(statFile)
+ data = JSON.parse(prevData)
+ } catch (e) {
+ logger.Log('Error at loading logs! (@ first run its normal)', logger.GetColor('redbg'))
+ console.log(e)
+ }
+
+ try {
+ var prevVData = utils.ReadFile(vStatFile)
+ vData = JSON.parse(prevVData)
+ } catch (e) {
+ logger.Log('Error at loading visit logs! (@ first run its normal)', logger.GetColor('redbg'))
+ console.log(e)
+ }
+}
+
+function LogStat (url) {
+ Inc(url)
+ AddVisitStat(url)
+ Save()
+}
+
+function Inc (value) {
+ if (value.startsWith('/?')) { value = '/' }
+ if (data[value] == undefined) { data[value] = 0 }
+ data[value]++
+}
+
+function AddVisitStat (name) {
+ var m = new Date()
+ const now = m.getFullYear() + '/' +
+ ('0' + (m.getMonth() + 1)).slice(-2) + '/' +
+ ('0' + m.getDate()).slice(-2)
+ if (vData[now] == undefined) { vData[now] = {} }
+ if (vData[now][name] == undefined) { vData[now][name] = 0 }
+ vData[now][name]++
+}
+
+function Save () {
+ writes++
+ if (writes == writeInterval) {
+ try {
+ utils.WriteFile(JSON.stringify(data), statFile)
+ // logger.Log("Stats wrote.");
+ } catch (e) {
+ logger.Log('Error at writing logs!', logger.GetColor('redbg'))
+ console.log(e)
+ }
+ try {
+ utils.WriteFile(JSON.stringify(vData), vStatFile)
+ // logger.Log("Stats wrote.");
+ } catch (e) {
+ logger.Log('Error at writing visit logs!', logger.GetColor('redbg'))
+ console.log(e)
+ }
+ writes = 0
+ }
+}
diff --git a/utils/utils.js b/utils/utils.js
new file mode 100644
index 0000000..86f5e78
--- /dev/null
+++ b/utils/utils.js
@@ -0,0 +1,98 @@
+module.exports = {
+ ReadFile: ReadFile,
+ WriteFile: WriteFile,
+ writeFileAsync: WriteFileAsync,
+ AppendToFile: AppendToFile,
+ Beep: Beep,
+ WriteBackup: WriteBackup,
+ FileExists: FileExists,
+ CreatePath: CreatePath,
+ GetAllFilesFromFolder: GetAllFilesFromFolder
+}
+
+var fs = require('fs')
+
+var logger = require('../utils/logger.js')
+
+const recievedFile = './stats/recieved'
+const manFile = './public/man.html'
+const logFile = './stats/logs'
+const dataFile = './public/data.json'
+
+function ReadFile (name) {
+ if (!FileExists(name)) { throw 'No such file: ' + name }
+ return fs.readFileSync(name, 'utf8')
+}
+
+function FileExists (path) {
+ return fs.existsSync(path)
+}
+
+function CreatePath (path, onlyPath) {
+ if (FileExists(path)) { return }
+
+ var p = path.split('/')
+ var currDir = p[0]
+ for (var i = 1; i < p.length; i++) {
+ if (currDir != '' && !fs.existsSync(currDir)) {
+ try {
+ fs.mkdirSync(currDir)
+ } catch (e) { console.log('Failed to make ' + currDir + ' directory... ') }
+ }
+ currDir += '/' + p[i]
+ }
+ if (onlyPath == undefined || onlyPath == false) { fs.writeFileSync(path, '') } else { fs.mkdirSync(path) }
+}
+
+function WriteFile (content, path) {
+ CreatePath(path)
+ fs.writeFileSync(path, content)
+}
+
+function WriteFileAsync (content, path) {
+ CreatePath(path)
+ fs.writeFile(path, content, function (err) {
+ if (err) {
+ logger.Log('Error writing file: ' + path + ' (sync)', logger.GetColor('redbg'))
+ }
+ })
+}
+
+function AppendToFile (data, file) {
+ CreatePath(file)
+ fs.appendFile(file, '\n' + data, function (err) {
+ if (err) { logger.Log('Error writing log file: ' + file + ' (sync)', logger.GetColor('redbg')) }
+ })
+}
+
+function Beep () {
+ try {
+ process.stdout.write('\x07')
+ } catch (e) {
+ console.log('error beepin')
+ }
+}
+
+function WriteBackup () {
+ try {
+ WriteFileAsync(ReadFile(dataFile), 'public/backs/data_' + new Date().toString())
+ } catch (e) {
+ logger.Log('Error backing up data json file!', logger.GetColor('redbg'))
+ console.log(e)
+ }
+}
+
+function GetAllFilesFromFolder (dir) {
+ var results = []
+
+ fs.readdirSync(dir).forEach(function (file) {
+ file = dir + '/' + file
+ var stat = fs.statSync(file)
+
+ if (stat && stat.isDirectory()) {
+ results = results.concat(_getAllFilesFromFolder(file))
+ } else results.push(file)
+ })
+
+ return results
+}
diff --git a/views/alldata.ejs b/views/qmining/alldata.ejs
similarity index 100%
rename from views/alldata.ejs
rename to views/qmining/alldata.ejs
diff --git a/views/allqr.ejs b/views/qmining/allqr.ejs
similarity index 100%
rename from views/allqr.ejs
rename to views/qmining/allqr.ejs
diff --git a/views/aludni.ejs b/views/qmining/aludni.ejs
similarity index 100%
rename from views/aludni.ejs
rename to views/qmining/aludni.ejs
diff --git a/views/b.ejs b/views/qmining/b.ejs
similarity index 100%
rename from views/b.ejs
rename to views/qmining/b.ejs
diff --git a/views/main.ejs b/views/qmining/main.ejs
similarity index 100%
rename from views/main.ejs
rename to views/qmining/main.ejs
diff --git a/views/man.ejs b/views/qmining/man.ejs
similarity index 100%
rename from views/man.ejs
rename to views/qmining/man.ejs
diff --git a/views/qa.ejs b/views/qmining/qa.ejs
similarity index 100%
rename from views/qa.ejs
rename to views/qmining/qa.ejs
diff --git a/views/uploaded.ejs b/views/qmining/uploaded.ejs
similarity index 100%
rename from views/uploaded.ejs
rename to views/qmining/uploaded.ejs
diff --git a/views/404.ejs b/views/shared/404.ejs
similarity index 100%
rename from views/404.ejs
rename to views/shared/404.ejs
diff --git a/views/uload.ejs b/views/sio/uload.ejs
similarity index 100%
rename from views/uload.ejs
rename to views/sio/uload.ejs
diff --git a/views/stuff/folders.ejs b/views/stuff/folders.ejs
new file mode 100644
index 0000000..ea6b70f
--- /dev/null
+++ b/views/stuff/folders.ejs
@@ -0,0 +1,96 @@
+
+
+
+
+
+ <%=dirname%>
+
+
+
+
+
+ <%=dirname%>
+
+
+ > Up one level
+
+
+
+ <% for (var i = 0; i < folders.length; i++) { %>
+
+
+
+ |
+
+ <% } %>
+
+
+