@flink-app/whatsapp-plugin 2.0.0-alpha.74
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/CHANGELOG.md +10 -0
- package/LICENSE +21 -0
- package/README.md +80 -0
- package/bin/flink-whatsapp.js +22 -0
- package/dist/WhatsappConnectionManager.d.ts +17 -0
- package/dist/WhatsappConnectionManager.js +60 -0
- package/dist/WhatsappTransport.d.ts +18 -0
- package/dist/WhatsappTransport.js +241 -0
- package/dist/autoRegisteredWhatsappHandlers.d.ts +6 -0
- package/dist/autoRegisteredWhatsappHandlers.js +8 -0
- package/dist/cli/send.d.ts +21 -0
- package/dist/cli/send.js +90 -0
- package/dist/compiler.d.ts +25 -0
- package/dist/compiler.js +27 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +192 -0
- package/dist/types.d.ts +252 -0
- package/dist/types.js +2 -0
- package/dist/whatsappContext.d.ts +4 -0
- package/dist/whatsappContext.js +236 -0
- package/dist/whatsappFormatting.d.ts +13 -0
- package/dist/whatsappFormatting.js +51 -0
- package/dist/whatsappRouter.d.ts +6 -0
- package/dist/whatsappRouter.js +54 -0
- package/package.json +39 -0
- package/src/WhatsappConnectionManager.ts +67 -0
- package/src/WhatsappTransport.ts +297 -0
- package/src/autoRegisteredWhatsappHandlers.ts +7 -0
- package/src/cli/send.ts +102 -0
- package/src/compiler.ts +32 -0
- package/src/index.ts +197 -0
- package/src/types.ts +284 -0
- package/src/whatsappContext.ts +385 -0
- package/src/whatsappFormatting.ts +59 -0
- package/src/whatsappRouter.ts +48 -0
- package/tsconfig.dist.json +4 -0
- package/tsconfig.json +24 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
26
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.toWhatsappFormat = void 0;
|
|
30
|
+
exports.whatsappPlugin = whatsappPlugin;
|
|
31
|
+
const crypto = __importStar(require("crypto"));
|
|
32
|
+
const flink_1 = require("@flink-app/flink");
|
|
33
|
+
const autoRegisteredWhatsappHandlers_1 = require("./autoRegisteredWhatsappHandlers");
|
|
34
|
+
const WhatsappConnectionManager_1 = require("./WhatsappConnectionManager");
|
|
35
|
+
const whatsappContext_1 = require("./whatsappContext");
|
|
36
|
+
const whatsappRouter_1 = require("./whatsappRouter");
|
|
37
|
+
const WhatsappTransport_1 = require("./WhatsappTransport");
|
|
38
|
+
const log = flink_1.FlinkLogFactory.createLogger("flink.whatsapp-plugin");
|
|
39
|
+
__exportStar(require("./types"), exports);
|
|
40
|
+
__exportStar(require("./autoRegisteredWhatsappHandlers"), exports);
|
|
41
|
+
__exportStar(require("./whatsappRouter"), exports);
|
|
42
|
+
var whatsappFormatting_1 = require("./whatsappFormatting");
|
|
43
|
+
Object.defineProperty(exports, "toWhatsappFormat", { enumerable: true, get: function () { return whatsappFormatting_1.toWhatsappFormat; } });
|
|
44
|
+
function whatsappPlugin(options) {
|
|
45
|
+
var _a;
|
|
46
|
+
const manager = new WhatsappConnectionManager_1.WhatsappConnectionManager();
|
|
47
|
+
let appCtx;
|
|
48
|
+
const webhookPath = (_a = options.webhookPath) !== null && _a !== void 0 ? _a : "/webhooks/whatsapp";
|
|
49
|
+
const handleIncomingMessage = async (message) => {
|
|
50
|
+
var _a;
|
|
51
|
+
const user = options.resolveUser
|
|
52
|
+
? await options.resolveUser(message, appCtx)
|
|
53
|
+
: undefined;
|
|
54
|
+
const permissions = user && options.resolvePermissions
|
|
55
|
+
? await options.resolvePermissions(user, appCtx)
|
|
56
|
+
: [];
|
|
57
|
+
const scopedWhatsapp = (0, whatsappContext_1.createScopedWhatsappContext)(manager, message.connectionId);
|
|
58
|
+
let handled = false;
|
|
59
|
+
for (const h of autoRegisteredWhatsappHandlers_1.autoRegisteredWhatsappHandlers) {
|
|
60
|
+
if (!(0, whatsappRouter_1.matchesRoute)(message, (_a = h.Route) !== null && _a !== void 0 ? _a : {}))
|
|
61
|
+
continue;
|
|
62
|
+
handled = true;
|
|
63
|
+
try {
|
|
64
|
+
await flink_1.requestContext.run({
|
|
65
|
+
reqId: crypto.randomUUID(),
|
|
66
|
+
user,
|
|
67
|
+
userPermissions: permissions,
|
|
68
|
+
timestamp: Date.now(),
|
|
69
|
+
}, () => h.default({
|
|
70
|
+
ctx: appCtx,
|
|
71
|
+
message,
|
|
72
|
+
user,
|
|
73
|
+
permissions,
|
|
74
|
+
whatsapp: scopedWhatsapp,
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
log.error(`WhatsApp handler error for message from "${message.from}"`, err);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!handled && options.onUnhandled) {
|
|
82
|
+
await options.onUnhandled(message, appCtx);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const addConnection = (id, connection) => {
|
|
86
|
+
manager.add(id, connection);
|
|
87
|
+
};
|
|
88
|
+
const pluginCtx = (0, whatsappContext_1.createWhatsappContext)(manager, addConnection);
|
|
89
|
+
return {
|
|
90
|
+
id: "whatsapp",
|
|
91
|
+
ctx: pluginCtx,
|
|
92
|
+
init: async (app) => {
|
|
93
|
+
appCtx = app.ctx;
|
|
94
|
+
// Add connections
|
|
95
|
+
if (options.connection) {
|
|
96
|
+
addConnection("default", options.connection);
|
|
97
|
+
}
|
|
98
|
+
if (options.connections) {
|
|
99
|
+
for (const [id, conn] of Object.entries(options.connections)) {
|
|
100
|
+
addConnection(id, conn);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (options.loadConnections) {
|
|
104
|
+
const loaded = await options.loadConnections(app.ctx);
|
|
105
|
+
for (const [id, conn] of Object.entries(loaded)) {
|
|
106
|
+
addConnection(id, conn);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (!app.expressApp) {
|
|
110
|
+
log.warn("No Express app available — webhook routes not registered");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// GET webhook — Meta verification challenge
|
|
114
|
+
app.expressApp.get(webhookPath, (req, res) => {
|
|
115
|
+
const mode = req.query["hub.mode"];
|
|
116
|
+
const token = req.query["hub.verify_token"];
|
|
117
|
+
const challenge = req.query["hub.challenge"];
|
|
118
|
+
// Find any connection with a matching verify token
|
|
119
|
+
const connectionIds = manager.list();
|
|
120
|
+
const verified = connectionIds.some((id) => {
|
|
121
|
+
const conn = manager.get(id);
|
|
122
|
+
return conn.verifyToken === token;
|
|
123
|
+
});
|
|
124
|
+
if (mode === "subscribe" && verified) {
|
|
125
|
+
log.info("Webhook verification successful");
|
|
126
|
+
res.status(200).send(challenge);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
log.warn("Webhook verification failed");
|
|
130
|
+
res.sendStatus(403);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
// POST webhook — receive messages and status updates
|
|
134
|
+
app.expressApp.post(webhookPath, async (req, res) => {
|
|
135
|
+
var _a, _b, _c;
|
|
136
|
+
// Always respond 200 quickly to avoid Meta retries
|
|
137
|
+
res.sendStatus(200);
|
|
138
|
+
try {
|
|
139
|
+
// Verify signature
|
|
140
|
+
const signature = req.headers["x-hub-signature-256"];
|
|
141
|
+
if (signature) {
|
|
142
|
+
const rawBody = (_a = req.rawBody) !== null && _a !== void 0 ? _a : JSON.stringify(req.body);
|
|
143
|
+
const secrets = manager.getAppSecrets();
|
|
144
|
+
const verified = secrets.some((secret) => (0, WhatsappTransport_1.verifySignature)(rawBody, signature, secret));
|
|
145
|
+
if (!verified) {
|
|
146
|
+
log.warn("Webhook signature verification failed");
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const payload = req.body;
|
|
151
|
+
if (!(payload === null || payload === void 0 ? void 0 : payload.entry))
|
|
152
|
+
return;
|
|
153
|
+
// Extract phone number ID from the payload to resolve connection
|
|
154
|
+
for (const entry of payload.entry) {
|
|
155
|
+
for (const change of (_b = entry === null || entry === void 0 ? void 0 : entry.changes) !== null && _b !== void 0 ? _b : []) {
|
|
156
|
+
if ((change === null || change === void 0 ? void 0 : change.field) !== "messages")
|
|
157
|
+
continue;
|
|
158
|
+
const value = change === null || change === void 0 ? void 0 : change.value;
|
|
159
|
+
if (!((_c = value === null || value === void 0 ? void 0 : value.metadata) === null || _c === void 0 ? void 0 : _c.phone_number_id))
|
|
160
|
+
continue;
|
|
161
|
+
const phoneNumberId = value.metadata.phone_number_id;
|
|
162
|
+
const connectionId = manager.resolveConnectionId(phoneNumberId);
|
|
163
|
+
if (!connectionId) {
|
|
164
|
+
log.warn(`No connection found for phone number ID: ${phoneNumberId}`);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const { messages, statuses } = (0, WhatsappTransport_1.normalizeWebhookPayload)({ entry: [{ changes: [change] }] }, connectionId, phoneNumberId);
|
|
168
|
+
// Handle messages
|
|
169
|
+
for (const message of messages) {
|
|
170
|
+
handleIncomingMessage(message).catch((err) => {
|
|
171
|
+
log.error("Error handling WhatsApp message", err);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
// Handle status updates
|
|
175
|
+
if (options.onStatusUpdate) {
|
|
176
|
+
for (const status of statuses) {
|
|
177
|
+
options.onStatusUpdate(status, appCtx).catch((err) => {
|
|
178
|
+
log.error("Error handling WhatsApp status update", err);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
log.error("Error processing WhatsApp webhook", err);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
log.info(`WhatsApp webhook registered at ${webhookPath}`);
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/** Connection options for a single WhatsApp Business phone number */
|
|
2
|
+
export interface WhatsappConnectionOptions {
|
|
3
|
+
/** Permanent access token from Meta System User */
|
|
4
|
+
accessToken: string;
|
|
5
|
+
/** The business phone number ID */
|
|
6
|
+
phoneNumberId: string;
|
|
7
|
+
/** Token for webhook verification challenge */
|
|
8
|
+
verifyToken: string;
|
|
9
|
+
/** App secret for HMAC-SHA256 signature verification */
|
|
10
|
+
appSecret: string;
|
|
11
|
+
/** Graph API version (default: "v21.0") */
|
|
12
|
+
graphApiVersion?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Plugin options */
|
|
15
|
+
export interface WhatsappPluginOptions<TCtx> {
|
|
16
|
+
/** Single connection (stored as "default"). Mutually exclusive with `connections`. */
|
|
17
|
+
connection?: WhatsappConnectionOptions;
|
|
18
|
+
/** Multiple named connections at startup. Mutually exclusive with `connection`. */
|
|
19
|
+
connections?: Record<string, WhatsappConnectionOptions>;
|
|
20
|
+
/** Load connections dynamically at init time (receives app context with repos, plugins, etc.) */
|
|
21
|
+
loadConnections?: (ctx: TCtx) => Promise<Record<string, WhatsappConnectionOptions>>;
|
|
22
|
+
/** Webhook path for receiving messages (default: "/webhooks/whatsapp") */
|
|
23
|
+
webhookPath?: string;
|
|
24
|
+
/** Resolve authenticated user from a WhatsApp message */
|
|
25
|
+
resolveUser?: (message: WhatsappMessage, ctx: TCtx) => Promise<any>;
|
|
26
|
+
/** Resolve permissions for a resolved user */
|
|
27
|
+
resolvePermissions?: (user: any, ctx: TCtx) => Promise<string[]>;
|
|
28
|
+
/** Called when no WhatsappHandler matched the incoming message */
|
|
29
|
+
onUnhandled?: (message: WhatsappMessage, ctx: TCtx) => Promise<void>;
|
|
30
|
+
/** Called when a status update is received (sent, delivered, read, failed) */
|
|
31
|
+
onStatusUpdate?: (status: WhatsappStatusUpdate, ctx: TCtx) => Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
/** WhatsApp message types */
|
|
34
|
+
export type WhatsappMessageType = "text" | "image" | "video" | "audio" | "document" | "sticker" | "location" | "contacts" | "interactive" | "reaction" | "order" | "unknown";
|
|
35
|
+
/** Media info from incoming messages */
|
|
36
|
+
export interface WhatsappMedia {
|
|
37
|
+
/** Media ID (use downloadMedia to fetch content) */
|
|
38
|
+
id: string;
|
|
39
|
+
mimeType: string;
|
|
40
|
+
sha256?: string;
|
|
41
|
+
caption?: string;
|
|
42
|
+
filename?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Location from incoming messages */
|
|
45
|
+
export interface WhatsappLocation {
|
|
46
|
+
latitude: number;
|
|
47
|
+
longitude: number;
|
|
48
|
+
name?: string;
|
|
49
|
+
address?: string;
|
|
50
|
+
}
|
|
51
|
+
/** Contact from incoming messages */
|
|
52
|
+
export interface WhatsappContact {
|
|
53
|
+
name: {
|
|
54
|
+
formatted_name: string;
|
|
55
|
+
first_name?: string;
|
|
56
|
+
last_name?: string;
|
|
57
|
+
};
|
|
58
|
+
phones?: Array<{
|
|
59
|
+
phone: string;
|
|
60
|
+
type?: string;
|
|
61
|
+
wa_id?: string;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
/** Interactive reply (button or list selection) from incoming messages */
|
|
65
|
+
export interface WhatsappInteractiveReply {
|
|
66
|
+
type: "button_reply" | "list_reply";
|
|
67
|
+
/** Button reply ID */
|
|
68
|
+
buttonReplyId?: string;
|
|
69
|
+
/** Button reply title */
|
|
70
|
+
buttonReplyTitle?: string;
|
|
71
|
+
/** List reply ID */
|
|
72
|
+
listReplyId?: string;
|
|
73
|
+
/** List reply title */
|
|
74
|
+
listReplyTitle?: string;
|
|
75
|
+
/** List reply description */
|
|
76
|
+
listReplyDescription?: string;
|
|
77
|
+
}
|
|
78
|
+
/** Reaction from incoming messages */
|
|
79
|
+
export interface WhatsappReaction {
|
|
80
|
+
/** Message ID being reacted to */
|
|
81
|
+
messageId: string;
|
|
82
|
+
/** Emoji used as reaction (empty string means reaction removed) */
|
|
83
|
+
emoji: string;
|
|
84
|
+
}
|
|
85
|
+
/** Normalized inbound WhatsApp message */
|
|
86
|
+
export interface WhatsappMessage {
|
|
87
|
+
/** Connection that received this message ("default" for single-connection mode) */
|
|
88
|
+
connectionId: string;
|
|
89
|
+
/** WhatsApp message ID (wamid.XX) */
|
|
90
|
+
messageId: string;
|
|
91
|
+
/** Sender phone number (wa_id) */
|
|
92
|
+
from: string;
|
|
93
|
+
/** Recipient phone number ID */
|
|
94
|
+
to: string;
|
|
95
|
+
/** Sender display name from contacts[].profile.name */
|
|
96
|
+
senderName: string;
|
|
97
|
+
/** Unix timestamp */
|
|
98
|
+
timestamp: number;
|
|
99
|
+
/** Message type */
|
|
100
|
+
type: WhatsappMessageType;
|
|
101
|
+
/** Text content (for type "text") */
|
|
102
|
+
text?: string;
|
|
103
|
+
/** Media info (for image/video/audio/document/sticker) */
|
|
104
|
+
media?: WhatsappMedia;
|
|
105
|
+
/** Location (for type "location") */
|
|
106
|
+
location?: WhatsappLocation;
|
|
107
|
+
/** Contacts (for type "contacts") */
|
|
108
|
+
contacts?: WhatsappContact[];
|
|
109
|
+
/** Interactive reply (for button/list replies) */
|
|
110
|
+
interactive?: WhatsappInteractiveReply;
|
|
111
|
+
/** Reaction (for type "reaction") */
|
|
112
|
+
reaction?: WhatsappReaction;
|
|
113
|
+
/** Raw webhook payload */
|
|
114
|
+
raw: Record<string, any>;
|
|
115
|
+
}
|
|
116
|
+
/** Route matching criteria for a WhatsappHandler */
|
|
117
|
+
export interface WhatsappRouteProps {
|
|
118
|
+
/** Match by message type */
|
|
119
|
+
type?: WhatsappMessageType | WhatsappMessageType[];
|
|
120
|
+
/** Match by sender phone number */
|
|
121
|
+
from?: string | RegExp | ((msg: WhatsappMessage) => boolean);
|
|
122
|
+
/** Match message text */
|
|
123
|
+
pattern?: string | RegExp | ((msg: WhatsappMessage) => boolean);
|
|
124
|
+
/** Only match messages from specific connection(s). Omit to match all connections. */
|
|
125
|
+
connectionId?: string | string[];
|
|
126
|
+
}
|
|
127
|
+
/** Handler function type */
|
|
128
|
+
export type WhatsappHandler<TCtx> = (args: {
|
|
129
|
+
ctx: TCtx;
|
|
130
|
+
message: WhatsappMessage;
|
|
131
|
+
user?: any;
|
|
132
|
+
permissions?: string[];
|
|
133
|
+
whatsapp: ScopedWhatsappContext;
|
|
134
|
+
}) => Promise<void>;
|
|
135
|
+
/** Handler file shape (populated by compiler) */
|
|
136
|
+
export interface WhatsappHandlerFile<TCtx = any> {
|
|
137
|
+
Route?: WhatsappRouteProps;
|
|
138
|
+
default: WhatsappHandler<TCtx>;
|
|
139
|
+
__file?: string;
|
|
140
|
+
}
|
|
141
|
+
/** Send options for the raw send method */
|
|
142
|
+
export interface WhatsappSendOptions {
|
|
143
|
+
/** Recipient phone number */
|
|
144
|
+
to: string;
|
|
145
|
+
/** Message type */
|
|
146
|
+
type: string;
|
|
147
|
+
/** Message payload (type-specific) */
|
|
148
|
+
[key: string]: any;
|
|
149
|
+
}
|
|
150
|
+
/** Result from sending a message */
|
|
151
|
+
export interface WhatsappSendResult {
|
|
152
|
+
messageId: string;
|
|
153
|
+
}
|
|
154
|
+
/** Media payload for sending images/documents/etc */
|
|
155
|
+
export interface WhatsappMediaPayload {
|
|
156
|
+
/** Media ID (from uploadMedia) */
|
|
157
|
+
id?: string;
|
|
158
|
+
/** Public URL to the media */
|
|
159
|
+
link?: string;
|
|
160
|
+
}
|
|
161
|
+
/** Interactive button */
|
|
162
|
+
export interface WhatsappButton {
|
|
163
|
+
id: string;
|
|
164
|
+
title: string;
|
|
165
|
+
}
|
|
166
|
+
/** List section for interactive list messages */
|
|
167
|
+
export interface WhatsappListSection {
|
|
168
|
+
title: string;
|
|
169
|
+
rows: Array<{
|
|
170
|
+
id: string;
|
|
171
|
+
title: string;
|
|
172
|
+
description?: string;
|
|
173
|
+
}>;
|
|
174
|
+
}
|
|
175
|
+
/** Template for sending template messages */
|
|
176
|
+
export interface WhatsappTemplate {
|
|
177
|
+
name: string;
|
|
178
|
+
language: {
|
|
179
|
+
code: string;
|
|
180
|
+
};
|
|
181
|
+
components?: Array<Record<string, any>>;
|
|
182
|
+
}
|
|
183
|
+
/** Status update from webhook */
|
|
184
|
+
export interface WhatsappStatusUpdate {
|
|
185
|
+
connectionId: string;
|
|
186
|
+
messageId: string;
|
|
187
|
+
recipientId: string;
|
|
188
|
+
status: "sent" | "delivered" | "read" | "failed";
|
|
189
|
+
timestamp: number;
|
|
190
|
+
errors?: WhatsappError[];
|
|
191
|
+
}
|
|
192
|
+
/** WhatsApp API error */
|
|
193
|
+
export interface WhatsappError {
|
|
194
|
+
code: number;
|
|
195
|
+
title: string;
|
|
196
|
+
message?: string;
|
|
197
|
+
href?: string;
|
|
198
|
+
}
|
|
199
|
+
/** Scoped context bound to a specific connection — no management methods */
|
|
200
|
+
export interface ScopedWhatsappContext {
|
|
201
|
+
/** Send a raw message payload */
|
|
202
|
+
send(opts: WhatsappSendOptions): Promise<WhatsappSendResult>;
|
|
203
|
+
/** Send a text message */
|
|
204
|
+
sendText(to: string, text: string): Promise<WhatsappSendResult>;
|
|
205
|
+
/** Send a template message (required outside 24h window) */
|
|
206
|
+
sendTemplate(to: string, template: WhatsappTemplate): Promise<WhatsappSendResult>;
|
|
207
|
+
/** Send an image */
|
|
208
|
+
sendImage(to: string, image: WhatsappMediaPayload, caption?: string): Promise<WhatsappSendResult>;
|
|
209
|
+
/** Send a document */
|
|
210
|
+
sendDocument(to: string, doc: WhatsappMediaPayload, caption?: string): Promise<WhatsappSendResult>;
|
|
211
|
+
/** Send interactive buttons (max 3) */
|
|
212
|
+
sendInteractiveButtons(to: string, body: string, buttons: WhatsappButton[]): Promise<WhatsappSendResult>;
|
|
213
|
+
/** Send interactive list */
|
|
214
|
+
sendInteractiveList(to: string, body: string, buttonText: string, sections: WhatsappListSection[]): Promise<WhatsappSendResult>;
|
|
215
|
+
/** Send a location */
|
|
216
|
+
sendLocation(to: string, location: WhatsappLocation): Promise<WhatsappSendResult>;
|
|
217
|
+
/** Send a reaction emoji to a message */
|
|
218
|
+
sendReaction(to: string, messageId: string, emoji: string): Promise<WhatsappSendResult>;
|
|
219
|
+
/** Reply to a message with text */
|
|
220
|
+
reply(message: WhatsappMessage, text: string): Promise<WhatsappSendResult>;
|
|
221
|
+
/** Mark a message as read */
|
|
222
|
+
markAsRead(messageId: string): Promise<void>;
|
|
223
|
+
/** Upload media and return the media ID */
|
|
224
|
+
uploadMedia(file: Buffer, mimeType: string, filename: string): Promise<string>;
|
|
225
|
+
/** Download media by ID */
|
|
226
|
+
downloadMedia(mediaId: string): Promise<Buffer>;
|
|
227
|
+
}
|
|
228
|
+
/** Full WhatsApp API — exposed at runtime as ctx.plugins.whatsapp */
|
|
229
|
+
export interface WhatsappApi extends ScopedWhatsappContext {
|
|
230
|
+
/** Add a new connection at runtime */
|
|
231
|
+
addConnection(id: string, connection: WhatsappConnectionOptions): void;
|
|
232
|
+
/** Remove a connection */
|
|
233
|
+
removeConnection(id: string): void;
|
|
234
|
+
/** Get a scoped context bound to a specific connection */
|
|
235
|
+
withConnection(id: string): ScopedWhatsappContext;
|
|
236
|
+
/** List active connection IDs */
|
|
237
|
+
listConnections(): string[];
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Plugin context type — extend your app Ctx with this to get typed access to ctx.plugins.whatsapp.
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* import { FlinkContext } from "@flink-app/flink";
|
|
244
|
+
* import { WhatsappPluginCtx } from "@flink-app/whatsapp-plugin";
|
|
245
|
+
*
|
|
246
|
+
* export interface Ctx extends FlinkContext<WhatsappPluginCtx> {
|
|
247
|
+
* repos: { ... };
|
|
248
|
+
* }
|
|
249
|
+
*/
|
|
250
|
+
export interface WhatsappPluginCtx {
|
|
251
|
+
whatsapp: WhatsappApi;
|
|
252
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { WhatsappConnectionManager } from "./WhatsappConnectionManager";
|
|
2
|
+
import { WhatsappApi, WhatsappConnectionOptions, ScopedWhatsappContext } from "./types";
|
|
3
|
+
export declare function createWhatsappContext(manager: WhatsappConnectionManager, addConnectionFn: (id: string, connection: WhatsappConnectionOptions) => void): WhatsappApi;
|
|
4
|
+
export declare function createScopedWhatsappContext(manager: WhatsappConnectionManager, connectionId: string): ScopedWhatsappContext;
|