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.
- package/LICENSE +21 -0
- package/README.md +307 -0
- package/dist/client/index.cjs +771 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +168 -0
- package/dist/client/index.d.ts +168 -0
- package/dist/client/index.js +764 -0
- package/dist/client/index.js.map +1 -0
- package/dist/server/index.cjs +153 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +117 -0
- package/dist/server/index.d.ts +117 -0
- package/dist/server/index.js +149 -0
- package/dist/server/index.js.map +1 -0
- package/package.json +85 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Indigo AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# simple-support-chat
|
|
2
|
+
|
|
3
|
+
Embeddable chat widget SDK that routes customer messages to Slack threads. Open-source alternative to Crisp, Intercom, and Drift.
|
|
4
|
+
|
|
5
|
+
- Zero cost, self-hosted
|
|
6
|
+
- Slack-native: messages appear as threads in your workspace
|
|
7
|
+
- Works with any React app (Next.js, Remix, Vite, etc.)
|
|
8
|
+
- Anonymous and authenticated user support
|
|
9
|
+
- No external CSS or Tailwind dependency
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add simple-support-chat
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
React 18+ is a peer dependency for the client widget. The server handler works without React.
|
|
18
|
+
|
|
19
|
+
## Slack App Setup
|
|
20
|
+
|
|
21
|
+
Follow these steps to create a Slack App for your workspace. Each open-source user creates their own app -- there is no shared OAuth install flow.
|
|
22
|
+
|
|
23
|
+
### Step 1: Create the App
|
|
24
|
+
|
|
25
|
+
1. Go to [api.slack.com/apps](https://api.slack.com/apps) and click **Create New App**
|
|
26
|
+
2. Choose **From scratch**
|
|
27
|
+
3. Name it (e.g., "Support Chat") and select your workspace
|
|
28
|
+
4. Click **Create App**
|
|
29
|
+
|
|
30
|
+
### Step 2: Configure Bot Token Scopes
|
|
31
|
+
|
|
32
|
+
Navigate to **OAuth & Permissions** in the sidebar and scroll to **Bot Token Scopes**. Add these three scopes:
|
|
33
|
+
|
|
34
|
+
| Scope | Purpose |
|
|
35
|
+
|-------|---------|
|
|
36
|
+
| `chat:write` | Post messages to channels |
|
|
37
|
+
| `chat:write.customize` | Customize the bot's display name and icon per message |
|
|
38
|
+
| `users:read` | Read basic user profile information |
|
|
39
|
+
|
|
40
|
+
These are the minimum required scopes. You do not need any User Token Scopes.
|
|
41
|
+
|
|
42
|
+
### Step 3: Install to Workspace
|
|
43
|
+
|
|
44
|
+
1. Scroll to the top of **OAuth & Permissions** and click **Install to Workspace**
|
|
45
|
+
2. Review the permissions and click **Allow**
|
|
46
|
+
3. Copy the **Bot User OAuth Token** -- it starts with `xoxb-`
|
|
47
|
+
|
|
48
|
+
### Step 4: Get Your Channel ID
|
|
49
|
+
|
|
50
|
+
You need the channel ID (not the channel name) for configuration:
|
|
51
|
+
|
|
52
|
+
1. Open Slack and navigate to the channel you want to use for support
|
|
53
|
+
2. Click the channel name at the top to open channel details
|
|
54
|
+
3. Scroll to the bottom of the details panel -- the Channel ID is displayed there (e.g., `C0123ABCDEF`)
|
|
55
|
+
|
|
56
|
+
Alternatively, right-click the channel name, click **Copy link**, and extract the ID from the URL.
|
|
57
|
+
|
|
58
|
+
### Step 5: Invite the Bot
|
|
59
|
+
|
|
60
|
+
The bot must be a member of the channel to post messages. In Slack, type:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
/invite @YourBotName
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Step 6: Set Environment Variables
|
|
67
|
+
|
|
68
|
+
Add these to your `.env` file:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
SLACK_BOT_TOKEN=xoxb-your-token-here
|
|
72
|
+
SLACK_CHANNEL_ID=C0123ABCDEF
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Validate Your Token
|
|
76
|
+
|
|
77
|
+
Use the built-in `validateSlackToken` utility to verify your token works before going live:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { validateSlackToken } from "simple-support-chat/server";
|
|
81
|
+
|
|
82
|
+
const result = await validateSlackToken(process.env.SLACK_BOT_TOKEN!);
|
|
83
|
+
if (result.ok) {
|
|
84
|
+
console.log(`Connected to workspace: ${result.team} as ${result.user}`);
|
|
85
|
+
} else {
|
|
86
|
+
console.error(`Token validation failed: ${result.error}`);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Troubleshooting
|
|
91
|
+
|
|
92
|
+
| Problem | Solution |
|
|
93
|
+
|---------|----------|
|
|
94
|
+
| `not_in_channel` error | Invite the bot to the channel with `/invite @BotName` |
|
|
95
|
+
| `invalid_auth` error | Check that your token starts with `xoxb-` and has not been revoked |
|
|
96
|
+
| `channel_not_found` error | Verify you are using the Channel ID (e.g., `C0123ABCDEF`), not the channel name |
|
|
97
|
+
| Messages not appearing | Confirm the bot has `chat:write` scope and the channel ID is correct |
|
|
98
|
+
| Bot name/icon not customizing | Ensure `chat:write.customize` scope is added |
|
|
99
|
+
|
|
100
|
+
## Quick Start
|
|
101
|
+
|
|
102
|
+
### 1. Server: Create API Route
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// app/api/support/route.ts (Next.js App Router)
|
|
106
|
+
import { createSupportHandler } from "simple-support-chat/server";
|
|
107
|
+
|
|
108
|
+
export const POST = createSupportHandler({
|
|
109
|
+
slackBotToken: process.env.SLACK_BOT_TOKEN!,
|
|
110
|
+
slackChannel: process.env.SLACK_CHANNEL_ID!,
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 2. Client: Add the Chat Widget
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
import { ChatBubble } from "simple-support-chat";
|
|
118
|
+
|
|
119
|
+
export default function App() {
|
|
120
|
+
return (
|
|
121
|
+
<>
|
|
122
|
+
{/* Your app content */}
|
|
123
|
+
<ChatBubble apiUrl="/api/support" />
|
|
124
|
+
</>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
That's it! Messages from your users will appear as threaded conversations in your Slack channel.
|
|
130
|
+
|
|
131
|
+
## Configuration
|
|
132
|
+
|
|
133
|
+
### ChatBubble Props
|
|
134
|
+
|
|
135
|
+
| Prop | Type | Default | Description |
|
|
136
|
+
|------|------|---------|-------------|
|
|
137
|
+
| `apiUrl` | `string` | (required) | URL of your support API endpoint |
|
|
138
|
+
| `position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | `'bottom-right'` | Bubble position |
|
|
139
|
+
| `color` | `string` | `'#2563eb'` | Primary color (bubble, header, sent messages) |
|
|
140
|
+
| `title` | `string` | `'Support'` | Chat panel header title |
|
|
141
|
+
| `placeholder` | `string` | `'Type a message...'` | Input placeholder text |
|
|
142
|
+
| `show` | `boolean` | `true` | Show/hide the floating bubble |
|
|
143
|
+
| `user` | `ChatUser` | `undefined` | Authenticated user info |
|
|
144
|
+
|
|
145
|
+
### Server Options
|
|
146
|
+
|
|
147
|
+
| Option | Type | Description |
|
|
148
|
+
|--------|------|-------------|
|
|
149
|
+
| `slackBotToken` | `string` | Slack Bot OAuth Token (`xoxb-...`) |
|
|
150
|
+
| `slackChannel` | `string` | Slack channel ID |
|
|
151
|
+
| `botName` | `string` | Custom bot display name |
|
|
152
|
+
| `botIcon` | `string` | Bot icon emoji (e.g., `:speech_balloon:`) |
|
|
153
|
+
| `onMessage` | `(data) => void` | Callback on each message |
|
|
154
|
+
|
|
155
|
+
## Identity Integration
|
|
156
|
+
|
|
157
|
+
Pass the logged-in user's identity to the widget so Slack threads show who they are:
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import { ChatBubble } from "simple-support-chat";
|
|
161
|
+
import { useAuth } from "./your-auth"; // your auth provider
|
|
162
|
+
|
|
163
|
+
export function SupportWidget() {
|
|
164
|
+
const { user } = useAuth();
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<ChatBubble
|
|
168
|
+
apiUrl="/api/support"
|
|
169
|
+
user={user ? { id: user.id, name: user.name, email: user.email } : undefined}
|
|
170
|
+
/>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
When a user is identified, Slack threads are titled: **Support: John Doe (john@example.com)**
|
|
176
|
+
When anonymous: **Support: Anonymous (session abc12345)**
|
|
177
|
+
|
|
178
|
+
## Modal Mode
|
|
179
|
+
|
|
180
|
+
Hide the floating bubble and trigger the chat from a custom "Contact Us" element using the `useSupportChat` hook and `SupportChatModal` component:
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
import { SupportChatModal, useSupportChat } from "simple-support-chat";
|
|
184
|
+
|
|
185
|
+
function App() {
|
|
186
|
+
const { open, close, isOpen } = useSupportChat();
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<>
|
|
190
|
+
<nav>
|
|
191
|
+
<button onClick={open}>Contact Us</button>
|
|
192
|
+
</nav>
|
|
193
|
+
|
|
194
|
+
<SupportChatModal
|
|
195
|
+
apiUrl="/api/support"
|
|
196
|
+
isOpen={isOpen}
|
|
197
|
+
onClose={close}
|
|
198
|
+
title="Get Help"
|
|
199
|
+
color="#10b981"
|
|
200
|
+
user={currentUser}
|
|
201
|
+
/>
|
|
202
|
+
</>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The modal renders centered on desktop (max-width 500px) with a backdrop overlay, and full-screen on mobile. You can place the trigger button anywhere -- nav bar, footer, settings page, error boundaries, etc.
|
|
208
|
+
|
|
209
|
+
### SupportChatModal Props
|
|
210
|
+
|
|
211
|
+
| Prop | Type | Default | Description |
|
|
212
|
+
|------|------|---------|-------------|
|
|
213
|
+
| `apiUrl` | `string` | (required) | URL of your support API endpoint |
|
|
214
|
+
| `isOpen` | `boolean` | (required) | Whether the modal is open |
|
|
215
|
+
| `onClose` | `() => void` | (required) | Callback to close the modal |
|
|
216
|
+
| `color` | `string` | `'#2563eb'` | Primary color |
|
|
217
|
+
| `title` | `string` | `'Contact Us'` | Modal header title |
|
|
218
|
+
| `placeholder` | `string` | `'Type a message...'` | Input placeholder text |
|
|
219
|
+
| `user` | `ChatUser` | `undefined` | Authenticated user info |
|
|
220
|
+
|
|
221
|
+
### useSupportChat Hook
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
import { useSupportChat } from "simple-support-chat";
|
|
225
|
+
|
|
226
|
+
function ContactButton() {
|
|
227
|
+
const { open } = useSupportChat();
|
|
228
|
+
return <button onClick={open}>Contact Us</button>;
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Express / Connect Handler
|
|
233
|
+
|
|
234
|
+
For Express-style frameworks (Express, Fastify with express compat, etc.), use `createExpressHandler` instead:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import express from "express";
|
|
238
|
+
import { createExpressHandler } from "simple-support-chat/server";
|
|
239
|
+
|
|
240
|
+
const app = express();
|
|
241
|
+
app.use(express.json());
|
|
242
|
+
|
|
243
|
+
app.post(
|
|
244
|
+
"/api/support",
|
|
245
|
+
createExpressHandler({
|
|
246
|
+
slackBotToken: process.env.SLACK_BOT_TOKEN!,
|
|
247
|
+
slackChannel: process.env.SLACK_CHANNEL_ID!,
|
|
248
|
+
botName: "Support Bot",
|
|
249
|
+
botIcon: ":speech_balloon:",
|
|
250
|
+
onMessage: (data) => {
|
|
251
|
+
console.log("New support message:", data.message);
|
|
252
|
+
},
|
|
253
|
+
}),
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
app.listen(3000);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
The Express handler uses the same threading logic as the Web API handler. The request body must be parsed before the handler runs (use `express.json()` middleware).
|
|
260
|
+
|
|
261
|
+
## API Reference
|
|
262
|
+
|
|
263
|
+
### Server Exports (`simple-support-chat/server`)
|
|
264
|
+
|
|
265
|
+
| Export | Description |
|
|
266
|
+
|--------|-------------|
|
|
267
|
+
| `createSupportHandler(options)` | Returns a Web API `(Request) => Promise<Response>` handler |
|
|
268
|
+
| `createExpressHandler(options)` | Returns an Express `(req, res) => Promise<void>` handler |
|
|
269
|
+
| `validateSlackToken(token)` | Validates a Slack token via `auth.test` |
|
|
270
|
+
|
|
271
|
+
### Client Exports (`simple-support-chat`)
|
|
272
|
+
|
|
273
|
+
| Export | Description |
|
|
274
|
+
|--------|-------------|
|
|
275
|
+
| `ChatBubble` | Floating chat bubble React component |
|
|
276
|
+
| `SupportChatModal` | Modal-based chat React component |
|
|
277
|
+
| `useSupportChat()` | Hook returning `{ open, close, toggle, isOpen }` |
|
|
278
|
+
| `useChatEngine(options)` | Low-level hook for chat message state |
|
|
279
|
+
| `collectAnonymousContext()` | Collects browser context (page URL, user agent, etc.) |
|
|
280
|
+
| `getSessionId()` | Gets or creates a persistent anonymous session ID |
|
|
281
|
+
|
|
282
|
+
### Types
|
|
283
|
+
|
|
284
|
+
All TypeScript types are exported from both entry points:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
// Client types
|
|
288
|
+
import type { ChatBubbleProps, ChatUser, SupportChatModalProps, ChatMessage } from "simple-support-chat";
|
|
289
|
+
|
|
290
|
+
// Server types
|
|
291
|
+
import type { SupportHandlerOptions, IncomingMessage, HandlerResponse } from "simple-support-chat/server";
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Development
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
pnpm install
|
|
298
|
+
pnpm dev # Watch mode
|
|
299
|
+
pnpm test # Run tests
|
|
300
|
+
pnpm typecheck # TypeScript check
|
|
301
|
+
pnpm lint # ESLint
|
|
302
|
+
pnpm build # Production build
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## License
|
|
306
|
+
|
|
307
|
+
MIT
|