@marktoflow/integrations 2.0.0-alpha.14 → 2.0.0-alpha.15
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/adapters/claude-agent-hooks.d.ts +176 -0
- package/dist/adapters/claude-agent-types.d.ts +34 -34
- package/dist/adapters/codex-types.d.ts +20 -20
- package/dist/adapters/github-copilot-types.d.ts +16 -16
- package/dist/adapters/ollama-types.d.ts +128 -128
- package/dist/services/github.d.ts +3 -0
- package/dist/services/gmail-trigger.d.ts +92 -0
- package/dist/services/gmail.d.ts +116 -0
- package/dist/services/google-calendar.d.ts +220 -0
- package/dist/services/google-docs.d.ts +197 -0
- package/dist/services/google-drive.d.ts +149 -0
- package/dist/services/google-sheets.d.ts +165 -0
- package/dist/services/http.d.ts +120 -0
- package/dist/services/hubspot.d.ts +315 -0
- package/dist/services/hubspot.d.ts.map +1 -0
- package/dist/services/hubspot.js +246 -0
- package/dist/services/hubspot.js.map +1 -0
- package/dist/services/jira.d.ts +3 -0
- package/dist/services/linear.d.ts +163 -0
- package/dist/services/mysql.d.ts +91 -0
- package/dist/services/outlook-trigger.d.ts +121 -0
- package/dist/services/outlook.d.ts +237 -0
- package/dist/services/postgres.d.ts +83 -0
- package/dist/services/salesforce.d.ts +269 -0
- package/dist/services/salesforce.d.ts.map +1 -0
- package/dist/services/salesforce.js +286 -0
- package/dist/services/salesforce.js.map +1 -0
- package/dist/services/slack-socket.d.ts +18 -0
- package/dist/services/slack.d.ts +3 -0
- package/dist/services/whatsapp.d.ts +311 -0
- package/package.json +5 -2
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Salesforce Integration
|
|
3
|
+
*
|
|
4
|
+
* CRM and sales automation via Salesforce REST API.
|
|
5
|
+
* API Docs: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/
|
|
6
|
+
* SDK: https://jsforce.github.io/
|
|
7
|
+
*/
|
|
8
|
+
import jsforce from 'jsforce';
|
|
9
|
+
/**
|
|
10
|
+
* Salesforce client for workflow integration
|
|
11
|
+
*/
|
|
12
|
+
export class SalesforceClient {
|
|
13
|
+
conn;
|
|
14
|
+
constructor(conn) {
|
|
15
|
+
this.conn = conn;
|
|
16
|
+
}
|
|
17
|
+
// ==================== Authentication ====================
|
|
18
|
+
/**
|
|
19
|
+
* Get connection info
|
|
20
|
+
*/
|
|
21
|
+
getConnectionInfo() {
|
|
22
|
+
return {
|
|
23
|
+
accessToken: this.conn.accessToken,
|
|
24
|
+
instanceUrl: this.conn.instanceUrl,
|
|
25
|
+
userId: this.conn.userInfo?.id,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// ==================== Leads ====================
|
|
29
|
+
/**
|
|
30
|
+
* Create a lead
|
|
31
|
+
*/
|
|
32
|
+
async createLead(lead) {
|
|
33
|
+
return (await this.conn.sobject('Lead').create(lead));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get a lead by ID
|
|
37
|
+
*/
|
|
38
|
+
async getLead(id) {
|
|
39
|
+
return (await this.conn.sobject('Lead').retrieve(id));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Update a lead
|
|
43
|
+
*/
|
|
44
|
+
async updateLead(id, data) {
|
|
45
|
+
return (await this.conn.sobject('Lead').update({ Id: id, ...data }));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Delete a lead
|
|
49
|
+
*/
|
|
50
|
+
async deleteLead(id) {
|
|
51
|
+
return (await this.conn.sobject('Lead').destroy(id));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Query leads
|
|
55
|
+
*/
|
|
56
|
+
async queryLeads(conditions) {
|
|
57
|
+
const query = `SELECT Id, FirstName, LastName, Company, Email, Phone, Status, LeadSource FROM Lead${conditions ? ` WHERE ${conditions}` : ''}`;
|
|
58
|
+
return (await this.conn.query(query));
|
|
59
|
+
}
|
|
60
|
+
// ==================== Accounts ====================
|
|
61
|
+
/**
|
|
62
|
+
* Create an account
|
|
63
|
+
*/
|
|
64
|
+
async createAccount(account) {
|
|
65
|
+
return (await this.conn.sobject('Account').create(account));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get an account by ID
|
|
69
|
+
*/
|
|
70
|
+
async getAccount(id) {
|
|
71
|
+
return (await this.conn.sobject('Account').retrieve(id));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Update an account
|
|
75
|
+
*/
|
|
76
|
+
async updateAccount(id, data) {
|
|
77
|
+
return (await this.conn.sobject('Account').update({ Id: id, ...data }));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Delete an account
|
|
81
|
+
*/
|
|
82
|
+
async deleteAccount(id) {
|
|
83
|
+
return (await this.conn.sobject('Account').destroy(id));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Query accounts
|
|
87
|
+
*/
|
|
88
|
+
async queryAccounts(conditions) {
|
|
89
|
+
const query = `SELECT Id, Name, Type, Industry, Website, Phone FROM Account${conditions ? ` WHERE ${conditions}` : ''}`;
|
|
90
|
+
return (await this.conn.query(query));
|
|
91
|
+
}
|
|
92
|
+
// ==================== Contacts ====================
|
|
93
|
+
/**
|
|
94
|
+
* Create a contact
|
|
95
|
+
*/
|
|
96
|
+
async createContact(contact) {
|
|
97
|
+
return (await this.conn.sobject('Contact').create(contact));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get a contact by ID
|
|
101
|
+
*/
|
|
102
|
+
async getContact(id) {
|
|
103
|
+
return (await this.conn.sobject('Contact').retrieve(id));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Update a contact
|
|
107
|
+
*/
|
|
108
|
+
async updateContact(id, data) {
|
|
109
|
+
return (await this.conn.sobject('Contact').update({ Id: id, ...data }));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Delete a contact
|
|
113
|
+
*/
|
|
114
|
+
async deleteContact(id) {
|
|
115
|
+
return (await this.conn.sobject('Contact').destroy(id));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Query contacts
|
|
119
|
+
*/
|
|
120
|
+
async queryContacts(conditions) {
|
|
121
|
+
const query = `SELECT Id, FirstName, LastName, AccountId, Email, Phone, Title FROM Contact${conditions ? ` WHERE ${conditions}` : ''}`;
|
|
122
|
+
return (await this.conn.query(query));
|
|
123
|
+
}
|
|
124
|
+
// ==================== Opportunities ====================
|
|
125
|
+
/**
|
|
126
|
+
* Create an opportunity
|
|
127
|
+
*/
|
|
128
|
+
async createOpportunity(opportunity) {
|
|
129
|
+
return (await this.conn.sobject('Opportunity').create(opportunity));
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get an opportunity by ID
|
|
133
|
+
*/
|
|
134
|
+
async getOpportunity(id) {
|
|
135
|
+
return (await this.conn.sobject('Opportunity').retrieve(id));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Update an opportunity
|
|
139
|
+
*/
|
|
140
|
+
async updateOpportunity(id, data) {
|
|
141
|
+
return (await this.conn.sobject('Opportunity').update({ Id: id, ...data }));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Delete an opportunity
|
|
145
|
+
*/
|
|
146
|
+
async deleteOpportunity(id) {
|
|
147
|
+
return (await this.conn.sobject('Opportunity').destroy(id));
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Query opportunities
|
|
151
|
+
*/
|
|
152
|
+
async queryOpportunities(conditions) {
|
|
153
|
+
const query = `SELECT Id, Name, AccountId, StageName, Amount, CloseDate, Probability FROM Opportunity${conditions ? ` WHERE ${conditions}` : ''}`;
|
|
154
|
+
return (await this.conn.query(query));
|
|
155
|
+
}
|
|
156
|
+
// ==================== Cases ====================
|
|
157
|
+
/**
|
|
158
|
+
* Create a case
|
|
159
|
+
*/
|
|
160
|
+
async createCase(caseData) {
|
|
161
|
+
return (await this.conn.sobject('Case').create(caseData));
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get a case by ID
|
|
165
|
+
*/
|
|
166
|
+
async getCase(id) {
|
|
167
|
+
return (await this.conn.sobject('Case').retrieve(id));
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Update a case
|
|
171
|
+
*/
|
|
172
|
+
async updateCase(id, data) {
|
|
173
|
+
return (await this.conn.sobject('Case').update({ Id: id, ...data }));
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Delete a case
|
|
177
|
+
*/
|
|
178
|
+
async deleteCase(id) {
|
|
179
|
+
return (await this.conn.sobject('Case').destroy(id));
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Query cases
|
|
183
|
+
*/
|
|
184
|
+
async queryCases(conditions) {
|
|
185
|
+
const query = `SELECT Id, Subject, Status, Priority, Origin, AccountId, ContactId FROM Case${conditions ? ` WHERE ${conditions}` : ''}`;
|
|
186
|
+
return (await this.conn.query(query));
|
|
187
|
+
}
|
|
188
|
+
// ==================== Generic Operations ====================
|
|
189
|
+
/**
|
|
190
|
+
* Create a record of any sobject type
|
|
191
|
+
*/
|
|
192
|
+
async createRecord(options) {
|
|
193
|
+
return (await this.conn.sobject(options.sobject).create(options.data));
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get a record by ID
|
|
197
|
+
*/
|
|
198
|
+
async getRecord(sobject, id) {
|
|
199
|
+
return (await this.conn.sobject(sobject).retrieve(id));
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Update a record
|
|
203
|
+
*/
|
|
204
|
+
async updateRecord(options) {
|
|
205
|
+
return (await this.conn.sobject(options.sobject).update({ Id: options.id, ...options.data }));
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Delete a record
|
|
209
|
+
*/
|
|
210
|
+
async deleteRecord(options) {
|
|
211
|
+
return (await this.conn.sobject(options.sobject).destroy(options.id));
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Execute SOQL query
|
|
215
|
+
*/
|
|
216
|
+
async query(options) {
|
|
217
|
+
if (options.useToolingApi) {
|
|
218
|
+
return (await this.conn.tooling.query(options.query));
|
|
219
|
+
}
|
|
220
|
+
return (await this.conn.query(options.query));
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Execute SOSL search
|
|
224
|
+
*/
|
|
225
|
+
async search(options) {
|
|
226
|
+
return (await this.conn.search(options.search));
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Describe an sobject
|
|
230
|
+
*/
|
|
231
|
+
async describe(options) {
|
|
232
|
+
return (await this.conn.sobject(options.sobject).describe());
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Get global describe (all sobjects)
|
|
236
|
+
*/
|
|
237
|
+
async describeGlobal() {
|
|
238
|
+
return (await this.conn.describeGlobal());
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Get current user info
|
|
242
|
+
*/
|
|
243
|
+
async getUserInfo() {
|
|
244
|
+
return (await this.conn.identity());
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get org limits
|
|
248
|
+
*/
|
|
249
|
+
async getLimits() {
|
|
250
|
+
return (await this.conn.limits());
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
export const SalesforceInitializer = {
|
|
254
|
+
async initialize(_module, config) {
|
|
255
|
+
const loginUrl = config.options?.['login_url'] || 'https://login.salesforce.com';
|
|
256
|
+
const instanceUrl = config.auth?.['instance_url'];
|
|
257
|
+
const accessToken = config.auth?.['access_token'];
|
|
258
|
+
const username = config.auth?.['username'];
|
|
259
|
+
const password = config.auth?.['password'];
|
|
260
|
+
const securityToken = config.auth?.['security_token'];
|
|
261
|
+
let conn;
|
|
262
|
+
if (accessToken && instanceUrl) {
|
|
263
|
+
// Use existing access token
|
|
264
|
+
conn = new jsforce.Connection({
|
|
265
|
+
instanceUrl,
|
|
266
|
+
accessToken,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
else if (username && password) {
|
|
270
|
+
// Login with username/password
|
|
271
|
+
conn = new jsforce.Connection({ loginUrl });
|
|
272
|
+
const passwordWithToken = securityToken ? password + securityToken : password;
|
|
273
|
+
await conn.login(username, passwordWithToken);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
throw new Error('Salesforce SDK requires either (auth.access_token + auth.instance_url) or (auth.username + auth.password)');
|
|
277
|
+
}
|
|
278
|
+
const client = new SalesforceClient(conn);
|
|
279
|
+
return {
|
|
280
|
+
client,
|
|
281
|
+
actions: client,
|
|
282
|
+
connection: conn, // Expose raw connection for advanced use
|
|
283
|
+
};
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
//# sourceMappingURL=salesforce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"salesforce.js","sourceRoot":"","sources":["../../src/services/salesforce.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SlackSocketTriggerConfig {
|
|
2
|
+
appToken: string;
|
|
3
|
+
botToken: string;
|
|
4
|
+
triggers: Array<{
|
|
5
|
+
id: string;
|
|
6
|
+
event: 'message' | 'app_mention';
|
|
7
|
+
handler: (payload: Record<string, unknown>) => Promise<void>;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
export declare class SlackSocketTrigger {
|
|
11
|
+
private config;
|
|
12
|
+
private app?;
|
|
13
|
+
private handlersRegistered;
|
|
14
|
+
constructor(config: SlackSocketTriggerConfig);
|
|
15
|
+
start(): Promise<void>;
|
|
16
|
+
stop(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=slack-socket.d.ts.map
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WhatsApp Integration
|
|
3
|
+
*
|
|
4
|
+
* Messaging platform via WhatsApp Business API.
|
|
5
|
+
* API Docs: https://developers.facebook.com/docs/whatsapp/cloud-api
|
|
6
|
+
*/
|
|
7
|
+
import { SDKInitializer } from '@marktoflow/core';
|
|
8
|
+
export interface WhatsAppMessage {
|
|
9
|
+
messagingProduct: 'whatsapp';
|
|
10
|
+
to: string;
|
|
11
|
+
type: 'text' | 'template' | 'image' | 'video' | 'document' | 'audio' | 'location' | 'contacts' | 'interactive';
|
|
12
|
+
text?: {
|
|
13
|
+
body: string;
|
|
14
|
+
previewUrl?: boolean;
|
|
15
|
+
};
|
|
16
|
+
template?: WhatsAppTemplate;
|
|
17
|
+
image?: {
|
|
18
|
+
link?: string;
|
|
19
|
+
id?: string;
|
|
20
|
+
caption?: string;
|
|
21
|
+
};
|
|
22
|
+
video?: {
|
|
23
|
+
link?: string;
|
|
24
|
+
id?: string;
|
|
25
|
+
caption?: string;
|
|
26
|
+
};
|
|
27
|
+
document?: {
|
|
28
|
+
link?: string;
|
|
29
|
+
id?: string;
|
|
30
|
+
caption?: string;
|
|
31
|
+
filename?: string;
|
|
32
|
+
};
|
|
33
|
+
audio?: {
|
|
34
|
+
link?: string;
|
|
35
|
+
id?: string;
|
|
36
|
+
};
|
|
37
|
+
location?: {
|
|
38
|
+
longitude: number;
|
|
39
|
+
latitude: number;
|
|
40
|
+
name?: string;
|
|
41
|
+
address?: string;
|
|
42
|
+
};
|
|
43
|
+
interactive?: WhatsAppInteractive;
|
|
44
|
+
}
|
|
45
|
+
export interface WhatsAppTemplate {
|
|
46
|
+
name: string;
|
|
47
|
+
language: {
|
|
48
|
+
code: string;
|
|
49
|
+
};
|
|
50
|
+
components?: WhatsAppTemplateComponent[];
|
|
51
|
+
}
|
|
52
|
+
export interface WhatsAppTemplateComponent {
|
|
53
|
+
type: 'header' | 'body' | 'button';
|
|
54
|
+
parameters: WhatsAppTemplateParameter[];
|
|
55
|
+
}
|
|
56
|
+
export interface WhatsAppTemplateParameter {
|
|
57
|
+
type: 'text' | 'currency' | 'date_time' | 'image' | 'document' | 'video';
|
|
58
|
+
text?: string;
|
|
59
|
+
currency?: {
|
|
60
|
+
fallbackValue: string;
|
|
61
|
+
code: string;
|
|
62
|
+
amount1000: number;
|
|
63
|
+
};
|
|
64
|
+
dateTime?: {
|
|
65
|
+
fallbackValue: string;
|
|
66
|
+
};
|
|
67
|
+
image?: {
|
|
68
|
+
link: string;
|
|
69
|
+
};
|
|
70
|
+
document?: {
|
|
71
|
+
link: string;
|
|
72
|
+
filename?: string;
|
|
73
|
+
};
|
|
74
|
+
video?: {
|
|
75
|
+
link: string;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export interface WhatsAppInteractive {
|
|
79
|
+
type: 'button' | 'list' | 'product' | 'product_list';
|
|
80
|
+
header?: {
|
|
81
|
+
type: 'text' | 'image' | 'video' | 'document';
|
|
82
|
+
text?: string;
|
|
83
|
+
image?: {
|
|
84
|
+
link: string;
|
|
85
|
+
};
|
|
86
|
+
video?: {
|
|
87
|
+
link: string;
|
|
88
|
+
};
|
|
89
|
+
document?: {
|
|
90
|
+
link: string;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
body: {
|
|
94
|
+
text: string;
|
|
95
|
+
};
|
|
96
|
+
footer?: {
|
|
97
|
+
text: string;
|
|
98
|
+
};
|
|
99
|
+
action: WhatsAppAction;
|
|
100
|
+
}
|
|
101
|
+
export interface WhatsAppAction {
|
|
102
|
+
buttons?: WhatsAppButton[];
|
|
103
|
+
button?: string;
|
|
104
|
+
sections?: WhatsAppSection[];
|
|
105
|
+
}
|
|
106
|
+
export interface WhatsAppButton {
|
|
107
|
+
type: 'reply';
|
|
108
|
+
reply: {
|
|
109
|
+
id: string;
|
|
110
|
+
title: string;
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export interface WhatsAppSection {
|
|
114
|
+
title?: string;
|
|
115
|
+
rows: WhatsAppRow[];
|
|
116
|
+
}
|
|
117
|
+
export interface WhatsAppRow {
|
|
118
|
+
id: string;
|
|
119
|
+
title: string;
|
|
120
|
+
description?: string;
|
|
121
|
+
}
|
|
122
|
+
export interface SendTextOptions {
|
|
123
|
+
to: string;
|
|
124
|
+
text: string;
|
|
125
|
+
previewUrl?: boolean;
|
|
126
|
+
}
|
|
127
|
+
export interface SendTemplateOptions {
|
|
128
|
+
to: string;
|
|
129
|
+
templateName: string;
|
|
130
|
+
languageCode: string;
|
|
131
|
+
components?: WhatsAppTemplateComponent[];
|
|
132
|
+
}
|
|
133
|
+
export interface SendMediaOptions {
|
|
134
|
+
to: string;
|
|
135
|
+
mediaUrl?: string;
|
|
136
|
+
mediaId?: string;
|
|
137
|
+
caption?: string;
|
|
138
|
+
filename?: string;
|
|
139
|
+
}
|
|
140
|
+
export interface SendLocationOptions {
|
|
141
|
+
to: string;
|
|
142
|
+
longitude: number;
|
|
143
|
+
latitude: number;
|
|
144
|
+
name?: string;
|
|
145
|
+
address?: string;
|
|
146
|
+
}
|
|
147
|
+
export interface SendInteractiveOptions {
|
|
148
|
+
to: string;
|
|
149
|
+
type: 'button' | 'list';
|
|
150
|
+
bodyText: string;
|
|
151
|
+
headerText?: string;
|
|
152
|
+
footerText?: string;
|
|
153
|
+
buttons?: {
|
|
154
|
+
id: string;
|
|
155
|
+
title: string;
|
|
156
|
+
}[];
|
|
157
|
+
listButton?: string;
|
|
158
|
+
sections?: WhatsAppSection[];
|
|
159
|
+
}
|
|
160
|
+
export interface WhatsAppWebhookMessage {
|
|
161
|
+
from: string;
|
|
162
|
+
id: string;
|
|
163
|
+
timestamp: string;
|
|
164
|
+
type: string;
|
|
165
|
+
text?: {
|
|
166
|
+
body: string;
|
|
167
|
+
};
|
|
168
|
+
image?: {
|
|
169
|
+
id: string;
|
|
170
|
+
mimeType: string;
|
|
171
|
+
sha256: string;
|
|
172
|
+
};
|
|
173
|
+
video?: {
|
|
174
|
+
id: string;
|
|
175
|
+
mimeType: string;
|
|
176
|
+
sha256: string;
|
|
177
|
+
};
|
|
178
|
+
document?: {
|
|
179
|
+
id: string;
|
|
180
|
+
mimeType: string;
|
|
181
|
+
sha256: string;
|
|
182
|
+
filename: string;
|
|
183
|
+
};
|
|
184
|
+
audio?: {
|
|
185
|
+
id: string;
|
|
186
|
+
mimeType: string;
|
|
187
|
+
sha256: string;
|
|
188
|
+
};
|
|
189
|
+
location?: {
|
|
190
|
+
longitude: number;
|
|
191
|
+
latitude: number;
|
|
192
|
+
name?: string;
|
|
193
|
+
address?: string;
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* WhatsApp Business API client for workflow integration
|
|
198
|
+
*/
|
|
199
|
+
export declare class WhatsAppClient {
|
|
200
|
+
private accessToken;
|
|
201
|
+
private apiUrl;
|
|
202
|
+
constructor(phoneNumberId: string, accessToken: string);
|
|
203
|
+
private request;
|
|
204
|
+
/**
|
|
205
|
+
* Send a text message
|
|
206
|
+
*/
|
|
207
|
+
sendText(options: SendTextOptions): Promise<{
|
|
208
|
+
messagingProduct: string;
|
|
209
|
+
contacts: unknown[];
|
|
210
|
+
messages: {
|
|
211
|
+
id: string;
|
|
212
|
+
}[];
|
|
213
|
+
}>;
|
|
214
|
+
/**
|
|
215
|
+
* Send a template message
|
|
216
|
+
*/
|
|
217
|
+
sendTemplate(options: SendTemplateOptions): Promise<{
|
|
218
|
+
messagingProduct: string;
|
|
219
|
+
contacts: unknown[];
|
|
220
|
+
messages: {
|
|
221
|
+
id: string;
|
|
222
|
+
}[];
|
|
223
|
+
}>;
|
|
224
|
+
/**
|
|
225
|
+
* Send an image
|
|
226
|
+
*/
|
|
227
|
+
sendImage(options: SendMediaOptions): Promise<{
|
|
228
|
+
messagingProduct: string;
|
|
229
|
+
contacts: unknown[];
|
|
230
|
+
messages: {
|
|
231
|
+
id: string;
|
|
232
|
+
}[];
|
|
233
|
+
}>;
|
|
234
|
+
/**
|
|
235
|
+
* Send a video
|
|
236
|
+
*/
|
|
237
|
+
sendVideo(options: SendMediaOptions): Promise<{
|
|
238
|
+
messagingProduct: string;
|
|
239
|
+
contacts: unknown[];
|
|
240
|
+
messages: {
|
|
241
|
+
id: string;
|
|
242
|
+
}[];
|
|
243
|
+
}>;
|
|
244
|
+
/**
|
|
245
|
+
* Send a document
|
|
246
|
+
*/
|
|
247
|
+
sendDocument(options: SendMediaOptions): Promise<{
|
|
248
|
+
messagingProduct: string;
|
|
249
|
+
contacts: unknown[];
|
|
250
|
+
messages: {
|
|
251
|
+
id: string;
|
|
252
|
+
}[];
|
|
253
|
+
}>;
|
|
254
|
+
/**
|
|
255
|
+
* Send an audio
|
|
256
|
+
*/
|
|
257
|
+
sendAudio(options: Omit<SendMediaOptions, 'caption' | 'filename'>): Promise<{
|
|
258
|
+
messagingProduct: string;
|
|
259
|
+
contacts: unknown[];
|
|
260
|
+
messages: {
|
|
261
|
+
id: string;
|
|
262
|
+
}[];
|
|
263
|
+
}>;
|
|
264
|
+
/**
|
|
265
|
+
* Send a location
|
|
266
|
+
*/
|
|
267
|
+
sendLocation(options: SendLocationOptions): Promise<{
|
|
268
|
+
messagingProduct: string;
|
|
269
|
+
contacts: unknown[];
|
|
270
|
+
messages: {
|
|
271
|
+
id: string;
|
|
272
|
+
}[];
|
|
273
|
+
}>;
|
|
274
|
+
/**
|
|
275
|
+
* Send an interactive message (buttons or list)
|
|
276
|
+
*/
|
|
277
|
+
sendInteractive(options: SendInteractiveOptions): Promise<{
|
|
278
|
+
messagingProduct: string;
|
|
279
|
+
contacts: unknown[];
|
|
280
|
+
messages: {
|
|
281
|
+
id: string;
|
|
282
|
+
}[];
|
|
283
|
+
}>;
|
|
284
|
+
/**
|
|
285
|
+
* Mark a message as read
|
|
286
|
+
*/
|
|
287
|
+
markAsRead(messageId: string): Promise<{
|
|
288
|
+
success: boolean;
|
|
289
|
+
}>;
|
|
290
|
+
/**
|
|
291
|
+
* Get media URL
|
|
292
|
+
*/
|
|
293
|
+
getMediaUrl(mediaId: string): Promise<{
|
|
294
|
+
url: string;
|
|
295
|
+
mimeType: string;
|
|
296
|
+
sha256: string;
|
|
297
|
+
fileSize: number;
|
|
298
|
+
}>;
|
|
299
|
+
/**
|
|
300
|
+
* Download media
|
|
301
|
+
*/
|
|
302
|
+
downloadMedia(mediaId: string): Promise<Buffer>;
|
|
303
|
+
/**
|
|
304
|
+
* Upload media
|
|
305
|
+
*/
|
|
306
|
+
uploadMedia(file: Buffer, mimeType: string): Promise<{
|
|
307
|
+
id: string;
|
|
308
|
+
}>;
|
|
309
|
+
}
|
|
310
|
+
export declare const WhatsAppInitializer: SDKInitializer;
|
|
311
|
+
//# sourceMappingURL=whatsapp.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marktoflow/integrations",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.15",
|
|
4
4
|
"description": "Standard integrations for marktoflow",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@aws-sdk/client-s3": "^3.712.0",
|
|
31
31
|
"@github/copilot-sdk": "^0.1.18",
|
|
32
32
|
"@mailchimp/mailchimp_marketing": "^3.0.80",
|
|
33
|
-
"@marktoflow/core": "2.0.0-alpha.
|
|
33
|
+
"@marktoflow/core": "2.0.0-alpha.15",
|
|
34
34
|
"@microsoft/microsoft-graph-client": "^3.0.7",
|
|
35
35
|
"@octokit/rest": "^22.0.1",
|
|
36
36
|
"@openai/codex-sdk": "^0.91.0",
|
|
@@ -94,5 +94,8 @@
|
|
|
94
94
|
"license": "Apache-2.0",
|
|
95
95
|
"devDependencies": {
|
|
96
96
|
"@types/pg": "^8.16.0"
|
|
97
|
+
},
|
|
98
|
+
"publishConfig": {
|
|
99
|
+
"registry": "https://registry.npmjs.org/"
|
|
97
100
|
}
|
|
98
101
|
}
|