const http = require("http"); const convertFileFormat = require("../src/fileFormatConverter"); const httpServer = require("../src/httpServer"); const parseURL = require("../src/parseURL"); const parseJokes = require("../src/parseJokes"); const languages = require("../src/languages"); const tr = require("../src/translate"); const FilteredJoke = require("../src/classes/FilteredJoke"); const jsl = require("svjsl"); const settings = require("../settings"); jsl.unused(http); const meta = { "name": "Joke", "desc": "Returns a joke from the specified category / categories that is also matching the provided (optional) filters", "usage": { "method": "GET", "url": `${settings.info.docsURL}/joke/{CATEGORY}`, "supportedParams": [ "safe-mode", "format", "blacklistFlags", "type", "contains", "idRange", "lang", "amount" ] } }; /** * Calls this endpoint * @param {http.IncomingMessage} req The HTTP server request * @param {http.ServerResponse} res The HTTP server response * @param {Array} url URL path array gotten from the URL parser module * @param {Object} params URL query params gotten from the URL parser module * @param {String} format The file format to respond with */ const call = (req, res, url, params, format) => { jsl.unused([req, url]); let filterJoke = new FilteredJoke(parseJokes.allJokes); //#SECTION category validation let category = (url[settings.httpServer.urlPathOffset + 1]|| "(empty)").toLowerCase() || ""; let includesSplitChar = false; settings.jokes.splitChars.forEach(splC => { if(!jsl.isEmpty(category) && category.includes(splC)) includesSplitChar = true; }); if(includesSplitChar) category = category.split(settings.jokes.splitCharRegex); // resolve category aliases if(Array.isArray(category)) category = parseJokes.resolveCategoryAliases(category); else category = parseJokes.resolveCategoryAlias(category); let categoryValid = false; [settings.jokes.possible.anyCategoryName, ...settings.jokes.possible.categories].forEach(cat => { if(typeof category == "string") { if(category.toLowerCase() == cat.toLowerCase()) categoryValid = true; } else if(Array.isArray(category)) { if(category.map(c => c.toLowerCase()).includes(cat.toLowerCase())) categoryValid = true; } }); let fCat = false; if(!Array.isArray(category)) fCat = filterJoke.setAllowedCategories([category]); else fCat = filterJoke.setAllowedCategories(category); let langCode = settings.languages.defaultLanguage; //#SECTION language if(params && !jsl.isEmpty(params["lang"])) { try { langCode = params["lang"].toString(); if(languages.isValidLang(langCode) === true) filterJoke.setLanguage(langCode); else return isErrored(res, format, tr(langCode, "invalidLangCode", langCode), langCode); } catch(err) { return isErrored(res, format, tr(langCode, "invalidLangCodeNoArg"), langCode); } } //#SECTION safe mode if(params && !jsl.isEmpty(params["safe-mode"]) && params["safe-mode"] === true) filterJoke.setSafeMode(true); if(!fCat || !categoryValid) { let avlCats = [settings.jokes.possible.anyCategoryName, ...settings.jokes.possible.categories].join(", "); let catName = category.length == undefined || typeof category != "object" ? category : category.join(", "); return isErrored(res, format, tr(langCode, "invalidCategory", catName, avlCats), langCode); } let jokeAmount = 1; if(!jsl.isEmpty(params)) { //#SECTION type if(!jsl.isEmpty(params["type"]) && settings.jokes.possible.types.map(t => t.toLowerCase()).includes(params["type"].toLowerCase())) { if(!filterJoke.setAllowedType(params["type"].toLowerCase())) return isErrored(res, format, tr(langCode, "invalidType", params["type"], settings.jokes.possible.types.join(", ")), langCode); } //#SECTION contains if(!jsl.isEmpty(params["contains"])) { if(!filterJoke.setSearchString(params["contains"].toLowerCase())) return isErrored(res, format, tr(langCode, "invalidType", params["type"], settings.jokes.possible.types.join(", ")), langCode); } //#SECTION idRange if(!jsl.isEmpty(params["idRange"])) { try { if(params["idRange"].match(settings.jokes.splitCharRegex)) { let splitParams = params["idRange"].split(settings.jokes.splitCharRegex); if(!splitParams[0] && splitParams[1]) splitParams[0] = splitParams[1]; if(!splitParams[1] && splitParams[0]) splitParams[1] = splitParams[0]; if(!filterJoke.setIdRange(parseInt(splitParams[0]), parseInt(splitParams[1]))) return isErrored(res, format, tr(langCode, "idRangeInvalid", splitParams[0], splitParams[1], (parseJokes.jokeCountPerLang[langCode] - 1)), langCode); } else { let id = parseInt(params["idRange"]); if(!filterJoke.setIdRange(id, id, langCode)) return isErrored(res, format, tr(langCode, "idRangeInvalidSingle", params["idRange"], (parseJokes.jokeCountPerLang[langCode] - 1)), langCode); } } catch(err) { return isErrored(res, format, tr(langCode, "idRangeInvalidGeneric", err), langCode); } } //#SECTION blacklistFlags if(!jsl.isEmpty(params["blacklistFlags"])) { let flags = params["blacklistFlags"].split(settings.jokes.splitCharRegex) || []; let erroredFlags = []; flags.forEach(fl => { if(!settings.jokes.possible.flags.includes(fl)) erroredFlags.push(fl); }); if(erroredFlags.length > 0) return isErrored(res, format, tr(langCode, "invalidFlags", flags.join(", "), settings.jokes.possible.flags.join(", ")), langCode); let fFlg = filterJoke.setBlacklistFlags(flags); if(!fFlg) return isErrored(res, format, tr(langCode, "invalidFlags", flags.join(", "), settings.jokes.possible.flags.join(", ")), langCode); } //#SECTION amount if(!jsl.isEmpty(params["amount"])) { jokeAmount = parseInt(params["amount"]); if(isNaN(jokeAmount) || jokeAmount < 1) jokeAmount = 1; if(jokeAmount > settings.jokes.maxAmount) jokeAmount = settings.jokes.maxAmount; let fAmt = filterJoke.setAmount(jokeAmount); if(!fAmt) return isErrored(res, format, tr(langCode, "amountInternalError", fAmt), langCode); } } filterJoke.getJokes(filterJoke.getAmount()).then(jokesArray => { let responseText = ""; if(jokeAmount == 1) { let singleObj = { error: false, ...jokesArray[0] }; responseText = convertFileFormat.auto(format, singleObj, langCode); } else { let multiObj = {}; if(format != "xml") { multiObj = { error: false, amount: (jokesArray.length || 1), jokes: jokesArray }; } else { multiObj = { error: false, amount: (jokesArray.length || 1), jokes: { "joke": jokesArray } }; } responseText = convertFileFormat.auto(format, multiObj, langCode); } if(jokeAmount > settings.jokes.encodeAmount) httpServer.tryServeEncoded(req, res, responseText, parseURL.getMimeTypeFromFileFormatString(format)); else httpServer.pipeString(res, responseText, parseURL.getMimeTypeFromFileFormatString(format)); }).catch(err => { return isErrored(res, format, tr(langCode, "errorWhileFinalizing", Array.isArray(err) ? err.join("; ") : err), langCode); }); }; /** * Responds with a preformatted error message * @param {http.ServerResponse} res * @param {String} format * @param {String} msg * @param {String} lang 2-char lang code * @param {...any} args Arguments to replace numbered %-placeholders with. Only use objects that are strings or convertable to them with `.toString()`! */ const isErrored = (res, format, msg, lang, ...args) => { let errFromRegistry = require("." + settings.errors.errorMessagesPath)["106"]; let errorObj = {}; let insArgs = (texts, insertions) => { if(!Array.isArray(insertions) || insertions.length <= 0) return texts; insertions.forEach((ins, i) => { if(Array.isArray(texts)) texts = texts.map(tx => tx.replace(`%${i + 1}`, ins)); else if(typeof texts == "string") texts = texts.replace(`%${i + 1}`, ins); }); return texts; }; if(format != "xml") { errorObj = { error: true, internalError: false, code: 106, message: insArgs(errFromRegistry.errorMessage[lang], args) || insArgs(errFromRegistry.errorMessage[settings.languages.defaultLanguage], args), causedBy: insArgs(errFromRegistry.causedBy[lang], args) || insArgs(errFromRegistry.causedBy[settings.languages.defaultLanguage], args), additionalInfo: msg, timestamp: new Date().getTime() }; } else if(format == "xml") { errorObj = { error: true, internalError: false, code: 106, message: insArgs(errFromRegistry.errorMessage[lang], args) || insArgs(errFromRegistry.errorMessage[settings.languages.defaultLanguage], args), causedBy: {"cause": insArgs(errFromRegistry.causedBy[lang], args) || insArgs(errFromRegistry.causedBy[settings.languages.defaultLanguage], args)}, additionalInfo: msg, timestamp: new Date().getTime() }; } let responseText = convertFileFormat.auto(format, errorObj, lang); httpServer.pipeString(res, responseText, parseURL.getMimeTypeFromFileFormatString(format), 400); }; module.exports = { meta, call };