rushchatbot 1.6.9 → 1.7.1
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/README.md +1 -1
- package/package/INTEGRATION.md +1 -1
- package/package/README.md +1 -1
- package/package/package.json +1 -1
- package/package/src/client/ChatWidget.tsx +3 -0
- package/package/src/core/RushChatbot.ts +26 -1
- package/package/src/server/index.ts +19 -6
- package/package/src/shared/types.ts +2 -1
- package/package/src/vanilla/index.ts +7 -0
- package/package/src/vue/ChatWidget.vue +3 -0
- package/package/src/vue2/ChatWidget.vue +3 -0
- package/package.json +1 -1
- package/server/data/analytics.json +30 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# RushChatbot
|
|
2
2
|
|
|
3
|
-
**Version:** 1.
|
|
3
|
+
**Version:** 1.7.1
|
|
4
4
|
|
|
5
5
|
Embeddable hybrid chatbot plugin that combines a click-based FAQ tree with a free-text query engine backed by n8n. Ships as an npm package with a Node.js/Express server and widgets for React, Vue 3, and Vanilla JS.
|
|
6
6
|
|
package/package/INTEGRATION.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# RushChatbot
|
|
2
2
|
|
|
3
|
-
**Version:** 1.
|
|
3
|
+
**Version:** 1.7.0
|
|
4
4
|
|
|
5
5
|
Embeddable hybrid chatbot plugin that combines a click-based FAQ tree with a free-text query engine backed by n8n. Ships as an npm package with a Node.js/Express server and widgets for React, Vue 3, and Vanilla JS.
|
|
6
6
|
|
package/package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# RushChatbot
|
|
2
2
|
|
|
3
|
-
**Version:** 1.
|
|
3
|
+
**Version:** 1.7.1
|
|
4
4
|
|
|
5
5
|
Embeddable hybrid chatbot plugin that combines a click-based FAQ tree with a free-text query engine backed by n8n. Ships as an npm package with a Node.js/Express server and widgets for React, Vue 3, and Vanilla JS.
|
|
6
6
|
|
package/package/package.json
CHANGED
|
@@ -44,6 +44,9 @@ function ThinkingDots({ color }: { color: string }) {
|
|
|
44
44
|
.rushchatbot-md h1 { font-size: 1.1em; }
|
|
45
45
|
.rushchatbot-md h2 { font-size: 1em; }
|
|
46
46
|
.rushchatbot-md h3 { font-size: 0.95em; }
|
|
47
|
+
.rushchatbot-md code { font-family: monospace; font-size: 0.9em; background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; }
|
|
48
|
+
.rushchatbot-md pre { background: rgba(0,0,0,0.08); border-radius: 6px; padding: 10px 12px; overflow-x: auto; margin: 6px 0; }
|
|
49
|
+
.rushchatbot-md pre code { background: none; padding: 0; font-size: 0.85em; white-space: pre; }
|
|
47
50
|
`}</style>
|
|
48
51
|
</span>
|
|
49
52
|
)
|
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
import { marked } from 'marked'
|
|
2
2
|
import type { Message, Question, WidgetSettings } from '../shared/types'
|
|
3
3
|
|
|
4
|
-
marked.
|
|
4
|
+
marked.use({
|
|
5
|
+
breaks: true,
|
|
6
|
+
renderer: {
|
|
7
|
+
code({ text, lang }: { text: string; lang?: string }): string {
|
|
8
|
+
const escaped = text
|
|
9
|
+
.replace(/&/g, '&')
|
|
10
|
+
.replace(/</g, '<')
|
|
11
|
+
.replace(/>/g, '>')
|
|
12
|
+
const langAttr = lang ? ` data-lang="${lang}"` : ''
|
|
13
|
+
return `<pre${langAttr}><code>${escaped}</code></pre>`
|
|
14
|
+
},
|
|
15
|
+
codespan({ text }: { text: string }): string {
|
|
16
|
+
const escaped = text
|
|
17
|
+
.replace(/&/g, '&')
|
|
18
|
+
.replace(/</g, '<')
|
|
19
|
+
.replace(/>/g, '>')
|
|
20
|
+
return `<code>${escaped}</code>`
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
})
|
|
5
24
|
|
|
6
25
|
export interface RushChatbotConfig {
|
|
7
26
|
apiBase?: string
|
|
@@ -170,6 +189,12 @@ export class RushChatbot {
|
|
|
170
189
|
|
|
171
190
|
this.setState({ loading: false })
|
|
172
191
|
|
|
192
|
+
if (res.source === 'n8n_error' || res.source === 'error') {
|
|
193
|
+
this.addMessage({ role: 'bot', text: res.answer ?? "I'm having trouble right now. Please try again in a moment. ⏳", source: res.source, isError: true })
|
|
194
|
+
this.setState({ showMain: true })
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
|
|
173
198
|
if (res.paragraphs?.length > 0) {
|
|
174
199
|
for (const p of res.paragraphs) {
|
|
175
200
|
this.addMessage({ role: 'bot', text: p, source: res.source })
|
|
@@ -224,12 +224,25 @@ export function createServer(options: RushChatbotServerOptions) {
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
saveAnalytics({ sessionId: sid, questionId: 'n8n', timestamp: new Date().toISOString(), type: 'n8n' })
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
227
|
+
try {
|
|
228
|
+
const { data } = await axios.post(
|
|
229
|
+
webhookUrl,
|
|
230
|
+
{ query: query.trim(), sessionId: sid },
|
|
231
|
+
{ timeout: timeoutMs, headers: { 'Content-Type': 'application/json', 'x-session-id': sid } }
|
|
232
|
+
)
|
|
233
|
+
return res.json({ ...parseN8nResponse(data), source: 'n8n' })
|
|
234
|
+
} catch (n8nErr: unknown) {
|
|
235
|
+
const n8nMsg = n8nErr instanceof Error ? n8nErr.message : String(n8nErr)
|
|
236
|
+
const isTimeout = n8nMsg.includes('timeout') || n8nMsg.includes('ECONNREFUSED') || n8nMsg.includes('ENOTFOUND')
|
|
237
|
+
console.error('[RushChatbot n8n error]', sanitizeLog(n8nMsg))
|
|
238
|
+
saveAnalytics({ sessionId: sid, questionId: 'n8n_error', timestamp: new Date().toISOString(), type: 'error' })
|
|
239
|
+
return res.status(502).json({
|
|
240
|
+
answer: isTimeout
|
|
241
|
+
? "I'm having trouble reaching my knowledge base right now. Please try again in a moment. ⏳"
|
|
242
|
+
: cfg.chat.fallbackMessage,
|
|
243
|
+
source: 'n8n_error',
|
|
244
|
+
})
|
|
245
|
+
}
|
|
233
246
|
|
|
234
247
|
} catch (err: unknown) {
|
|
235
248
|
const message = err instanceof Error ? err.message : String(err)
|
|
@@ -38,10 +38,11 @@ export interface Message {
|
|
|
38
38
|
id: string
|
|
39
39
|
role: 'user' | 'bot'
|
|
40
40
|
text: string
|
|
41
|
-
source?: 'config' | 'n8n' | 'error'
|
|
41
|
+
source?: 'config' | 'n8n' | 'n8n_error' | 'error'
|
|
42
42
|
subquestions?: Question[]
|
|
43
43
|
isFollowUp?: boolean
|
|
44
44
|
isEndChat?: boolean
|
|
45
|
+
isError?: boolean
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
export interface WidgetSettings {
|
|
@@ -56,6 +56,9 @@ const STYLES = `
|
|
|
56
56
|
.rushchatbot-md h1 { font-size: 1.1em; }
|
|
57
57
|
.rushchatbot-md h2 { font-size: 1em; }
|
|
58
58
|
.rushchatbot-md h3 { font-size: 0.95em; }
|
|
59
|
+
.rushchatbot-md code { font-family: monospace; font-size: 0.9em; background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; }
|
|
60
|
+
.rushchatbot-md pre { background: rgba(0,0,0,0.08); border-radius: 6px; padding: 10px 12px; overflow-x: auto; margin: 6px 0; }
|
|
61
|
+
.rushchatbot-md pre code { background: none; padding: 0; font-size: 0.85em; white-space: pre; }
|
|
59
62
|
`
|
|
60
63
|
|
|
61
64
|
const OBSERVED = [
|
|
@@ -372,6 +375,10 @@ class RushChatbotElement extends HTMLElement {
|
|
|
372
375
|
mdDiv.className = 'rushchatbot-md'
|
|
373
376
|
mdDiv.innerHTML = parseMarkdown(msg.text)
|
|
374
377
|
bubble.appendChild(mdDiv)
|
|
378
|
+
if (msg.isError) {
|
|
379
|
+
bubble.style.border = '1px solid #EF4444'
|
|
380
|
+
bubble.style.color = '#B91C1C'
|
|
381
|
+
}
|
|
375
382
|
if (msg.isEndChat) {
|
|
376
383
|
const newChatBtn = document.createElement('button')
|
|
377
384
|
newChatBtn.className = 'new-chat-btn'
|
|
@@ -195,4 +195,7 @@ const sendBtnStyle = computed(() => ({
|
|
|
195
195
|
.rushchatbot-md h1 { font-size: 1.1em; }
|
|
196
196
|
.rushchatbot-md h2 { font-size: 1em; }
|
|
197
197
|
.rushchatbot-md h3 { font-size: 0.95em; }
|
|
198
|
+
.rushchatbot-md code { font-family: monospace; font-size: 0.9em; background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; }
|
|
199
|
+
.rushchatbot-md pre { background: rgba(0,0,0,0.08); border-radius: 6px; padding: 10px 12px; overflow-x: auto; margin: 6px 0; }
|
|
200
|
+
.rushchatbot-md pre code { background: none; padding: 0; font-size: 0.85em; white-space: pre; }
|
|
198
201
|
</style>
|
|
@@ -201,4 +201,7 @@ export default {
|
|
|
201
201
|
0%, 80%, 100% { transform: translateY(0); opacity: 0.4; }
|
|
202
202
|
40% { transform: translateY(-6px); opacity: 1; }
|
|
203
203
|
}
|
|
204
|
+
.rushchatbot-md code { font-family: monospace; font-size: 0.9em; background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; }
|
|
205
|
+
.rushchatbot-md pre { background: rgba(0,0,0,0.08); border-radius: 6px; padding: 10px 12px; overflow-x: auto; margin: 6px 0; }
|
|
206
|
+
.rushchatbot-md pre code { background: none; padding: 0; font-size: 0.85em; white-space: pre; }
|
|
204
207
|
</style>
|
package/package.json
CHANGED
|
@@ -797,5 +797,35 @@
|
|
|
797
797
|
"questionId": "q1",
|
|
798
798
|
"timestamp": "2026-09-25T06:14:13.722Z",
|
|
799
799
|
"type": "click"
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
803
|
+
"questionId": "q3",
|
|
804
|
+
"timestamp": "2026-09-25T07:29:56.881Z",
|
|
805
|
+
"type": "click"
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
809
|
+
"questionId": "q3h",
|
|
810
|
+
"timestamp": "2026-09-25T07:29:59.276Z",
|
|
811
|
+
"type": "click"
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
815
|
+
"questionId": "q6",
|
|
816
|
+
"timestamp": "2026-09-25T07:30:05.858Z",
|
|
817
|
+
"type": "click"
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
821
|
+
"questionId": "q8",
|
|
822
|
+
"timestamp": "2026-09-25T07:31:58.988Z",
|
|
823
|
+
"type": "click"
|
|
824
|
+
},
|
|
825
|
+
{
|
|
826
|
+
"sessionId": "sess_mugn4uve_9yrh57",
|
|
827
|
+
"questionId": "q8c",
|
|
828
|
+
"timestamp": "2026-09-25T07:32:01.577Z",
|
|
829
|
+
"type": "click"
|
|
800
830
|
}
|
|
801
831
|
]
|