@diegoaltoworks/chatter 0.26.0 → 0.28.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/dist/channels/gates.d.ts +90 -0
- package/dist/channels/gates.d.ts.map +1 -0
- package/dist/channels/gates.test.d.ts +2 -0
- package/dist/channels/gates.test.d.ts.map +1 -0
- package/dist/channels/index.d.ts +47 -0
- package/dist/channels/index.d.ts.map +1 -0
- package/dist/channels/index.js +137 -0
- package/dist/channels/index.mjs +105 -0
- package/dist/channels/senders.d.ts +30 -0
- package/dist/channels/senders.d.ts.map +1 -0
- package/dist/channels/senders.test.d.ts +2 -0
- package/dist/channels/senders.test.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +83 -2
- package/dist/index.mjs +83 -2
- package/dist/server.d.ts +21 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -2
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-agnostic reply gates — pure and testable, zero imports from the
|
|
3
|
+
* rest of chatter. A transport (WhatsApp, or any future channel) resolves
|
|
4
|
+
* its own wire format into a {@link ChannelMessage} and calls
|
|
5
|
+
* {@link decideChannelAction} to learn whether to answer, stay quiet, or
|
|
6
|
+
* flip a mute switch. Nothing here knows about jids, Baileys, or any
|
|
7
|
+
* specific bot's name — mention/reply-to detection is the transport's job;
|
|
8
|
+
* this module only combines the already-resolved booleans with allowlist,
|
|
9
|
+
* mute, and rate-limit policy.
|
|
10
|
+
*/
|
|
11
|
+
/** The slice of an inbound message every channel can resolve into this shape. */
|
|
12
|
+
export interface ChannelMessage {
|
|
13
|
+
/** The conversation this message belongs to — a DM thread or a group. */
|
|
14
|
+
chatId: string;
|
|
15
|
+
senderId: string;
|
|
16
|
+
text: string;
|
|
17
|
+
/** True for a 1:1 conversation; false for a group/channel with other members. */
|
|
18
|
+
isDirectMessage: boolean;
|
|
19
|
+
/** Resolved by the caller: does this message @-mention the bot? */
|
|
20
|
+
mentionsBot: boolean;
|
|
21
|
+
/** Resolved by the caller: is this message a reply to one of the bot's own messages? */
|
|
22
|
+
isReplyToBot: boolean;
|
|
23
|
+
/** True when the bot itself sent this message (including via another session — see {@link isEffectivelyFromSelf}). */
|
|
24
|
+
fromBot: boolean;
|
|
25
|
+
}
|
|
26
|
+
export type ChannelAction = "reply" | "ignore" | "mute" | "unmute";
|
|
27
|
+
export interface ChannelGateConfig {
|
|
28
|
+
/** Chats eligible for a reply. Empty = every chat is eligible (the master allowlist switch). */
|
|
29
|
+
allowedChats: string[];
|
|
30
|
+
/** Group chats currently muted — addressed messages there are ignored until unmuted. Has no effect on DMs, which always reply. */
|
|
31
|
+
mutedChats: Set<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Caller-supplied trigger for "go quiet in this group chat". No default:
|
|
34
|
+
* this module ships no bot name or in-character phrasing, so mute/unmute
|
|
35
|
+
* are inert unless a host configures a pattern. Never applied to DMs.
|
|
36
|
+
*/
|
|
37
|
+
muteRegex?: RegExp;
|
|
38
|
+
unmuteRegex?: RegExp;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* DMs always reply. In groups, only an allowlisted, unmuted chat gets a
|
|
42
|
+
* reply, and only when addressed (mentioned or replying to the bot) —
|
|
43
|
+
* merely mentioning the bot's name in third person is not an invitation.
|
|
44
|
+
* The unmute check runs before the muted-chat gate so an unmute phrase can
|
|
45
|
+
* un-silence the bot in the same message, and both run after the allowlist
|
|
46
|
+
* so a chat the bot isn't eligible for can't toggle its mute state.
|
|
47
|
+
*/
|
|
48
|
+
export declare function decideChannelAction(msg: ChannelMessage, config: ChannelGateConfig): ChannelAction;
|
|
49
|
+
/**
|
|
50
|
+
* Sliding-window rate limiter (pure, injectable clock): true = allowed and
|
|
51
|
+
* counted. One instance guards one budget — callers wanting separate DM and
|
|
52
|
+
* group budgets create two (see {@link underReplyRateLimit}).
|
|
53
|
+
*/
|
|
54
|
+
export declare function createSlidingWindowRateLimiter(maxPerWindow: number, windowMs: number, now?: () => number): (key: string) => boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Routes a chat to its DM or group rate limiter so a flood in one can't
|
|
57
|
+
* spend the other's budget — a DM flood used to be entirely unthrottled
|
|
58
|
+
* before this split existed. Each limiter is expected to be its own
|
|
59
|
+
* {@link createSlidingWindowRateLimiter} instance.
|
|
60
|
+
*/
|
|
61
|
+
export declare function underReplyRateLimit(msg: Pick<ChannelMessage, "chatId" | "isDirectMessage">, limiters: {
|
|
62
|
+
dm: (chatId: string) => boolean;
|
|
63
|
+
group: (chatId: string) => boolean;
|
|
64
|
+
}): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Every session's own identities ever seen, keyed by session id. A
|
|
67
|
+
* multi-session deployment (e.g. two linked numbers in one process) gives
|
|
68
|
+
* each session only its own identity from its transport — a message that
|
|
69
|
+
* actually came from a *different* session's identity looks like a
|
|
70
|
+
* stranger, not "us", and the two sessions can ping-pong replies at each
|
|
71
|
+
* other indefinitely. Callers populate this directly (`registry.set(sessionId, ids)`)
|
|
72
|
+
* as each session's identities become known, typically on connect and kept
|
|
73
|
+
* in sync as they change.
|
|
74
|
+
*
|
|
75
|
+
* Entries are NOT expected to be removed when a session disconnects: a
|
|
76
|
+
* session's own identity is still "us" while it reconnects, and losing that
|
|
77
|
+
* during exactly the disconnect/reconnect window this guard exists for
|
|
78
|
+
* would defeat it.
|
|
79
|
+
*/
|
|
80
|
+
export type SessionIdentityRegistry = Map<string, string[]>;
|
|
81
|
+
/** True when `id` matches an identity of ANY session ever registered, not just the one that received the message. */
|
|
82
|
+
export declare function isFromAnySession(id: string | undefined, registry: SessionIdentityRegistry): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* The one predicate transports need before deciding fromBot: a message is
|
|
85
|
+
* effectively from the bot itself if the transport already flagged it so,
|
|
86
|
+
* or if its sender id matches an identity registered by any session (the
|
|
87
|
+
* loop-guard case a single session's own flag can't catch).
|
|
88
|
+
*/
|
|
89
|
+
export declare function isEffectivelyFromSelf(msg: Pick<ChannelMessage, "fromBot" | "senderId">, registry: SessionIdentityRegistry): boolean;
|
|
90
|
+
//# sourceMappingURL=gates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../src/channels/gates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,eAAe,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,WAAW,EAAE,OAAO,CAAC;IACrB,wFAAwF;IACxF,YAAY,EAAE,OAAO,CAAC;IACtB,sHAAsH;IACtH,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnE,MAAM,WAAW,iBAAiB;IAChC,gGAAgG;IAChG,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kIAAkI;IAClI,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAOD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,iBAAiB,GAAG,aAAa,CAYjG;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,MAAM,MAAiB,SAGD,MAAM,KAAG,OAAO,CAY5C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,iBAAiB,CAAC,EACvD,QAAQ,EAAE;IAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GAChF,OAAO,CAET;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,uBAAuB,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAE5D,qHAAqH;AACrH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,QAAQ,EAAE,uBAAuB,GAChC,OAAO,CAMT;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,UAAU,CAAC,EACjD,QAAQ,EAAE,uBAAuB,GAChC,OAAO,CAET"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gates.test.d.ts","sourceRoot":"","sources":["../../src/channels/gates.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel SPI — the seam a transport (WhatsApp, or any future channel)
|
|
3
|
+
* implements to attach to a chatter server. `createServer` starts every
|
|
4
|
+
* configured channel with the same {@link ServerDependencies} route
|
|
5
|
+
* factories and custom routes receive, so a channel can call
|
|
6
|
+
* `prepareChat`/`answerOnce`, share `deps.db`, etc. without knowing how the
|
|
7
|
+
* server was assembled — and `channel.start(deps)` works identically when
|
|
8
|
+
* called directly, outside `createServer`, for standalone use.
|
|
9
|
+
*
|
|
10
|
+
* Published as the `@diegoaltoworks/chatter/channels` subpath alongside the
|
|
11
|
+
* pure decision gates ({@link decideChannelAction} and friends) and the
|
|
12
|
+
* {@link ChannelSenderRegistry}, so a transport package needs one import for
|
|
13
|
+
* the whole channel toolkit. Zero required dependencies beyond the core
|
|
14
|
+
* package itself.
|
|
15
|
+
*
|
|
16
|
+
* @packageDocumentation
|
|
17
|
+
*/
|
|
18
|
+
import type { ServerDependencies } from "../types";
|
|
19
|
+
export interface Channel {
|
|
20
|
+
/** Human-readable name, used in start-failure log lines and as the sender-registry key. */
|
|
21
|
+
name: string;
|
|
22
|
+
/**
|
|
23
|
+
* Called once, after routes are mounted, with the full server dependencies.
|
|
24
|
+
* Awaited if it returns a promise. A rejection/throw is logged and isolated
|
|
25
|
+
* — one channel failing to start never crashes the server or the other
|
|
26
|
+
* configured channels.
|
|
27
|
+
*
|
|
28
|
+
* `createServer` awaits this before the app is handed back and starts
|
|
29
|
+
* serving requests, so `start` should return once the transport is
|
|
30
|
+
* *initiated* (a socket opened, a listener registered) — not once a remote
|
|
31
|
+
* connection or pairing flow completes. An implementation that awaits a
|
|
32
|
+
* slow handshake blocks the whole server from ever starting.
|
|
33
|
+
*/
|
|
34
|
+
start(deps: ServerDependencies): void | Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Called by the host's own shutdown path via the disposer `createServer`
|
|
37
|
+
* returns (`app.stopChannels()`, see `ChatterApp` in `./server`) — never
|
|
38
|
+
* automatically on a process signal, since a library installing its own
|
|
39
|
+
* SIGTERM/SIGINT handler and exiting the process would race a host's
|
|
40
|
+
* shutdown logic.
|
|
41
|
+
*/
|
|
42
|
+
stop?(): void | Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
export * from "./gates";
|
|
45
|
+
export type { ChannelSender, ChannelSenderRegistry } from "./senders";
|
|
46
|
+
export { createSenderRegistry } from "./senders";
|
|
47
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/channels/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,OAAO;IACtB,2FAA2F;IAC3F,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,cAAc,SAAS,CAAC;AACxB,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/channels/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createSenderRegistry: () => createSenderRegistry,
|
|
24
|
+
createSlidingWindowRateLimiter: () => createSlidingWindowRateLimiter,
|
|
25
|
+
decideChannelAction: () => decideChannelAction,
|
|
26
|
+
isEffectivelyFromSelf: () => isEffectivelyFromSelf,
|
|
27
|
+
isFromAnySession: () => isFromAnySession,
|
|
28
|
+
underReplyRateLimit: () => underReplyRateLimit
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
|
|
32
|
+
// src/channels/gates.ts
|
|
33
|
+
function matches(re, text) {
|
|
34
|
+
return new RegExp(re.source, re.flags.replace(/[gy]/g, "")).test(text);
|
|
35
|
+
}
|
|
36
|
+
function decideChannelAction(msg, config) {
|
|
37
|
+
if (msg.fromBot || !msg.text.trim()) return "ignore";
|
|
38
|
+
if (!msg.isDirectMessage) {
|
|
39
|
+
if (config.allowedChats.length > 0 && !config.allowedChats.includes(msg.chatId)) {
|
|
40
|
+
return "ignore";
|
|
41
|
+
}
|
|
42
|
+
if (config.muteRegex && matches(config.muteRegex, msg.text)) return "mute";
|
|
43
|
+
if (config.unmuteRegex && matches(config.unmuteRegex, msg.text)) return "unmute";
|
|
44
|
+
if (config.mutedChats.has(msg.chatId)) return "ignore";
|
|
45
|
+
return msg.mentionsBot || msg.isReplyToBot ? "reply" : "ignore";
|
|
46
|
+
}
|
|
47
|
+
return "reply";
|
|
48
|
+
}
|
|
49
|
+
function createSlidingWindowRateLimiter(maxPerWindow, windowMs, now = Date.now) {
|
|
50
|
+
const log = /* @__PURE__ */ new Map();
|
|
51
|
+
return function allow(key) {
|
|
52
|
+
const cutoff = now() - windowMs;
|
|
53
|
+
const entries = (log.get(key) ?? []).filter((t) => t > cutoff);
|
|
54
|
+
if (entries.length >= maxPerWindow) {
|
|
55
|
+
if (entries.length > 0) log.set(key, entries);
|
|
56
|
+
else log.delete(key);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
entries.push(now());
|
|
60
|
+
log.set(key, entries);
|
|
61
|
+
return true;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function underReplyRateLimit(msg, limiters) {
|
|
65
|
+
return msg.isDirectMessage ? limiters.dm(msg.chatId) : limiters.group(msg.chatId);
|
|
66
|
+
}
|
|
67
|
+
function isFromAnySession(id, registry) {
|
|
68
|
+
if (!id) return false;
|
|
69
|
+
for (const ids of registry.values()) {
|
|
70
|
+
if (ids.includes(id)) return true;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
function isEffectivelyFromSelf(msg, registry) {
|
|
75
|
+
return msg.fromBot || isFromAnySession(msg.senderId, registry);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/channels/senders.ts
|
|
79
|
+
function createSenderRegistry() {
|
|
80
|
+
const senders = /* @__PURE__ */ new Map();
|
|
81
|
+
return {
|
|
82
|
+
register(name, sender) {
|
|
83
|
+
if (senders.has(name)) {
|
|
84
|
+
console.warn(`Channel sender "${name}" is already registered; overwriting.`);
|
|
85
|
+
}
|
|
86
|
+
senders.set(name, sender);
|
|
87
|
+
},
|
|
88
|
+
unregister(name) {
|
|
89
|
+
senders.delete(name);
|
|
90
|
+
},
|
|
91
|
+
available(name) {
|
|
92
|
+
return senders.has(name);
|
|
93
|
+
},
|
|
94
|
+
async sendText(name, chatId, text) {
|
|
95
|
+
const sender = senders.get(name);
|
|
96
|
+
if (!sender) return false;
|
|
97
|
+
try {
|
|
98
|
+
await sender.sendText(chatId, text);
|
|
99
|
+
return true;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.warn(`Channel sender "${name}" sendText failed:`, error);
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
async sendVoice(name, chatId, payload) {
|
|
106
|
+
const sender = senders.get(name);
|
|
107
|
+
if (!sender?.sendVoice) return false;
|
|
108
|
+
try {
|
|
109
|
+
await sender.sendVoice(chatId, payload);
|
|
110
|
+
return true;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.warn(`Channel sender "${name}" sendVoice failed:`, error);
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
async sendMedia(name, chatId, payload) {
|
|
117
|
+
const sender = senders.get(name);
|
|
118
|
+
if (!sender?.sendMedia) return false;
|
|
119
|
+
try {
|
|
120
|
+
await sender.sendMedia(chatId, payload);
|
|
121
|
+
return true;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.warn(`Channel sender "${name}" sendMedia failed:`, error);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
130
|
+
0 && (module.exports = {
|
|
131
|
+
createSenderRegistry,
|
|
132
|
+
createSlidingWindowRateLimiter,
|
|
133
|
+
decideChannelAction,
|
|
134
|
+
isEffectivelyFromSelf,
|
|
135
|
+
isFromAnySession,
|
|
136
|
+
underReplyRateLimit
|
|
137
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// src/channels/gates.ts
|
|
2
|
+
function matches(re, text) {
|
|
3
|
+
return new RegExp(re.source, re.flags.replace(/[gy]/g, "")).test(text);
|
|
4
|
+
}
|
|
5
|
+
function decideChannelAction(msg, config) {
|
|
6
|
+
if (msg.fromBot || !msg.text.trim()) return "ignore";
|
|
7
|
+
if (!msg.isDirectMessage) {
|
|
8
|
+
if (config.allowedChats.length > 0 && !config.allowedChats.includes(msg.chatId)) {
|
|
9
|
+
return "ignore";
|
|
10
|
+
}
|
|
11
|
+
if (config.muteRegex && matches(config.muteRegex, msg.text)) return "mute";
|
|
12
|
+
if (config.unmuteRegex && matches(config.unmuteRegex, msg.text)) return "unmute";
|
|
13
|
+
if (config.mutedChats.has(msg.chatId)) return "ignore";
|
|
14
|
+
return msg.mentionsBot || msg.isReplyToBot ? "reply" : "ignore";
|
|
15
|
+
}
|
|
16
|
+
return "reply";
|
|
17
|
+
}
|
|
18
|
+
function createSlidingWindowRateLimiter(maxPerWindow, windowMs, now = Date.now) {
|
|
19
|
+
const log = /* @__PURE__ */ new Map();
|
|
20
|
+
return function allow(key) {
|
|
21
|
+
const cutoff = now() - windowMs;
|
|
22
|
+
const entries = (log.get(key) ?? []).filter((t) => t > cutoff);
|
|
23
|
+
if (entries.length >= maxPerWindow) {
|
|
24
|
+
if (entries.length > 0) log.set(key, entries);
|
|
25
|
+
else log.delete(key);
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
entries.push(now());
|
|
29
|
+
log.set(key, entries);
|
|
30
|
+
return true;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function underReplyRateLimit(msg, limiters) {
|
|
34
|
+
return msg.isDirectMessage ? limiters.dm(msg.chatId) : limiters.group(msg.chatId);
|
|
35
|
+
}
|
|
36
|
+
function isFromAnySession(id, registry) {
|
|
37
|
+
if (!id) return false;
|
|
38
|
+
for (const ids of registry.values()) {
|
|
39
|
+
if (ids.includes(id)) return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
function isEffectivelyFromSelf(msg, registry) {
|
|
44
|
+
return msg.fromBot || isFromAnySession(msg.senderId, registry);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/channels/senders.ts
|
|
48
|
+
function createSenderRegistry() {
|
|
49
|
+
const senders = /* @__PURE__ */ new Map();
|
|
50
|
+
return {
|
|
51
|
+
register(name, sender) {
|
|
52
|
+
if (senders.has(name)) {
|
|
53
|
+
console.warn(`Channel sender "${name}" is already registered; overwriting.`);
|
|
54
|
+
}
|
|
55
|
+
senders.set(name, sender);
|
|
56
|
+
},
|
|
57
|
+
unregister(name) {
|
|
58
|
+
senders.delete(name);
|
|
59
|
+
},
|
|
60
|
+
available(name) {
|
|
61
|
+
return senders.has(name);
|
|
62
|
+
},
|
|
63
|
+
async sendText(name, chatId, text) {
|
|
64
|
+
const sender = senders.get(name);
|
|
65
|
+
if (!sender) return false;
|
|
66
|
+
try {
|
|
67
|
+
await sender.sendText(chatId, text);
|
|
68
|
+
return true;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.warn(`Channel sender "${name}" sendText failed:`, error);
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
async sendVoice(name, chatId, payload) {
|
|
75
|
+
const sender = senders.get(name);
|
|
76
|
+
if (!sender?.sendVoice) return false;
|
|
77
|
+
try {
|
|
78
|
+
await sender.sendVoice(chatId, payload);
|
|
79
|
+
return true;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.warn(`Channel sender "${name}" sendVoice failed:`, error);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
async sendMedia(name, chatId, payload) {
|
|
86
|
+
const sender = senders.get(name);
|
|
87
|
+
if (!sender?.sendMedia) return false;
|
|
88
|
+
try {
|
|
89
|
+
await sender.sendMedia(chatId, payload);
|
|
90
|
+
return true;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.warn(`Channel sender "${name}" sendMedia failed:`, error);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
createSenderRegistry,
|
|
100
|
+
createSlidingWindowRateLimiter,
|
|
101
|
+
decideChannelAction,
|
|
102
|
+
isEffectivelyFromSelf,
|
|
103
|
+
isFromAnySession,
|
|
104
|
+
underReplyRateLimit
|
|
105
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-agnostic outbound registry. Brain-side features (a scheduler, the
|
|
3
|
+
* flows engine) send messages by channel name without importing a transport
|
|
4
|
+
* or knowing whether it is currently connected — a channel registers its
|
|
5
|
+
* sender on start and unregisters it on stop, and every send degrades to
|
|
6
|
+
* `false` instead of throwing when the name is unknown or the underlying
|
|
7
|
+
* send fails, so a disconnected channel can never crash a caller.
|
|
8
|
+
*/
|
|
9
|
+
/** What a channel implements to receive outbound sends. Voice/media payload shapes are transport-defined. */
|
|
10
|
+
export interface ChannelSender {
|
|
11
|
+
sendText(chatId: string, text: string): Promise<void>;
|
|
12
|
+
/** Optional: channels without voice support (or test fakes) stay valid without it. */
|
|
13
|
+
sendVoice?(chatId: string, payload: unknown): Promise<void>;
|
|
14
|
+
/** Optional: channels without media support (or test fakes) stay valid without it. */
|
|
15
|
+
sendMedia?(chatId: string, payload: unknown): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export interface ChannelSenderRegistry {
|
|
18
|
+
register(name: string, sender: ChannelSender): void;
|
|
19
|
+
unregister(name: string): void;
|
|
20
|
+
/** True when a sender is registered under `name` (regardless of what it optionally supports). */
|
|
21
|
+
available(name: string): boolean;
|
|
22
|
+
sendText(name: string, chatId: string, text: string): Promise<boolean>;
|
|
23
|
+
/** False (not a throw) when the sender is missing, disconnected, or lacks voice support. */
|
|
24
|
+
sendVoice(name: string, chatId: string, payload: unknown): Promise<boolean>;
|
|
25
|
+
/** False (not a throw) when the sender is missing, disconnected, or lacks media support. */
|
|
26
|
+
sendMedia(name: string, chatId: string, payload: unknown): Promise<boolean>;
|
|
27
|
+
}
|
|
28
|
+
/** One registry per server: channels register on start, callers send by name. */
|
|
29
|
+
export declare function createSenderRegistry(): ChannelSenderRegistry;
|
|
30
|
+
//# sourceMappingURL=senders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"senders.d.ts","sourceRoot":"","sources":["../../src/channels/senders.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,6GAA6G;AAC7G,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,sFAAsF;IACtF,SAAS,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,sFAAsF;IACtF,SAAS,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IACpD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iGAAiG;IACjG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvE,4FAA4F;IAC5F,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5E,4FAA4F;IAC5F,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7E;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,IAAI,qBAAqB,CAkD5D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"senders.test.d.ts","sourceRoot":"","sources":["../../src/channels/senders.test.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export type { ApiKeyOptions, ApiKeyPayload, VerifyResult } from "./auth";
|
|
14
14
|
export { ApiKeyManager } from "./auth";
|
|
15
|
+
export type { Channel } from "./channels";
|
|
15
16
|
export type { ChatBotConfig, ChatButtonConfig, ChatConfig, ChatMessage, ChatMode, StreamCallbacks, } from "./client";
|
|
16
17
|
export { Chat, ChatBot, ChatButton } from "./client";
|
|
17
18
|
export type { AnswerFn, AnswerFnInput, AnswerFnResult, AnswerOptions, AnswerUsage, } from "./core/answer";
|
|
@@ -39,6 +40,7 @@ export { demoRoutes } from "./routes/demo";
|
|
|
39
40
|
export { openaiRoutes } from "./routes/openai";
|
|
40
41
|
export { privateRoutes } from "./routes/private";
|
|
41
42
|
export { publicRoutes } from "./routes/public";
|
|
43
|
+
export type { ChatterApp } from "./server";
|
|
42
44
|
export { createServer } from "./server";
|
|
43
45
|
export type { BotBranding, BotChatConfig, BotIdentity, ChatterConfig, CustomRoutes, EmbeddingChunk, KnowledgeDocument, ServerDependencies, } from "./types";
|
|
44
46
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAIvC,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,QAAQ,EACR,eAAe,GAChB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACrD,YAAY,EACV,QAAQ,EACR,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,GACZ,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACzD,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,YAAY,EACV,WAAW,EACX,aAAa,EACb,WAAW,EACX,aAAa,EACb,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -33171,6 +33171,58 @@ var import_hono5 = require("hono");
|
|
|
33171
33171
|
var import_bun = require("hono/bun");
|
|
33172
33172
|
var import_openai2 = __toESM(require("openai"));
|
|
33173
33173
|
|
|
33174
|
+
// src/channels/senders.ts
|
|
33175
|
+
function createSenderRegistry() {
|
|
33176
|
+
const senders = /* @__PURE__ */ new Map();
|
|
33177
|
+
return {
|
|
33178
|
+
register(name, sender) {
|
|
33179
|
+
if (senders.has(name)) {
|
|
33180
|
+
console.warn(`Channel sender "${name}" is already registered; overwriting.`);
|
|
33181
|
+
}
|
|
33182
|
+
senders.set(name, sender);
|
|
33183
|
+
},
|
|
33184
|
+
unregister(name) {
|
|
33185
|
+
senders.delete(name);
|
|
33186
|
+
},
|
|
33187
|
+
available(name) {
|
|
33188
|
+
return senders.has(name);
|
|
33189
|
+
},
|
|
33190
|
+
async sendText(name, chatId, text) {
|
|
33191
|
+
const sender = senders.get(name);
|
|
33192
|
+
if (!sender) return false;
|
|
33193
|
+
try {
|
|
33194
|
+
await sender.sendText(chatId, text);
|
|
33195
|
+
return true;
|
|
33196
|
+
} catch (error51) {
|
|
33197
|
+
console.warn(`Channel sender "${name}" sendText failed:`, error51);
|
|
33198
|
+
return false;
|
|
33199
|
+
}
|
|
33200
|
+
},
|
|
33201
|
+
async sendVoice(name, chatId, payload) {
|
|
33202
|
+
const sender = senders.get(name);
|
|
33203
|
+
if (!sender?.sendVoice) return false;
|
|
33204
|
+
try {
|
|
33205
|
+
await sender.sendVoice(chatId, payload);
|
|
33206
|
+
return true;
|
|
33207
|
+
} catch (error51) {
|
|
33208
|
+
console.warn(`Channel sender "${name}" sendVoice failed:`, error51);
|
|
33209
|
+
return false;
|
|
33210
|
+
}
|
|
33211
|
+
},
|
|
33212
|
+
async sendMedia(name, chatId, payload) {
|
|
33213
|
+
const sender = senders.get(name);
|
|
33214
|
+
if (!sender?.sendMedia) return false;
|
|
33215
|
+
try {
|
|
33216
|
+
await sender.sendMedia(chatId, payload);
|
|
33217
|
+
return true;
|
|
33218
|
+
} catch (error51) {
|
|
33219
|
+
console.warn(`Channel sender "${name}" sendMedia failed:`, error51);
|
|
33220
|
+
return false;
|
|
33221
|
+
}
|
|
33222
|
+
}
|
|
33223
|
+
};
|
|
33224
|
+
}
|
|
33225
|
+
|
|
33174
33226
|
// src/core/widgets.ts
|
|
33175
33227
|
var import_node_fs3 = require("node:fs");
|
|
33176
33228
|
var import_node_path3 = require("node:path");
|
|
@@ -33296,7 +33348,8 @@ async function createServer(config2) {
|
|
|
33296
33348
|
app.get("/demo/chatbot", (0, import_bun.serveStatic)({ path: `${publicDir}/demo-chatbot.html` }));
|
|
33297
33349
|
app.get("/demo/react", (0, import_bun.serveStatic)({ path: `${publicDir}/react-demo.html` }));
|
|
33298
33350
|
}
|
|
33299
|
-
const
|
|
33351
|
+
const senders = createSenderRegistry();
|
|
33352
|
+
const deps = { client, store, db, config: config2, prompts, apiKeyManager, senders };
|
|
33300
33353
|
if (enablePublic) {
|
|
33301
33354
|
app.route("/", publicRoutes(deps));
|
|
33302
33355
|
}
|
|
@@ -33312,8 +33365,36 @@ async function createServer(config2) {
|
|
|
33312
33365
|
if (config2.customRoutes) {
|
|
33313
33366
|
await config2.customRoutes(app, deps);
|
|
33314
33367
|
}
|
|
33368
|
+
const started = [];
|
|
33369
|
+
if (config2.channels && config2.channels.length > 0) {
|
|
33370
|
+
for (const channel of config2.channels) {
|
|
33371
|
+
try {
|
|
33372
|
+
await channel.start(deps);
|
|
33373
|
+
started.push(channel);
|
|
33374
|
+
console.log(`\u2705 Channel "${channel.name}" started`);
|
|
33375
|
+
} catch (error51) {
|
|
33376
|
+
console.error(`\u274C Channel "${channel.name}" failed to start:`, error51);
|
|
33377
|
+
}
|
|
33378
|
+
}
|
|
33379
|
+
}
|
|
33315
33380
|
console.log(`\u2705 ${config2.bot.name} server ready`);
|
|
33316
|
-
return app
|
|
33381
|
+
return Object.assign(app, {
|
|
33382
|
+
// Scoped to this call's started channels, not global — two servers in
|
|
33383
|
+
// one process each stop only their own. A channel's `stop()` throwing
|
|
33384
|
+
// (sync or async) is caught per-channel so one misbehaving channel can't
|
|
33385
|
+
// stop the rest from cleaning up.
|
|
33386
|
+
stopChannels: async () => {
|
|
33387
|
+
await Promise.allSettled(
|
|
33388
|
+
started.map(async (channel) => {
|
|
33389
|
+
try {
|
|
33390
|
+
await channel.stop?.();
|
|
33391
|
+
} catch (error51) {
|
|
33392
|
+
console.error(`Channel "${channel.name}" failed to stop:`, error51);
|
|
33393
|
+
}
|
|
33394
|
+
})
|
|
33395
|
+
);
|
|
33396
|
+
}
|
|
33397
|
+
});
|
|
33317
33398
|
}
|
|
33318
33399
|
// Annotate the CommonJS export names for ESM import in node:
|
|
33319
33400
|
0 && (module.exports = {
|
package/dist/index.mjs
CHANGED
|
@@ -33134,6 +33134,58 @@ import { Hono as Hono5 } from "hono";
|
|
|
33134
33134
|
import { serveStatic } from "hono/bun";
|
|
33135
33135
|
import OpenAI2 from "openai";
|
|
33136
33136
|
|
|
33137
|
+
// src/channels/senders.ts
|
|
33138
|
+
function createSenderRegistry() {
|
|
33139
|
+
const senders = /* @__PURE__ */ new Map();
|
|
33140
|
+
return {
|
|
33141
|
+
register(name, sender) {
|
|
33142
|
+
if (senders.has(name)) {
|
|
33143
|
+
console.warn(`Channel sender "${name}" is already registered; overwriting.`);
|
|
33144
|
+
}
|
|
33145
|
+
senders.set(name, sender);
|
|
33146
|
+
},
|
|
33147
|
+
unregister(name) {
|
|
33148
|
+
senders.delete(name);
|
|
33149
|
+
},
|
|
33150
|
+
available(name) {
|
|
33151
|
+
return senders.has(name);
|
|
33152
|
+
},
|
|
33153
|
+
async sendText(name, chatId, text) {
|
|
33154
|
+
const sender = senders.get(name);
|
|
33155
|
+
if (!sender) return false;
|
|
33156
|
+
try {
|
|
33157
|
+
await sender.sendText(chatId, text);
|
|
33158
|
+
return true;
|
|
33159
|
+
} catch (error51) {
|
|
33160
|
+
console.warn(`Channel sender "${name}" sendText failed:`, error51);
|
|
33161
|
+
return false;
|
|
33162
|
+
}
|
|
33163
|
+
},
|
|
33164
|
+
async sendVoice(name, chatId, payload) {
|
|
33165
|
+
const sender = senders.get(name);
|
|
33166
|
+
if (!sender?.sendVoice) return false;
|
|
33167
|
+
try {
|
|
33168
|
+
await sender.sendVoice(chatId, payload);
|
|
33169
|
+
return true;
|
|
33170
|
+
} catch (error51) {
|
|
33171
|
+
console.warn(`Channel sender "${name}" sendVoice failed:`, error51);
|
|
33172
|
+
return false;
|
|
33173
|
+
}
|
|
33174
|
+
},
|
|
33175
|
+
async sendMedia(name, chatId, payload) {
|
|
33176
|
+
const sender = senders.get(name);
|
|
33177
|
+
if (!sender?.sendMedia) return false;
|
|
33178
|
+
try {
|
|
33179
|
+
await sender.sendMedia(chatId, payload);
|
|
33180
|
+
return true;
|
|
33181
|
+
} catch (error51) {
|
|
33182
|
+
console.warn(`Channel sender "${name}" sendMedia failed:`, error51);
|
|
33183
|
+
return false;
|
|
33184
|
+
}
|
|
33185
|
+
}
|
|
33186
|
+
};
|
|
33187
|
+
}
|
|
33188
|
+
|
|
33137
33189
|
// src/core/widgets.ts
|
|
33138
33190
|
import { existsSync } from "node:fs";
|
|
33139
33191
|
import { dirname, join as join3 } from "node:path";
|
|
@@ -33258,7 +33310,8 @@ async function createServer(config2) {
|
|
|
33258
33310
|
app.get("/demo/chatbot", serveStatic({ path: `${publicDir}/demo-chatbot.html` }));
|
|
33259
33311
|
app.get("/demo/react", serveStatic({ path: `${publicDir}/react-demo.html` }));
|
|
33260
33312
|
}
|
|
33261
|
-
const
|
|
33313
|
+
const senders = createSenderRegistry();
|
|
33314
|
+
const deps = { client, store, db, config: config2, prompts, apiKeyManager, senders };
|
|
33262
33315
|
if (enablePublic) {
|
|
33263
33316
|
app.route("/", publicRoutes(deps));
|
|
33264
33317
|
}
|
|
@@ -33274,8 +33327,36 @@ async function createServer(config2) {
|
|
|
33274
33327
|
if (config2.customRoutes) {
|
|
33275
33328
|
await config2.customRoutes(app, deps);
|
|
33276
33329
|
}
|
|
33330
|
+
const started = [];
|
|
33331
|
+
if (config2.channels && config2.channels.length > 0) {
|
|
33332
|
+
for (const channel of config2.channels) {
|
|
33333
|
+
try {
|
|
33334
|
+
await channel.start(deps);
|
|
33335
|
+
started.push(channel);
|
|
33336
|
+
console.log(`\u2705 Channel "${channel.name}" started`);
|
|
33337
|
+
} catch (error51) {
|
|
33338
|
+
console.error(`\u274C Channel "${channel.name}" failed to start:`, error51);
|
|
33339
|
+
}
|
|
33340
|
+
}
|
|
33341
|
+
}
|
|
33277
33342
|
console.log(`\u2705 ${config2.bot.name} server ready`);
|
|
33278
|
-
return app
|
|
33343
|
+
return Object.assign(app, {
|
|
33344
|
+
// Scoped to this call's started channels, not global — two servers in
|
|
33345
|
+
// one process each stop only their own. A channel's `stop()` throwing
|
|
33346
|
+
// (sync or async) is caught per-channel so one misbehaving channel can't
|
|
33347
|
+
// stop the rest from cleaning up.
|
|
33348
|
+
stopChannels: async () => {
|
|
33349
|
+
await Promise.allSettled(
|
|
33350
|
+
started.map(async (channel) => {
|
|
33351
|
+
try {
|
|
33352
|
+
await channel.stop?.();
|
|
33353
|
+
} catch (error51) {
|
|
33354
|
+
console.error(`Channel "${channel.name}" failed to stop:`, error51);
|
|
33355
|
+
}
|
|
33356
|
+
})
|
|
33357
|
+
);
|
|
33358
|
+
}
|
|
33359
|
+
});
|
|
33279
33360
|
}
|
|
33280
33361
|
var export_Chat = import_client3.Chat;
|
|
33281
33362
|
var export_ChatBot = import_client3.ChatBot;
|
package/dist/server.d.ts
CHANGED
|
@@ -5,11 +5,30 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { Hono } from "hono";
|
|
7
7
|
import type { ChatterConfig } from "./types";
|
|
8
|
+
/**
|
|
9
|
+
* A Chatter server: the Hono app plus a disposer for anything `createServer`
|
|
10
|
+
* started outside the request/response cycle. Deliberately not a signal
|
|
11
|
+
* handler — a library calling `process.exit` on a host's process would race
|
|
12
|
+
* the host's own shutdown (e.g. an in-flight-request drain) and override its
|
|
13
|
+
* exit code. Hosts that start channels wire `stopChannels` into their own
|
|
14
|
+
* shutdown path:
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* const app = await createServer(config);
|
|
18
|
+
* process.on("SIGTERM", async () => {
|
|
19
|
+
* await app.stopChannels();
|
|
20
|
+
* process.exit(0);
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export type ChatterApp = Hono & {
|
|
25
|
+
stopChannels: () => Promise<void>;
|
|
26
|
+
};
|
|
8
27
|
/**
|
|
9
28
|
* Create a Chatter server instance
|
|
10
29
|
*
|
|
11
30
|
* @param config - Chatter configuration
|
|
12
|
-
* @returns Configured Hono app instance
|
|
31
|
+
* @returns Configured Hono app instance, plus `stopChannels()` — see {@link ChatterApp}
|
|
13
32
|
*/
|
|
14
|
-
export declare function createServer(config: ChatterConfig): Promise<
|
|
33
|
+
export declare function createServer(config: ChatterConfig): Promise<ChatterApp>;
|
|
15
34
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAc5B,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,SAAS,CAAC;AAEjE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG;IAAE,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAEtE;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAkM7E"}
|
package/dist/types.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { Client as LibsqlClient } from "@libsql/client";
|
|
8
8
|
import type { Hono } from "hono";
|
|
9
9
|
import type OpenAI from "openai";
|
|
10
|
+
import type { Channel, ChannelSenderRegistry } from "./channels";
|
|
10
11
|
import type { AnswerFn } from "./core/answer";
|
|
11
12
|
import type { BucketsFor } from "./core/buckets";
|
|
12
13
|
import type { PromptLoader } from "./core/prompts";
|
|
@@ -174,6 +175,15 @@ export interface ChatterConfig {
|
|
|
174
175
|
bucketsFor?: BucketsFor;
|
|
175
176
|
/** Custom route handler for advanced use cases. May be async — see {@link CustomRoutes} */
|
|
176
177
|
customRoutes?: CustomRoutes;
|
|
178
|
+
/**
|
|
179
|
+
* Transports (WhatsApp, or any future channel) to start alongside the
|
|
180
|
+
* server. Each is started after routes are mounted with the same
|
|
181
|
+
* {@link ServerDependencies} route factories receive; a channel that fails
|
|
182
|
+
* to start is logged and skipped rather than crashing the server. See the
|
|
183
|
+
* `./channels` subpath for the {@link Channel} SPI, the reply-decision
|
|
184
|
+
* gates, and the sender registry channels register into.
|
|
185
|
+
*/
|
|
186
|
+
channels?: Channel[];
|
|
177
187
|
}
|
|
178
188
|
/**
|
|
179
189
|
* Mounts additional routes on the server's Hono app.
|
|
@@ -217,6 +227,13 @@ export interface ServerDependencies {
|
|
|
217
227
|
error?: string;
|
|
218
228
|
}>;
|
|
219
229
|
};
|
|
230
|
+
/**
|
|
231
|
+
* The channel sender registry every configured channel is started with.
|
|
232
|
+
* A channel registers itself here (`deps.senders.register(channel.name, sender)`)
|
|
233
|
+
* so custom routes and other channels can send outbound messages by
|
|
234
|
+
* channel name without importing the transport.
|
|
235
|
+
*/
|
|
236
|
+
senders: ChannelSenderRegistry;
|
|
220
237
|
}
|
|
221
238
|
/**
|
|
222
239
|
* Knowledge document
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegoaltoworks/chatter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Embeddable AI chatbot framework with RAG, authentication, and customizable widgets",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -33,6 +33,11 @@
|
|
|
33
33
|
"types": "./dist/usage/index.d.ts",
|
|
34
34
|
"import": "./dist/usage/index.mjs",
|
|
35
35
|
"require": "./dist/usage/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./channels": {
|
|
38
|
+
"types": "./dist/channels/index.d.ts",
|
|
39
|
+
"import": "./dist/channels/index.mjs",
|
|
40
|
+
"require": "./dist/channels/index.js"
|
|
36
41
|
}
|
|
37
42
|
},
|
|
38
43
|
"files": [
|
|
@@ -42,13 +47,14 @@
|
|
|
42
47
|
],
|
|
43
48
|
"scripts": {
|
|
44
49
|
"dev": "bun --watch src/index.ts",
|
|
45
|
-
"build": "bun run build:clean && bun run build:types && bun run build:esm && bun run build:cjs && bun run build:mcp && bun run build:usage && bun run build:bin && bun run build:client && bun run build:copy-client",
|
|
50
|
+
"build": "bun run build:clean && bun run build:types && bun run build:esm && bun run build:cjs && bun run build:mcp && bun run build:usage && bun run build:channels && bun run build:bin && bun run build:client && bun run build:copy-client",
|
|
46
51
|
"build:clean": "rm -rf dist",
|
|
47
52
|
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
|
48
53
|
"build:esm": "esbuild src/index.ts --bundle --format=esm --outfile=dist/index.mjs --external:hono --external:openai --external:@libsql/client --external:jose --platform=node --target=node24",
|
|
49
54
|
"build:cjs": "esbuild src/index.ts --bundle --format=cjs --outfile=dist/index.js --external:hono --external:openai --external:@libsql/client --external:jose --platform=node --target=node24",
|
|
50
55
|
"build:mcp": "esbuild src/mcp-server.ts --bundle --format=esm --outfile=dist/mcp-server.mjs --external:@modelcontextprotocol/sdk --external:openai --external:@libsql/client --external:zod --platform=node --target=node24 && esbuild src/mcp-server.ts --bundle --format=cjs --outfile=dist/mcp-server.js --external:@modelcontextprotocol/sdk --external:openai --external:@libsql/client --external:zod --platform=node --target=node24",
|
|
51
56
|
"build:usage": "esbuild src/usage/index.ts --bundle --format=esm --outfile=dist/usage/index.mjs --external:@libsql/client --platform=node --target=node24 && esbuild src/usage/index.ts --bundle --format=cjs --outfile=dist/usage/index.js --external:@libsql/client --platform=node --target=node24",
|
|
57
|
+
"build:channels": "esbuild src/channels/index.ts --bundle --format=esm --outfile=dist/channels/index.mjs --external:hono --external:openai --external:@libsql/client --platform=node --target=node24 && esbuild src/channels/index.ts --bundle --format=cjs --outfile=dist/channels/index.js --external:hono --external:openai --external:@libsql/client --platform=node --target=node24",
|
|
52
58
|
"build:bin": "esbuild bin/create-apikey.ts --bundle --format=cjs --outfile=dist/bin/create-apikey.js --external:jose --platform=node --target=node18 --banner:js='#!/usr/bin/env node'",
|
|
53
59
|
"build:client": "cd src/client && bun run build",
|
|
54
60
|
"build:copy-client": "mkdir -p dist/static && cp src/client/dist/chatter.js src/client/dist/chatter.css dist/static/",
|