simple-support-chat 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,149 @@
1
+ import { WebClient } from '@slack/web-api';
2
+
3
+ // src/server/handler.ts
4
+ async function handleMessage(body, options) {
5
+ const { slack, slackChannel, botName, botIcon, onMessage, threadMap } = options;
6
+ if (!body.message || typeof body.message !== "string") {
7
+ return {
8
+ data: { success: false, error: "Missing required field: message" },
9
+ status: 400
10
+ };
11
+ }
12
+ if (!body.sessionId || typeof body.sessionId !== "string") {
13
+ return {
14
+ data: { success: false, error: "Missing required field: sessionId" },
15
+ status: 400
16
+ };
17
+ }
18
+ if (onMessage) {
19
+ try {
20
+ await onMessage(body);
21
+ } catch {
22
+ }
23
+ }
24
+ const lookupKey = body.user?.id ?? body.sessionId;
25
+ const existingThread = threadMap.get(lookupKey);
26
+ try {
27
+ const userLabel = body.user ? `${body.user.name ?? "User"} (${body.user.email ?? body.user.id})` : `Anonymous (session ${body.sessionId.substring(0, 8)})`;
28
+ const contextLines = [];
29
+ if (body.context?.pageUrl) contextLines.push(`Page: ${body.context.pageUrl}`);
30
+ if (body.context?.userAgent) contextLines.push(`Browser: ${body.context.userAgent}`);
31
+ if (body.context?.timezone) contextLines.push(`Timezone: ${body.context.timezone}`);
32
+ if (existingThread) {
33
+ await slack.chat.postMessage({
34
+ channel: slackChannel,
35
+ thread_ts: existingThread,
36
+ text: body.message,
37
+ ...botName ? { username: botName } : {},
38
+ ...botIcon ? { icon_emoji: botIcon } : {}
39
+ });
40
+ return {
41
+ data: { success: true, threadId: existingThread },
42
+ status: 200
43
+ };
44
+ } else {
45
+ const headerText = `*Support: ${userLabel}*
46
+ ${contextLines.join("\n")}`;
47
+ const result = await slack.chat.postMessage({
48
+ channel: slackChannel,
49
+ text: `${headerText}
50
+
51
+ ${body.message}`,
52
+ ...botName ? { username: botName } : {},
53
+ ...botIcon ? { icon_emoji: botIcon } : {}
54
+ });
55
+ const threadTs = result.ts;
56
+ if (!threadTs) {
57
+ return {
58
+ data: { success: false, error: "Slack did not return thread timestamp" },
59
+ status: 500
60
+ };
61
+ }
62
+ threadMap.set(lookupKey, threadTs);
63
+ return {
64
+ data: { success: true, threadId: threadTs },
65
+ status: 200
66
+ };
67
+ }
68
+ } catch (err) {
69
+ const message = err instanceof Error ? err.message : "Unknown Slack error";
70
+ return {
71
+ data: { success: false, error: `Slack API error: ${message}` },
72
+ status: 500
73
+ };
74
+ }
75
+ }
76
+ function createSupportHandler(options) {
77
+ const { slackBotToken, slackChannel, botName, botIcon, onMessage } = options;
78
+ const slack = new WebClient(slackBotToken);
79
+ const threadMap = /* @__PURE__ */ new Map();
80
+ return async (request) => {
81
+ if (request.method !== "POST") {
82
+ return jsonResponse(
83
+ { success: false, error: "Method not allowed" },
84
+ 405
85
+ );
86
+ }
87
+ let body;
88
+ try {
89
+ body = await request.json();
90
+ } catch {
91
+ return jsonResponse(
92
+ { success: false, error: "Invalid JSON body" },
93
+ 400
94
+ );
95
+ }
96
+ const result = await handleMessage(body, {
97
+ slack,
98
+ slackChannel,
99
+ botName,
100
+ botIcon,
101
+ onMessage,
102
+ threadMap
103
+ });
104
+ return jsonResponse(result.data, result.status);
105
+ };
106
+ }
107
+ function createExpressHandler(options) {
108
+ const { slackBotToken, slackChannel, botName, botIcon, onMessage } = options;
109
+ const slack = new WebClient(slackBotToken);
110
+ const threadMap = /* @__PURE__ */ new Map();
111
+ return async (req, res) => {
112
+ const body = req.body;
113
+ const result = await handleMessage(body, {
114
+ slack,
115
+ slackChannel,
116
+ botName,
117
+ botIcon,
118
+ onMessage,
119
+ threadMap
120
+ });
121
+ res.status(result.status).json(result.data);
122
+ };
123
+ }
124
+ async function validateSlackToken(token) {
125
+ try {
126
+ const slack = new WebClient(token);
127
+ const result = await slack.auth.test();
128
+ return {
129
+ ok: true,
130
+ team: result.team,
131
+ user: result.user
132
+ };
133
+ } catch (err) {
134
+ return {
135
+ ok: false,
136
+ error: err instanceof Error ? err.message : "Invalid token"
137
+ };
138
+ }
139
+ }
140
+ function jsonResponse(data, status = 200) {
141
+ return new Response(JSON.stringify(data), {
142
+ status,
143
+ headers: { "Content-Type": "application/json" }
144
+ });
145
+ }
146
+
147
+ export { createExpressHandler, createSupportHandler, validateSlackToken };
148
+ //# sourceMappingURL=index.js.map
149
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/handler.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { WebClient } from \"@slack/web-api\";\r\nimport type {\r\n SupportHandlerOptions,\r\n IncomingMessage,\r\n HandlerResponse,\r\n ExpressRequest,\r\n ExpressResponse,\r\n} from \"./types\";\r\n\r\n/**\r\n * Core logic shared by both the Web API handler and the Express handler.\r\n * Accepts a parsed body and returns the response data + status code.\r\n */\r\nasync function handleMessage(\r\n body: IncomingMessage,\r\n options: {\r\n slack: WebClient;\r\n slackChannel: string;\r\n botName?: string;\r\n botIcon?: string;\r\n onMessage?: (data: IncomingMessage) => void | Promise<void>;\r\n threadMap: Map<string, string>;\r\n },\r\n): Promise<{ data: HandlerResponse; status: number }> {\r\n const { slack, slackChannel, botName, botIcon, onMessage, threadMap } = options;\r\n\r\n // Validate required fields\r\n if (!body.message || typeof body.message !== \"string\") {\r\n return {\r\n data: { success: false, error: \"Missing required field: message\" },\r\n status: 400,\r\n };\r\n }\r\n\r\n if (!body.sessionId || typeof body.sessionId !== \"string\") {\r\n return {\r\n data: { success: false, error: \"Missing required field: sessionId\" },\r\n status: 400,\r\n };\r\n }\r\n\r\n // Fire onMessage callback\r\n if (onMessage) {\r\n try {\r\n await onMessage(body);\r\n } catch {\r\n // Don't block on callback errors\r\n }\r\n }\r\n\r\n const lookupKey = body.user?.id ?? body.sessionId;\r\n const existingThread = threadMap.get(lookupKey);\r\n\r\n try {\r\n // Build the Slack message\r\n const userLabel = body.user\r\n ? `${body.user.name ?? \"User\"} (${body.user.email ?? body.user.id})`\r\n : `Anonymous (session ${body.sessionId.substring(0, 8)})`;\r\n\r\n const contextLines: string[] = [];\r\n if (body.context?.pageUrl) contextLines.push(`Page: ${body.context.pageUrl}`);\r\n if (body.context?.userAgent) contextLines.push(`Browser: ${body.context.userAgent}`);\r\n if (body.context?.timezone) contextLines.push(`Timezone: ${body.context.timezone}`);\r\n\r\n if (existingThread) {\r\n // Reply to existing thread\r\n await slack.chat.postMessage({\r\n channel: slackChannel,\r\n thread_ts: existingThread,\r\n text: body.message,\r\n ...(botName ? { username: botName } : {}),\r\n ...(botIcon ? { icon_emoji: botIcon } : {}),\r\n });\r\n\r\n return {\r\n data: { success: true, threadId: existingThread },\r\n status: 200,\r\n };\r\n } else {\r\n // Create new thread\r\n const headerText = `*Support: ${userLabel}*\\n${contextLines.join(\"\\n\")}`;\r\n\r\n const result = await slack.chat.postMessage({\r\n channel: slackChannel,\r\n text: `${headerText}\\n\\n${body.message}`,\r\n ...(botName ? { username: botName } : {}),\r\n ...(botIcon ? { icon_emoji: botIcon } : {}),\r\n });\r\n\r\n const threadTs = result.ts;\r\n if (!threadTs) {\r\n return {\r\n data: { success: false, error: \"Slack did not return thread timestamp\" },\r\n status: 500,\r\n };\r\n }\r\n\r\n threadMap.set(lookupKey, threadTs);\r\n\r\n return {\r\n data: { success: true, threadId: threadTs },\r\n status: 200,\r\n };\r\n }\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : \"Unknown Slack error\";\r\n return {\r\n data: { success: false, error: `Slack API error: ${message}` },\r\n status: 500,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Creates a support chat POST handler that routes messages to Slack threads.\r\n *\r\n * The returned function accepts a standard Web API `Request` and returns a `Response`.\r\n * This makes it compatible with Next.js App Router, Remix, Hono, and any framework\r\n * that supports the Web Request/Response standard.\r\n *\r\n * For Express/Connect-style frameworks, use `createExpressHandler()` instead.\r\n *\r\n * @example Next.js App Router\r\n * ```ts\r\n * // app/api/support/route.ts\r\n * import { createSupportHandler } from 'support-chat/server';\r\n *\r\n * export const POST = createSupportHandler({\r\n * slackBotToken: process.env.SLACK_BOT_TOKEN!,\r\n * slackChannel: process.env.SLACK_CHANNEL!,\r\n * });\r\n * ```\r\n */\r\nexport function createSupportHandler(options: SupportHandlerOptions) {\r\n const { slackBotToken, slackChannel, botName, botIcon, onMessage } = options;\r\n\r\n const slack = new WebClient(slackBotToken);\r\n const threadMap = new Map<string, string>();\r\n\r\n return async (request: Request): Promise<Response> => {\r\n // Only accept POST\r\n if (request.method !== \"POST\") {\r\n return jsonResponse(\r\n { success: false, error: \"Method not allowed\" } satisfies HandlerResponse,\r\n 405,\r\n );\r\n }\r\n\r\n let body: IncomingMessage;\r\n try {\r\n body = (await request.json()) as IncomingMessage;\r\n } catch {\r\n return jsonResponse(\r\n { success: false, error: \"Invalid JSON body\" } satisfies HandlerResponse,\r\n 400,\r\n );\r\n }\r\n\r\n const result = await handleMessage(body, {\r\n slack,\r\n slackChannel,\r\n botName,\r\n botIcon,\r\n onMessage,\r\n threadMap,\r\n });\r\n\r\n return jsonResponse(result.data, result.status);\r\n };\r\n}\r\n\r\n/**\r\n * Creates an Express/Connect-compatible support chat handler.\r\n *\r\n * The returned function uses the Express `(req, res)` signature.\r\n * The request body must be parsed (use `express.json()` middleware).\r\n *\r\n * @example Express\r\n * ```ts\r\n * import express from 'express';\r\n * import { createExpressHandler } from 'support-chat/server';\r\n *\r\n * const app = express();\r\n * app.use(express.json());\r\n *\r\n * app.post('/api/support', createExpressHandler({\r\n * slackBotToken: process.env.SLACK_BOT_TOKEN!,\r\n * slackChannel: process.env.SLACK_CHANNEL!,\r\n * }));\r\n * ```\r\n */\r\nexport function createExpressHandler(options: SupportHandlerOptions) {\r\n const { slackBotToken, slackChannel, botName, botIcon, onMessage } = options;\r\n\r\n const slack = new WebClient(slackBotToken);\r\n const threadMap = new Map<string, string>();\r\n\r\n return async (req: ExpressRequest, res: ExpressResponse): Promise<void> => {\r\n const body = req.body as IncomingMessage;\r\n\r\n const result = await handleMessage(body, {\r\n slack,\r\n slackChannel,\r\n botName,\r\n botIcon,\r\n onMessage,\r\n threadMap,\r\n });\r\n\r\n res.status(result.status).json(result.data);\r\n };\r\n}\r\n\r\n/** Validate a Slack bot token by calling auth.test */\r\nexport async function validateSlackToken(\r\n token: string,\r\n): Promise<{ ok: boolean; team?: string; user?: string; error?: string }> {\r\n try {\r\n const slack = new WebClient(token);\r\n const result = await slack.auth.test();\r\n return {\r\n ok: true,\r\n team: result.team,\r\n user: result.user,\r\n };\r\n } catch (err) {\r\n return {\r\n ok: false,\r\n error: err instanceof Error ? err.message : \"Invalid token\",\r\n };\r\n }\r\n}\r\n\r\nfunction jsonResponse(data: HandlerResponse, status = 200): Response {\r\n return new Response(JSON.stringify(data), {\r\n status,\r\n headers: { \"Content-Type\": \"application/json\" },\r\n });\r\n}\r\n"]}
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "simple-support-chat",
3
+ "version": "0.1.0",
4
+ "description": "Embeddable chat widget SDK that routes customer messages to Slack threads. Open-source Crisp alternative.",
5
+ "license": "MIT",
6
+ "author": "Indigo AI",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/getindigo/support-chat"
10
+ },
11
+ "type": "module",
12
+ "exports": {
13
+ ".": {
14
+ "import": {
15
+ "types": "./dist/client/index.d.ts",
16
+ "default": "./dist/client/index.js"
17
+ },
18
+ "require": {
19
+ "types": "./dist/client/index.d.cts",
20
+ "default": "./dist/client/index.cjs"
21
+ }
22
+ },
23
+ "./server": {
24
+ "import": {
25
+ "types": "./dist/server/index.d.ts",
26
+ "default": "./dist/server/index.js"
27
+ },
28
+ "require": {
29
+ "types": "./dist/server/index.d.cts",
30
+ "default": "./dist/server/index.cjs"
31
+ }
32
+ }
33
+ },
34
+ "main": "./dist/client/index.cjs",
35
+ "module": "./dist/client/index.js",
36
+ "types": "./dist/client/index.d.ts",
37
+ "files": [
38
+ "dist",
39
+ "README.md",
40
+ "LICENSE"
41
+ ],
42
+ "scripts": {
43
+ "build": "tsup",
44
+ "dev": "tsup --watch",
45
+ "typecheck": "tsc --noEmit",
46
+ "lint": "eslint src/",
47
+ "lint:fix": "eslint src/ --fix",
48
+ "format": "prettier --write \"src/**/*.{ts,tsx}\"",
49
+ "format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
50
+ "test": "vitest run",
51
+ "test:watch": "vitest"
52
+ },
53
+ "peerDependencies": {
54
+ "react": ">=18.0.0",
55
+ "react-dom": ">=18.0.0"
56
+ },
57
+ "peerDependenciesMeta": {
58
+ "react": {
59
+ "optional": true
60
+ },
61
+ "react-dom": {
62
+ "optional": true
63
+ }
64
+ },
65
+ "dependencies": {
66
+ "@slack/web-api": "^7.8.0"
67
+ },
68
+ "devDependencies": {
69
+ "@testing-library/jest-dom": "^6.6.3",
70
+ "@testing-library/react": "^16.1.0",
71
+ "@types/react": "^19.0.0",
72
+ "@types/react-dom": "^19.0.0",
73
+ "eslint": "^9.17.0",
74
+ "eslint-config-prettier": "^10.0.1",
75
+ "globals": "^15.14.0",
76
+ "jsdom": "^26.0.0",
77
+ "prettier": "^3.4.2",
78
+ "react": "^19.0.0",
79
+ "react-dom": "^19.0.0",
80
+ "tsup": "^8.3.5",
81
+ "typescript": "^5.7.2",
82
+ "typescript-eslint": "^8.18.0",
83
+ "vitest": "^3.0.0"
84
+ }
85
+ }