From f61fffefb3703a6fd107cbfe3f1a8583faacd24d Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Mon, 29 Jun 2026 17:38:20 -0300 Subject: [PATCH 1/9] fix: adds custom start message in the help command --- commands/subcommands/Help.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/commands/subcommands/Help.ts b/commands/subcommands/Help.ts index 90a625b..5c14949 100644 --- a/commands/subcommands/Help.ts +++ b/commands/subcommands/Help.ts @@ -10,10 +10,11 @@ export async function helpCommand(app: TimeOffApp, context: SlashCommandContext, const message = `*Time Off App Help*\n\n` + `*Commands*\n` + - `• \`/time-off start\` - Start a new time off\n` + - `• \`/time-off end\` - End an existing time off\n` + - `• \`/time-off status\` - Check the current time off status\n` + - `• \`/time-off help\` - Display this help message\n`; + `• \`/time-off start\` starts new time off period\n` + + `• \`/time-off start [message]\` start a new time off period with a custom message\n` + + `• \`/time-off end\` ends current time off period\n` + + `• \`/time-off status\` shows current time off status\n` + + `• \`/time-off help\` shows this list\n`; const notifier = new AppNotifier(app, read); await notifier.notifyUser(room, sender, message); From 43bdbecc0061e9660512a03677601c8b78062959 Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Mon, 29 Jun 2026 17:51:39 -0300 Subject: [PATCH 2/9] fix: show bot reply name as Time-Off instead of TimeOff Bot --- notifiers/AppNotifier.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifiers/AppNotifier.ts b/notifiers/AppNotifier.ts index 8321cbc..ff1ae5a 100644 --- a/notifiers/AppNotifier.ts +++ b/notifiers/AppNotifier.ts @@ -6,7 +6,7 @@ import { LayoutBlock } from '@rocket.chat/ui-kit'; import { TimeOffApp } from '../TimeOffApp'; export class AppNotifier implements IAppNotifier { - private readonly TimeOffName: string = 'TimeOff Bot'; + private readonly TimeOffName: string = 'Time-Off'; constructor(private readonly app: TimeOffApp, private readonly read: IRead) {} From f2d69ff5466cf53e5d65c2f1f215ea2018b022e6 Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Mon, 29 Jun 2026 17:53:01 -0300 Subject: [PATCH 3/9] fix: changes app's name in the list command --- commands/subcommands/Help.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/subcommands/Help.ts b/commands/subcommands/Help.ts index 5c14949..84d5d75 100644 --- a/commands/subcommands/Help.ts +++ b/commands/subcommands/Help.ts @@ -8,7 +8,7 @@ export async function helpCommand(app: TimeOffApp, context: SlashCommandContext, const room = context.getRoom(); const message = - `*Time Off App Help*\n\n` + + `*Time-Off Help*\n\n` + `*Commands*\n` + `• \`/time-off start\` starts new time off period\n` + `• \`/time-off start [message]\` start a new time off period with a custom message\n` + From 8ad98d82f366b426b1f7cd92042466ae9baac31a Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Mon, 29 Jun 2026 18:02:16 -0300 Subject: [PATCH 4/9] fix: refine Time-off UX copy and naming consistency - Normalize display name and help header to 'Time-off' - Reword the not-started notification to clarify auto-reply is off --- commands/subcommands/Help.ts | 2 +- helpers/NotificationMessage.ts | 2 +- notifiers/AppNotifier.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/commands/subcommands/Help.ts b/commands/subcommands/Help.ts index 84d5d75..30b8b97 100644 --- a/commands/subcommands/Help.ts +++ b/commands/subcommands/Help.ts @@ -8,7 +8,7 @@ export async function helpCommand(app: TimeOffApp, context: SlashCommandContext, const room = context.getRoom(); const message = - `*Time-Off Help*\n\n` + + `*Time-off Help*\n\n` + `*Commands*\n` + `• \`/time-off start\` starts new time off period\n` + `• \`/time-off start [message]\` start a new time off period with a custom message\n` + diff --git a/helpers/NotificationMessage.ts b/helpers/NotificationMessage.ts index 4f1bee9..9dc958b 100644 --- a/helpers/NotificationMessage.ts +++ b/helpers/NotificationMessage.ts @@ -2,7 +2,7 @@ export const NOTIFICATION_MESSAGES = { started: `You are marked as Time Off, we will see you when you get back. Use \`/time-off end\` to end your time off.`, default_reply_message: `I am out of office, I will get back to you soon.`, ended: `Welcome back! You are no longer marked as Time Off.`, - not_started: `You are not marked as Time Off. Use \`/time-off start\` to start your time off.`, + not_started: `*Time-off off*\nI will not automatically reply to any direct messages you receive.`, status_in: `You are currently marked as Time Off. Your message is: `, status_out: `You are \`not\` marked as Time Off.`, error: `An error occurred while updating your Time Off status. Please try again later.`, diff --git a/notifiers/AppNotifier.ts b/notifiers/AppNotifier.ts index ff1ae5a..b77e8c4 100644 --- a/notifiers/AppNotifier.ts +++ b/notifiers/AppNotifier.ts @@ -6,7 +6,7 @@ import { LayoutBlock } from '@rocket.chat/ui-kit'; import { TimeOffApp } from '../TimeOffApp'; export class AppNotifier implements IAppNotifier { - private readonly TimeOffName: string = 'Time-Off'; + private readonly TimeOffName: string = 'Time-off'; constructor(private readonly app: TimeOffApp, private readonly read: IRead) {} From 5180c1d33be42839b983abf5c9e25ed49e03763f Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Mon, 29 Jun 2026 18:08:28 -0300 Subject: [PATCH 5/9] feat: add friendly hint when typing /time-off command Add i18n description and params example so the slash command autocomplete shows helpful usage text before the command runs. --- commands/TimeOffCommand.ts | 4 ++-- i18n/en.json | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 i18n/en.json diff --git a/commands/TimeOffCommand.ts b/commands/TimeOffCommand.ts index e975605..79cdd48 100644 --- a/commands/TimeOffCommand.ts +++ b/commands/TimeOffCommand.ts @@ -9,8 +9,8 @@ import { statusCommand } from './subcommands/Status'; export class TimeOffCommand implements ISlashCommand { public command = 'time-off'; - public i18nParamsExample = ''; - public i18nDescription = ''; + public i18nParamsExample = 'Time_Off_Command_Params_Example'; + public i18nDescription = 'Time_Off_Command_Description'; public providesPreview = false; constructor(private readonly app: TimeOffApp) {} diff --git a/i18n/en.json b/i18n/en.json new file mode 100644 index 0000000..bb7eb0d --- /dev/null +++ b/i18n/en.json @@ -0,0 +1,4 @@ +{ + "Time_Off_Command_Description": "Manage your time off — start, end, or check your status", + "Time_Off_Command_Params_Example": "start | end | status | help" +} From bdc8cfecd973b34867aca55bd566590b77f1f50e Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Mon, 29 Jun 2026 18:14:02 -0300 Subject: [PATCH 6/9] fix: align status-out message with not-started copy Reword the 'not marked as Time Off' status reply to match the not-started message, clarifying that auto-replies are off. --- helpers/NotificationMessage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/NotificationMessage.ts b/helpers/NotificationMessage.ts index 9dc958b..57237e9 100644 --- a/helpers/NotificationMessage.ts +++ b/helpers/NotificationMessage.ts @@ -4,6 +4,6 @@ export const NOTIFICATION_MESSAGES = { ended: `Welcome back! You are no longer marked as Time Off.`, not_started: `*Time-off off*\nI will not automatically reply to any direct messages you receive.`, status_in: `You are currently marked as Time Off. Your message is: `, - status_out: `You are \`not\` marked as Time Off.`, + status_out: `*Time-off off*\nI will not automatically reply to any direct messages you receive.`, error: `An error occurred while updating your Time Off status. Please try again later.`, }; From 686b68a1c9a1e254ddd082cde6c47761288d466b Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Mon, 29 Jun 2026 18:27:23 -0300 Subject: [PATCH 7/9] fix: render time-off message as blockquote instead of preview block Replace the preview LayoutBlock with a section that quotes the user's left-behind message using markdown blockquote formatting. --- helpers/TimeOffMessageFormatter.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/helpers/TimeOffMessageFormatter.ts b/helpers/TimeOffMessageFormatter.ts index 3f26e3f..70d43bb 100644 --- a/helpers/TimeOffMessageFormatter.ts +++ b/helpers/TimeOffMessageFormatter.ts @@ -11,14 +11,14 @@ export class TimeOffMessageFormatter { }, }, { - type: 'preview', - title: [], - description: [ - { - type: 'mrkdwn', - text: message, - }, - ], + type: 'section', + text: { + type: 'mrkdwn', + text: message + .split('\n') + .map((line) => `> ${line}`) + .join('\n'), + }, }, ]; } From e5ef7ccdd39d6a7cae1001d4fd2635af933f11a4 Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Thu, 2 Jul 2026 12:19:19 -0300 Subject: [PATCH 8/9] feat: clarify time-off start confirmation message Reword the start confirmation to emphasize the app only auto-replies to DMs and that the user must manually end with /time-off end. Show the custom reply message as a blockquote when one is provided. --- commands/subcommands/Start.ts | 10 +++++----- helpers/NotificationMessage.ts | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/commands/subcommands/Start.ts b/commands/subcommands/Start.ts index 5d3d395..8d7ec02 100644 --- a/commands/subcommands/Start.ts +++ b/commands/subcommands/Start.ts @@ -19,10 +19,8 @@ export async function startCommand( const room = context.getRoom(); const notifier = new AppNotifier(app, read); - let message = context.getArguments().join(' ').replace(CommandEnum.START, ''); - if (!message) { - message = NOTIFICATION_MESSAGES.default_reply_message; - } + const customMessage = context.getArguments().join(' ').replace(CommandEnum.START, '').trim(); + const message = customMessage || NOTIFICATION_MESSAGES.default_reply_message; const timeOffEntry: ITimeOff = { coreUserId: currentUser.id, @@ -36,6 +34,8 @@ export async function startCommand( const timeOffService = new TimeOffService(timeOffRepository); const savedTimeOff = await timeOffService.saveTimeOff(timeOffEntry); - const notificationMessage = !savedTimeOff ? NOTIFICATION_MESSAGES.error : NOTIFICATION_MESSAGES.started; + const notificationMessage = !savedTimeOff + ? NOTIFICATION_MESSAGES.error + : NOTIFICATION_MESSAGES.started(customMessage || undefined); await notifier.notifyUser(room, currentUser, notificationMessage); } diff --git a/helpers/NotificationMessage.ts b/helpers/NotificationMessage.ts index 57237e9..0ee743a 100644 --- a/helpers/NotificationMessage.ts +++ b/helpers/NotificationMessage.ts @@ -1,5 +1,18 @@ export const NOTIFICATION_MESSAGES = { - started: `You are marked as Time Off, we will see you when you get back. Use \`/time-off end\` to end your time off.`, + started: (customMessage?: string): string => { + const intro = `*Time off on*\nI will automatically reply to any direct messages you receive to let the sender know you're on time off.`; + const reminder = `Remember to use the \`/time-off end\` command when you get back.`; + + if (customMessage) { + const quoted = customMessage + .split('\n') + .map((line) => `> ${line}`) + .join('\n'); + return `${intro}\n\nI will also include the following custom message:\n${quoted}\n\n${reminder}`; + } + + return `${intro}\n\n${reminder}`; + }, default_reply_message: `I am out of office, I will get back to you soon.`, ended: `Welcome back! You are no longer marked as Time Off.`, not_started: `*Time-off off*\nI will not automatically reply to any direct messages you receive.`, From a9466d93e0f61ac33e419a25a124859e47c61cbd Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Thu, 2 Jul 2026 12:50:59 -0300 Subject: [PATCH 9/9] fix: stop sending fabricated reply messages on user's behalf The app no longer stores or sends a default out-of-office message the user never wrote. When no custom message is set, senders simply see that the user is on time off, and the start confirmation and status command note that no custom reply message was set. --- commands/subcommands/Start.ts | 3 +-- commands/subcommands/Status.ts | 4 +++- handlers/PostMessageSentHandler.ts | 7 +++++-- helpers/NotificationMessage.ts | 4 ++-- helpers/TimeOffMessageFormatter.ts | 31 ++++++++++++++++++++---------- interfaces/ITimeOff.ts | 2 +- 6 files changed, 33 insertions(+), 18 deletions(-) diff --git a/commands/subcommands/Start.ts b/commands/subcommands/Start.ts index 8d7ec02..c12e2ae 100644 --- a/commands/subcommands/Start.ts +++ b/commands/subcommands/Start.ts @@ -20,13 +20,12 @@ export async function startCommand( const notifier = new AppNotifier(app, read); const customMessage = context.getArguments().join(' ').replace(CommandEnum.START, '').trim(); - const message = customMessage || NOTIFICATION_MESSAGES.default_reply_message; const timeOffEntry: ITimeOff = { coreUserId: currentUser.id, username: currentUser.username, status: TimeOffStatus.ON_TIME_OFF, - message: message, + message: customMessage || undefined, lastNotifiedAtBySenderId: {}, }; diff --git a/commands/subcommands/Status.ts b/commands/subcommands/Status.ts index 6355143..9e8518f 100644 --- a/commands/subcommands/Status.ts +++ b/commands/subcommands/Status.ts @@ -30,7 +30,9 @@ export async function statusCommand( } if (timeOffEntry.status === TimeOffStatus.ON_TIME_OFF) { - notificationMessage = `${NOTIFICATION_MESSAGES.status_in}${timeOffEntry.message}`; + notificationMessage = timeOffEntry.message + ? `${NOTIFICATION_MESSAGES.status_in}${timeOffEntry.message}` + : NOTIFICATION_MESSAGES.status_in_no_message; } else { notificationMessage = `${NOTIFICATION_MESSAGES.status_out}`; } diff --git a/handlers/PostMessageSentHandler.ts b/handlers/PostMessageSentHandler.ts index 58680f6..11be2c6 100644 --- a/handlers/PostMessageSentHandler.ts +++ b/handlers/PostMessageSentHandler.ts @@ -58,10 +58,13 @@ export class PostMessageSentHandler { message: IMessage, sender: IUser, receiver: IUser, - timeOffMessage: string, + timeOffMessage?: string, ): Promise { const formattedMessage = TimeOffMessageFormatter.format(receiver.username, timeOffMessage); - await this.notifier.notifyUser(message.room, sender, timeOffMessage, formattedMessage); + const fallbackText = timeOffMessage + ? `${receiver.username} is currently on time off. They left the following message: ${timeOffMessage}` + : `${receiver.username} is currently on time off.`; + await this.notifier.notifyUser(message.room, sender, fallbackText, formattedMessage); } private shouldSendNotification(timeOffEntry: ITimeOff | undefined, senderId: string): timeOffEntry is ITimeOff { diff --git a/helpers/NotificationMessage.ts b/helpers/NotificationMessage.ts index 0ee743a..89828ee 100644 --- a/helpers/NotificationMessage.ts +++ b/helpers/NotificationMessage.ts @@ -11,12 +11,12 @@ export const NOTIFICATION_MESSAGES = { return `${intro}\n\nI will also include the following custom message:\n${quoted}\n\n${reminder}`; } - return `${intro}\n\n${reminder}`; + return `${intro}\n\nYou have not set a custom reply message.\n\n${reminder}`; }, - default_reply_message: `I am out of office, I will get back to you soon.`, ended: `Welcome back! You are no longer marked as Time Off.`, not_started: `*Time-off off*\nI will not automatically reply to any direct messages you receive.`, status_in: `You are currently marked as Time Off. Your message is: `, + status_in_no_message: `You are currently marked as Time Off. You have not set a custom reply message.`, status_out: `*Time-off off*\nI will not automatically reply to any direct messages you receive.`, error: `An error occurred while updating your Time Off status. Please try again later.`, }; diff --git a/helpers/TimeOffMessageFormatter.ts b/helpers/TimeOffMessageFormatter.ts index 70d43bb..3912be2 100644 --- a/helpers/TimeOffMessageFormatter.ts +++ b/helpers/TimeOffMessageFormatter.ts @@ -1,25 +1,36 @@ import { LayoutBlock } from '@rocket.chat/ui-kit'; export class TimeOffMessageFormatter { - public static format(username: string, message: string): LayoutBlock[] { - return [ + public static format(username: string, message?: string): LayoutBlock[] { + const headerText = message + ? `${username} is currently on time off. They left the following message:` + : `${username} is currently on time off.`; + + const blocks: LayoutBlock[] = [ { type: 'section', text: { type: 'mrkdwn', - text: `${username} is currently *unavailable* to answer your message, however they left the following message:\n\n`, + text: headerText, }, }, - { + ]; + + if (message) { + const quoted = message + .split('\n') + .map((line) => `> ${line}`) + .join('\n'); + + blocks.push({ type: 'section', text: { type: 'mrkdwn', - text: message - .split('\n') - .map((line) => `> ${line}`) - .join('\n'), + text: quoted, }, - }, - ]; + }); + } + + return blocks; } } diff --git a/interfaces/ITimeOff.ts b/interfaces/ITimeOff.ts index 974dfc6..db8fcec 100644 --- a/interfaces/ITimeOff.ts +++ b/interfaces/ITimeOff.ts @@ -3,7 +3,7 @@ import { TimeOffStatus } from '../enums/Status'; export interface ITimeOff { coreUserId: string; username: string; - message: string; + message?: string; status: TimeOffStatus; lastNotifiedAtBySenderId?: Record; }