Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/api/src/handlers/slack/events/message-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1587,7 +1587,7 @@ async function isSlackEvalLauncherEnabled(): Promise<boolean> {
);
}

export async function handleSlackEntryEvent(params: {
async function handleSlackEntryEvent(params: {
event: SlackEvent;
slackInstallation: SlackInstallation;
slack: SlackNotifier;
Expand Down
108 changes: 22 additions & 86 deletions apps/api/src/handlers/slack/events/reactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,9 @@ import {
TASK_SUGGESTION_TYPES,
} from '../constants.js';
import type { SlackWebhookContext } from '../context.js';
import {
isConfiguredSlackReaction,
isThumbsUpReaction,
} from '../helpers/event-normalization.js';
import { isThumbsUpReaction } from '../helpers/event-normalization.js';
import { postTaskSuggestionStartedMessage } from '../helpers/thread-posting.js';
import { lookupSlackUserMapping } from '../helpers/user-mapping.js';
import { handleSlackEntryEvent } from './message-entry.js';
import {
runTaskSuggestionReactionContention,
type TaskSuggestionReactionLaunchResult,
Expand Down Expand Up @@ -715,103 +711,43 @@ export async function handleReactionAddedEvent(params: {
return;
}

const isThumbsUp = isThumbsUpReaction(event.reaction);
const isSummonReaction = isConfiguredSlackReaction(
event.reaction,
reactionNames.summonEmoji,
);

if (event.user === context.slackInstallation.botUserId) {
apiLogger.debug(
`[SlackWebhook] Ignoring self-reaction from bot user ${event.user} on ${event.item.channel}:${event.item.ts}`,
);
return;
}

if (isThumbsUp) {
apiLogger.debug(
`[SetupSuggestionLifecycle] Processing thumbs-up reaction team=${context.teamId} channel=${event.item.channel} messageTs=${event.item.ts} reaction=${event.reaction} user=${event.user}`,
);
const setupSuggestionLockKey = `${SLACK_SETUP_SUGGESTION_LOCK_PREFIX}${event.item.channel}:${event.item.ts}`;
const setupSuggestionHandled = await launchTaskSuggestionTaskWithContention(
{
lockKey: setupSuggestionLockKey,
channelId: event.item.channel,
messageTs: event.item.ts,
launch: () =>
launchTaskSuggestionTaskFromReaction({
teamId: context.teamId,
slack: context.slack,
reactionEvent: event,
ackEmoji: reactionNames.ackEmoji,
completionEmoji: reactionNames.completionEmoji,
}),
},
);

if (setupSuggestionHandled) {
apiLogger.debug(
`[SlackWebhook] Setup suggestion reaction handled for ${event.item.channel}:${event.item.ts}`,
);
return;
}

apiLogger.debug(
`[SlackWebhook] Reaction no-op for ${event.item.channel}:${event.item.ts}`,
);
}

if (!isSummonReaction) {
if (!isThumbsUpReaction(event.reaction)) {
return;
}

const sourceMessage = await context.slack.getMessage({
channel: event.item.channel,
apiLogger.debug(
`[SetupSuggestionLifecycle] Processing thumbs-up reaction team=${context.teamId} channel=${event.item.channel} messageTs=${event.item.ts} reaction=${event.reaction} user=${event.user}`,
);
const setupSuggestionLockKey = `${SLACK_SETUP_SUGGESTION_LOCK_PREFIX}${event.item.channel}:${event.item.ts}`;
const setupSuggestionHandled = await launchTaskSuggestionTaskWithContention({
lockKey: setupSuggestionLockKey,
channelId: event.item.channel,
messageTs: event.item.ts,
launch: () =>
launchTaskSuggestionTaskFromReaction({
teamId: context.teamId,
slack: context.slack,
reactionEvent: event,
ackEmoji: reactionNames.ackEmoji,
completionEmoji: reactionNames.completionEmoji,
}),
});

if (!sourceMessage) {
if (setupSuggestionHandled) {
apiLogger.debug(
`[SlackWebhook] Summon reaction source message unavailable for ${event.item.channel}:${event.item.ts}`,
`[SlackWebhook] Setup suggestion reaction handled for ${event.item.channel}:${event.item.ts}`,
);
return;
}

const isRoomoteAuthoredBotMessage =
sourceMessage.user === context.slackInstallation.botUserId ||
sourceMessage.app_id === context.slackInstallation.appId;

if (isRoomoteAuthoredBotMessage) {
apiLogger.debug(
`[SlackWebhook] Ignoring summon reaction on Roomote-authored bot message ${event.item.channel}:${event.item.ts}`,
);
return;
}

if (sourceMessage.text.trim().length === 0 && !sourceMessage.files?.length) {
apiLogger.debug(
`[SlackWebhook] Ignoring summon reaction on empty source message ${event.item.channel}:${event.item.ts}`,
);
return;
}

await handleSlackEntryEvent({
event: {
type: 'message',
channel: event.item.channel,
user: event.user,
text: sourceMessage.text,
ts: sourceMessage.ts,
thread_ts: sourceMessage.thread_ts ?? sourceMessage.ts,
app_id: sourceMessage.app_id,
files: sourceMessage.files,
attachments: sourceMessage.attachments,
},
slackInstallation: context.slackInstallation as never,
slack: context.slack,
teamId: context.teamId,
ackEmoji: reactionNames.ackEmoji,
completionEmoji: reactionNames.completionEmoji,
skipThreadFollowupHandling: true,
});
apiLogger.debug(
`[SlackWebhook] Reaction no-op for ${event.item.channel}:${event.item.ts}`,
);
}
11 changes: 0 additions & 11 deletions apps/api/src/handlers/slack/helpers/event-normalization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,6 @@ export function isThumbsUpReaction(reaction: string): boolean {
return THUMBS_UP_REACTIONS.has(normalizedReaction);
}

export function isConfiguredSlackReaction(
reaction: string,
configuredEmoji: string | null | undefined,
): boolean {
if (!configuredEmoji) {
return false;
}

return normalizeSlackReactionName(reaction) === configuredEmoji;
}

function coerceSlackWorkflowInputToString(value: unknown): string | undefined {
if (typeof value === 'string') {
const trimmed = value.trim();
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Public, user-facing documentation:
- setup and self-hosting
- product concepts and how Roomote works
- admin and configuration workflows (dashboard, environments, skills,
automations, integrations, vibes, personal settings)
automations, integrations, personal settings)
- integrations and common user-facing tasks

## What does NOT belong here
Expand Down
1 change: 0 additions & 1 deletion apps/docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@
"skills",
"automations",
"users",
"vibes",
"anonymous-analytics",
"personal-settings"
]
Expand Down
91 changes: 0 additions & 91 deletions apps/docs/vibes.mdx

This file was deleted.

Binary file not shown.
Binary file removed apps/web/public/vibes/ideas/party-parrot.png
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed apps/web/public/vibes/ideas/roomote_up_black.png
Binary file not shown.
Binary file removed apps/web/public/vibes/ideas/roomote_up_green.png
Binary file not shown.
Binary file removed apps/web/public/vibes/ideas/ship-it.png
Binary file not shown.
Binary file removed apps/web/public/vibes/ideas/sparkles.png
Binary file not shown.

This file was deleted.

5 changes: 0 additions & 5 deletions apps/web/src/app/(authenticated)/settings/vibes/page.tsx

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading