From 17b18b6d1d27d2f2ac62c5c612d2e72711a251fc Mon Sep 17 00:00:00 2001 From: chinmayshewale Date: Sat, 1 Apr 2023 21:34:57 +0530 Subject: [PATCH 1/2] Implemented the functionality --- DemoApp.ts | 65 +++++++++++++++-- app.json | 8 ++- commands/ScheduleCommand.ts | 83 ++++++++++++++++++++++ enums/actionId.ts | 10 +++ enums/blockId.ts | 4 ++ enums/viewId.ts | 4 ++ handlers/ViewSubmit.ts | 135 ++++++++++++++++++++++++++++++++++++ lib/persistence.ts | 1 + lib/sendMessage.ts | 49 +++++++++++-- lib/sendNotification.ts | 2 +- modals/addJobModal.ts | 93 +++++++++++++++++++++++++ modals/addReminderModal.ts | 113 ++++++++++++++++++++++++++++++ 12 files changed, 552 insertions(+), 15 deletions(-) create mode 100644 commands/ScheduleCommand.ts create mode 100644 enums/actionId.ts create mode 100644 enums/blockId.ts create mode 100644 enums/viewId.ts create mode 100644 lib/persistence.ts create mode 100644 modals/addJobModal.ts create mode 100644 modals/addReminderModal.ts diff --git a/DemoApp.ts b/DemoApp.ts index 9397c43..257a0fc 100644 --- a/DemoApp.ts +++ b/DemoApp.ts @@ -24,17 +24,14 @@ import { ExampleEndpoint } from "./api/ExampleEndPoint"; import { ApiWithPersistence } from "./api/PersistenceWithEndPoint"; import { ExampleCommand } from "./commands/ExampleCommand"; import { IncrementCommand } from "./commands/IncrementCommand"; +import { ScheduleCommand } from "./commands/ScheduleCommand"; import { buttons } from "./config/Buttons"; import { settings } from "./config/Settings"; import { ExampleActionButtonHandler } from "./handlers/ActionButton"; import { ExampleViewSubmitHandler } from "./handlers/ViewSubmit"; export class DemoAppApp extends App { - constructor( - info: IAppInfo, - logger: ILogger, - accessors: IAppAccessors, - ) { + constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) { super(info, logger, accessors); } @@ -63,12 +60,63 @@ export class DemoAppApp extends App { new ExampleCommand(this) ); await configuration.slashCommands.provideSlashCommand( - new IncrementCommand(this), + new IncrementCommand(this) + ); + await configuration.slashCommands.provideSlashCommand( + new ScheduleCommand(this) ); // Registering Action Buttons await Promise.all( buttons.map((button) => configuration.ui.registerButton(button)) ); + // Registring schedluing processors + await configuration.scheduler.registerProcessors([ + { + id: "reminder", + processor: async (jobContext, read, modify, http, persis) => { + const block = modify.getCreator().getBlockBuilder(); + const time = jobContext.time; + const text = jobContext.message; + const username = jobContext.username; + block.addSectionBlock({ + text: block.newPlainTextObject(`@${username} You created a reminder to remind you at ${time} + *About* + ${text}`), + }); + const message = modify + .getCreator() + .startMessage() + .addBlocks(block.getBlocks()) + .setRoom( + (await read + .getRoomReader() + .getById(jobContext.room))! + ); + await modify.getCreator().finish(message); + }, + }, + { + id: "job", + processor: async (jobContext, read, modify, http, persis) => { + const block = modify.getCreator().getBlockBuilder(); + block.addSectionBlock({ + text: block.newPlainTextObject(`@${jobContext.username} You created a recurring reminder to remind you at an interval of ${jobContext.interval} + *About* + ${jobContext.message}`), + }); + const message = modify + .getCreator() + .startMessage() + .addBlocks(block.getBlocks()) + .setRoom( + (await read + .getRoomReader() + .getById(jobContext.room))! + ); + await modify.getCreator().finish(message); + }, + }, + ]); } public async onSettingUpdated( @@ -81,7 +129,10 @@ export class DemoAppApp extends App { // this will show in ADMIN > APPS > INSTALLED APP > THIS APP > LOGS // Log from inside the app // note that you can pass both any or a list or any - let list_to_log = ["Some Setting was Updated. SUCCESS MESSAGE: ", setting] + let list_to_log = [ + "Some Setting was Updated. SUCCESS MESSAGE: ", + setting, + ]; // you can have a different type of logs: this.getLogger().success(list_to_log); this.getLogger().info(list_to_log); diff --git a/app.json b/app.json index 28da0f1..971e10c 100644 --- a/app.json +++ b/app.json @@ -1,7 +1,7 @@ { "id": "67a4e34b-5c04-4c9c-9358-3d0fd217cefd", "version": "0.0.1", - "requiredApiVersion": "^1.19.0", + "requiredApiVersion": "^1.36.0", "iconFile": "icon.png", "author": { "name": "Duda Nogueira", @@ -34,6 +34,12 @@ }, { "name": "persistence" + }, + { + "name": "ui.interact" + }, + { + "name": "scheduler" } ] } \ No newline at end of file diff --git a/commands/ScheduleCommand.ts b/commands/ScheduleCommand.ts new file mode 100644 index 0000000..091e9fd --- /dev/null +++ b/commands/ScheduleCommand.ts @@ -0,0 +1,83 @@ +import { + IHttp, + IModify, + IRead, +} from "@rocket.chat/apps-engine/definition/accessors"; +import { IRoom, RoomType } from "@rocket.chat/apps-engine/definition/rooms"; +import { + ISlashCommand, + SlashCommandContext, +} from "@rocket.chat/apps-engine/definition/slashcommands"; +import { IUser } from "@rocket.chat/apps-engine/definition/users"; +import { DemoAppApp } from "../DemoApp"; +import { sendMessage } from "../lib/sendMessage"; +import { sendNotification } from "../lib/sendNotification"; +import { addJob } from "../modals/addJobModal"; +import { addReminder } from "../modals/addReminderModal"; + +export class ScheduleCommand implements ISlashCommand { + public command = "schedule"; // here is where you define the command name, + // users will need to run /schedule to trigger this command + public i18nParamsExample = "ScheduleCommand_Params"; + public i18nDescription = "ScheduleCommand_Description"; + public providesPreview = false; + + constructor(private readonly app: DemoAppApp) {} + + public async executor( + context: SlashCommandContext, + read: IRead, + modify: IModify, + http: IHttp + ): Promise { + // lets log this slash command call + this.app + .getLogger() + .info( + `Slash Command /${ + this.command + } initiated. Trigger id: ${context.getTriggerId()} with arguments ${context.getArguments()}` + ); + // let's discover if we have a subcommand + const [subcommand] = context.getArguments(); + const room = context.getRoom(); + const sender = context.getSender(); + read.getUserReader(); + // lets define a deult help message + const helpMessage = ` + *You can schedule your tasks or reminders with this command* + Schedule a reminder -> \`/schedule [reminder|r]\` + Schedule a recurring reminer -> \`/schedule [job|j]\` + Helper message for this command -> \`/schedule [help|h]\``; + if (!subcommand) { + // no subcommand, let's just show that + var message = `No Subcommand :confounded: + ${helpMessage}`; + await sendNotification(modify, room, sender, message); + } else { + switch ( + subcommand // Try to match the argument in the list of allowed subcommands + ) { + case "r": + case "reminder": // If Subcommand is reminder or r then + await addReminder(context, read, modify); + break; + + case "j": + case "job": // If Subcommand is job or then + await addJob(context, read, modify); + break; + + case "delete": + await modify.getScheduler().cancelAllJobs(); + break; + + case "h": + case "help": + default: // If subcommand is some giberish or help or h is the subcommand then send help message + await sendNotification(modify, room, sender, helpMessage); + break; + } + } + } +} diff --git a/enums/actionId.ts b/enums/actionId.ts new file mode 100644 index 0000000..de791f5 --- /dev/null +++ b/enums/actionId.ts @@ -0,0 +1,10 @@ +export enum actionId { + MESSAGE='message', + DATE='date', + INTERVAL='interval', + FORMAT='format', + TIME='time', + REMINDER_SUBMIT='reminder_submit', + JOB_SUBMIT='job-submit', + ROOM='room' +} diff --git a/enums/blockId.ts b/enums/blockId.ts new file mode 100644 index 0000000..54d8969 --- /dev/null +++ b/enums/blockId.ts @@ -0,0 +1,4 @@ +export enum blockId { + REMINDER='reminder', + JOB='job' +} diff --git a/enums/viewId.ts b/enums/viewId.ts new file mode 100644 index 0000000..4fa8c2b --- /dev/null +++ b/enums/viewId.ts @@ -0,0 +1,4 @@ +export enum viewId { + REMINDER='reminder', + JOB='job' +} diff --git a/handlers/ViewSubmit.ts b/handlers/ViewSubmit.ts index 0ae9c5d..f125488 100644 --- a/handlers/ViewSubmit.ts +++ b/handlers/ViewSubmit.ts @@ -5,7 +5,13 @@ import { IPersistence, IRead, } from "@rocket.chat/apps-engine/definition/accessors"; +import { IRoom } from "@rocket.chat/apps-engine/definition/rooms"; import { UIKitViewSubmitInteractionContext } from "@rocket.chat/apps-engine/definition/uikit"; +import { actionId } from "../enums/actionId"; +import { blockId } from "../enums/blockId"; +import { viewId } from "../enums/viewId"; +import { getDirectRoom, sendMessage } from "../lib/sendMessage"; +import { sendNotification } from "../lib/sendNotification"; export class ExampleViewSubmitHandler { public async executor( @@ -16,6 +22,135 @@ export class ExampleViewSubmitHandler { modify: IModify, logger?: ILogger ) { + const { user, view } = context.getInteractionData(); + let roomId: string, room: IRoom; + + let response = "The request couldn't be performed"; + try { + switch (view.id) { + case viewId.REMINDER: + const date = + view.state?.[blockId.REMINDER]?.[actionId.DATE]; + const time = + view.state?.[blockId.REMINDER]?.[actionId.TIME]; + const message = + view.state?.[blockId.REMINDER]?.[actionId.MESSAGE]; + const format = + view.state?.[blockId.REMINDER]?.[actionId.FORMAT]; + roomId = view.state?.[blockId.REMINDER]?.[actionId.ROOM]; + if (roomId === "DM") { + roomId = (await getDirectRoom( + read, + modify, + (await read.getUserReader().getAppUser())!, + context.getInteractionData().user.username + ))!; + } + console.log(roomId + "Roomid"); + room = (await read.getRoomReader().getById(roomId))!; + const jobContext = { + time: date + " " + time + format, + message: message, + username: user.username, + room: roomId, + }; + const reminder = { + id: "reminder", + when: date + time + format + user.utcOffset, + data: jobContext, + }; + if (user.id) { + const jobId = await modify + .getScheduler() + .scheduleOnce(reminder); + response = `Yoohoo I have created a reminder to remind you at ${ + date + " " + time + " " + format + " " + }\n + About + ${message}`; + if ( + view.state?.[blockId.JOB]?.[actionId.ROOM] === "DM" + ) { + await sendMessage( + modify, + room!, + (await read.getUserReader().getAppUser())!, + response + ); + } else { + await sendNotification( + modify, + room!, + (await read.getUserReader().getAppUser())!, + response + ); + } + } + break; + case viewId.JOB: + const text = view.state?.[blockId.JOB]?.[actionId.MESSAGE]; + const interval = + view.state?.[blockId.JOB]?.[actionId.INTERVAL]; + const number = view.state?.[blockId.JOB]?.[actionId.FORMAT]; + const jobmessage = + view.state?.[blockId.JOB]?.[actionId.MESSAGE]; + roomId = view.state?.[blockId.JOB]?.[actionId.ROOM]; + if (roomId === "DM") { + roomId = (await getDirectRoom( + read, + modify, + (await read.getUserReader().getAppUser())!, + context.getInteractionData().user.username + ))!; + } + room = (await read.getRoomReader().getById(roomId))!; + const data = { + message: text, + interval: + number + " " + interval + " " + user.utcOffset, + username: user.username, + }; + const job = { + id: "job", + interval: number + " " + interval, + data: data, + room: roomId, + }; + if (user.id) { + let jobid = await modify + .getScheduler() + .scheduleRecurring(job); + console.log(jobid); + console.log(response); + response = `Yoohoo you have successfully created a recurring reminder of interval ${ + number + " " + interval + } + About + ${jobmessage}`; + if ( + view.state?.[blockId.JOB]?.[actionId.ROOM] === "DM" + ) { + await sendMessage( + modify, + room!, + (await read.getUserReader().getAppUser())!, + response + ); + } else { + await sendNotification( + modify, + room!, + (await read.getUserReader().getAppUser())!, + response + ); + } + } + break; + } + } catch (e) { + console.log(e); + console.log("Error submitting view"); + } return { success: true, }; diff --git a/lib/persistence.ts b/lib/persistence.ts new file mode 100644 index 0000000..2d52d61 --- /dev/null +++ b/lib/persistence.ts @@ -0,0 +1 @@ +// erdg diff --git a/lib/sendMessage.ts b/lib/sendMessage.ts index 15f6703..34af14c 100644 --- a/lib/sendMessage.ts +++ b/lib/sendMessage.ts @@ -1,18 +1,55 @@ -import { IModify, IPersistence, IRead } from '@rocket.chat/apps-engine/definition/accessors'; -import { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; -import { IUser } from '@rocket.chat/apps-engine/definition/users'; +import { + IModify, + IPersistence, + IRead, +} from "@rocket.chat/apps-engine/definition/accessors"; +import { IRoom, RoomType } from "@rocket.chat/apps-engine/definition/rooms"; +import { IUser } from "@rocket.chat/apps-engine/definition/users"; export async function sendMessage( modify: IModify, room: IRoom, sender: IUser, - message: string, + message: string ): Promise { - - const msg = modify.getCreator().startMessage() + const msg = modify + .getCreator() + .startMessage() .setSender(sender) .setRoom(room) .setText(message); return await modify.getCreator().finish(msg); } + +export async function getDirectRoom( + read: IRead, + modify: IModify, + appUser: IUser, + username: string +) { + const usernames = [appUser.username, username]; + let room: IRoom; + try { + room = await read.getRoomReader().getDirectByUsernames(usernames); + } catch (error) { + console.log(error); + return; + } + + if (room) { + return room.id; + } else { + let roomId: string; + + // Create direct room between botUser and username + const newRoom = modify + .getCreator() + .startRoom() + .setType(RoomType.DIRECT_MESSAGE) + .setCreator(appUser) + .setMembersToBeAddedByUsernames(usernames); + roomId = await modify.getCreator().finish(newRoom); + return roomId; + } +} diff --git a/lib/sendNotification.ts b/lib/sendNotification.ts index 95f7a79..e729b91 100644 --- a/lib/sendNotification.ts +++ b/lib/sendNotification.ts @@ -21,7 +21,7 @@ export async function sendNotification( const block = modify.getCreator().getBlockBuilder(); // we want this block to have a Text supporting MarkDown block.addSectionBlock({ - text: block.newMarkdownTextObject(message), + text: block.newPlainTextObject(message), }); // now let's set the blocks in our message diff --git a/modals/addJobModal.ts b/modals/addJobModal.ts new file mode 100644 index 0000000..ad04584 --- /dev/null +++ b/modals/addJobModal.ts @@ -0,0 +1,93 @@ +import { IModify, IRead } from "@rocket.chat/apps-engine/definition/accessors"; +import { SlashCommandContext } from "@rocket.chat/apps-engine/definition/slashcommands"; +import { actionId } from "../enums/actionId"; +import { blockId } from "../enums/blockId"; +import { viewId } from "../enums/viewId"; + +export async function addJob( + context: SlashCommandContext, + read: IRead, + modify: IModify +) { + // Setting the viewId to identify action after view submit + const block = modify.getCreator().getBlockBuilder(); + + block.addInputBlock({ + blockId: blockId.JOB, + element: block.newPlainTextInputElement({ + actionId: actionId.MESSAGE, + multiline: !0, + placeholder: block.newPlainTextObject( + "Message to get reminded about" + ), + }), + label: block.newPlainTextObject("Reminder message"), + }); + block.addInputBlock({ + blockId: blockId.JOB, + element: block.newStaticSelectElement({ + actionId: actionId.ROOM, + options: [ + { + text: block.newPlainTextObject("Room"), + value: context.getRoom().id, + }, + { + text: block.newPlainTextObject("Direct message"), + value: "DM", + }, + ], + placeholder: block.newPlainTextObject("Room"), + }), + label: block.newPlainTextObject("Room"), + }); + block.addInputBlock({ + blockId: blockId.JOB, + element: block.newStaticSelectElement({ + actionId: actionId.INTERVAL, + placeholder: block.newPlainTextObject("Interval"), + options: [ + { + text: block.newPlainTextObject("Days"), + value: "days", + }, + { + text: block.newPlainTextObject("Minutes"), + value: "minutes", + }, + { + text: block.newPlainTextObject("Hours"), + value: "hours", + }, + ], + }), + label: block.newPlainTextObject("Interval"), + }); + + block.addInputBlock({ + blockId: blockId.JOB, + element: block.newPlainTextInputElement({ + actionId: actionId.FORMAT, + placeholder: block.newPlainTextObject( + "The interval in which you want to get reminded" + ), + }), + label: block.newPlainTextObject("Number of intervals"), + }); + const modal = { + id: viewId.JOB, + title: block.newPlainTextObject("Reminder"), + close: block.newButtonElement({ + text: block.newPlainTextObject("Close"), + }), + submit: block.newButtonElement({ + actionId: actionId.JOB_SUBMIT, + text: block.newPlainTextObject("Create"), + }), + blocks: block.getBlocks(), + }; + const triggerId = context.getTriggerId()!; + await modify + .getUiController() + .openModalView(modal, { triggerId }, context.getSender()); +} diff --git a/modals/addReminderModal.ts b/modals/addReminderModal.ts new file mode 100644 index 0000000..c6795b7 --- /dev/null +++ b/modals/addReminderModal.ts @@ -0,0 +1,113 @@ +import { IModify, IRead } from "@rocket.chat/apps-engine/definition/accessors"; +import { SlashCommandContext } from "@rocket.chat/apps-engine/definition/slashcommands"; +import { + BlockElementType, + TextObjectType, +} from "@rocket.chat/apps-engine/definition/uikit"; +import { IUIKitModalViewParam } from "@rocket.chat/apps-engine/definition/uikit/UIKitInteractionResponder"; +import { actionId } from "../enums/actionId"; +import { blockId } from "../enums/blockId"; +import { viewId } from "../enums/viewId"; + +export async function addReminder( + context: SlashCommandContext, + read: IRead, + modify: IModify +) { + // Setting the viewId to identify action after view submit + const block = modify.getCreator().getBlockBuilder(); + block.addInputBlock({ + blockId: blockId.REMINDER, + element: { + actionId: actionId.DATE, + type: "datepicker" as BlockElementType, + placeholder: block.newPlainTextObject("Enter the date"), + }, + label: block.newPlainTextObject("Date"), + }); + // block.addInputBlock({ + // blockId: blockId.REMINDER, + // element: block.newStaticSelectElement({ + // actionId: actionId.TIME, + // placeholder: block.newPlainTextObject("Choose what time"), + // options: [ + // { + // text: block.newPlainTextObject("10:45"), + // value: "10:45", + // }, + // ], + // }), + // label: block.newPlainTextObject("Time"), + // }); + block.addInputBlock({ + blockId: blockId.REMINDER, + element: block.newPlainTextInputElement({ + actionId: actionId.TIME, + placeholder: block.newPlainTextObject("Eg:- 10:45"), + }), + label: block.newPlainTextObject("Time"), + }); + block.addInputBlock({ + blockId: blockId.REMINDER, + element: block.newStaticSelectElement({ + actionId: actionId.FORMAT, + placeholder: block.newPlainTextObject("Choose what time"), + options: [ + { + text: block.newPlainTextObject("AM"), + value: "AM", + }, + { + text: block.newPlainTextObject("PM"), + value: "PM", + }, + ], + }), + label: block.newPlainTextObject("AM/PM"), + }); + block.addInputBlock({ + blockId: blockId.REMINDER, + element: block.newPlainTextInputElement({ + actionId: actionId.MESSAGE, + multiline: !0, + placeholder: block.newPlainTextObject( + "Message to get reminded about" + ), + }), + label: block.newPlainTextObject("Reminder message"), + }); + block.addInputBlock({ + blockId: blockId.REMINDER, + element: block.newStaticSelectElement({ + actionId: actionId.ROOM, + options: [ + { + text: block.newPlainTextObject("Room"), + value: context.getRoom().id, + }, + { + text: block.newPlainTextObject("Direct message"), + value: "DM", + }, + ], + placeholder: block.newPlainTextObject("Room"), + }), + label: block.newPlainTextObject("Room"), + }); + const modal = { + id: viewId.REMINDER, + title: block.newPlainTextObject("Reminder"), + close: block.newButtonElement({ + text: block.newPlainTextObject("Close"), + }), + submit: block.newButtonElement({ + actionId: actionId.REMINDER_SUBMIT, + text: block.newPlainTextObject("Create"), + }), + blocks: block.getBlocks(), + }; + const triggerId = context.getTriggerId()!; + await modify + .getUiController() + .openModalView(modal, { triggerId }, context.getSender()); +} From 91b32c4bb1849134d80edc420daf0062689bb3ef Mon Sep 17 00:00:00 2001 From: chinmayshewale Date: Mon, 3 Apr 2023 04:22:37 +0530 Subject: [PATCH 2/2] Added comments --- DemoApp.ts | 1 + commands/ScheduleCommand.ts | 2 +- enums/actionId.ts | 6 + enums/blockId.ts | 6 + enums/viewId.ts | 6 + handlers/ViewSubmit.ts | 59 ++++-- lib/persistence.ts | 1 - modals/addJobModal.ts | 35 +++- modals/addReminderModal.ts | 57 ++++-- package-lock.json | 359 +++++++++++++++++++++++++++--------- 10 files changed, 405 insertions(+), 127 deletions(-) delete mode 100644 lib/persistence.ts diff --git a/DemoApp.ts b/DemoApp.ts index 257a0fc..4a9bbfc 100644 --- a/DemoApp.ts +++ b/DemoApp.ts @@ -70,6 +70,7 @@ export class DemoAppApp extends App { buttons.map((button) => configuration.ui.registerButton(button)) ); // Registring schedluing processors + // This processor can be scheduled using the process id await configuration.scheduler.registerProcessors([ { id: "reminder", diff --git a/commands/ScheduleCommand.ts b/commands/ScheduleCommand.ts index 091e9fd..9fb2990 100644 --- a/commands/ScheduleCommand.ts +++ b/commands/ScheduleCommand.ts @@ -43,7 +43,7 @@ export class ScheduleCommand implements ISlashCommand { const room = context.getRoom(); const sender = context.getSender(); read.getUserReader(); - // lets define a deult help message + // lets define a default help message const helpMessage = ` *You can schedule your tasks or reminders with this command* Schedule a reminder -> \`/schedule [reminder|r]\` diff --git a/enums/actionId.ts b/enums/actionId.ts index de791f5..329fa9c 100644 --- a/enums/actionId.ts +++ b/enums/actionId.ts @@ -1,3 +1,9 @@ +/* +Enums help to reduce redundancy in the code, as they allow you to define a set of +related constantsin a single place and then reuse them throughout your code. +This makes your code more maintainable, as any changes to the set of allowed values +can be made in one place, rather than scattered throughout your codebase.*/ + export enum actionId { MESSAGE='message', DATE='date', diff --git a/enums/blockId.ts b/enums/blockId.ts index 54d8969..c625750 100644 --- a/enums/blockId.ts +++ b/enums/blockId.ts @@ -1,3 +1,9 @@ +/* +Enums help to reduce redundancy in the code, as they allow you to define a set of +related constantsin a single place and then reuse them throughout your code. +This makes your code more maintainable, as any changes to the set of allowed values +can be made in one place, rather than scattered throughout your codebase. +*/ export enum blockId { REMINDER='reminder', JOB='job' diff --git a/enums/viewId.ts b/enums/viewId.ts index 4fa8c2b..12d61d0 100644 --- a/enums/viewId.ts +++ b/enums/viewId.ts @@ -1,3 +1,9 @@ +/* +Enums help to reduce redundancy in the code, as they allow you to define a set of +related constantsin a single place and then reuse them throughout your code. +This makes your code more maintainable, as any changes to the set of allowed values +can be made in one place, rather than scattered throughout your codebase. +*/ export enum viewId { REMINDER='reminder', JOB='job' diff --git a/handlers/ViewSubmit.ts b/handlers/ViewSubmit.ts index f125488..a84069d 100644 --- a/handlers/ViewSubmit.ts +++ b/handlers/ViewSubmit.ts @@ -12,6 +12,7 @@ import { blockId } from "../enums/blockId"; import { viewId } from "../enums/viewId"; import { getDirectRoom, sendMessage } from "../lib/sendMessage"; import { sendNotification } from "../lib/sendNotification"; +import { IOnetimeSchedule, IRecurringSchedule } from "@rocket.chat/apps-engine/definition/scheduler"; export class ExampleViewSubmitHandler { public async executor( @@ -22,11 +23,41 @@ export class ExampleViewSubmitHandler { modify: IModify, logger?: ILogger ) { + // Once view is submitted the conntrol of the app is here + // We get the user details who submitted the view + // and the view object which contains details related to the view const { user, view } = context.getInteractionData(); let roomId: string, room: IRoom; let response = "The request couldn't be performed"; try { + // We handle which view is submitted on the basis of viewId configured in enums/viewId + + /* + The view object has a state object which contains all data related to the + view blocks and elements which user has entered in the input block or + the app has set + The object is constructed like this for example: + view ={ + state:{ + blockId1:{ + actionId1:data1, + actionId2:data2, + . + . + }, + blockId2:{ + actionId:data1, + actionId:data2, + }, + . + . + . + } + } + + We can access the data as as shown below + */ switch (view.id) { case viewId.REMINDER: const date = @@ -39,6 +70,7 @@ export class ExampleViewSubmitHandler { view.state?.[blockId.REMINDER]?.[actionId.FORMAT]; roomId = view.state?.[blockId.REMINDER]?.[actionId.ROOM]; if (roomId === "DM") { + // Retrieving the roomId of the direct message roomId = (await getDirectRoom( read, modify, @@ -46,28 +78,30 @@ export class ExampleViewSubmitHandler { context.getInteractionData().user.username ))!; } - console.log(roomId + "Roomid"); room = (await read.getRoomReader().getById(roomId))!; + // Configuring the job context for the scheduler processor const jobContext = { time: date + " " + time + format, message: message, username: user.username, room: roomId, }; - const reminder = { + // Creating a one time scheduler + const reminder: IOnetimeSchedule = { id: "reminder", when: date + time + format + user.utcOffset, data: jobContext, }; if (user.id) { + // Now we schedule the process using the scheduler and it returns us the jobId for the process const jobId = await modify .getScheduler() .scheduleOnce(reminder); - response = `Yoohoo I have created a reminder to remind you at ${ - date + " " + time + " " + format + " " - }\n + response = `Yoohoo I have created a reminder to remind you at ${date + " " + time + " " + format + " " + }\n About ${message}`; + // once it is configured we send a message for confimation if ( view.state?.[blockId.JOB]?.[actionId.ROOM] === "DM" ) { @@ -104,27 +138,28 @@ export class ExampleViewSubmitHandler { ))!; } room = (await read.getRoomReader().getById(roomId))!; + // Configuring the job context for the scheduler processor const data = { message: text, interval: number + " " + interval + " " + user.utcOffset, username: user.username, + room: roomId, }; - const job = { + // Creating a recurring scheduler + const job: IRecurringSchedule = { id: "job", interval: number + " " + interval, data: data, - room: roomId, }; if (user.id) { + // Now we schedule the process using the scheduler and it returns us the jobId for the process + let jobid = await modify .getScheduler() .scheduleRecurring(job); - console.log(jobid); - console.log(response); - response = `Yoohoo you have successfully created a recurring reminder of interval ${ - number + " " + interval - } + response = `Yoohoo you have successfully created a recurring reminder of interval ${number + " " + interval + } About ${jobmessage}`; if ( diff --git a/lib/persistence.ts b/lib/persistence.ts deleted file mode 100644 index 2d52d61..0000000 --- a/lib/persistence.ts +++ /dev/null @@ -1 +0,0 @@ -// erdg diff --git a/modals/addJobModal.ts b/modals/addJobModal.ts index ad04584..70838c3 100644 --- a/modals/addJobModal.ts +++ b/modals/addJobModal.ts @@ -9,9 +9,21 @@ export async function addJob( read: IRead, modify: IModify ) { - // Setting the viewId to identify action after view submit + // We start building the modal by getting an instance of block builder + // We can build a modal using blocks and elements in the block const block = modify.getCreator().getBlockBuilder(); +/* + We now add blocks to the block builder using various methods + Here we are adding an input block + A block must have an element and a label , other are optional + blockId is an identifier for the block and actionId is an identifier for the element + Also an action is triggered for the action block and can be handled using the + actionId of the element + We use enums for blockIds or actionIds which eliminates the redundancy and + errors in the code + */ + // Adding multiline input block for reminder message block.addInputBlock({ blockId: blockId.JOB, element: block.newPlainTextInputElement({ @@ -23,6 +35,8 @@ export async function addJob( }), label: block.newPlainTextObject("Reminder message"), }); + + // Adding option block for selecting the room to send the reminder block.addInputBlock({ blockId: blockId.JOB, element: block.newStaticSelectElement({ @@ -30,7 +44,7 @@ export async function addJob( options: [ { text: block.newPlainTextObject("Room"), - value: context.getRoom().id, + value: context.getRoom().id, // Here the value is the roomId which we will use in further configuration }, { text: block.newPlainTextObject("Direct message"), @@ -41,6 +55,8 @@ export async function addJob( }), label: block.newPlainTextObject("Room"), }); + + // Adding option input block for time interval selection block.addInputBlock({ blockId: blockId.JOB, element: block.newStaticSelectElement({ @@ -64,6 +80,7 @@ export async function addJob( label: block.newPlainTextObject("Interval"), }); + // Adding an input block for geting the number of interval block.addInputBlock({ blockId: blockId.JOB, element: block.newPlainTextInputElement({ @@ -74,6 +91,13 @@ export async function addJob( }), label: block.newPlainTextObject("Number of intervals"), }); + + /* + After adding all blocks we now have to set the viewId to + the modal and also provide a submit action button + Once the user clicks on the submit button the viewSubmitHandler is + triggered with id given here as view.JOB + */ const modal = { id: viewId.JOB, title: block.newPlainTextObject("Reminder"), @@ -87,7 +111,14 @@ export async function addJob( blocks: block.getBlocks(), }; const triggerId = context.getTriggerId()!; + /* + Once all this is configured we open the view with the help of + UI controller by providing modal, triggerId and the sender details + This opens a view for the user + */ await modify .getUiController() .openModalView(modal, { triggerId }, context.getSender()); + // Now if the user submmits the view the ../handlers/ViewSubmit where we have the + // viewsubmithandler gets triggered } diff --git a/modals/addReminderModal.ts b/modals/addReminderModal.ts index c6795b7..4d1b8b9 100644 --- a/modals/addReminderModal.ts +++ b/modals/addReminderModal.ts @@ -14,8 +14,22 @@ export async function addReminder( read: IRead, modify: IModify ) { - // Setting the viewId to identify action after view submit + // We start building the modal by getting an instance of block builder + // We can build a modal using blocks and elements in the block const block = modify.getCreator().getBlockBuilder(); + + /* + We now add blocks to the block builder using various methods + Here we are adding an input block + A block must have an element and a label , other are optional + blockId is an identifier for the block and actionId is an identifier for the element + Also an action is triggered for the action block and can be handled using the + actionId of the element + We use enums for blockIds or actionIds which eliminates the redundancy and + errors in the code + */ + + // Adding a datepicker input block block.addInputBlock({ blockId: blockId.REMINDER, element: { @@ -25,20 +39,7 @@ export async function addReminder( }, label: block.newPlainTextObject("Date"), }); - // block.addInputBlock({ - // blockId: blockId.REMINDER, - // element: block.newStaticSelectElement({ - // actionId: actionId.TIME, - // placeholder: block.newPlainTextObject("Choose what time"), - // options: [ - // { - // text: block.newPlainTextObject("10:45"), - // value: "10:45", - // }, - // ], - // }), - // label: block.newPlainTextObject("Time"), - // }); + // Adding a time input block block.addInputBlock({ blockId: blockId.REMINDER, element: block.newPlainTextInputElement({ @@ -47,15 +48,16 @@ export async function addReminder( }), label: block.newPlainTextObject("Time"), }); + // Adding a time format input block which is a input to select among only AM and PM block.addInputBlock({ blockId: blockId.REMINDER, element: block.newStaticSelectElement({ actionId: actionId.FORMAT, - placeholder: block.newPlainTextObject("Choose what time"), + placeholder: block.newPlainTextObject("Choose what time"), // A placeholder shown to user for input options: [ { - text: block.newPlainTextObject("AM"), - value: "AM", + text: block.newPlainTextObject("AM"), // Option text which user sees + value: "AM", // The value of the option we use in our app }, { text: block.newPlainTextObject("PM"), @@ -65,6 +67,7 @@ export async function addReminder( }), label: block.newPlainTextObject("AM/PM"), }); + // Adding a multiline input block for reminder message input block.addInputBlock({ blockId: blockId.REMINDER, element: block.newPlainTextInputElement({ @@ -76,6 +79,7 @@ export async function addReminder( }), label: block.newPlainTextObject("Reminder message"), }); + // Adding the option input block to ask for which room to share the reminder message block.addInputBlock({ blockId: blockId.REMINDER, element: block.newStaticSelectElement({ @@ -83,7 +87,8 @@ export async function addReminder( options: [ { text: block.newPlainTextObject("Room"), - value: context.getRoom().id, + value: context.getRoom().id, // Observer here the value , + // the value here is the Id of the room }, { text: block.newPlainTextObject("Direct message"), @@ -94,6 +99,12 @@ export async function addReminder( }), label: block.newPlainTextObject("Room"), }); + /* + After adding all blocks we now have to set the viewId to + the modal and also provide a submit action button + Once the user clicks on the submit button the viewSubmitHandler is + triggered with id given here as view.REMINDER + */ const modal = { id: viewId.REMINDER, title: block.newPlainTextObject("Reminder"), @@ -107,7 +118,15 @@ export async function addReminder( blocks: block.getBlocks(), }; const triggerId = context.getTriggerId()!; + /* + Once all this is configured we open the view with the help of + UI controller by providing modal, triggerId and the sender details + This opens a view for the user + */ await modify .getUiController() .openModalView(modal, { triggerId }, context.getSender()); + // Now if the user submmits the view the ../handlers/ViewSubmit where we have the + // viewsubmithandler gets triggered + } diff --git a/package-lock.json b/package-lock.json index 5f41c38..611dcfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,368 +1,504 @@ { + "name": "Rocket.Chat.Demo.App", + "lockfileVersion": 3, "requires": true, - "lockfileVersion": 1, - "dependencies": { - "@babel/code-frame": { + "packages": { + "": { + "devDependencies": { + "@rocket.chat/apps-engine": "^1.19.0", + "@types/node": "14.14.6", + "tslint": "^5.10.0", + "typescript": "^4.0.5" + } + }, + "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, - "requires": { + "dependencies": { "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-validator-identifier": { + "node_modules/@babel/helper-validator-identifier": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/highlight": { + "node_modules/@babel/highlight": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@rocket.chat/apps-engine": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/@rocket.chat/apps-engine/-/apps-engine-1.33.0.tgz", - "integrity": "sha512-cotl2KL8VMZ4TS6gM+bIjGEfLA2pON0dIfrDLHQU+UorKbDOcivDWRv83E+9MXqoFyGa5Pc6c6GqiNwdJvQlyw==", + "node_modules/@rocket.chat/apps-engine": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/@rocket.chat/apps-engine/-/apps-engine-1.37.0.tgz", + "integrity": "sha512-faFmNo0k71DdULr59vWN8a6cAcheKW09Y6HuEz61VkMAqmE/6slcIFJ1uTUKM/qDFTPeSXlC20EI8YZ2C9Azgg==", "dev": true, - "requires": { + "dependencies": { "adm-zip": "^0.5.9", "cryptiles": "^4.1.3", + "jose": "^4.11.1", "lodash.clonedeep": "^4.5.0", "semver": "^5.7.1", "stack-trace": "0.0.10", - "uuid": "^3.4.0" + "uuid": "^3.4.0", + "vm2": "^3.9.11" + }, + "peerDependencies": { + "@rocket.chat/ui-kit": "next" } }, - "@types/node": { + "node_modules/@rocket.chat/ui-kit": { + "version": "0.32.0-dev.234", + "resolved": "https://registry.npmjs.org/@rocket.chat/ui-kit/-/ui-kit-0.32.0-dev.234.tgz", + "integrity": "sha512-8CqDUTiq7e9O36i24vycDdA/whLOSwEKQHi4p2gJC+X0cgrwQs9kWBXogE59c8Lp8tVcn9ah1ADC/6zUuswoSQ==", + "dev": true, + "peer": true + }, + "node_modules/@types/node": { "version": "14.14.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.6.tgz", "integrity": "sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==", "dev": true }, - "adm-zip": { + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { "version": "0.5.9", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.9.tgz", "integrity": "sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.0" + } }, - "ansi-styles": { + "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "requires": { + "dependencies": { "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "argparse": { + "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "requires": { + "dependencies": { "sprintf-js": "~1.0.2" } }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "boom": { + "node_modules/boom": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/boom/-/boom-7.3.0.tgz", "integrity": "sha512-Swpoyi2t5+GhOEGw8rEsKvTxFLIDiiKoUc2gsoV6Lyr43LHBIzch3k2MvYUs8RTROrIkVJ3Al0TkaOGjnb+B6A==", + "deprecated": "This module has moved and is now available at @hapi/boom. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.", "dev": true, - "requires": { + "dependencies": { "hoek": "6.x.x" } }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "builtin-modules": { + "node_modules/builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "chalk": { + "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "color-convert": { + "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "requires": { + "dependencies": { "color-name": "1.1.3" } }, - "color-name": { + "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "commander": { + "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "cryptiles": { + "node_modules/cryptiles": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-4.1.3.tgz", "integrity": "sha512-gT9nyTMSUC1JnziQpPbxKGBbUg8VL7Zn2NB4E1cJYvuXdElHrwxrV9bmltZGDzet45zSDGyYceueke1TjynGzw==", + "deprecated": "This module has moved and is now available at @hapi/cryptiles. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.", "dev": true, - "requires": { + "dependencies": { "boom": "7.x.x" } }, - "diff": { + "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.3.1" + } }, - "escape-string-regexp": { + "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8.0" + } }, - "esprima": { + "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "function-bind": { + "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "glob": { + "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, - "requires": { + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "has": { + "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, - "requires": { + "dependencies": { "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" } }, - "has-flag": { + "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "hoek": { + "node_modules/hoek": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==", + "deprecated": "This module has moved and is now available at @hapi/hoek. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.", "dev": true }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, - "requires": { + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "is-core-module": { + "node_modules/is-core-module": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, - "requires": { + "dependencies": { "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "js-tokens": { + "node_modules/jose": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.13.1.tgz", + "integrity": "sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, - "js-yaml": { + "node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, - "requires": { + "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "lodash.clonedeep": { + "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "dev": true }, - "minimatch": { + "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, - "mkdirp": { + "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, - "requires": { + "dependencies": { "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "requires": { + "dependencies": { "wrappy": "1" } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-parse": { + "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "resolve": { + "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, - "requires": { + "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "semver": { + "node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "dev": true, + "bin": { + "semver": "bin/semver" + } }, - "sprintf-js": { + "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "stack-trace": { + "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "supports-color": { + "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "requires": { + "dependencies": { "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "supports-preserve-symlinks-flag": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "tslib": { + "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "tslint": { + "node_modules/tslint": { "version": "5.20.1", "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.0.0", "builtin-modules": "^1.1.1", "chalk": "^2.3.0", @@ -376,30 +512,69 @@ "semver": "^5.3.0", "tslib": "^1.8.0", "tsutils": "^2.29.0" + }, + "bin": { + "tslint": "bin/tslint" + }, + "engines": { + "node": ">=4.8.0" + }, + "peerDependencies": { + "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev" } }, - "tsutils": { + "node_modules/tsutils": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", "dev": true, - "requires": { + "dependencies": { "tslib": "^1.8.1" + }, + "peerDependencies": { + "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" } }, - "typescript": { + "node_modules/typescript": { "version": "4.7.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", - "dev": true + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } }, - "uuid": { + "node_modules/uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/vm2": { + "version": "3.9.14", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.14.tgz", + "integrity": "sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==", + "dev": true, + "dependencies": { + "acorn": "^8.7.0", + "acorn-walk": "^8.2.0" + }, + "bin": { + "vm2": "bin/vm2" + }, + "engines": { + "node": ">=6.0" + } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",