kaneo (minimalist kanban) fork to experiment adding a tangled integration github.com/usekaneo/kaneo
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at cd7cada2f86b4e866a15b4323bb8d6d7ab5bba8b 83 lines 2.1 kB view raw
1import * as v from "valibot"; 2 3const slackWebhookUrlPattern = 4 /^https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9]+\/[A-Za-z0-9]+\/[A-Za-z0-9]+$/; 5 6export const slackEventKeys = [ 7 "taskCreated", 8 "taskStatusChanged", 9 "taskPriorityChanged", 10 "taskTitleChanged", 11 "taskDescriptionChanged", 12 "taskCommentCreated", 13] as const; 14 15export type SlackEventKey = (typeof slackEventKeys)[number]; 16 17export const slackConfigSchema = v.object({ 18 webhookUrl: v.pipe( 19 v.string(), 20 v.regex(slackWebhookUrlPattern, "Invalid Slack webhook URL"), 21 ), 22 channelName: v.optional(v.string()), 23 events: v.optional( 24 v.object({ 25 taskCreated: v.optional(v.boolean()), 26 taskStatusChanged: v.optional(v.boolean()), 27 taskPriorityChanged: v.optional(v.boolean()), 28 taskTitleChanged: v.optional(v.boolean()), 29 taskDescriptionChanged: v.optional(v.boolean()), 30 taskCommentCreated: v.optional(v.boolean()), 31 }), 32 ), 33}); 34 35export type SlackConfig = v.InferOutput<typeof slackConfigSchema>; 36 37export const defaultSlackEvents: Record<SlackEventKey, boolean> = { 38 taskCreated: true, 39 taskStatusChanged: true, 40 taskPriorityChanged: false, 41 taskTitleChanged: false, 42 taskDescriptionChanged: false, 43 taskCommentCreated: true, 44}; 45 46export function getDefaultSlackConfig(webhookUrl: string): SlackConfig { 47 return { 48 webhookUrl, 49 events: { ...defaultSlackEvents }, 50 }; 51} 52 53export function normalizeSlackConfig(config: SlackConfig): SlackConfig { 54 return { 55 ...config, 56 channelName: config.channelName?.trim() || undefined, 57 events: { 58 ...defaultSlackEvents, 59 ...(config.events ?? {}), 60 }, 61 }; 62} 63 64export async function validateSlackConfig( 65 config: unknown, 66): Promise<{ valid: boolean; errors?: string[] }> { 67 try { 68 v.parse(slackConfigSchema, config); 69 return { valid: true }; 70 } catch (error) { 71 if (error instanceof v.ValiError) { 72 return { 73 valid: false, 74 errors: error.issues.map((issue) => issue.message), 75 }; 76 } 77 78 return { 79 valid: false, 80 errors: [error instanceof Error ? error.message : "Invalid config"], 81 }; 82 } 83}