diff --git a/examples/bot.js b/examples/bot.js index 5cf7777..50f3cd0 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -1,6 +1,6 @@ -import { Lottus } from "../index.js"; +import { lottus } from "../index.js"; -let input = new Lottus(); +let input = lottus(); input.at("main", // info diff --git a/examples/inputs.js b/examples/inputs.js index 0989416..edb9b0a 100644 --- a/examples/inputs.js +++ b/examples/inputs.js @@ -1,6 +1,6 @@ -import { Lottus } from "../index.js"; +import { lottus, redirect } from "../index.js"; -let input = new Lottus(); +let input = lottus(); input.info("main", async (_, res) => { res.form = {input: "name"}; @@ -14,7 +14,7 @@ input.form("main", async (req, res) => { if(req.prompt && req.prompt.trim().length > 0){ req.parameters = {name: req.prompt}; - res = await input.redirect("profession", req); + res = await redirect("profession", req); }else { res.body = "Name cannot be empty"; } @@ -34,7 +34,7 @@ input.form("profession", async (req, res) => { if(req.prompt.trim().length > 0){ req.parameters = {profession: req.prompt}; - res = await input.redirect("show_details", req); + res = await redirect("show_details", req); } else { res.body = "Name cannot be empty" } @@ -46,7 +46,7 @@ input.info("show_details", async (req, res) => { res.form = false; for(const key in req.parameters){ - res.body += "[" + key + ":" + req.parameters[key] + "]"; + res.body = "[" + key + ":" + req.parameters[key] + "]"; } return res; diff --git a/examples/options.js b/examples/options.js index 7568fe1..18f2473 100644 --- a/examples/options.js +++ b/examples/options.js @@ -1,6 +1,6 @@ -import { Lottus } from "../index.js"; +import { format_message, lottus, redirect } from "../index.js"; -let options = new Lottus(); +let options = new lottus(); options.info("main", async (_, msg) => { msg.form = {input: "options"}; @@ -16,11 +16,11 @@ options.form("main", async (req, msg) => { msg.body += "\nUnknow option"; if(req.prompt.trim() === "1"){ - msg = options.redirect("name"); + msg = redirect("name"); } if(req.prompt.trim() === "2"){ - msg = options.redirect("profession"); + msg = redirect("profession"); } return msg; @@ -40,7 +40,7 @@ options.form("name", async (req, msg) => { msg.body += "\nUnknow option"; if(req.prompt.trim() === "0"){ - msg = options.redirect("main"); + msg = redirect("main"); } return msg; @@ -59,7 +59,7 @@ options.form("profession", async (req, msg) => { msg.body += "\nUnknow option"; if(req.prompt.trim() === "0"){ - msg = options.redirect("main"); + msg = redirect("main"); } return msg; @@ -75,4 +75,4 @@ message = await options.process({prompt:"0"}, message); console.log(message); message = await options.process({prompt:"2"}, message); -console.log(message); \ No newline at end of file +console.log(format_message(message)); \ No newline at end of file diff --git a/examples/reverse.js b/examples/reverse.js index 9ee5e5d..125372a 100644 --- a/examples/reverse.js +++ b/examples/reverse.js @@ -1,6 +1,6 @@ -import { Lottus } from "../index.js"; +import { lottus, format_message } from "../index.js"; -let bot = new Lottus(); +let bot = lottus(); bot.info("main", async (req, res) => { res.body = req.sentence.split("").reverse().join(""); @@ -8,4 +8,4 @@ bot.info("main", async (req, res) => { return res; }); -console.log((await bot.process({sentence: "hello world"}))); +console.log(format_message(await bot.process({sentence: "hello world"}))); diff --git a/examples/reverse_with_params.js b/examples/reverse_with_params.js index d60738b..0d5ce0d 100644 --- a/examples/reverse_with_params.js +++ b/examples/reverse_with_params.js @@ -1,6 +1,6 @@ -import { Lottus } from "../index.js"; +import { lottus } from "../index.js"; -let reverse = new Lottus(); +let reverse = new lottus(); reverse.info("main", async (req, res) => { res.body = "Unkonw language"; diff --git a/index.js b/index.js index 2d7e917..f61abe8 100644 --- a/index.js +++ b/index.js @@ -5,146 +5,42 @@ export { INFORMATION, Message, - Lottus, - Blueprint, + lottus, + redirect, + blueprint, - create_options_processor, - create_input_processor, - create_form_processor -} - -const FORM = "FORM"; -const INFORMATION = "INFORMATION"; - - -class Message { - /** - * - * @param {string} name - */ - constructor(name){ - this.name = name; - } + options_processor, + input_processor, + form_processor, - get type(){ - return this.form?FORM:INFORMATION; - } + format_message } - -class Blueprint { - __infos = Object.create(null); - __forms = Object.create(null); - - /** - * - * @param {string} location - * @param {Message|Message} result - * @returns {Blueprint} - */ - info(location, result){ - if(typeof location !== "string" || !location){ - throw new Error("location must be a non-empty string"); +/** + * + * @param {object} options + * @returns {__lottus} + */ +function lottus(options){ + if((typeof options === "object")){ + if(options.debug){ + bot.debug = options.debug; } - - if(typeof result === "function" || result instanceof Message){ - this.__infos[location] = result; - return this; + if(options.entrypoint){ + bot.entrypoint = options.entrypoint; } - - throw new Error("func must be a function or Message"); - } - - /** - * - * @param {string} location - * @param {Message|Message} result - * @returns {Blueprint} - */ - get(location, result){ - this.info(location, result); - - return this; - } - - /** - * - * @param {string} location - * @param {Message|Message} result - * @returns {Blueprint} - */ - information(location, result){ - this.info(location, result); - - return this; - } - - /** - * - * @param {string} location - * @param {Message|Message} result - * @returns {Blueprint} - */ - form(location, result){ - if(typeof location !== "string" || !location){ - throw new Error("location must be a non-empty string"); - } - - if(typeof result === "function" || result instanceof Message){ - this.__forms[location] = result; - return this; + if(options.description){ + bot.description = options.description; } - - throw new Error("result must be a function or Message"); } - /** - * - * @param {string} location - * @param {Message|Function} result - * @returns {Blueprint} - */ - post(location, result){ - this.form(location, result); - - return this; - } - - /** - * - * @param {string} location - * @param {Message|Function} info - * @param {Message|Function} proc - * @returns {Blueprint} - */ - at(location, info, proc){ - this.info(location, info); - this.form(location, proc); - - return this; - } - - /** - * - * @returns {object} - */ - get_forms(){ - return this.__forms; - } - - /** - * - * @returns {object} - */ - get_infos(){ - return this.__infos; - } + return bot; } -class Lottus { - __infos = Object.create(null); - __forms = Object.create(null); +class __lottus { + #gets = Object.create(null); + #posts = Object.create(null); debug = false; entrypoint = "main"; @@ -154,15 +50,15 @@ class Lottus { * * @param {string} location * @param {Message|Function} result - * @returns {Lottus} + * @returns {__lottus} */ - information(location, result){ + get(location, result){ if(typeof location !== "string" || !location){ throw new Error("location must be a non-empty string"); } if(typeof result === "function" || result instanceof Message){ - this.__infos[location] = result; + this.#gets[location] = result; return this; } @@ -173,45 +69,35 @@ class Lottus { * * @param {string} location * @param {Message|Function} result - * @returns {Lottus} + * @returns {__lottus} */ info(location, result){ - return this.information(location, result); + return this.get(location, result); } /** * * @param {string} location * @param {Message|Function} result - * @returns {Lottus} + * @returns {__lottus} */ - get(location, result){ - return this.info(location, result); + information(location, result){ + return this.get(location, result); } /** * * @param {string} location * @param {Message|Function} result - * @returns {Lottus} + * @returns {__lottus} */ post(location, result){ - return this.form(location, result); - } - - /** - * - * @param {string} location - * @param {Message|Function} result - * @returns {Lottus} - */ - form(location, result){ if(typeof location !== "string" || !location){ throw new Error("location must be a non-empty string"); } if(typeof result === "function" || result instanceof Message){ - this.__forms[location] = result; + this.#posts[location] = result; return this; } @@ -221,82 +107,27 @@ class Lottus { /** * * @param {string} location - * @param {Message|Function} info - * @param {Message|Function} proc - * @returns {Lottus} + * @param {Message|Function} result + * @returns {__lottus} */ - at(location, info, proc){ - this.info(location, info); - this.form(location, proc); - - return this; - } - - get_processor(route_type, location){ - if(route_type === INFORMATION){ - return this.__infos[location]; - } - - if(route_type === FORM){ - return this.__forms[location]; - } + form(location, result){ + return this.post(location, result); } /** * * @param {string} location - * @param {Request} request - * @param {string} method + * @param {Message|Function} get + * @param {Message|Function} post * @returns */ - async redirect(location, request, method = INFORMATION){ - if(this.debug){ - console.log("Lottus.redirect input", method, location, request); - } - - if(typeof location !== "string"){ - throw new Error("Argument location must be a string"); - } - - if(![FORM, INFORMATION].includes(method)){ - throw new Error("Argument method must be a string [INFORMATION|FORM]"); - } - - if(!request || !request.parameters){ - request = {parameters: Object.create(null)} - } - - let response = new Message(location); - - const processor = this.get_processor(method, location); - - if(!processor){ - throw new Error("Could not find a processor for "+ location); - } - - let result = null; - if(processor instanceof Message){ - result = processor; - }else if(typeof processor === "function"){ - result = await processor(request, response); - }else{ - throw new Error(method + " " + location +" must be a function or Message"); - } - - if(this.debug){ - console.log("Lottus.redirect output", result); - } - - if(!result || !(result instanceof Message)){ - throw new Error("Processor must return a Response response"); - } - - return result; + at(location, get, post){ + return this.get(location, get).post(location, post); } /** * - * @param {Request} request + * @param {object} request * @param {Message} last_message * @returns {Message} */ @@ -326,10 +157,6 @@ class Lottus { return result; } - get_route_type(message){ - return message.form?FORM:INFORMATION; - } - /** * * @param {Request} request @@ -345,7 +172,7 @@ class Lottus { throw new Error("Argument message must be of type Message") } - const processor = this.get_processor(this.get_route_type(message), message.name); + const processor = get_processor(get_route_type(message), message.name); if(!processor){ throw new Error("Could not find a processor for", message.name); @@ -371,214 +198,474 @@ class Lottus { return result; } + get gets(){ + return this.#gets; + } + + get posts(){ + return this.#posts; + } /** * - * @param {Blueprint} bp + * @param {__blueprint} bp */ - addBlueprint(bp){ - if(!(bp instanceof Blueprint)){ - throw new Error("bp must be of type Blueprint"); + add_blueprint(bp){ + if(!(bp instanceof __blueprint)){ + throw new Error("bp must be of type __blueprint"); } - this.__infos = {...this.__infos, ...bp.get_infos()}; - this.__forms = {...this.__forms, ...bp.get_forms()}; - - console.log(this.__infos); - console.log(this.__forms); + this.#gets = {...this.#gets, ...bp.infos}; + this.#posts = {...this.#posts, ...bp.forms}; } } - -Message.prototype.header = ""; -Message.prototype.body = ""; -Message.prototype.footer = "Powered by Lottus"; - /** * - * @param {string} label - * @param {string} next - * @param {object} params + * @param {object} options + * @returns */ -Message.prototype.addAutoOption = function addAutoOption(label, next, params){ - if(!this.form){ - this.form = {options: new Map()}; +function blueprint(options){ + return new __blueprint(); +} + + +class __blueprint { + #__infos = Object.create(null); + #__forms = Object.create(null); + + /** + * + * @param {string} location + * @param {Message|Message} result + * @returns {__blueprint} + */ + info(location, result){ + if(typeof location !== "string" || !location){ + throw new Error("location must be a non-empty string"); + } + + if(typeof result === "function" || result instanceof Message){ + this.#__infos[location] = result; + return this; + } + + throw new Error("func must be a function or Message"); } - if(!this.form.options){ - this.form.options = new Map(); + /** + * + * @param {string} location + * @param {Message|Message} result + * @returns {__blueprint} + */ + get(location, result){ + this.info(location, result); + + return this; } - let key = this.form.options.size + 1; + /** + * + * @param {string} location + * @param {Message|Message} result + * @returns {__blueprint} + */ + information(location, result){ + this.info(location, result); - if(!label){ - throw new Error("Argument label cannot be null nor empty"); + return this; } - this.form.options.set(key.toString(), {label, next, params}); -} + /** + * + * @param {string} location + * @param {Message|Message} result + * @returns {__blueprint} + */ + form(location, result){ + if(typeof location !== "string" || !location){ + throw new Error("location must be a non-empty string"); + } + if(typeof result === "function" || result instanceof Message){ + this.#__forms[location] = result; + return this; + } -/** - * - * @param {string} key - * @param {string} label - * @param {string} next - * @param {object} params - */ -Message.prototype.addOption = function addOption(key, label, next, params){ - if(!this.form){ - this.form = {options: new Map()}; + throw new Error("result must be a function or Message"); } - if(!this.form.options){ - this.form.options = new Map(); + /** + * + * @param {string} location + * @param {Message|Function} result + * @returns {__blueprint} + */ + post(location, result){ + this.form(location, result); + + return this; + } + + /** + * + * @param {string} location + * @param {Message|Function} get + * @param {Message|Function} post + * @returns {__blueprint} + */ + at(location, get, post){ + this.info(location, get); + this.form(location, post); + + return this; } - if(key === undefined || key === null){ - key = this.form.options.size + 1; + /** + * + * @returns {object} + */ + get forms(){ + return this.#__forms; } - if(!label){ - throw new Error("Argument label cannot be null nor empty"); + /** + * + * @returns {object} + */ + get infos(){ + return this.#__infos; } +} + - this.form.options.set(key.toString(), {label, next, params}); +let bot = new __lottus(); + + +const FORM = "FORM"; +const INFORMATION = "INFORMATION"; + +/** + * + * @param {Message} message + * @returns {string} + */ +function get_route_type(message){ + return message.form?FORM:INFORMATION; } /** * - * @param {string} name - * @param {string} type - * @param {string} next + * @param {string} route_type + * @param {string} location + * @returns {Message|Function} */ -Message.prototype.addInput = function addInput(name, type, next){ - if(!this.form){ - this.form = {input: {name, type, next}}; +function get_processor(route_type, location){ + if(route_type === INFORMATION){ + return bot.gets[location]; } - if(!this.form.input){ - this.form = {input: {name, type, next}}; + if(route_type === FORM){ + return bot.posts[location]; } } - /** * - * @param {Lottus} bot - * @returns {Function} + * @param {string} location + * @param {object} request + * @param {string} method + * @returns {Message} */ -function create_options_processor(bot){ +async function redirect(location, request, method = INFORMATION){ + if(typeof location !== "string"){ + throw new Error("Argument location must be a string"); + } + + if(![FORM, INFORMATION].includes(method)){ + throw new Error("Argument method must be a string [INFORMATION|FORM]"); + } + + if(!request || !request.parameters){ + request = {parameters: Object.create(null)} + } + + let response = new Message(location); + + const processor = get_processor(method, location); + + if(!processor){ + throw new Error("Could not find a processor for "+ location); + } + + let result = null; + if(processor instanceof Message){ + result = processor; + }else if(typeof processor === "function"){ + result = await processor(request, response); + }else{ + throw new Error(method + " " + location +" must be a function or Message"); + } + + if(!result || !(result instanceof Message)){ + throw new Error("Processor must return a Response response"); + } + + if(request.parameters){ + response.parameters = request.parameters; + } + + return result; +} + + +class Message { /** * - * @param {Request} req - * @param {Message} res - * @returns {Message} + * @param {string} name + */ + body = ""; + footer = ""; + title = ""; + + constructor(name){ + this.name = name; + } + + get type(){ + return this.form?FORM:INFORMATION; + } + + /** + * + * @param {*} options + * @example {name: "name", type: "string", next: "surname"} */ - async function create_options_processor(req, res){ - const options = req.form?.options; + addInput(options){ + if(typeof options === "object"){ + if(!this.form){ + this.form = {input: {}}; + } - if(options){ - const option = options.get(req.prompt.toString()); + if(!this.form.input){ + this.form = {input: {}} + } - if(option){ - req.selected_option = option; - let next = req.form?.next; + if(options.name){ + this.form.input.name = options.name; + } - if(req.selected_option.next){ - next = req.selected_option.next; - } + if(options.type){ + this.form.input.type = options.type; + } + + if(options.next){ + this.form.input.next = options.next; + } - return await bot.redirect(next, req); - } else { - res.error = "You selected an invalid option"; + if(options.description){ + this.form.input.description = options.description; } + } else { + this.form = options; + } + } + + /** + * + * @param {object} option + * @example {key: 1, label: "name", next: "display_name", parameters: {selected: "name"}} + */ + addOption(option){ + if(!(typeof option === "object")){ + throw new Error("option must be an object"); + } + + if(!this.form){ + this.form = {options: new Map()}; + } + + if(!this.form.options){ + this.form.options = new Map(); } + + if(option.key === undefined || option.key === null){ + option.key = this.form.options.size + 1; + } + + const label = option.label; + const next = option.next; + const parameters = option.parameters; - return res; + this.form.options.set(option.key.toString(), {label, next, parameters}); } - return create_options_processor; -} + /** + * + * @param {object} option + * @example {lable: "name", next: "display_name", parameters: {selected: "name"}} + */ + addAutoOption(option){ + if(!this.form){ + this.form = {options: new Map()}; + } + + if(!this.form.options){ + this.form.options = new Map(); + } + + let key = this.form.options.size + 1; + const label = option.label; + const next = option.next; + const parameters = option.parameters; + + this.form.options.set(key.toString(), {label, next, parameters}); + } +} /** * - * @param {Lottus} bot - * @returns {Function} + * @param {object} req + * @param {Message} res + * @returns {Message} */ -function create_input_processor(bot){ - /** - * - * @param {Request} req - * @param {Message} res - * @returns {Message} - */ - async function input_processor(req, res){ - const input = req.form?.input; +async function options_processor(req, res){ + const options = req.form?.options; + + if(options){ + const option = options.get(req.prompt.toString()); + + if(option){ + req.selected_option = option; + let next = req.form?.next; + + if(req.selected_option.next){ + next = req.selected_option.next; + } - if(input){ - req.input = input; - // let next = req.form?.next; - return await bot.redirect(input.next, req); + return await redirect(next, req); } else { res.error = "You selected an invalid option"; } - - return res; } - return input_processor; + return res; } +/** + * + * @param {object} req + * @param {Message} res + * @returns {Message} + */ +async function input_processor(req, res){ + const input = req.form?.input; + + if(input){ + req.input = input; + // let next = req.form?.next; + return await redirect(input.next, req); + } else { + res.error = "You selected an invalid option"; + } + + return res; +} /** * - * @param {Lottus} bot - * @returns {Function} + * @param {object} req + * @param {Message} res + * @returns {Message} */ -function create_form_processor(bot){ - /** - * - * @param {Request} req - * @param {Message} res - * @returns {Message} - */ - async function form_processor(req, res){ - const options = req.form?.options; - const input = req.form?.input; +async function form_processor(req, res){ + const options = req.form?.options; + const input = req.form?.input; - if(options){ - const option = options.get(req.prompt); + if(options){ + const option = options.get(req.prompt); - if(option){ - req.selected_option = option; - let next = req.form?.next; + if(option){ + req.selected_option = option; + let next = req.form?.next; - if(req.selected_option.next){ - next = req.selected_option.next; - } + if(req.selected_option.next){ + next = req.selected_option.next; + } - return await bot.redirect(next, req); - } else { - res.error = "You selected an invalid option"; + if(option.parameters){ + req.parameters = option.parameters; } - } - if(input){ - req.input = input; + return await redirect(next, req); + } else { + res.error = "You selected an invalid option"; + } + } - let next = req.form?.next; + if(input){ + const name = input.name; + const value = req.prompt; + + if(input.type === "number"){ + if(!parseFloat(value)){ + res.error = "Please provide a number"; - return await bot.redirect(next, req); + return res; + } } - if(!res.error){ - res.error = "The form has no input nor options"; + let parameter = {} + parameter[name] = value; + + req = {parameters: parameter}; + + let next = input?.next; + + return await redirect(next, req); + } + + if(!res.error){ + res.error = "The form has no input nor options"; + } + + return res; +} + +/** + * + * @param {Message} message + * @returns {string} + */ +function format_message(message, parameters){ + let result = ""; + if(!(message instanceof Message)){ + throw new Error("Argument message must be of type Message"); + } + + if(message.title){ + result += message.title + "\n"; + } + + if(message.body){ + result += message.body + "\n"; + } + + if(message.error){ + result += message.error + "\n"; + } + + if(message.form.options){ + for(const option of message.form.options){ + result += option[0] + " - " + option[1].label + "\n"; } + } - return res; + if(message.footer){ + result += message.footer + "\n"; } - return form_processor; + return result; } \ No newline at end of file