├── .gitignore ├── .npmignore ├── tests ├── support │ └── jasmine.json ├── operation_manager.ts └── reply_manager.ts ├── tsconfig.json ├── index.ts ├── examples ├── bot.ts ├── reply.ts └── operations.ts ├── src ├── model.ts ├── operation_manager.ts ├── queue_manager.ts └── reply_manager.ts ├── package.json ├── index.d.ts └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | *.js 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | tsconfig.json 2 | *.ts 3 | !index.d.ts 4 | -------------------------------------------------------------------------------- /tests/support/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "tests", 3 | "spec_files": [ 4 | "*_manager.js" 5 | ], 6 | "stopSpecOnExpectationFailure": true, 7 | "random": false 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "types": [ 6 | "node", 7 | "node-telegram-bot-api" 8 | ], 9 | }, 10 | "exclude": [ 11 | "node_modules/", 12 | "src/model.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | export { default as ReplyManager } from "./src/reply_manager"; 2 | export { default as OperationManager } from "./src/operation_manager"; 3 | 4 | // QueueManager should be imported only if you want to create your new queue 5 | // with blackjack and hookers (cit.) 6 | export { default as QueueManager } from "./src/queue_manager"; 7 | -------------------------------------------------------------------------------- /examples/bot.ts: -------------------------------------------------------------------------------- 1 | import * as TelegramBot from "node-telegram-bot-api"; 2 | 3 | if (process.argv.length < 3) { 4 | console.error("Specify a token to continue."); 5 | console.error("FORMAT: npm run -- "); 6 | process.exit(1); 7 | } 8 | 9 | const bot = new TelegramBot(process.argv[2], { polling: true }); 10 | 11 | bot.on("polling_error", (msg) => console.log(msg)); 12 | 13 | export default bot; 14 | -------------------------------------------------------------------------------- /src/model.ts: -------------------------------------------------------------------------------- 1 | export interface QueueObject { 2 | [key: string]: T[] 3 | } 4 | 5 | export type Hashable = number | string; 6 | 7 | export interface ReplyData { 8 | [key: string]: any 9 | } 10 | 11 | export interface Operation { 12 | command: string 13 | } 14 | 15 | export interface Reply { 16 | action: (data?: ReplyData) => RegisteredResult | undefined | void, 17 | previousData?: any 18 | } 19 | 20 | export interface RegisteredResult { 21 | repeat?: boolean; 22 | [key: string]: any; 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-telegram-operation-manager", 3 | "version": "1.2.0", 4 | "description": "A suite to manage replies and operations in telegram bot written in node.js", 5 | "main": "index.js", 6 | "scripts": { 7 | "reply": "node examples/reply.js", 8 | "prereply": "npm run build", 9 | "operations": "node examples/operations.js", 10 | "preoperations": "npm run build", 11 | "prepublishOnly": "npm run build", 12 | "test": "jasmine --config=tests/support/jasmine.json", 13 | "pretest": "npm run build", 14 | "build": "tsc", 15 | "prebuild": "command -v tsc || npm install -g typescript" 16 | }, 17 | "keywords": [ 18 | "node", 19 | "telegram", 20 | "bot", 21 | "api", 22 | "reply" 23 | ], 24 | "author": "Alexander P. Cerutti", 25 | "license": "MIT", 26 | "devDependencies": { 27 | "@types/jasmine": "^2.8.9", 28 | "@types/node-telegram-bot-api": "^0.30.3", 29 | "jasmine": "^3.3.0", 30 | "node-telegram-bot-api": "^0.30.0", 31 | "node-telegram-keyboard-wrapper": "^2.0.0", 32 | "typescript": "^3.2.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/operation_manager.ts: -------------------------------------------------------------------------------- 1 | import { OperationManager } from ".."; 2 | import "jasmine"; 3 | 4 | describe("Operation Manager", () => { 5 | let opm: OperationManager; 6 | 7 | beforeEach(() => { 8 | opm = new OperationManager(); 9 | }); 10 | 11 | it("Registers an action", () => { 12 | opm.register("001", "test1"); 13 | 14 | expect(opm.hasActive("001")).toBe(true); 15 | }); 16 | 17 | it("Ending one of one action", () => { 18 | opm.register("001", "test1"); 19 | 20 | opm.end("001"); 21 | 22 | expect(opm.onGoing("001").length).toBe(0); 23 | }); 24 | 25 | it("Ending one of two actions without name", () => { 26 | opm.maxConcurrent = 2; 27 | opm.register("001", "test1"); 28 | opm.register("001", "test2"); 29 | 30 | opm.end("001"); 31 | 32 | expect(opm.onGoing("001")[0].command).toBe("test2"); 33 | }); 34 | 35 | it("Ending one of two actions with name", () => { 36 | opm.maxConcurrent = 2; 37 | opm.register("001", "test1"); 38 | opm.register("001", "test2"); 39 | 40 | opm.end("001", "test2"); 41 | 42 | expect(opm.onGoing("001")[0].command).toBe("test1"); 43 | }); 44 | 45 | it("Testing operation exceeding", () => { 46 | opm.maxConcurrent = 2; 47 | opm.register("001", "test1"); 48 | opm.register("001", "test2"); 49 | opm.register("001", "test3"); 50 | 51 | expect(opm.onGoing("001").length).toBe(2); 52 | }); 53 | 54 | it("Wipes out 001's queue.", () => { 55 | opm.maxConcurrent = 4; 56 | opm.register("001", "test1"); 57 | opm.register("001", "test2"); 58 | opm.register("001", "test3"); 59 | opm.register("001", "test4"); 60 | 61 | opm.empty("001"); 62 | 63 | expect(opm.onGoing("001").length).toBe(0); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /tests/reply_manager.ts: -------------------------------------------------------------------------------- 1 | import { ReplyManager } from ".."; 2 | import "jasmine"; 3 | 4 | describe("test1", () => { 5 | let reply; 6 | 7 | beforeEach(() => { 8 | reply = new ReplyManager(); 9 | }); 10 | 11 | it("Registers a new action", () => { 12 | reply.register("001", () => { 13 | console.log("test 1 for push"); 14 | }); 15 | 16 | expect(reply.pending("001").length).toBe(1); 17 | }); 18 | 19 | it("Expects replies", () => { 20 | reply 21 | .register("001", () => { 22 | console.log("test 1 for register reply"); 23 | }) 24 | .register("001", () => { 25 | console.log("test 2 for register reply"); 26 | }) 27 | 28 | expect(reply.expects("001")).toBe(true); 29 | }); 30 | 31 | it("Will execute a reply", () => { 32 | reply 33 | .register("001", () => { 34 | // doing nothing to show nothing in the console. 35 | }) 36 | .register("001", () => { 37 | // doing nothing to show nothing in the console. 38 | // only this should remain 39 | }) 40 | 41 | reply.execute("001", {}); 42 | 43 | expect(reply.pending("001").length).toBe(1); 44 | }); 45 | 46 | it("Removes all the actions", () => { 47 | reply 48 | .register("001", () => { 49 | // doing nothing to show nothing in the console. 50 | }) 51 | .register("001", () => { 52 | // doing nothing to show nothing in the console. 53 | // only this should remain 54 | }) 55 | .cancelAll("001"); 56 | 57 | expect(reply.pending("001").length).toBe(0); 58 | }); 59 | 60 | it("Pops out the last element", () => { 61 | reply 62 | .register("001", () => { 63 | // doing nothing to show nothing in the console. 64 | }) 65 | .register("001", () => { 66 | // doing nothing to show nothing in the console. 67 | // only this should remain 68 | }); 69 | 70 | reply.pop("001"); 71 | 72 | expect(reply.pending("001").length).toBe(1); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /src/operation_manager.ts: -------------------------------------------------------------------------------- 1 | import { Hashable, Operation } from "./model"; 2 | import QueueManager from "./queue_manager"; 3 | 4 | export default class OperationManager extends QueueManager { 5 | private _maxConcurrent = 1 6 | 7 | constructor() { 8 | super(); 9 | } 10 | 11 | /** 12 | * Pushes a new operation in the queue. 13 | * @param id 14 | * @param command 15 | * @param action - action to be executed straight after the pushing. 16 | * @returns {any | undefined} - the result of the callback or none. 17 | */ 18 | 19 | register(id: Hashable, command: string, action?: Function): any | undefined { 20 | if (this._maxConcurrent > 0 && this.onGoing(id).length === this._maxConcurrent) { 21 | console.error("Operation: _maxConcurrent operation reached. No more ops will be added to the queue."); 22 | return; 23 | } 24 | 25 | this.push(id, { command }); 26 | 27 | if (typeof action === "function") { 28 | return action(); 29 | } 30 | } 31 | 32 | /** 33 | * Terminates the current action with a specific id. 34 | * @param id 35 | * @returns {Operation} - The removed Operation object. 36 | */ 37 | 38 | end(id: Hashable, commandName?: string): Operation { 39 | if (commandName) { 40 | return this.cherryPickRemove(id, (value) => value.command === commandName); 41 | } 42 | 43 | return this.remove(id); 44 | } 45 | 46 | /** 47 | * Fetches all the current active operations 48 | * @param id 49 | */ 50 | 51 | onGoing(id: Hashable): Operation[] { 52 | return this.all(id); 53 | } 54 | 55 | /** 56 | * Checks if there is at least one of active operation that matches the id 57 | * by checking if the amount is lower than the concurrent maximum. 58 | * @param id 59 | * @returns {boolean} 60 | */ 61 | 62 | hasActive(id: Hashable): boolean { 63 | return this.has(id); 64 | } 65 | 66 | /** 67 | * Checks amount of active operations against the limit 68 | * @param id 69 | * @returns {boolean} The result in boolean 70 | */ 71 | 72 | hasReachedMaximum(id: Hashable): boolean { 73 | const opsAmount = this.all(id).length; 74 | return opsAmount > 0 && opsAmount <= this._maxConcurrent; 75 | } 76 | 77 | /** 78 | * Fully empty Operation queue for a specific user 79 | * @param id 80 | * @returns boolean 81 | */ 82 | 83 | empty(id: Hashable): boolean { 84 | this.emptyQueue(id); 85 | return true; 86 | } 87 | 88 | set maxConcurrent(newValue: number) { 89 | if (typeof newValue !== "number") { 90 | return; 91 | } 92 | 93 | this._maxConcurrent = newValue; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/queue_manager.ts: -------------------------------------------------------------------------------- 1 | import { Hashable, QueueObject } from "./model"; 2 | 3 | export default class QueueManager { 4 | private _queue: QueueObject = {}; 5 | 6 | /** 7 | * Pushes an object into the queue. 8 | * @param id - Queue identifier 9 | * @param object 10 | * 11 | */ 12 | 13 | protected push(id: Hashable, object: T): number { 14 | if (!this._queue[id]) { 15 | this._queue[id] = [object]; 16 | } else { 17 | this._queue[id].push(object); 18 | } 19 | 20 | return this.all(id).length; 21 | } 22 | 23 | /** 24 | * Removes the first element of a specific queue. 25 | * @param id - queue identifier 26 | * @returns - The shifted element 27 | */ 28 | 29 | protected remove(id: Hashable): T { 30 | return this._queue[id].shift(); 31 | } 32 | 33 | /** 34 | * Removes all the objects in a specific queue 35 | * @param id 36 | */ 37 | 38 | protected removeAll(id: Hashable): void { 39 | this._queue[id] = []; 40 | } 41 | 42 | /** 43 | * Removes an element from a specific queue through a criteria 44 | * @param id - queue identifier 45 | * @param criteria - Function to be used to find the element 46 | * @returns boolean 47 | */ 48 | 49 | protected cherryPickRemove(id: Hashable, criteria: (value: T) => boolean): T { 50 | let elementIndex = this._queue[id].findIndex(criteria); 51 | 52 | if (elementIndex === -1) { 53 | return {}; 54 | } 55 | 56 | let element = this._queue[id][elementIndex]; 57 | 58 | this._queue[id].splice(elementIndex, 1); 59 | return element; 60 | } 61 | 62 | /** 63 | * Checks if a queue has any objects 64 | * @param id 65 | */ 66 | 67 | protected has(id: Hashable): boolean { 68 | return !!(this._queue[id] ? this._queue[id].length : 0); 69 | } 70 | 71 | /** 72 | * Fetches all the objects of a specific queue 73 | * @param id 74 | * @returns Array of objects 75 | */ 76 | 77 | protected all(id: Hashable): T[] { 78 | return this._queue[id] || []; 79 | } 80 | 81 | /** 82 | * Fetches the first object of a specific queue. 83 | * @param id 84 | * @returns The element fetched. 85 | */ 86 | 87 | protected get(id: Hashable): T { 88 | return this._queue[id] && this._queue[id].length ? this._queue[id][0] : {}; 89 | } 90 | 91 | /** 92 | * Pops out the last object of a specific queue. 93 | * @param id 94 | * @returns The popped element 95 | */ 96 | 97 | protected pop(id: Hashable): T { 98 | return this._queue[id] && this._queue[id].length ? this._queue[id].pop() : {}; 99 | } 100 | 101 | /** 102 | * Wipes out one specific queue or the whole queue set, 103 | * based on the presence of id parameter. 104 | * @param id - id of the queue to be wiped out. Wipes out every queue if omitted. 105 | * @returns the amount of object or queue removed. 106 | */ 107 | 108 | protected emptyQueue(id?: Hashable): number { 109 | let queueLen: number; 110 | 111 | if (id && this._queue[id]) { 112 | queueLen = this._queue[id].length; 113 | delete this._queue[id]; 114 | } else { 115 | queueLen = Object.keys(this._queue).length; 116 | this._queue = >{}; 117 | } 118 | 119 | return queueLen; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/reply_manager.ts: -------------------------------------------------------------------------------- 1 | import { Reply, Hashable, ReplyData, RegisteredResult } from "./model"; 2 | import QueueManager from "./queue_manager"; 3 | 4 | export default class ReplyManager extends QueueManager { 5 | constructor() { 6 | super(); 7 | } 8 | 9 | /** 10 | * Adds a new reply to the list 11 | * @param id - identifier of the new element 12 | * @param command - relative command 13 | * @param action - action to be executed on execution 14 | * @returns {number} - the amount of actions added under a specific identifier. 15 | */ 16 | 17 | register(id: Hashable, action: (data?: ReplyData) => RegisteredResult | undefined | void): this { 18 | this.push(id, { action }); 19 | 20 | return this; 21 | } 22 | 23 | /** 24 | * Removes all the replies matching with a specific id. 25 | * @param id 26 | */ 27 | 28 | cancelAll(id: Hashable): this { 29 | this.removeAll(id); 30 | return this; 31 | } 32 | 33 | /** 34 | * Pops out the last reply from the queue of a specific user. 35 | * @param id 36 | */ 37 | 38 | pop(id: Hashable): Reply { 39 | return super.pop(id); 40 | } 41 | 42 | /** 43 | * Checks if there are some awaiting replies in queue with a specific identifier. 44 | * @param id 45 | * @returns {boolean} 46 | */ 47 | 48 | expects(id: Hashable): boolean { 49 | return this.has(id); 50 | } 51 | 52 | /** 53 | * Executes an action with optional data and removes it if successful. 54 | * @param id - identifier 55 | * @param data - optional data to be passed inside your function. 56 | * 57 | */ 58 | 59 | execute(id: Hashable, data: ReplyData = {}): void { 60 | let next: Reply = this.get(id); 61 | let callback = next.action; 62 | 63 | // Passing the previous data to the function 64 | // We are setting the previousData below 65 | data.previousData = next.previousData || undefined; 66 | 67 | let result = callback(data); 68 | 69 | // We want to allow a successful execution in case of: 70 | // a. Missing result (undefined) 71 | // b. Object but not array result 72 | // c. Object with a falsy repeat property 73 | // d. Object without repeat property 74 | 75 | if (result === undefined) { 76 | this.remove(id); 77 | return; 78 | } 79 | 80 | if (typeof result === "object" && !Array.isArray(result)) { 81 | if (!result.repeat) { 82 | // if we have to want to repeat, we have to shift the reply 83 | this.remove(id); 84 | } 85 | 86 | let nextReply = this.get(id); 87 | 88 | // this.get returns an empty object if there 89 | // is no other reply in the id's queue. 90 | if (!Object.keys(nextReply).length) { 91 | return; 92 | } 93 | 94 | delete result.repeat; // even if it does not exists 95 | nextReply.previousData = result; 96 | } 97 | } 98 | 99 | /** 100 | * Checks if possible to ignore current reply and skip to next. 101 | * @param id 102 | * @returns {boolean} - False if there are no replies left. True otherwise. 103 | */ 104 | 105 | skip(id: Hashable): boolean { 106 | if (!this.expects(id)) { 107 | return false; 108 | } 109 | 110 | this.execute(id, {}); 111 | return true; 112 | } 113 | 114 | /** 115 | * Returns pending replies that match against parameter. 116 | * @param id - queue identifier 117 | * @returns {Reply[]} 118 | */ 119 | 120 | pending(id: Hashable): Reply[] { 121 | return this.all(id); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "node-telegram-operation-manager" { 2 | interface QueueObject { 3 | [key: string]: T[] 4 | } 5 | 6 | interface Operation { 7 | command: string 8 | } 9 | 10 | interface ReplyData { 11 | [key: string]: any 12 | } 13 | 14 | interface Reply { 15 | action: Function, 16 | previousData?: any 17 | } 18 | 19 | interface RegisteredResult { 20 | repeat?: boolean; 21 | [key: string]: any; 22 | } 23 | 24 | class QueueManager { 25 | /** 26 | * Pushes the element into the queue. 27 | * @param element 28 | * @returns {number} - The amount of elements inside 29 | * the queue for the same id (element.id) 30 | */ 31 | 32 | protected push(id: number | string, object: T): number; 33 | 34 | /** 35 | * Removes from the queue the first element that matches 36 | * the provided predicate. 37 | * @param criteria 38 | * @returns {T} - The removed element 39 | */ 40 | 41 | protected remove(id: number | string): T; 42 | 43 | /** 44 | * Removes all the element that match with the provided id. 45 | * @param id 46 | */ 47 | 48 | protected removeAll(id: number | string): void; 49 | 50 | /** 51 | * Removes an element by searching for it using a criteria, like a cherry pick. 52 | * @param id 53 | * @param criteria 54 | */ 55 | 56 | protected cherryPickRemove(id: number | string, criteria: (value: T) => boolean): T; 57 | 58 | /** 59 | * Checks if there are elements in queue that match 60 | * with the criteria. 61 | * @param criteria 62 | * @returns {boolean} - True if there are elements, false otherwise. 63 | */ 64 | 65 | protected has(id: number | string): boolean; 66 | 67 | /** 68 | * Retrives all the elements that match the criteria 69 | * @param criteria 70 | * @returns {T[]} - Array of elements 71 | */ 72 | 73 | protected all(id: number | string): T[]; 74 | 75 | /** 76 | * Returns the first element that match the criteria. 77 | * @param criteria 78 | * @return {T} - the first element. 79 | */ 80 | 81 | protected get(id: number | string): T; 82 | 83 | /** 84 | * Pops out the last element of the id's queue. 85 | * @param id 86 | * @returns {T} - the last element 87 | */ 88 | 89 | protected pop(id: number | string): T; 90 | 91 | /** 92 | * Wipes out all the queue. 93 | * @param id - If has an id, id's queue will be wiped out. 94 | * All the queues will otherwise. 95 | */ 96 | 97 | protected emptyQueue(id?: number | string): number; 98 | } 99 | 100 | class ReplyManager extends QueueManager { 101 | 102 | /** 103 | * Adds a reply to the queue 104 | * @param id - the ID to which assign the action 105 | * @param action - a callback to register 106 | * @returns {this} - this (ReplyManager) 107 | */ 108 | 109 | public register(id: number | string, action: (data?: ReplyData) => RegisteredResult): this; 110 | 111 | /** 112 | * Removes all the replies for this id. 113 | * @param id 114 | * @returns {this} - this 115 | */ 116 | 117 | public cancelAll(id: number | string): this; 118 | 119 | /** 120 | * Pops the last reply for this id; 121 | * @param id 122 | */ 123 | 124 | public pop(id: number | string): Reply; 125 | 126 | /** 127 | * Checks if a specific id has some left replies 128 | * @param id 129 | */ 130 | 131 | public expects(id: number | string): boolean; 132 | 133 | /** 134 | * Fires the first reply for a specific id, 135 | * passing to it some optional arbitrary data 136 | * @param id 137 | * @param data 138 | */ 139 | 140 | public execute(id: number | string, data?: ReplyData): void; 141 | 142 | /** 143 | * Skips the current reply expectation 144 | * only if there is at least one next 145 | * @param id - specific identifier 146 | * @returns {boolean} - check result 147 | */ 148 | 149 | public skip(id: number | string): boolean; 150 | 151 | /** 152 | * Fetch all the pending replies for this id 153 | * @param id 154 | * @returns {Reply[]} - all the replies 155 | */ 156 | 157 | public pending(id: number | string): Reply[]; 158 | } 159 | 160 | class OperationManager extends QueueManager { 161 | 162 | public maxConcurrent: number; 163 | 164 | /** 165 | * Adds a new operation to the queue. 166 | * @param id 167 | * @param command 168 | * @param callback 169 | */ 170 | 171 | public register(id: number | string, command: string, callback?: Function): any; 172 | 173 | /** 174 | * Cancels the current action or a specific action for a specific id 175 | * @param id 176 | * @param commandName - name to be used to identify a specific element to be removed. 177 | * @return {Operations} - the cancelled operation 178 | */ 179 | 180 | public end(id: number | string, commandName?: string): Operation; 181 | 182 | /** 183 | * Fetches all the current queued operations for a specific identifier. 184 | * @param id 185 | */ 186 | 187 | public onGoing(id: number | string): Operation[]; 188 | 189 | /** 190 | * Checks if the current identifier has an ongoing operations 191 | * @param id 192 | * @returns {boolean} - The result of the check 193 | */ 194 | 195 | public hasActive(id: number | string): boolean; 196 | 197 | /** 198 | * Wipes out all the operation queue. 199 | * I hope you know what you are doing. 200 | */ 201 | 202 | public empty(): boolean; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /examples/reply.ts: -------------------------------------------------------------------------------- 1 | import bot from "./bot"; 2 | import ReplyManager from "../src/reply_manager"; 3 | import { MessageEntity } from "node-telegram-bot-api"; 4 | import { ReplyKeyboard } from "node-telegram-keyboard-wrapper"; 5 | import { ReplyData } from "../src/model"; 6 | 7 | //*************************// 8 | //* EXECUTE THIS, TRUST ME*// 9 | //*************************// 10 | 11 | const reply = new ReplyManager(); 12 | const keyboards = [ 13 | new ReplyKeyboard("I hate replies"), 14 | (new ReplyKeyboard()).addRow("I hate replies", "I love replies"), 15 | new ReplyKeyboard("Show me potatoes!") 16 | ]; 17 | 18 | bot.onText(/\/multireply/, (message) => { 19 | bot.sendMessage(message.from.id, `Hey there! 😎 Some replies got registered! Follow my instructions to discover how this works. Send me a message now!`); 20 | 21 | // I could use any identifier I want, but I suggest to use user.id. 22 | // Command is an arbitrary string and optional string 23 | // If you don't want to pass a command, set it as empty 24 | reply 25 | .register(message.from.id, (someData?: ReplyData) => { 26 | bot.sendMessage(message.from.id, "Great! Now send me another message."); 27 | }) 28 | .register(message.from.id, (someData?: ReplyData) => { 29 | let nextText = "See? You can register your replies easily by setting in your message listener the following check." 30 | + "\nAssuming you are using [node-telegram-bot-api](https://github.com/yagop/node-telegram-bot-api) and you istantiated globally ReplyManager, you can do it by:" 31 | + "\n\n```" 32 | + "\nconst reply = new ReplyManager();" 33 | + "\nbot.on(\"message\", (msg) => {" 34 | + "\n\t// this requires the id and let you push optional data in form of object." 35 | + "\n\tif (reply.expects(msg.from.id)) {" 36 | + "\n\t\tlet { text, entities } = msg;" 37 | + "\n\t\treply.execute(msg.from.id, { text, entities });" 38 | + "\n\t}" 39 | + "\n});```" 40 | + "\n\nNow send me this text: \"I hate replies\""; 41 | 42 | let messageOpts = Object.assign({ 43 | parse_mode: "Markdown", 44 | disable_web_page_preview: true 45 | }, keyboards[0].open({ resize_keyboard: true })); 46 | 47 | bot.sendMessage(message.from.id, nextText, messageOpts); 48 | }) 49 | .register(message.from.id, (someData?: ReplyData) => { 50 | let nextText: string; 51 | 52 | const messageOpts1 = Object.assign({ 53 | parse_mode: "Markdown", 54 | }, keyboards[1].open({ resize_keyboard: true })); 55 | 56 | const messageOpts2 = Object.assign({ 57 | parse_mode: "Markdown", 58 | }, keyboards[2].open({ resize_keyboard: true })); 59 | 60 | if (someData.text === "I love replies") { 61 | nextText = "✔ Good! Conditional checks can use optional data that can be passed at your own discretion through `reply.execute()` (as above)" 62 | + "\n\nThis is how you can set them:" 63 | + "\n\n\t\`.add(identifier, (someData?: RegisteredResult) => { ... })\`" 64 | + "\n\nYou can also set some data to be passed (or accumulated) through replies." 65 | + " Instead of returning an object with `{ repeat: true }` or _undefined_, return an object with any value you want." 66 | + "\nFor this example, I'm going to return an object with a property called \"potatoes\"." 67 | + "\nYou can also return datas from a failed session, inserting in the same object of `{ repeat: true }`, other keys/values." 68 | + "\n\nNow send me another message with written inside *Show me potatoes!*."; 69 | 70 | bot.sendMessage(message.from.id, nextText, messageOpts2); 71 | return { 72 | potatoes: "I like them fried!" 73 | } 74 | } else { 75 | nextText = "❌ Great, but the required text in this reply was \"I love replies\"." 76 | + "\n\nYou can make repeat a reply until it satisfies your conditions by returning `{ repeat: true }` inside the function." 77 | + "\n\nIf you omit that key or return it as true or omit the full return, the reply will be considered as to not be repeated." 78 | + "\n\nNow try again. This time try to send me both \"I have replies\" (again) and \"I love replies\" and see what happen."; 79 | 80 | bot.sendMessage(message.from.id, nextText, messageOpts1); 81 | return { repeat: true }; 82 | } 83 | }) 84 | .register(message.from.id, (someData?: ReplyData) => { 85 | if (someData.text !== "Show me potatoes!") { 86 | bot.sendMessage(message.from.id, "Nope! The next is not correct! Try again."); 87 | return { repeat: true }; 88 | } 89 | 90 | let nextText = "You can access them by using `someData.previousData`." 91 | + "\nSo, we will access to potatoes by saying: `someData.previousData.potatoes`." 92 | + "\nAwesome! Isn't it?" 93 | + "\n\nNow send me another message!"; 94 | 95 | bot.sendMessage(message.from.id, nextText, Object.assign({}, 96 | keyboards[2].close(), 97 | { parse_mode: "Markdown" } 98 | )); 99 | }) 100 | .register(message.from.id, (someData?: ReplyData) => { 101 | bot.sendMessage(message.from.id, "You are the best! Start now by looking at the [documentation](https://github.com/alexandercerutti/node-telegram-operation-manager#class_reply). 😉 Hope you have enjoyed the tutorial!", { parse_mode: "Markdown" }); 102 | }); 103 | }); 104 | 105 | bot.on("message", (msg) => { 106 | // this requires the id and let you push optional data in form of object. 107 | 108 | if (!hasEntity("bot_command", msg.entities) && reply.expects(msg.from.id)) { 109 | let { text, entities } = msg; 110 | reply.execute(msg.from.id, { text, entities }); 111 | } 112 | }); 113 | 114 | function hasEntity(entity: string, entities?: MessageEntity[]) { 115 | if (!entities || !entities.length) { 116 | return false; 117 | } 118 | 119 | return entities.some(e => e.type === entity); 120 | } 121 | -------------------------------------------------------------------------------- /examples/operations.ts: -------------------------------------------------------------------------------- 1 | import bot from "./bot"; 2 | import { MessageEntity } from "node-telegram-bot-api"; 3 | import { ReplyManager, OperationManager } from ".."; 4 | import { ReplyData } from "../src/model"; 5 | import { ReplyKeyboard } from "node-telegram-keyboard-wrapper"; 6 | 7 | //*************************// 8 | //* EXECUTE THIS, TRUST ME*// 9 | //*************************// 10 | 11 | const Opm = new OperationManager(); 12 | const reply = new ReplyManager(); 13 | 14 | const keyboards = [ 15 | new ReplyKeyboard("Show me how!"), 16 | new ReplyKeyboard("Show me more"), 17 | new ReplyKeyboard("How can I do that?"), 18 | ]; 19 | 20 | bot.onText(/\/operations/, (message) => { 21 | if (Opm.hasReachedMaximum(message.from.id)) { 22 | bot.sendMessage(message.from.id, "Unable to execute /operations. Another op is on going."); 23 | return false; 24 | } 25 | 26 | Opm.register(message.from.id, "operations", function () { 27 | let messageText = "Hey there! 😎 Welcome to this operation example!" 28 | + "\nIn this example we'll see how Operation queue works." 29 | + "\n\nOperation queue is mainly designed to keep track about commands that users interact with." 30 | + "\nTechnically a user should be using one command at the time, but it may start more and you, developer, may not want it." 31 | + "\nTherefore, this system may be good if combined with replies." 32 | + "\n\nSend me *Show me how!* to discover."; 33 | 34 | bot.sendMessage(message.from.id, messageText, Object.assign({ 35 | parse_mode: "Markdown" 36 | }, 37 | keyboards[0].open({ 38 | resize_keyboard: true 39 | }))); 40 | 41 | reply 42 | .register(message.from.id, (someData?: ReplyData) => { 43 | if (someData.text !== "Show me how!") { 44 | bot.sendMessage(message.from.id, "Unknown command. Retry with \"Show me how!\"."); 45 | return { repeat: true }; 46 | } 47 | 48 | let nextText = "You can `register` an operation as in the following snippet." 49 | + "\nHere I'm assuming you are still using [node-telegram-bot-api](https://github.com/yagop/node-telegram-bot-api)." 50 | + "\n\n```" 51 | + "\nconst Opm = new OperationManager();`" 52 | + "\nbot.onText(/\\/yourCommand/, () => {" 53 | + "\n\t\tOpm.register(message.from.id, \"yourOperationIdentifier\", function() {" 54 | + "\n\t\t\t\t// Everything in this function is executed immediately after the creation of command is successful." 55 | + "\n\t\t\t\t// If you exceed the limit, the action won't be registered and function not executed." 56 | + "\n\t\t\t\t// So you can use it to execute depending actions, like reply registration." 57 | + "\n\t\t});" 58 | + "\n});" 59 | + "\n\n```" 60 | + "\n\nEasy, isn't it? 😉" 61 | + "\nSend me *Show me more* to continue."; 62 | 63 | bot.sendMessage(message.from.id, nextText, Object.assign({ 64 | parse_mode: "Markdown", 65 | disable_web_page_preview: true 66 | }, 67 | keyboards[1].open({ 68 | resize_keyboard: true 69 | }) 70 | )); 71 | }) 72 | .register(message.from.id, (someData?: ReplyData) => { 73 | if (someData.text !== "Show me more") { 74 | bot.sendMessage(message.from.id, "Unknown command. Retry with \"Show me more\"."); 75 | return { repeat: true }; 76 | } 77 | 78 | let nextText = "You can look at the code of this example to see how replies are registered." 79 | + "\n\nNow, you are executing command /operations. But, let's say that a user of your bot will now execute another command" 80 | + " like, I don't know, /dontclickthiscommand (you can try!), and you developer, don't want this to happen." 81 | + "\nSo what to do? It's easy." 82 | + "\nYou can add to your commands function a check: `Opm.hasReachedMaximum()`." 83 | + "\nSee the code for more." 84 | + "\n\nWhat is Maximum? And what if you want to let different actions to be executed at the same time?" 85 | + "\nSend me *How can I do that?* to discover."; 86 | 87 | bot.sendMessage(message.from.id, nextText, Object.assign({ parse_mode: "Markdown" }, keyboards[2].open({ resize_keyboard: true }))); 88 | }) 89 | .register(message.from.id, (someData?: ReplyData) => { 90 | if (someData.text !== "How can I do that?") { 91 | bot.sendMessage(message.from.id, "Unknown command. Retry with \"Show me more\"."); 92 | return { repeat: true }; 93 | } 94 | 95 | let nextText = "It's easy! You only have to set this property:" 96 | + "\n\n\t`Opm.maxConcurrent = N;`" 97 | + "\n\nWhere 'N' is the number of max operations you want to let be executed at the same time." 98 | + "\n\nSet it to 0 to allow infinite operations." 99 | + "\n\n\nYou are the best! Start now by looking at the [documentation](https://github.com/alexandercerutti/node-telegram-operation-manager#class_operation). 😉 Hope you have enjoyed the tutorial!"; 100 | 101 | bot.sendMessage(message.from.id, nextText, Object.assign({ parse_mode: "Markdown" }, keyboards[2].close())); 102 | }); 103 | }); 104 | }); 105 | 106 | bot.onText(/\/dontclickthiscommand/, (message) => { 107 | if (Opm.hasReachedMaximum(message.from.id)) { 108 | bot.sendMessage(message.from.id, "Unable to start this operation. You are executing another operation."); 109 | return; 110 | } 111 | 112 | bot.sendMessage(message.from.id, "A check has been executed an you are not executing anything else. 👍"); 113 | }); 114 | 115 | bot.on("message", (msg) => { 116 | if (!hasEntity("bot_command", msg.entities) && Opm.hasActive(msg.from.id) && reply.expects(msg.from.id)) { 117 | reply.execute(msg.from.id, { text: msg.text, entities: msg.entities }); 118 | } 119 | }); 120 | 121 | function hasEntity(entity: string, entities?: MessageEntity[]) { 122 | if (!entities || !entities.length) { 123 | return false; 124 | } 125 | 126 | return entities.some(e => e.type === entity); 127 | } 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATION NOTICE 2 | 3 | After several years since its creation and latest publised version, I've decided to deprecate this package as this was born as a personal need while developing bots. 4 | Moreover, this package does not even provide a correct support for asyncronous operations and therefore cannot be considered "modern" at all, IMHO. 5 | 6 | # Node Telegram Operation Manager 7 | 8 | This package purpose is to help managing the operations in Telegram bots in Node.js. 9 | 10 | ## Installation 11 | 12 | ```sh 13 | $ npm install --save node-telegram-operation-manager 14 | ``` 15 |
16 | 17 | ## Architecture 18 | 19 | The package exports 3 classes to achive different purposes: 20 | - Queue Manager 21 | - Reply Manager 22 | - Operation Manager 23 | 24 | The purposes are described below. 25 | 26 | The second and the third extends Queue Manager to access to queue manipulation methods. 27 | 28 | This is not a typical implementation of Queue. In fact this is a set of Queues, all identified by a custom identifier, which might be e.g. the user id the telegram bot interacts with. 29 | The package makes available also operation to remove elements from the queue from the bottom (pop) or inside the queue (cherrypickRemove). 30 | 31 | Queue Manager **is exported** to let developers to implement their own queues. This class is recommended to be exported only for this purpose, nothing else. 32 | 33 | ### Reply Manager 34 | 35 | Reply Manager is specifically studied to register common set of actions to be executed or repeated in a flow which can be, for example, a sequence of message from a user. 36 | 37 | Actually, if not native, Telegram bot api wrappers make it difficult to manage this aspect. 38 | 39 | ### Operation Manager 40 | 41 | Operation Manager is designed to manage the amount of operations can be active at the same time for a specific user. 42 | 43 | Operation Manager is not that useful to be used alone. In fact, it should be using to wrap the registration of replies. 44 | 45 | ### Queue Manager 46 | 47 | Queue Manager is the responsible class for queue managing. Although exported, it shouldn't be used except for implementing other queues. 48 | 49 |
50 |
51 | 52 | ## Examples 53 | 54 | Two working examples are provided in [example folder](/examples). 55 | 56 | They are structured to be a step-by-step examples in a chat with a bot you will choose by passing the token to the respective commands as below. 57 | 58 | ```sh 59 | # ==================================# 60 | # Execute these two commands before # 61 | # ==================================# 62 | 63 | # Go to project folder - assuming you installed it as module. 64 | $ cd node_modules/node-telegram-operation-manager 65 | 66 | # Development packages 67 | $ npm install -D; 68 | 69 | # ========================= 70 | 71 | $ npm run reply -- 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11 72 | # or 73 | $ npm run operations -- 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11 74 | ``` 75 | 76 |
77 |
78 | 79 | ## Tests 80 | 81 | Tests are also included. This package uses Jasmine as test suite. 82 | 83 | ```sh 84 | $ npm install -D; 85 | $ npm test 86 | ``` 87 | 88 |
89 |
90 | 91 | ## API Reference 92 | 93 | - [Reply Manager](#class_reply) 94 | - [.register()](#method_rep_register) 95 | - [.cancelAll()](#method_rep_cancelall) 96 | - [.pop()](#method_rep_pop) 97 | - [.expects()](#method_rep_expects) 98 | - [.execute()](#method_rep_execute) 99 | - [.skip()](#method_rep_skip) 100 | - [.pending()](#method_rep_pending) 101 | - [Operation Manager](#class_operation) 102 | - [.register()](#method_op_register) 103 | - [.end()](#method_op_end) 104 | - [.onGoing()](#method_op_ongoing) 105 | - [.hasActive()](#method_op_hasactive) 106 | - [.hasReachedMaximum()](#method_op_hrm) 107 | - [.empty()](#method_op_empty) 108 | - [.maxConcurrent](#property_op_maxconcurrent) 109 | - [Queue Manager](#class_qm) 110 | - [.push()](#method_qm_push) 111 | - [.remove()](#method_qm_remove) 112 | - [.removeAll()](#method_qm_removeall) 113 | - [.cherryPickRemove()](#method_qm_cpr) 114 | - [.has()](#method_qm_has) 115 | - [.all()](#method_qm_all) 116 | - [.get()](#method_qm_get) 117 | - [.pop()](#method_qm_pop) 118 | - [.emptyQueue()](#method_qm_emptyqueue) 119 | 120 |
121 | 122 | 123 | 124 | ## Reply Manager 125 |
126 | 127 | Reply Manager extends QueueManager with a type `Reply`, which will be used below to represent to express one pushed or pulled element. 128 | 129 | Please keep this as always valid: 130 | 131 | ```javascript 132 | // for Typescript 133 | import { ReplyManager } from "node-telegram-operation-manager"; 134 | 135 | // for Node.js 136 | const { ReplyManager } = require("node-telegram-operation-manager"); 137 | 138 | const reply = new ReplyManager(); 139 | ``` 140 | 141 | 142 | 143 | ### .register(); 144 | 145 | It adds a new action to be executed at the next user reply. 146 | 147 | ```javascript 148 | reply.register(id, action) : this; 149 | ``` 150 | 151 | **Description**: 152 | 153 | `action` parameter has the following signature: 154 | 155 | ```typescript 156 | action: (someData?: ReplyData) => RegisteredResponse 157 | ``` 158 | 159 | You can pass through the method `execute`, some arbitrary data to be used in the current response. They will appear in `someData`. 160 | 161 | It will also include a key, called `previousData`, which will be the data returned from the previous registered replies. If no data is returned from the previous responses, it won't have this key. 162 | 163 | To pass datas to the next reply, just return an object with your data. 164 | 165 | The default behavior is to remove from the queue the current reply. 166 | If current **user** reply does not satisfy your conditions, you can make them repeat, by returning: 167 | 168 | ```javascript 169 | { 170 | repeat: true, 171 | someOtherDatas: ... 172 | } 173 | ``` 174 | 175 | Omitting `repeat` key or setting it to `false`, will determine the default behavior to be executed. 176 | 177 | **Returns**: 178 | 179 | The object itself for chaining multiple replies listeners. 180 | 181 | **Arguments**: 182 | 183 | | Parameters | Description | Type | Optional | Default value | 184 | | ---------- | ----------- | ---- |:--------:|:-------------:| 185 | | id | Queue identifier | Number \| String | false | - | 186 | | action | Function to be executed | Function | false | - | 187 | 188 |
189 |
190 | 191 | 192 | 193 | ### .cancelAll() 194 | 195 | It removes all the actions for a specific identifier. 196 | 197 | ```javascript 198 | reply.cancelAll(id) : this; 199 | ``` 200 | 201 | **Returns**: 202 | 203 | The object itself for chaining. 204 | 205 | **Arguments**: 206 | 207 | | Parameters | Description | Type | Optional | Default value | 208 | | ---------- | ----------- | ---- |:--------:|:-------------:| 209 | | id | Queue identifier | Number \| String | false | - | 210 | 211 |
212 |
213 | 214 | 215 | 216 | ### .pop() 217 | 218 | It removes the last element from a specific queue. 219 | 220 | ```javascript 221 | reply.pop(id) : Reply; 222 | ``` 223 | 224 | **Returns**: 225 | 226 | The expelled element. 227 | 228 | **Arguments**: 229 | 230 | | Parameters | Description | Type | Optional | Default value | 231 | | ---------- | ----------- | ---- |:--------:|:-------------:| 232 | | id | Queue identifier | Number \| String | false | - | 233 | 234 |
235 |
236 | 237 | 238 | 239 | ### .expects() 240 | 241 | It checks if there are any element in queue for the specified identifier. Useful on telegram message listener. 242 | 243 | ```typescript 244 | reply.expects(id) : boolean; 245 | ``` 246 | 247 | **Returns**: The result of the check. 248 | 249 | **Arguments**: 250 | 251 | | Parameters | Description | Type | Optional | Default value | 252 | | ---------- | ----------- | ---- |:--------:|:-------------:| 253 | | id | Queue identifier | Number \| String | false | - | 254 | 255 |
256 |
257 | 258 | 259 | 260 | ### .execute() 261 | 262 | It execute the next registered action in the queue and removes it from the queue. 263 | 264 | ```javascript 265 | reply.execute(id, data); 266 | ``` 267 | 268 | **Arguments**: 269 | 270 | | Parameters | Description | Type | Optional | Default value | 271 | | ---------- | ----------- | ---- |:--------:|:-------------:| 272 | | id | Queue identifier | Number \| String | false | - | 273 | | data | data to be passed to the reply | Object | true | `{}` | 274 | 275 |
276 |
277 | 278 | 279 | 280 | ### .skip() 281 | 282 | Skips the execution of the current reply and pass to the next one. Before skipping, it checks if there are other replies in the queue. 283 | It will succeed only if the current reply is **not** the last. 284 | 285 | ```typescript 286 | reply.skip(id) : boolean; 287 | ``` 288 | 289 | **Returns**: 290 | 291 | Whether current reply got skipped or not. 292 | 293 | 294 | **Arguments**: 295 | 296 | | Parameters | Description | Type | Optional | Default value | 297 | | ---------- | ----------- | ---- |:--------:|:-------------:| 298 | | id | Queue identifier | Number \| String | false | - | 299 | 300 |
301 |
302 | 303 | 304 | 305 | ### .pending() 306 | 307 | It fetches all the pending replies object. 308 | Even this might be the most useless method in the class. 309 | 310 | ```typescript 311 | reply.pending(id) : Reply[]; 312 | ``` 313 | 314 | **Returns**: 315 | 316 | Whether current reply got skipped or not. 317 | 318 | **Arguments**: 319 | 320 | | Parameters | Description | Type | Optional | Default value | 321 | | ---------- | ----------- | ---- |:--------:|:-------------:| 322 | | id | Queue identifier | Number \| String | false | - | 323 | 324 |
325 |
326 | 327 | 328 | ## Operation Manager 329 |
330 | 331 | Operation Manager extends QueueManager with a type `Operation`, which will be used below to represent to express one element. 332 | 333 | Please keep this as always valid: 334 | 335 | ```javascript 336 | // for Typescript 337 | import { OperationManager } from "node-telegram-operation-manager"; 338 | 339 | // for Node.js 340 | const { OperationManager } = require("node-telegram-operation-manager"); 341 | 342 | const opm = new OperationManager(); 343 | ``` 344 | 345 | 346 | 347 | ### .register(); 348 | 349 | It adds a new operation in execution list. 350 | 351 | ```typescript 352 | opm.register(id, command, action) : any | undefined; 353 | ``` 354 | 355 | **Returns**: 356 | 357 | The result of action. 358 | 359 | **Arguments**: 360 | 361 | | Parameters | Description | Type | Optional | Default value | 362 | | ---------- | ----------- | ---- |:--------:|:-------------:| 363 | | id | Queue identifier | Number \| String | false | - | 364 | | command | Command identifier | String | false | - | 365 | | action | Function to be executed immediately after queue operation | Function | false | - | 366 | 367 |
368 |
369 | 370 | 371 | 372 | ### .end() 373 | 374 | It removes a specific action or all the actions for a specific identifier, based on the presence or absence of parameter `commandName`. 375 | 376 | ```typescript 377 | opm.end(id, commandName?) : Operation; 378 | ``` 379 | 380 | **Returns**: 381 | 382 | The object itself for chaining. 383 | 384 | **Arguments**: 385 | 386 | | Parameters | Description | Type | Optional | Default value | 387 | | ---------- | ----------- | ---- |:--------:|:-------------:| 388 | | id | Queue identifier | Number \| String | false | - | 389 | | commandName | Command of the element that will be removed | String | true | - | 390 | 391 |
392 |
393 | 394 | 395 | 396 | ### .onGoing() 397 | 398 | Fetches all the active operation. 399 | `Warning: might be useless.` 400 | 401 | ```typescript 402 | opm.onGoing(id) : Operation[]; 403 | ``` 404 | 405 | **Returns**: 406 | 407 | All on going operations for a specific identifier. 408 | 409 | **Arguments**: 410 | 411 | | Parameters | Description | Type | Optional | Default value | 412 | | ---------- | ----------- | ---- |:--------:|:-------------:| 413 | | id | Queue identifier | Number \| String | false | - | 414 | 415 |
416 |
417 | 418 | 419 | 420 | ### .hasActive() 421 | 422 | It checks if there are any active operations in queue for the specified identifier. 423 | 424 | ```typescript 425 | opm.hasActive(id) : boolean; 426 | ``` 427 | 428 | **Returns**: The result of the check. 429 | 430 | **Arguments**: 431 | 432 | | Parameters | Description | Type | Optional | Default value | 433 | | ---------- | ----------- | ---- |:--------:|:-------------:| 434 | | id | Queue identifier | Number \| String | false | - | 435 | 436 |
437 |
438 | 439 | 440 | 441 | ### .hasReachedMaximum() 442 | 443 | Checks whether the limit of concurrent operations has been reached. 444 | 445 | ```typescript 446 | opm.hasReachedMaximum(id): boolean; 447 | ``` 448 | 449 | **Arguments**: 450 | 451 | | Parameters | Description | Type | Optional | Default value | 452 | | ---------- | ----------- | ---- |:--------:|:-------------:| 453 | | id | Queue identifier | Number \| String | false | - | 454 | 455 |
456 |
457 | 458 | 459 | 460 | ### .empty() 461 | 462 | It wipes out a specific operation queue. 463 | 464 | ```typescript 465 | opm.empty(id) : boolean; 466 | ``` 467 | 468 | **Returns**: the operation result. 469 | 470 | **Arguments**: 471 | 472 | | Parameters | Description | Type | Optional | Default value | 473 | | ---------- | ----------- | ---- |:--------:|:-------------:| 474 | | id | Queue identifier | Number \| String | false | - | 475 | 476 |
477 |
478 | 479 | 480 | 481 | ### .maxConcurrent 482 | 483 | It determines the maximum of concurrent operation that may happen. The value must be a number. 484 | 485 | ```typescript 486 | opm.maxConcurrent = N; 487 | ``` 488 | 489 |
490 |
491 | 492 |
493 | 494 | 495 | ## Queue Manager 496 |
497 | 498 | Queue Manager is the basic class for all the classes above. 499 | QueueManager class has type `T`, that will be used to represent objects. 500 | 501 | > _**Please note** that this class should be used **only** as extension for other classes._ 502 | 503 | 504 | Please keep this as always valid: 505 | 506 | ```javascript 507 | // for Typescript 508 | import { QueueManager } from "node-telegram-operation-manager"; 509 | 510 | // for Node.js 511 | const { QueueManager } = require("node-telegram-operation-manager"); 512 | 513 | class newQueueOperator extends QueueManager { 514 | // Here T is QueueOperatorStruct 515 | } 516 | ``` 517 | 518 | 519 | 520 | ### .push(); 521 | 522 | It adds a new operation to the queue. 523 | 524 | ```typescript 525 | reply.push(id, object: T) : number; 526 | ``` 527 | 528 | **Returns**: 529 | 530 | The amount of elements 531 | 532 | **Arguments**: 533 | 534 | | Parameters | Description | Type | Optional | Default value | 535 | | ---------- | ----------- | ---- |:--------:|:-------------:| 536 | | id | Queue identifier | Number \| String | false | - | 537 | | object | The object to be pushed to the queue | Object | false | - | 538 | 539 |
540 |
541 | 542 | 543 | 544 | ### .remove() 545 | 546 | It removes the first element from a specific queue identified. 547 | 548 | ```typescript 549 | .remove(id) : T; 550 | ``` 551 | 552 | **Returns**: 553 | 554 | The removed object. 555 | 556 | **Arguments**: 557 | 558 | | Parameters | Description | Type | Optional | Default value | 559 | | ---------- | ----------- | ---- |:--------:|:-------------:| 560 | | id | Queue identifier | Number \| String | false | - | 561 | 562 |
563 |
564 | 565 | 566 | 567 | ### .removeAll() 568 | 569 | Removes all the elements from a specific queue. 570 | 571 | ```typescript 572 | .removeAll(id) : void; 573 | ``` 574 | 575 | **Arguments**: 576 | 577 | | Parameters | Description | Type | Optional | Default value | 578 | | ---------- | ----------- | ---- |:--------:|:-------------:| 579 | | id | Queue identifier | Number \| String | false | - | 580 | 581 |
582 |
583 | 584 | 585 | 586 | ### .cherryPickRemove() 587 | 588 | Removes a specific element from a specific queue. 589 | 590 | ```typescript 591 | .cherryPickRemove(id, criteria) : T; 592 | ``` 593 | 594 | **Returns**: The removed element if found, empty object otherwise. 595 | 596 | **Arguments**: 597 | 598 | | Parameters | Description | Type | Optional | Default value | 599 | | ---------- | ----------- | ---- |:--------:|:-------------:| 600 | | id | Queue identifier | Number \| String | false | - | 601 | | criteria | A function with this signature: `(value) => boolean` | Function | false | - | 602 | 603 |
604 |
605 | 606 | 607 | 608 | ### .has() 609 | 610 | Checks if a specific identifier has some elements inside its queue. 611 | 612 | ```javascript 613 | .has(id) : boolean; 614 | ``` 615 | 616 | **Returns**: 617 | 618 | The result of operation. 619 | 620 | **Arguments**: 621 | 622 | | Parameters | Description | Type | Optional | Default value | 623 | | ---------- | ----------- | ---- |:--------:|:-------------:| 624 | | id | Queue identifier | Number \| String | false | - | 625 | 626 |
627 |
628 | 629 | 630 | 631 | ### .all() 632 | 633 | Fetches all the elements in a specific queue. 634 | 635 | ```typescript 636 | .all(id) : T[]; 637 | ``` 638 | 639 | **Returns**: the operation result. 640 | 641 | **Arguments**: 642 | 643 | | Parameters | Description | Type | Optional | Default value | 644 | | ---------- | ----------- | ---- |:--------:|:-------------:| 645 | | id | Queue identifier | Number \| String | false | - | 646 | 647 |
648 |
649 | 650 | 651 | 652 | ### .get() 653 | 654 | Fetches the first element in a specific queue. 655 | 656 | ```typescript 657 | .get(id) : T; 658 | ``` 659 | 660 | **Returns**: the first element in the queue. 661 | 662 | **Arguments**: 663 | 664 | | Parameters | Description | Type | Optional | Default value | 665 | | ---------- | ----------- | ---- |:--------:|:-------------:| 666 | | id | Queue identifier | Number \| String | false | - | 667 | 668 |
669 |
670 | 671 | 672 | 673 | ### .get() 674 | 675 | Removes the last element from the queue. 676 | 677 | ```typescript 678 | .pop(id) : T; 679 | ``` 680 | 681 | **Returns**: the removed element. 682 | 683 | **Arguments**: 684 | 685 | | Parameters | Description | Type | Optional | Default value | 686 | | ---------- | ----------- | ---- |:--------:|:-------------:| 687 | | id | Queue identifier | Number \| String | false | - | 688 | 689 |
690 |
691 | 692 | 693 | 694 | ### .emptyQueue() 695 | 696 | Wipes out a specific queue or the whole set of queues, based on presence of `id` parameter. 697 | 698 | ```typescript 699 | .emptyQueue(id?) : number; 700 | ``` 701 | 702 | **Returns**: the amount of removed element or removed sets. 703 | 704 | **Arguments**: 705 | 706 | | Parameters | Description | Type | Optional | Default value | 707 | | ---------- | ----------- | ---- |:--------:|:-------------:| 708 | | id | Queue identifier | Number \| String | true | - | 709 | --------------------------------------------------------------------------------