Initial commit
This commit is contained in:
+233
@@ -0,0 +1,233 @@
|
||||
export interface SlackMessage {
|
||||
text?: string;
|
||||
blocks?: SlackBlock[];
|
||||
channel?: string;
|
||||
}
|
||||
|
||||
interface SlackBlock {
|
||||
type: string;
|
||||
text?: { type: string; text: string };
|
||||
elements?: unknown[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to a Slack webhook URL.
|
||||
*/
|
||||
export async function sendSlackMessage(
|
||||
webhookUrl: string,
|
||||
message: SlackMessage
|
||||
): Promise<void> {
|
||||
const res = await fetch(webhookUrl, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(message),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const body = await res.text();
|
||||
console.error("[Slack] Webhook failed:", res.status, body);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a plain text Slack notification.
|
||||
*/
|
||||
export async function sendSlackText(
|
||||
webhookUrl: string,
|
||||
text: string
|
||||
): Promise<void> {
|
||||
return sendSlackMessage(webhookUrl, { text });
|
||||
}
|
||||
|
||||
// ── Pre-built notification templates ─────────────────────────────────────────
|
||||
|
||||
export async function slackNotifyVersionUploaded(
|
||||
webhookUrl: string,
|
||||
params: {
|
||||
shotCode: string;
|
||||
versionLabel: string;
|
||||
artistName: string;
|
||||
projectName: string;
|
||||
reviewUrl: string;
|
||||
}
|
||||
) {
|
||||
await sendSlackMessage(webhookUrl, {
|
||||
blocks: [
|
||||
{
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text: `🎬 *New version uploaded*\n*${params.projectName}* · ${params.shotCode} ${params.versionLabel} by ${params.artistName}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "actions",
|
||||
elements: [
|
||||
{
|
||||
type: "button",
|
||||
text: { type: "plain_text", text: "Review Now" },
|
||||
url: params.reviewUrl,
|
||||
style: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export async function slackNotifyApproval(
|
||||
webhookUrl: string,
|
||||
params: {
|
||||
shotCode: string;
|
||||
versionLabel: string;
|
||||
status: "APPROVED" | "REJECTED" | "NEEDS_CHANGES";
|
||||
reviewerName: string;
|
||||
projectName: string;
|
||||
reviewUrl: string;
|
||||
}
|
||||
) {
|
||||
const icons = {
|
||||
APPROVED: "✅",
|
||||
REJECTED: "❌",
|
||||
NEEDS_CHANGES: "⚠️",
|
||||
};
|
||||
|
||||
const labels = {
|
||||
APPROVED: "approved",
|
||||
REJECTED: "rejected",
|
||||
NEEDS_CHANGES: "needs changes",
|
||||
};
|
||||
|
||||
const icon = icons[params.status];
|
||||
const label = labels[params.status];
|
||||
|
||||
await sendSlackMessage(webhookUrl, {
|
||||
blocks: [
|
||||
{
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text: `${icon} *${params.shotCode} ${params.versionLabel} ${label}*\nBy ${params.reviewerName} · ${params.projectName}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "actions",
|
||||
elements: [
|
||||
{
|
||||
type: "button",
|
||||
text: { type: "plain_text", text: "View Version" },
|
||||
url: params.reviewUrl,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export async function slackNotifyTaskReadyForReview(
|
||||
webhookUrl: string,
|
||||
params: {
|
||||
taskTitle: string;
|
||||
contextCode: string | null;
|
||||
artistName: string;
|
||||
projectName: string;
|
||||
taskUrl: string;
|
||||
}
|
||||
) {
|
||||
const label = params.contextCode ? `${params.contextCode} — ` : "";
|
||||
await sendSlackMessage(webhookUrl, {
|
||||
blocks: [
|
||||
{
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text: `👁 *Task ready for review*\n*${params.projectName}* · ${label}${params.taskTitle}\nSubmitted by ${params.artistName}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "actions",
|
||||
elements: [
|
||||
{
|
||||
type: "button",
|
||||
text: { type: "plain_text", text: "Review Task" },
|
||||
url: params.taskUrl,
|
||||
style: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export async function slackNotifyTaskAssigned(
|
||||
webhookUrl: string,
|
||||
params: {
|
||||
taskTitle: string;
|
||||
contextCode: string | null;
|
||||
artistName: string;
|
||||
assignedByName: string;
|
||||
projectName: string;
|
||||
taskUrl: string;
|
||||
}
|
||||
) {
|
||||
const label = params.contextCode ? `${params.contextCode} — ` : "";
|
||||
await sendSlackMessage(webhookUrl, {
|
||||
blocks: [
|
||||
{
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text: `📋 *Task assigned*\n*${params.projectName}* · ${label}${params.taskTitle}\nAssigned to ${params.artistName} by ${params.assignedByName}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "actions",
|
||||
elements: [
|
||||
{
|
||||
type: "button",
|
||||
text: { type: "plain_text", text: "View Task" },
|
||||
url: params.taskUrl,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export async function slackNotifyNewFeedback(
|
||||
webhookUrl: string,
|
||||
params: {
|
||||
shotCode: string;
|
||||
frameNumber: number;
|
||||
authorName: string;
|
||||
commentText: string;
|
||||
reviewUrl: string;
|
||||
}
|
||||
) {
|
||||
const truncated =
|
||||
params.commentText.length > 120
|
||||
? params.commentText.slice(0, 117) + "..."
|
||||
: params.commentText;
|
||||
|
||||
await sendSlackMessage(webhookUrl, {
|
||||
blocks: [
|
||||
{
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text: `💬 *New feedback* on ${params.shotCode} frame ${params.frameNumber}\n*${params.authorName}:* ${truncated}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "actions",
|
||||
elements: [
|
||||
{
|
||||
type: "button",
|
||||
text: { type: "plain_text", text: "Jump to Frame" },
|
||||
url: params.reviewUrl,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user