Skip to content

Add slack-summarizer extension #18959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

zahassanyum
Copy link

@zahassanyum zahassanyum commented May 5, 2025

Description

Summarize any Slack channel, thread, or message directly from Raycast using OpenAI.

Perfect for catching-up after vacations, keeping stakeholders in the loop, or turning noisy discussions into succinct, bullet-point briefs.

Screencast

Raycast.extension.mp4

Checklist

@raycastbot
Copy link
Collaborator

Congratulations on your new Raycast extension! 🚀

You can expect an initial review within five business days.

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

@raycastbot raycastbot added the new extension Label for PRs with new extensions label May 5, 2025
@zahassanyum zahassanyum marked this pull request as ready for review May 5, 2025 10:16
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 introduces a comprehensive Slack summarizer extension that leverages OpenAI to generate concise summaries of Slack channels and threads directly within Raycast.

  • Implements robust rate limiting and pagination handling in slackApi.js with exponential backoff for large channel histories
  • Uses PKCE OAuth flow for secure Slack authentication with proper scoping and token management
  • Includes intelligent caching strategies for channel lists (24h) and user data (14 days) to minimize API calls
  • Provides customizable OpenAI prompts through preferences while maintaining sensible defaults
  • Properly handles thread URL parsing and message formatting with user mention replacements

Note: The implementation follows Raycast's best practices and includes all necessary documentation and configuration files. The CHANGELOG.md correctly uses the {PR_MERGE_DATE} template.

💡 (2/5) Greptile learns from your feedback when you react with 👍/👎!

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

@@ -0,0 +1,3 @@
# Slack Summaries Changelog
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: Title 'Slack Summaries' in CHANGELOG.md doesn't match 'Slack Summarizer' in package.json title field

Suggested change
# Slack Summaries Changelog
# Slack Summarizer Changelog

"name": "summarize-channel",
"title": "Summarize Slack Channel",
"subtitle": "Slack",
"description": "Summarize a Slack channel threads",
Copy link
Contributor

Choose a reason for hiding this comment

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

syntax: Grammar issue in description: 'Summarize a Slack channel threads' should be 'Summarize Slack channel threads' or 'Summarize a Slack channel's threads'

Suggested change
"description": "Summarize a Slack channel threads",
"description": "Summarize Slack channel threads",

"name": "openaiPrompt",
"type": "textfield",
"title": "OpenAI prompt",
"description": "Custom prompt used when summarising a channel",
Copy link
Contributor

Choose a reason for hiding this comment

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

syntax: Inconsistent spelling: 'summarising' should be 'summarizing' to match American English used elsewhere

Suggested change
"description": "Custom prompt used when summarising a channel",
"description": "Custom prompt used when summarizing a channel",

Comment on lines +93 to +94
"placeholder": "gpt-4.1",
"default": "gpt-4.1",
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: gpt-4.1 is not a valid OpenAI model. Should use gpt-4 or gpt-3.5-turbo as placeholder and default

Suggested change
"placeholder": "gpt-4.1",
"default": "gpt-4.1",
"placeholder": "gpt-4",
"default": "gpt-4",

Comment on lines +34 to +39
} catch (error) {
toast.style = Toast.Style.Failure;
toast.title = "Couldn't generate summary";
if (error instanceof Error) {
toast.message = error.message;
}
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 setting toast properties

Suggested change
} catch (error) {
toast.style = Toast.Style.Failure;
toast.title = "Couldn't generate summary";
if (error instanceof Error) {
toast.message = error.message;
}
} catch (error) {
showFailureToast(error, { title: "Couldn't generate summary" });
if (error instanceof Error) {
toast.message = error.message;
}

].join(","),
});

let cachedApp;
Copy link
Contributor

Choose a reason for hiding this comment

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

style: cachedApp should be declared as let cachedApp: App | undefined to ensure proper typing

Suggested change
let cachedApp;
let cachedApp: App | undefined;

export async function getSlackApp() {
if (cachedApp) return cachedApp;

const accessToken = await slackOAuth.authorize(); // PKCE flow
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: slackOAuth.authorize() should be wrapped in try/catch to handle potential auth failures gracefully

Suggested change
const accessToken = await slackOAuth.authorize(); // PKCE flow
try {
const accessToken = await slackOAuth.authorize(); // PKCE flow
} catch (error) {
showFailureToast(error, { title: "Could not authorize with Slack" });
throw error;
}

Comment on lines +10 to +17
async function callOpenAIChatCompletion(messages, model = defaultModel) {
const res = await openai.chat.completions.create({
model,
temperature: 0.2,
messages,
});
return res.choices?.[0]?.message?.content?.trim() || "[No summary]";
}
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: API call not wrapped in try-catch block. Could fail silently if API errors occur.

Suggested change
async function callOpenAIChatCompletion(messages, model = defaultModel) {
const res = await openai.chat.completions.create({
model,
temperature: 0.2,
messages,
});
return res.choices?.[0]?.message?.content?.trim() || "[No summary]";
}
async function callOpenAIChatCompletion(messages, model = defaultModel) {
try {
const res = await openai.chat.completions.create({
model,
temperature: 0.2,
messages,
});
return res.choices?.[0]?.message?.content?.trim() || "[No summary]";
} catch (error) {
showFailureToast(error, { title: "Could not get OpenAI completion" });
return "[Error getting summary]";
}
}

import { callOpenAIChannel, callOpenAIThread } from "./openaiApi";
import { getChannelIdByName, getAllUsers, fetchFullThread, getThreadsForChannel } from "./slackApi";

let userMap = {};
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: Using a global mutable userMap could cause race conditions if multiple summarizations run concurrently. Consider making this scoped to each summarization request.

Comment on lines +59 to +60
const oldestTs = Math.floor(Date.now() / 1000) - days * 24 * 60 * 60;
const result = await getThreadsForChannel(channelId, oldestTs);
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Verify that days is a positive number to avoid invalid timestamp calculations

Suggested change
const oldestTs = Math.floor(Date.now() / 1000) - days * 24 * 60 * 60;
const result = await getThreadsForChannel(channelId, oldestTs);
if (days <= 0) throw new Error("Days must be a positive number");
const oldestTs = Math.floor(Date.now() / 1000) - days * 24 * 60 * 60;
const result = await getThreadsForChannel(channelId, oldestTs);

@zahassanyum zahassanyum closed this May 8, 2025
@zahassanyum zahassanyum deleted the ext/slack-summarizer branch May 8, 2025 15:38
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.

2 participants