Skip to content

feat: add slack-templated-message extension#17253

Merged
raycastbot merged 5 commits intoraycast:mainfrom
koh-sh:slack-templated-message
Mar 4, 2025
Merged

feat: add slack-templated-message extension#17253
raycastbot merged 5 commits intoraycast:mainfrom
koh-sh:slack-templated-message

Conversation

@koh-sh
Copy link
Contributor

@koh-sh koh-sh commented Feb 23, 2025

Description

A Raycast Extension for sending messages to Slack channels using customizable templates.

[Features]

  • 📝 Create and manage message templates
  • 💬 Send messages to Slack channels
  • 🧵 Thread reply support
  • 📤 Import/Export templates

Screencast

Screen.Recording.2025-02-23.at.18.08.12.mov

Checklist

@raycastbot raycastbot added the new extension Label for PRs with new extensions label Feb 23, 2025
@raycastbot
Copy link
Collaborator

Congratulations on your new Raycast extension! 🚀

Due to our current reduced availability, the initial review may take up to 10-15 business days

Once the PR is approved and merged, the extension will be available on our Store.

@pernielsentikaer
Copy link
Collaborator

Hi 👋

Thanks for your contribution 🔥

Wouldn't it make sense to add this to this extension by @momme-rtf? This way, we can have one Slack extension with all the cool features

@pernielsentikaer pernielsentikaer self-assigned this Mar 2, 2025
@koh-sh
Copy link
Contributor Author

koh-sh commented Mar 2, 2025

@pernielsentikaer

Hi there!

I did check out the existing extension, but I think mine should remain separate.

In my view, the existing extension seems to function primarily as a simple adapter for Slack, whereas my extension focuses on eliminating the routine of typing the same messages to send to Slack every day.
To achieve this purpose, this extension has its own template management functionality such as create/edit and import/export features.

I believe having separate extensions with different purposes is easier to maintain than merging different functionalities into one extension.
It also makes it clearer for users to find and understand each extension's specific purpose.

However, I understand the concern about having too many similar extensions in the store. I'd love to hear your thoughts on this.

@pernielsentikaer
Copy link
Collaborator

pernielsentikaer commented Mar 3, 2025

Thanks for the clarification, that makes sense 😊

@greptileai can you check this

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR adds a new Slack Templated Message extension that enables users to create, manage, and send customizable message templates to Slack channels with variable substitution and thread reply support.

  • The extension implements four main commands: Create Template, Send Message, Export Templates, and Import Templates with a clean UI and proper OAuth authentication
  • Robust template management system in src/lib/templates.ts handles storage, validation, and CRUD operations for message templates
  • Well-implemented variable substitution system in src/lib/slack.ts supports {date}, {time}, and {user} variables in templates
  • Thread reply functionality includes sophisticated timestamp validation and normalization for proper Slack thread integration
  • Shared components in src/components/shared.tsx provide reusable UI elements like ChannelDropdown and ThreadField across different commands

15 file(s) reviewed, 13 comment(s)
Edit PR Review Bot Settings | Greptile

1. Open Raycast and run `Send Slack Message`
2. Select a template to use
3. The message will be sent to the pre-configured channel with the variables automatically replaced
4. Press `⌘ + K` to edit or delete the selected template
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The README mentions using ⌘ + K to edit/delete templates, but in send-message.tsx the shortcuts are ⌘ + E for edit and ⌘ + backspace for delete.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use Keyboard.Shortcut.Common.Remove for delete

Comment on lines +24 to +31
const { token } = await getAccessToken();
if (!token) return;
const client = new WebClient(token);
const allChannels = await fetchAllChannels(client);
setChannels(allChannels);
} catch (error) {
console.error(error);
} finally {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Error handling only logs to console but doesn't show a user-facing error message. Consider using showFailureToast from @raycast/utils to provide feedback to the user when channel fetching fails.

Comment on lines +56 to +61
try {
// Initialize Slack client
const { token } = await getAccessToken();
if (!token) {
throw new Error("Failed to get authentication credentials");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider using showFailureToast from @raycast/utils instead of manually creating error toasts

Comment on lines +71 to +81
<List isLoading={isLoading}>
<List.Item
title={`Export ${templates.length} templates`}
subtitle={`Save to: ${DEFAULT_TEMPLATE_PATH}`}
actions={
<ActionPanel>
<Action title="Export" onAction={handleExport} />
</ActionPanel>
}
/>
</List>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding an empty state message when templates.length is 0 to improve user experience

Comment on lines +132 to +134
export async function writeTemplatesToFile(filePath: string, templates: SlackTemplate[]): Promise<void> {
await fs.writeFile(filePath, JSON.stringify(templates, null, 2));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Missing error handling for file writing operation. This function should be wrapped in a try/catch block like other file operations in this module.

Comment on lines +174 to +176
const templates = await loadTemplates();
const updatedTemplates = templates.map((t) => (t.name === originalName ? updatedTemplate : t));
await saveTemplatesToFile(updatedTemplates);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using loadTemplates() here could potentially show duplicate error toasts if there's an issue, since loadTemplates already shows a toast on error.

// Validate channel selection
const selectedChannel = channels.find((c) => c.id === values.slackChannelId);
if (!selectedChannel) {
throw new Error("Selected channel not found");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Throwing an error here will cause an unhandled exception. Consider using showCustomToast instead and returning early.

@pernielsentikaer
Copy link
Collaborator

Could you check the messages from the bot?

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@koh-sh koh-sh force-pushed the slack-templated-message branch from f08395b to 6c2af01 Compare March 3, 2025 16:11
@koh-sh koh-sh force-pushed the slack-templated-message branch from 6c2af01 to 9c5bd8a Compare March 3, 2025 16:29
@koh-sh
Copy link
Contributor Author

koh-sh commented Mar 3, 2025

@pernielsentikaer

Thank you for your review!
I've addressed all the review comments from the bot.
Could you please check it again?

@koh-sh koh-sh requested a review from pernielsentikaer March 3, 2025 16:34
@pernielsentikaer
Copy link
Collaborator

@greptileai please check again

Copy link
Collaborator

@pernielsentikaer pernielsentikaer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extension icon is not working well in both appearances; could you look into this? If you struggle to do so, then you can use our generator

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

(updates since last review)

This PR continues to evolve the Slack Templated Message extension with several important improvements to error handling and user experience.

  • Improved error handling across the extension by replacing thrown errors with showFailureToast from @raycast/utils for better user feedback
  • Added proper empty state handling in export-templates.tsx with isLoading flag to prevent flicker
  • Simplified mergeTemplates function in import-templates.tsx by removing unnecessary async keyword and string interpolation
  • Strengthened template validation in templates.ts with explicit array type checking for imported templates

The changes focus on making the extension more robust and user-friendly while maintaining clean code practices.

8 file(s) reviewed, 10 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines +24 to +26
.catch(async (error) => {
await handleOperationError(error, "export");
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider using showFailureToast from @raycast/utils to simplify error handling

Comment on lines +24 to +26
- `{date}` - Current date (YYYY-MM-DD)
- `{time}` - Current time (HH:MM)
- `{user}` - Your Slack username
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding examples for the date/time/user variables to make the format clearer, e.g. {date} - Current date (YYYY-MM-DD, e.g. 2024-03-04)

Comment on lines +83 to +87
// Validate channel selection
const selectedChannel = channels.find((c) => c.id === values.slackChannelId);
if (!selectedChannel) {
throw new Error("Selected channel not found");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Channel validation should happen before thread validation to fail fast. Move this check before line 67.

Comment on lines +122 to +124
<Form.TextField id="name" title="Template Name" placeholder="Enter the name of the template" />
<Form.TextArea id="message" title="Message" placeholder="Enter your message template" />
<ChannelDropdown channels={channels} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding maxLength constraints to name and message fields to prevent oversized templates

value={filePath}
onChange={setFilePath}
/>
<Form.Checkbox id="overwrite" label="Update duplicate Items" defaultValue={false} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Inconsistent capitalization in label - 'Items' should be lowercase to match UI style

Comment on lines +212 to +213
const templates = await loadTemplates();
const updatedTemplates = templates.filter((t) => t.name !== templateName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using loadTemplates() here but loadTemplatesFromFile() in updateTemplate() - should be consistent across similar operations

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Copy link
Collaborator

@pernielsentikaer pernielsentikaer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi 👋

Looks good to me, approved 🔥

@raycastbot raycastbot merged commit 0987446 into raycast:main Mar 4, 2025
2 checks passed
@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2025

Published to the Raycast Store:
https://raycast.com/koh-sh/slack-templated-message

@raycastbot
Copy link
Collaborator

🎉 🎉 🎉

Such a great contribution deserves a reward, but unfortunately we couldn't find your Raycast account based on your GitHub username (@koh-sh).
Please link your GitHub account to your Raycast account to receive your credits and soon be able to exchange them for some swag.

@koh-sh
Copy link
Contributor Author

koh-sh commented Mar 4, 2025

@pernielsentikaer
Thank you!

@koh-sh koh-sh deleted the slack-templated-message branch March 5, 2025 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new extension Label for PRs with new extensions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants