tiktok-business-messaging 0.0.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 +41 -0
- package/dist/index.cjs +335 -0
- package/dist/index.d.cts +263 -0
- package/dist/index.d.ts +263 -0
- package/dist/index.js +307 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# TikTok Business Messaging SDK
|
|
2
|
+
|
|
3
|
+
Lightweight **Node.js/TypeScript** wrapper for the TikTok Business Messaging API.
|
|
4
|
+
|
|
5
|
+
> Unofficial SDK. Use at your own risk.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Simple API client for messaging endpoints
|
|
10
|
+
- Built-in auth header handling
|
|
11
|
+
- Typed request/response models (TypeScript)
|
|
12
|
+
- Webhook signature verification helper
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install tiktok-business
|
|
18
|
+
# or
|
|
19
|
+
pnpm add tiktok-business
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { TikTokBusiness } from "tiktok-business-messaging-sdk";
|
|
26
|
+
|
|
27
|
+
const client = new TikTokBusiness({
|
|
28
|
+
appId: process.env.TT_APP_ID!,
|
|
29
|
+
appSecret: process.env.TT_APP_SECRET!,
|
|
30
|
+
accessToken: process.env.TT_ACCESS_TOKEN!,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
await client.messages.sendText({
|
|
34
|
+
conversationId: "conv_123",
|
|
35
|
+
text: "Hello from TikTok SDK 👋",
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
|
|
41
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
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/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
TikTokApiError: () => TikTokApiError,
|
|
24
|
+
TikTokBusiness: () => TikTokBusiness
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/constants.ts
|
|
29
|
+
var TIKTOK_API_BASE_URL = "https://business-api.tiktok.com/open_api/v1.3";
|
|
30
|
+
var TIKTOK_AUTH_BASE_URL = `${TIKTOK_API_BASE_URL}/tt_user/oauth2`;
|
|
31
|
+
var TIKTOK_ERROR_MESSAGES = {
|
|
32
|
+
[40001 /* NO_PERMISSION */]: "No permission to perform the related operation. Ensure you have the necessary scopes.",
|
|
33
|
+
[40002 /* PARAMETER_ERROR */]: "Parameter error. Check missing fields or incorrect formats in your request.",
|
|
34
|
+
[40007 /* OBJECT_NOT_FOUND */]: "The operation or object does not exist. Check your IDs or URLs.",
|
|
35
|
+
[40100 /* RATE_LIMIT_EXCEEDED */]: "Requests made too frequently. You are being throttled.",
|
|
36
|
+
[40105 /* INVALID_ACCESS_TOKEN */]: "Invalid or incorrect access token. Please refresh or re-authorize.",
|
|
37
|
+
[40908 /* UNSUPPORTED_FILE_TYPE */]: "The file type is unsupported. Refer to documentation for allowed formats.",
|
|
38
|
+
[40064 /* MESSAGE_BLOCKED */]: "The message is blocked due to TikTok direct message rules.",
|
|
39
|
+
[51065 /* SYSTEM_ERROR */]: "TikTok internal system error. Please retry the request.",
|
|
40
|
+
[40131 /* CODE_EXPIRED */]: "The authorization code has expired. Please re-authorize to get a new code."
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// src/error.ts
|
|
44
|
+
var TikTokApiError = class _TikTokApiError extends Error {
|
|
45
|
+
constructor(code, requestId) {
|
|
46
|
+
const friendlyMessage = TIKTOK_ERROR_MESSAGES[code] || "An unknown TikTok API error occurred.";
|
|
47
|
+
super(`TikTok API Error [${code}]: ${friendlyMessage}`);
|
|
48
|
+
this.code = code;
|
|
49
|
+
this.requestId = requestId;
|
|
50
|
+
this.name = "TikTokApiError";
|
|
51
|
+
this.message = friendlyMessage;
|
|
52
|
+
Object.setPrototypeOf(this, _TikTokApiError.prototype);
|
|
53
|
+
}
|
|
54
|
+
message;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/http.ts
|
|
58
|
+
var HttpClient = class {
|
|
59
|
+
constructor(config) {
|
|
60
|
+
this.config = config;
|
|
61
|
+
this.baseUrl = config.baseUrl ?? "https://business-api.tiktok.com/open_api/v1.3";
|
|
62
|
+
}
|
|
63
|
+
baseUrl;
|
|
64
|
+
get(path, params, opts) {
|
|
65
|
+
return this.request({ method: "GET", path, params, opts });
|
|
66
|
+
}
|
|
67
|
+
post(path, body, opts) {
|
|
68
|
+
return this.request({ method: "POST", path, body, opts });
|
|
69
|
+
}
|
|
70
|
+
put(path, body, opts) {
|
|
71
|
+
return this.request({ method: "PUT", path, body, opts });
|
|
72
|
+
}
|
|
73
|
+
patch(path, body, opts) {
|
|
74
|
+
return this.request({ method: "PATCH", path, body, opts });
|
|
75
|
+
}
|
|
76
|
+
delete(path, params, opts) {
|
|
77
|
+
return this.request({ method: "DELETE", path, params, opts });
|
|
78
|
+
}
|
|
79
|
+
upload(path, form, opts) {
|
|
80
|
+
return this.request({ method: "POST", path, form, opts });
|
|
81
|
+
}
|
|
82
|
+
async request({
|
|
83
|
+
method,
|
|
84
|
+
path,
|
|
85
|
+
params,
|
|
86
|
+
body,
|
|
87
|
+
form,
|
|
88
|
+
opts
|
|
89
|
+
}) {
|
|
90
|
+
const res = await fetch(
|
|
91
|
+
this.buildUrl(path, params),
|
|
92
|
+
this.buildInit(method, body, opts, form)
|
|
93
|
+
);
|
|
94
|
+
return this.parseResponse(res);
|
|
95
|
+
}
|
|
96
|
+
buildUrl(path, params) {
|
|
97
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
98
|
+
if (params) {
|
|
99
|
+
Object.entries(params).forEach(([k, v]) => {
|
|
100
|
+
if (v !== void 0 && v !== null && v !== "") {
|
|
101
|
+
url.searchParams.set(k, String(v));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return url.toString();
|
|
106
|
+
}
|
|
107
|
+
buildInit(method, body, opts, form) {
|
|
108
|
+
const headers = {
|
|
109
|
+
"Access-Token": opts.accessToken
|
|
110
|
+
};
|
|
111
|
+
if (!form) {
|
|
112
|
+
headers["Content-Type"] = "application/json";
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
method,
|
|
116
|
+
signal: AbortSignal.timeout(
|
|
117
|
+
opts.timeout ?? this.config.timeout ?? 1e4
|
|
118
|
+
),
|
|
119
|
+
headers,
|
|
120
|
+
...form ? { body: form } : body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
async parseResponse(res) {
|
|
124
|
+
console.debug(`[HttpClient] Parsing response for URL: ${res.url}`);
|
|
125
|
+
console.debug(
|
|
126
|
+
`[HttpClient] Response status: ${res.status} ${res.statusText}`
|
|
127
|
+
);
|
|
128
|
+
console.debug(`[HttpClient] Response data:`, await res.clone().text());
|
|
129
|
+
const json = await res.json();
|
|
130
|
+
if (json.code !== 0) {
|
|
131
|
+
throw new TikTokApiError(json.code, json.request_id);
|
|
132
|
+
}
|
|
133
|
+
return json.data;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// src/services/auth.ts
|
|
138
|
+
var Auth = class {
|
|
139
|
+
constructor(config) {
|
|
140
|
+
this.config = config;
|
|
141
|
+
}
|
|
142
|
+
getTokens(authCode) {
|
|
143
|
+
return this.post("/token/", {
|
|
144
|
+
client_id: this.config.clientKey,
|
|
145
|
+
client_secret: this.config.clientSecret,
|
|
146
|
+
grant_type: "authorization_code",
|
|
147
|
+
auth_code: decodeURIComponent(authCode),
|
|
148
|
+
redirect_uri: this.config.redirectUri
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
refreshTokens(refreshToken) {
|
|
152
|
+
return this.post("/refresh_token/", {
|
|
153
|
+
client_id: this.config.clientKey,
|
|
154
|
+
client_secret: this.config.clientSecret,
|
|
155
|
+
grant_type: "refresh_token",
|
|
156
|
+
refresh_token: refreshToken
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
revokeToken(accessToken) {
|
|
160
|
+
return this.post("/token/revoke/", {
|
|
161
|
+
client_id: this.config.clientKey,
|
|
162
|
+
client_secret: this.config.clientSecret,
|
|
163
|
+
token: accessToken
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
// for tt_users
|
|
167
|
+
getAuthorizationUrl(state) {
|
|
168
|
+
const params = new URLSearchParams({
|
|
169
|
+
client_key: this.config.clientKey,
|
|
170
|
+
response_type: "code",
|
|
171
|
+
scope: (this.config.scopes ?? []).join(","),
|
|
172
|
+
redirect_uri: this.config.redirectUri ?? "",
|
|
173
|
+
...state && { state }
|
|
174
|
+
});
|
|
175
|
+
return `https://www.tiktok.com/v2/auth/authorize/?${params.toString()}`;
|
|
176
|
+
}
|
|
177
|
+
async post(path, body) {
|
|
178
|
+
const response = await fetch(`${TIKTOK_AUTH_BASE_URL}${path}`, {
|
|
179
|
+
method: "POST",
|
|
180
|
+
headers: { "Content-Type": "application/json" },
|
|
181
|
+
body: JSON.stringify(body)
|
|
182
|
+
});
|
|
183
|
+
if (!response.ok) {
|
|
184
|
+
throw new TikTokApiError(response.status, response.statusText);
|
|
185
|
+
}
|
|
186
|
+
const json = await response.json();
|
|
187
|
+
if (json.code !== 0) {
|
|
188
|
+
throw new TikTokApiError(json.code, json.request_id);
|
|
189
|
+
}
|
|
190
|
+
return json.data;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// src/services/media.ts
|
|
195
|
+
var MediaService = class {
|
|
196
|
+
constructor(http) {
|
|
197
|
+
this.http = http;
|
|
198
|
+
}
|
|
199
|
+
upload(params, opts) {
|
|
200
|
+
const form = new FormData();
|
|
201
|
+
form.append("business_id", params.business_id);
|
|
202
|
+
form.append("file", params.file);
|
|
203
|
+
form.append("media_type", params.media_type);
|
|
204
|
+
return this.http.upload("/business/message/media/upload/", form, opts);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// src/services/messaging.ts
|
|
209
|
+
var Messaging = class {
|
|
210
|
+
constructor(http) {
|
|
211
|
+
this.http = http;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* List all conversations for a business account
|
|
215
|
+
*/
|
|
216
|
+
listConversations(params, opts) {
|
|
217
|
+
console.debug("[Messaging] listConversations called", { params });
|
|
218
|
+
return this.http.get("/business/message/conversation/list/", params, opts).then((response) => {
|
|
219
|
+
console.debug("[Messaging] listConversations succeeded");
|
|
220
|
+
return response;
|
|
221
|
+
}).catch((error) => {
|
|
222
|
+
console.error("[Messaging] listConversations failed", error);
|
|
223
|
+
throw error;
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get messages in a specific conversation
|
|
228
|
+
*/
|
|
229
|
+
listMessages(params, opts) {
|
|
230
|
+
console.log("Conversation ID:", encodeURIComponent(params.conversation_id));
|
|
231
|
+
return this.http.get(
|
|
232
|
+
"/business/message/content/list/",
|
|
233
|
+
{
|
|
234
|
+
...params
|
|
235
|
+
},
|
|
236
|
+
opts
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Send a text message to a conversation
|
|
241
|
+
*/
|
|
242
|
+
sendTextMessage(body, opts) {
|
|
243
|
+
const { ...restBody } = body;
|
|
244
|
+
return this.http.post(
|
|
245
|
+
"/business/message/send/",
|
|
246
|
+
{ recipient_type: "CONVERSATION", ...restBody },
|
|
247
|
+
opts
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Send an image message to a conversation
|
|
252
|
+
*/
|
|
253
|
+
sendImageMessage(body, opts) {
|
|
254
|
+
return this.http.post(
|
|
255
|
+
"/business/message/send/",
|
|
256
|
+
{
|
|
257
|
+
...body
|
|
258
|
+
},
|
|
259
|
+
opts
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// src/services/webhook.ts
|
|
265
|
+
var WebhookService = class {
|
|
266
|
+
constructor(config) {
|
|
267
|
+
this.config = config;
|
|
268
|
+
}
|
|
269
|
+
create(params) {
|
|
270
|
+
return this.post("/business/webhook/update/", {
|
|
271
|
+
event_type: params.event_type,
|
|
272
|
+
callback_url: params.callback_url
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
get(params) {
|
|
276
|
+
return this.fetch("/business/webhook/list/", params.event_type);
|
|
277
|
+
}
|
|
278
|
+
delete(params) {
|
|
279
|
+
return this.post("/business/webhook/delete/", {
|
|
280
|
+
event_type: params.event_type
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
async post(path, body) {
|
|
284
|
+
console.log("called post");
|
|
285
|
+
const response = await fetch(`${TIKTOK_API_BASE_URL}${path}`, {
|
|
286
|
+
method: "POST",
|
|
287
|
+
headers: { "Content-Type": "application/json" },
|
|
288
|
+
body: JSON.stringify({
|
|
289
|
+
app_id: this.config.clientKey,
|
|
290
|
+
secret: this.config.clientSecret,
|
|
291
|
+
...body
|
|
292
|
+
})
|
|
293
|
+
});
|
|
294
|
+
console.log(response.status, await response.text());
|
|
295
|
+
return this.parseResponse(response);
|
|
296
|
+
}
|
|
297
|
+
async fetch(path, event_type) {
|
|
298
|
+
const url = new URL(`${TIKTOK_API_BASE_URL}${path}`);
|
|
299
|
+
url.searchParams.set("app_id", this.config.clientKey);
|
|
300
|
+
url.searchParams.set("secret", this.config.clientSecret);
|
|
301
|
+
url.searchParams.set("event_type", event_type);
|
|
302
|
+
const response = await globalThis.fetch(url.toString());
|
|
303
|
+
return this.parseResponse(response);
|
|
304
|
+
}
|
|
305
|
+
async parseResponse(response) {
|
|
306
|
+
if (!response.ok) {
|
|
307
|
+
throw new TikTokApiError(response.status, response.statusText);
|
|
308
|
+
}
|
|
309
|
+
const json = await response.json();
|
|
310
|
+
if (json.code !== 0) {
|
|
311
|
+
throw new TikTokApiError(json.code, json.request_id);
|
|
312
|
+
}
|
|
313
|
+
return json.data;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// src/client.ts
|
|
318
|
+
var TikTokBusiness = class {
|
|
319
|
+
auth;
|
|
320
|
+
messaging;
|
|
321
|
+
media;
|
|
322
|
+
webhook;
|
|
323
|
+
constructor(config) {
|
|
324
|
+
const http = new HttpClient(config);
|
|
325
|
+
this.auth = new Auth(config);
|
|
326
|
+
this.messaging = new Messaging(http);
|
|
327
|
+
this.media = new MediaService(http);
|
|
328
|
+
this.webhook = new WebhookService(config);
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
332
|
+
0 && (module.exports = {
|
|
333
|
+
TikTokApiError,
|
|
334
|
+
TikTokBusiness
|
|
335
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
interface TikTokTokenResponse {
|
|
2
|
+
access_token: string;
|
|
3
|
+
refresh_token: string;
|
|
4
|
+
access_token_expire_in: number;
|
|
5
|
+
refresh_token_expire_in: number;
|
|
6
|
+
token_type: string;
|
|
7
|
+
scope: string;
|
|
8
|
+
open_id: string;
|
|
9
|
+
}
|
|
10
|
+
interface RefreshTokenResponse {
|
|
11
|
+
access_token: string;
|
|
12
|
+
access_token_expire_in: number;
|
|
13
|
+
refresh_token: string;
|
|
14
|
+
refresh_token_expire_in: number;
|
|
15
|
+
token_type: string;
|
|
16
|
+
scope: string;
|
|
17
|
+
open_id: string;
|
|
18
|
+
}
|
|
19
|
+
interface RevokeTokenResponse {
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface TikTokConfig {
|
|
23
|
+
clientKey: string;
|
|
24
|
+
clientSecret: string;
|
|
25
|
+
redirectUri?: string;
|
|
26
|
+
scopes?: string[];
|
|
27
|
+
baseUrl?: string;
|
|
28
|
+
timeout?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type MediaType = "IMAGE";
|
|
32
|
+
interface UploadMediaRequest {
|
|
33
|
+
business_id: string;
|
|
34
|
+
file: File | Blob;
|
|
35
|
+
media_type: MediaType;
|
|
36
|
+
}
|
|
37
|
+
interface MediaContent {
|
|
38
|
+
media_id: string;
|
|
39
|
+
}
|
|
40
|
+
type UploadMediaResponse = MediaContent;
|
|
41
|
+
|
|
42
|
+
type MessageType = "TEXT" | "SENDER_ACTION";
|
|
43
|
+
type SenderAction = "TYPING" | "MARK_READ";
|
|
44
|
+
type RecipientType = "CONVERSATION";
|
|
45
|
+
type ConversationStatus = "ACTIVE" | "ARCHIVED";
|
|
46
|
+
interface ListMessagesRequest {
|
|
47
|
+
business_id: string;
|
|
48
|
+
conversation_id: string;
|
|
49
|
+
}
|
|
50
|
+
interface Message {
|
|
51
|
+
message_id: string;
|
|
52
|
+
conversation_id: string;
|
|
53
|
+
message_type: MessageType;
|
|
54
|
+
create_time: number;
|
|
55
|
+
direction: "INBOUND" | "OUTBOUND";
|
|
56
|
+
status: "SENT" | "DELIVERED" | "READ" | "FAILED";
|
|
57
|
+
sender: {
|
|
58
|
+
open_id: string;
|
|
59
|
+
display_name: string;
|
|
60
|
+
avatar_url: string;
|
|
61
|
+
};
|
|
62
|
+
content: {
|
|
63
|
+
body: string;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
interface ListMessagesResponse {
|
|
67
|
+
messages: Message[];
|
|
68
|
+
next_cursor: string | null;
|
|
69
|
+
has_more: boolean;
|
|
70
|
+
total: number;
|
|
71
|
+
}
|
|
72
|
+
interface TextContent {
|
|
73
|
+
body: string;
|
|
74
|
+
}
|
|
75
|
+
interface SendTextMessageRequest {
|
|
76
|
+
conversation_id?: string;
|
|
77
|
+
business_id: string;
|
|
78
|
+
recipient: string;
|
|
79
|
+
message_type: MessageType;
|
|
80
|
+
recipient_type?: RecipientType;
|
|
81
|
+
text?: TextContent;
|
|
82
|
+
sender_action?: SenderAction;
|
|
83
|
+
}
|
|
84
|
+
interface SendTextMessageResponse {
|
|
85
|
+
message_id: string;
|
|
86
|
+
create_time: number;
|
|
87
|
+
}
|
|
88
|
+
type ConversationType = "STRANGER" | "SINGLE";
|
|
89
|
+
interface ReferralAd {
|
|
90
|
+
advertiser_id: string;
|
|
91
|
+
ad_id: string;
|
|
92
|
+
timestamp: number;
|
|
93
|
+
ad_name: string;
|
|
94
|
+
embed_url: string;
|
|
95
|
+
message_material_id?: string;
|
|
96
|
+
}
|
|
97
|
+
interface ReferralShortLink {
|
|
98
|
+
ref: string;
|
|
99
|
+
prefilled_message: string;
|
|
100
|
+
prefilled_message_audit_status: "PASS" | "REJECT";
|
|
101
|
+
}
|
|
102
|
+
interface ConversationReferral {
|
|
103
|
+
ad?: ReferralAd[];
|
|
104
|
+
short_link?: ReferralShortLink[];
|
|
105
|
+
}
|
|
106
|
+
interface Conversation {
|
|
107
|
+
conversation_id: string;
|
|
108
|
+
update_time: number;
|
|
109
|
+
referral: ConversationReferral;
|
|
110
|
+
}
|
|
111
|
+
interface ListConversationsRequest {
|
|
112
|
+
business_id: string;
|
|
113
|
+
cursor?: number;
|
|
114
|
+
conversation_type: ConversationType;
|
|
115
|
+
limit?: number;
|
|
116
|
+
}
|
|
117
|
+
interface ListConversationsResponse {
|
|
118
|
+
conversations: Conversation[];
|
|
119
|
+
has_more: boolean;
|
|
120
|
+
cursor: number;
|
|
121
|
+
}
|
|
122
|
+
interface SendImageMessageRequest {
|
|
123
|
+
business_id: string;
|
|
124
|
+
recipient_type: RecipientType;
|
|
125
|
+
message_type: "IMAGE";
|
|
126
|
+
recipient: string;
|
|
127
|
+
image: MediaContent;
|
|
128
|
+
}
|
|
129
|
+
interface SendImageMessageResponse {
|
|
130
|
+
message_id: string;
|
|
131
|
+
create_time: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
declare class Auth {
|
|
135
|
+
private readonly config;
|
|
136
|
+
constructor(config: TikTokConfig);
|
|
137
|
+
getTokens(authCode: string): Promise<TikTokTokenResponse>;
|
|
138
|
+
refreshTokens(refreshToken: string): Promise<RefreshTokenResponse>;
|
|
139
|
+
revokeToken(accessToken: string): Promise<RevokeTokenResponse>;
|
|
140
|
+
getAuthorizationUrl(state?: string): string;
|
|
141
|
+
private post;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
interface RequestOptions {
|
|
145
|
+
accessToken: string;
|
|
146
|
+
advertiserIds?: string[];
|
|
147
|
+
timeout?: number;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
declare class HttpClient {
|
|
151
|
+
private config;
|
|
152
|
+
private baseUrl;
|
|
153
|
+
constructor(config: TikTokConfig);
|
|
154
|
+
get<T>(path: string, params: object, opts: RequestOptions): Promise<T>;
|
|
155
|
+
post<T>(path: string, body: unknown, opts: RequestOptions): Promise<T>;
|
|
156
|
+
put<T>(path: string, body: unknown, opts: RequestOptions): Promise<T>;
|
|
157
|
+
patch<T>(path: string, body: unknown, opts: RequestOptions): Promise<T>;
|
|
158
|
+
delete<T>(path: string, params: object, opts: RequestOptions): Promise<T>;
|
|
159
|
+
upload<T>(path: string, form: FormData, opts: RequestOptions): Promise<T>;
|
|
160
|
+
private request;
|
|
161
|
+
private buildUrl;
|
|
162
|
+
private buildInit;
|
|
163
|
+
private parseResponse;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
declare class MediaService {
|
|
167
|
+
private readonly http;
|
|
168
|
+
constructor(http: HttpClient);
|
|
169
|
+
upload(params: UploadMediaRequest, opts: RequestOptions): Promise<UploadMediaResponse>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
declare class Messaging {
|
|
173
|
+
private readonly http;
|
|
174
|
+
constructor(http: HttpClient);
|
|
175
|
+
/**
|
|
176
|
+
* List all conversations for a business account
|
|
177
|
+
*/
|
|
178
|
+
listConversations(params: ListConversationsRequest, opts: RequestOptions): Promise<ListConversationsResponse>;
|
|
179
|
+
/**
|
|
180
|
+
* Get messages in a specific conversation
|
|
181
|
+
*/
|
|
182
|
+
listMessages(params: ListMessagesRequest, opts: RequestOptions): Promise<ListMessagesResponse>;
|
|
183
|
+
/**
|
|
184
|
+
* Send a text message to a conversation
|
|
185
|
+
*/
|
|
186
|
+
sendTextMessage(body: SendTextMessageRequest, opts: RequestOptions): Promise<SendTextMessageResponse>;
|
|
187
|
+
/**
|
|
188
|
+
* Send an image message to a conversation
|
|
189
|
+
*/
|
|
190
|
+
sendImageMessage(body: SendImageMessageRequest, opts: RequestOptions): Promise<SendImageMessageResponse>;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
type WebhookEventType = "DIRECT_MESSAGE";
|
|
194
|
+
interface CreateWebhookRequest {
|
|
195
|
+
callback_url: string;
|
|
196
|
+
event_type: WebhookEventType;
|
|
197
|
+
}
|
|
198
|
+
interface CreateWebhookResponse {
|
|
199
|
+
app_id: string;
|
|
200
|
+
event_type: WebhookEventType;
|
|
201
|
+
callback_url: string;
|
|
202
|
+
}
|
|
203
|
+
interface GetWebhookRequest {
|
|
204
|
+
event_type: WebhookEventType;
|
|
205
|
+
}
|
|
206
|
+
interface GetWebhookResponse {
|
|
207
|
+
app_id: string;
|
|
208
|
+
event_type: WebhookEventType;
|
|
209
|
+
callback_url?: string;
|
|
210
|
+
}
|
|
211
|
+
interface DeleteWebhookRequest {
|
|
212
|
+
event_type: WebhookEventType;
|
|
213
|
+
}
|
|
214
|
+
interface DeleteWebhookResponse {
|
|
215
|
+
app_id: string;
|
|
216
|
+
event_type: WebhookEventType;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
declare class WebhookService {
|
|
220
|
+
private readonly config;
|
|
221
|
+
constructor(config: TikTokConfig);
|
|
222
|
+
create(params: CreateWebhookRequest): Promise<CreateWebhookResponse>;
|
|
223
|
+
get(params: GetWebhookRequest): Promise<GetWebhookResponse>;
|
|
224
|
+
delete(params: DeleteWebhookRequest): Promise<DeleteWebhookResponse>;
|
|
225
|
+
private post;
|
|
226
|
+
private fetch;
|
|
227
|
+
private parseResponse;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
declare class TikTokBusiness {
|
|
231
|
+
readonly auth: Auth;
|
|
232
|
+
readonly messaging: Messaging;
|
|
233
|
+
readonly media: MediaService;
|
|
234
|
+
readonly webhook: WebhookService;
|
|
235
|
+
constructor(config: TikTokConfig);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* TikTok Business API Error Codes
|
|
240
|
+
*/
|
|
241
|
+
declare enum TikTokErrorCode {
|
|
242
|
+
NO_PERMISSION = 40001,
|
|
243
|
+
PARAMETER_ERROR = 40002,
|
|
244
|
+
OBJECT_NOT_FOUND = 40007,
|
|
245
|
+
RATE_LIMIT_EXCEEDED = 40100,
|
|
246
|
+
INVALID_ACCESS_TOKEN = 40105,
|
|
247
|
+
UNSUPPORTED_FILE_TYPE = 40908,
|
|
248
|
+
MESSAGE_BLOCKED = 40064,
|
|
249
|
+
SYSTEM_ERROR = 51065,
|
|
250
|
+
CODE_EXPIRED = 40131
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* TikTok API Errors
|
|
255
|
+
*/
|
|
256
|
+
declare class TikTokApiError extends Error {
|
|
257
|
+
readonly code: TikTokErrorCode;
|
|
258
|
+
readonly requestId?: string | undefined;
|
|
259
|
+
readonly message: string;
|
|
260
|
+
constructor(code: TikTokErrorCode, requestId?: string | undefined);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export { type Conversation, type ConversationReferral, type ConversationStatus, type ConversationType, type ListConversationsRequest, type ListConversationsResponse, type ListMessagesRequest, type ListMessagesResponse, type Message, type MessageType, type RecipientType, type ReferralAd, type ReferralShortLink, type RefreshTokenResponse, type RevokeTokenResponse, type SendImageMessageRequest, type SendImageMessageResponse, type SendTextMessageRequest, type SendTextMessageResponse, type SenderAction, type TextContent, TikTokApiError, TikTokBusiness, type TikTokConfig, type TikTokTokenResponse };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
interface TikTokTokenResponse {
|
|
2
|
+
access_token: string;
|
|
3
|
+
refresh_token: string;
|
|
4
|
+
access_token_expire_in: number;
|
|
5
|
+
refresh_token_expire_in: number;
|
|
6
|
+
token_type: string;
|
|
7
|
+
scope: string;
|
|
8
|
+
open_id: string;
|
|
9
|
+
}
|
|
10
|
+
interface RefreshTokenResponse {
|
|
11
|
+
access_token: string;
|
|
12
|
+
access_token_expire_in: number;
|
|
13
|
+
refresh_token: string;
|
|
14
|
+
refresh_token_expire_in: number;
|
|
15
|
+
token_type: string;
|
|
16
|
+
scope: string;
|
|
17
|
+
open_id: string;
|
|
18
|
+
}
|
|
19
|
+
interface RevokeTokenResponse {
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface TikTokConfig {
|
|
23
|
+
clientKey: string;
|
|
24
|
+
clientSecret: string;
|
|
25
|
+
redirectUri?: string;
|
|
26
|
+
scopes?: string[];
|
|
27
|
+
baseUrl?: string;
|
|
28
|
+
timeout?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type MediaType = "IMAGE";
|
|
32
|
+
interface UploadMediaRequest {
|
|
33
|
+
business_id: string;
|
|
34
|
+
file: File | Blob;
|
|
35
|
+
media_type: MediaType;
|
|
36
|
+
}
|
|
37
|
+
interface MediaContent {
|
|
38
|
+
media_id: string;
|
|
39
|
+
}
|
|
40
|
+
type UploadMediaResponse = MediaContent;
|
|
41
|
+
|
|
42
|
+
type MessageType = "TEXT" | "SENDER_ACTION";
|
|
43
|
+
type SenderAction = "TYPING" | "MARK_READ";
|
|
44
|
+
type RecipientType = "CONVERSATION";
|
|
45
|
+
type ConversationStatus = "ACTIVE" | "ARCHIVED";
|
|
46
|
+
interface ListMessagesRequest {
|
|
47
|
+
business_id: string;
|
|
48
|
+
conversation_id: string;
|
|
49
|
+
}
|
|
50
|
+
interface Message {
|
|
51
|
+
message_id: string;
|
|
52
|
+
conversation_id: string;
|
|
53
|
+
message_type: MessageType;
|
|
54
|
+
create_time: number;
|
|
55
|
+
direction: "INBOUND" | "OUTBOUND";
|
|
56
|
+
status: "SENT" | "DELIVERED" | "READ" | "FAILED";
|
|
57
|
+
sender: {
|
|
58
|
+
open_id: string;
|
|
59
|
+
display_name: string;
|
|
60
|
+
avatar_url: string;
|
|
61
|
+
};
|
|
62
|
+
content: {
|
|
63
|
+
body: string;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
interface ListMessagesResponse {
|
|
67
|
+
messages: Message[];
|
|
68
|
+
next_cursor: string | null;
|
|
69
|
+
has_more: boolean;
|
|
70
|
+
total: number;
|
|
71
|
+
}
|
|
72
|
+
interface TextContent {
|
|
73
|
+
body: string;
|
|
74
|
+
}
|
|
75
|
+
interface SendTextMessageRequest {
|
|
76
|
+
conversation_id?: string;
|
|
77
|
+
business_id: string;
|
|
78
|
+
recipient: string;
|
|
79
|
+
message_type: MessageType;
|
|
80
|
+
recipient_type?: RecipientType;
|
|
81
|
+
text?: TextContent;
|
|
82
|
+
sender_action?: SenderAction;
|
|
83
|
+
}
|
|
84
|
+
interface SendTextMessageResponse {
|
|
85
|
+
message_id: string;
|
|
86
|
+
create_time: number;
|
|
87
|
+
}
|
|
88
|
+
type ConversationType = "STRANGER" | "SINGLE";
|
|
89
|
+
interface ReferralAd {
|
|
90
|
+
advertiser_id: string;
|
|
91
|
+
ad_id: string;
|
|
92
|
+
timestamp: number;
|
|
93
|
+
ad_name: string;
|
|
94
|
+
embed_url: string;
|
|
95
|
+
message_material_id?: string;
|
|
96
|
+
}
|
|
97
|
+
interface ReferralShortLink {
|
|
98
|
+
ref: string;
|
|
99
|
+
prefilled_message: string;
|
|
100
|
+
prefilled_message_audit_status: "PASS" | "REJECT";
|
|
101
|
+
}
|
|
102
|
+
interface ConversationReferral {
|
|
103
|
+
ad?: ReferralAd[];
|
|
104
|
+
short_link?: ReferralShortLink[];
|
|
105
|
+
}
|
|
106
|
+
interface Conversation {
|
|
107
|
+
conversation_id: string;
|
|
108
|
+
update_time: number;
|
|
109
|
+
referral: ConversationReferral;
|
|
110
|
+
}
|
|
111
|
+
interface ListConversationsRequest {
|
|
112
|
+
business_id: string;
|
|
113
|
+
cursor?: number;
|
|
114
|
+
conversation_type: ConversationType;
|
|
115
|
+
limit?: number;
|
|
116
|
+
}
|
|
117
|
+
interface ListConversationsResponse {
|
|
118
|
+
conversations: Conversation[];
|
|
119
|
+
has_more: boolean;
|
|
120
|
+
cursor: number;
|
|
121
|
+
}
|
|
122
|
+
interface SendImageMessageRequest {
|
|
123
|
+
business_id: string;
|
|
124
|
+
recipient_type: RecipientType;
|
|
125
|
+
message_type: "IMAGE";
|
|
126
|
+
recipient: string;
|
|
127
|
+
image: MediaContent;
|
|
128
|
+
}
|
|
129
|
+
interface SendImageMessageResponse {
|
|
130
|
+
message_id: string;
|
|
131
|
+
create_time: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
declare class Auth {
|
|
135
|
+
private readonly config;
|
|
136
|
+
constructor(config: TikTokConfig);
|
|
137
|
+
getTokens(authCode: string): Promise<TikTokTokenResponse>;
|
|
138
|
+
refreshTokens(refreshToken: string): Promise<RefreshTokenResponse>;
|
|
139
|
+
revokeToken(accessToken: string): Promise<RevokeTokenResponse>;
|
|
140
|
+
getAuthorizationUrl(state?: string): string;
|
|
141
|
+
private post;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
interface RequestOptions {
|
|
145
|
+
accessToken: string;
|
|
146
|
+
advertiserIds?: string[];
|
|
147
|
+
timeout?: number;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
declare class HttpClient {
|
|
151
|
+
private config;
|
|
152
|
+
private baseUrl;
|
|
153
|
+
constructor(config: TikTokConfig);
|
|
154
|
+
get<T>(path: string, params: object, opts: RequestOptions): Promise<T>;
|
|
155
|
+
post<T>(path: string, body: unknown, opts: RequestOptions): Promise<T>;
|
|
156
|
+
put<T>(path: string, body: unknown, opts: RequestOptions): Promise<T>;
|
|
157
|
+
patch<T>(path: string, body: unknown, opts: RequestOptions): Promise<T>;
|
|
158
|
+
delete<T>(path: string, params: object, opts: RequestOptions): Promise<T>;
|
|
159
|
+
upload<T>(path: string, form: FormData, opts: RequestOptions): Promise<T>;
|
|
160
|
+
private request;
|
|
161
|
+
private buildUrl;
|
|
162
|
+
private buildInit;
|
|
163
|
+
private parseResponse;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
declare class MediaService {
|
|
167
|
+
private readonly http;
|
|
168
|
+
constructor(http: HttpClient);
|
|
169
|
+
upload(params: UploadMediaRequest, opts: RequestOptions): Promise<UploadMediaResponse>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
declare class Messaging {
|
|
173
|
+
private readonly http;
|
|
174
|
+
constructor(http: HttpClient);
|
|
175
|
+
/**
|
|
176
|
+
* List all conversations for a business account
|
|
177
|
+
*/
|
|
178
|
+
listConversations(params: ListConversationsRequest, opts: RequestOptions): Promise<ListConversationsResponse>;
|
|
179
|
+
/**
|
|
180
|
+
* Get messages in a specific conversation
|
|
181
|
+
*/
|
|
182
|
+
listMessages(params: ListMessagesRequest, opts: RequestOptions): Promise<ListMessagesResponse>;
|
|
183
|
+
/**
|
|
184
|
+
* Send a text message to a conversation
|
|
185
|
+
*/
|
|
186
|
+
sendTextMessage(body: SendTextMessageRequest, opts: RequestOptions): Promise<SendTextMessageResponse>;
|
|
187
|
+
/**
|
|
188
|
+
* Send an image message to a conversation
|
|
189
|
+
*/
|
|
190
|
+
sendImageMessage(body: SendImageMessageRequest, opts: RequestOptions): Promise<SendImageMessageResponse>;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
type WebhookEventType = "DIRECT_MESSAGE";
|
|
194
|
+
interface CreateWebhookRequest {
|
|
195
|
+
callback_url: string;
|
|
196
|
+
event_type: WebhookEventType;
|
|
197
|
+
}
|
|
198
|
+
interface CreateWebhookResponse {
|
|
199
|
+
app_id: string;
|
|
200
|
+
event_type: WebhookEventType;
|
|
201
|
+
callback_url: string;
|
|
202
|
+
}
|
|
203
|
+
interface GetWebhookRequest {
|
|
204
|
+
event_type: WebhookEventType;
|
|
205
|
+
}
|
|
206
|
+
interface GetWebhookResponse {
|
|
207
|
+
app_id: string;
|
|
208
|
+
event_type: WebhookEventType;
|
|
209
|
+
callback_url?: string;
|
|
210
|
+
}
|
|
211
|
+
interface DeleteWebhookRequest {
|
|
212
|
+
event_type: WebhookEventType;
|
|
213
|
+
}
|
|
214
|
+
interface DeleteWebhookResponse {
|
|
215
|
+
app_id: string;
|
|
216
|
+
event_type: WebhookEventType;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
declare class WebhookService {
|
|
220
|
+
private readonly config;
|
|
221
|
+
constructor(config: TikTokConfig);
|
|
222
|
+
create(params: CreateWebhookRequest): Promise<CreateWebhookResponse>;
|
|
223
|
+
get(params: GetWebhookRequest): Promise<GetWebhookResponse>;
|
|
224
|
+
delete(params: DeleteWebhookRequest): Promise<DeleteWebhookResponse>;
|
|
225
|
+
private post;
|
|
226
|
+
private fetch;
|
|
227
|
+
private parseResponse;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
declare class TikTokBusiness {
|
|
231
|
+
readonly auth: Auth;
|
|
232
|
+
readonly messaging: Messaging;
|
|
233
|
+
readonly media: MediaService;
|
|
234
|
+
readonly webhook: WebhookService;
|
|
235
|
+
constructor(config: TikTokConfig);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* TikTok Business API Error Codes
|
|
240
|
+
*/
|
|
241
|
+
declare enum TikTokErrorCode {
|
|
242
|
+
NO_PERMISSION = 40001,
|
|
243
|
+
PARAMETER_ERROR = 40002,
|
|
244
|
+
OBJECT_NOT_FOUND = 40007,
|
|
245
|
+
RATE_LIMIT_EXCEEDED = 40100,
|
|
246
|
+
INVALID_ACCESS_TOKEN = 40105,
|
|
247
|
+
UNSUPPORTED_FILE_TYPE = 40908,
|
|
248
|
+
MESSAGE_BLOCKED = 40064,
|
|
249
|
+
SYSTEM_ERROR = 51065,
|
|
250
|
+
CODE_EXPIRED = 40131
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* TikTok API Errors
|
|
255
|
+
*/
|
|
256
|
+
declare class TikTokApiError extends Error {
|
|
257
|
+
readonly code: TikTokErrorCode;
|
|
258
|
+
readonly requestId?: string | undefined;
|
|
259
|
+
readonly message: string;
|
|
260
|
+
constructor(code: TikTokErrorCode, requestId?: string | undefined);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export { type Conversation, type ConversationReferral, type ConversationStatus, type ConversationType, type ListConversationsRequest, type ListConversationsResponse, type ListMessagesRequest, type ListMessagesResponse, type Message, type MessageType, type RecipientType, type ReferralAd, type ReferralShortLink, type RefreshTokenResponse, type RevokeTokenResponse, type SendImageMessageRequest, type SendImageMessageResponse, type SendTextMessageRequest, type SendTextMessageResponse, type SenderAction, type TextContent, TikTokApiError, TikTokBusiness, type TikTokConfig, type TikTokTokenResponse };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// src/constants.ts
|
|
2
|
+
var TIKTOK_API_BASE_URL = "https://business-api.tiktok.com/open_api/v1.3";
|
|
3
|
+
var TIKTOK_AUTH_BASE_URL = `${TIKTOK_API_BASE_URL}/tt_user/oauth2`;
|
|
4
|
+
var TIKTOK_ERROR_MESSAGES = {
|
|
5
|
+
[40001 /* NO_PERMISSION */]: "No permission to perform the related operation. Ensure you have the necessary scopes.",
|
|
6
|
+
[40002 /* PARAMETER_ERROR */]: "Parameter error. Check missing fields or incorrect formats in your request.",
|
|
7
|
+
[40007 /* OBJECT_NOT_FOUND */]: "The operation or object does not exist. Check your IDs or URLs.",
|
|
8
|
+
[40100 /* RATE_LIMIT_EXCEEDED */]: "Requests made too frequently. You are being throttled.",
|
|
9
|
+
[40105 /* INVALID_ACCESS_TOKEN */]: "Invalid or incorrect access token. Please refresh or re-authorize.",
|
|
10
|
+
[40908 /* UNSUPPORTED_FILE_TYPE */]: "The file type is unsupported. Refer to documentation for allowed formats.",
|
|
11
|
+
[40064 /* MESSAGE_BLOCKED */]: "The message is blocked due to TikTok direct message rules.",
|
|
12
|
+
[51065 /* SYSTEM_ERROR */]: "TikTok internal system error. Please retry the request.",
|
|
13
|
+
[40131 /* CODE_EXPIRED */]: "The authorization code has expired. Please re-authorize to get a new code."
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// src/error.ts
|
|
17
|
+
var TikTokApiError = class _TikTokApiError extends Error {
|
|
18
|
+
constructor(code, requestId) {
|
|
19
|
+
const friendlyMessage = TIKTOK_ERROR_MESSAGES[code] || "An unknown TikTok API error occurred.";
|
|
20
|
+
super(`TikTok API Error [${code}]: ${friendlyMessage}`);
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.requestId = requestId;
|
|
23
|
+
this.name = "TikTokApiError";
|
|
24
|
+
this.message = friendlyMessage;
|
|
25
|
+
Object.setPrototypeOf(this, _TikTokApiError.prototype);
|
|
26
|
+
}
|
|
27
|
+
message;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/http.ts
|
|
31
|
+
var HttpClient = class {
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.config = config;
|
|
34
|
+
this.baseUrl = config.baseUrl ?? "https://business-api.tiktok.com/open_api/v1.3";
|
|
35
|
+
}
|
|
36
|
+
baseUrl;
|
|
37
|
+
get(path, params, opts) {
|
|
38
|
+
return this.request({ method: "GET", path, params, opts });
|
|
39
|
+
}
|
|
40
|
+
post(path, body, opts) {
|
|
41
|
+
return this.request({ method: "POST", path, body, opts });
|
|
42
|
+
}
|
|
43
|
+
put(path, body, opts) {
|
|
44
|
+
return this.request({ method: "PUT", path, body, opts });
|
|
45
|
+
}
|
|
46
|
+
patch(path, body, opts) {
|
|
47
|
+
return this.request({ method: "PATCH", path, body, opts });
|
|
48
|
+
}
|
|
49
|
+
delete(path, params, opts) {
|
|
50
|
+
return this.request({ method: "DELETE", path, params, opts });
|
|
51
|
+
}
|
|
52
|
+
upload(path, form, opts) {
|
|
53
|
+
return this.request({ method: "POST", path, form, opts });
|
|
54
|
+
}
|
|
55
|
+
async request({
|
|
56
|
+
method,
|
|
57
|
+
path,
|
|
58
|
+
params,
|
|
59
|
+
body,
|
|
60
|
+
form,
|
|
61
|
+
opts
|
|
62
|
+
}) {
|
|
63
|
+
const res = await fetch(
|
|
64
|
+
this.buildUrl(path, params),
|
|
65
|
+
this.buildInit(method, body, opts, form)
|
|
66
|
+
);
|
|
67
|
+
return this.parseResponse(res);
|
|
68
|
+
}
|
|
69
|
+
buildUrl(path, params) {
|
|
70
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
71
|
+
if (params) {
|
|
72
|
+
Object.entries(params).forEach(([k, v]) => {
|
|
73
|
+
if (v !== void 0 && v !== null && v !== "") {
|
|
74
|
+
url.searchParams.set(k, String(v));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return url.toString();
|
|
79
|
+
}
|
|
80
|
+
buildInit(method, body, opts, form) {
|
|
81
|
+
const headers = {
|
|
82
|
+
"Access-Token": opts.accessToken
|
|
83
|
+
};
|
|
84
|
+
if (!form) {
|
|
85
|
+
headers["Content-Type"] = "application/json";
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
method,
|
|
89
|
+
signal: AbortSignal.timeout(
|
|
90
|
+
opts.timeout ?? this.config.timeout ?? 1e4
|
|
91
|
+
),
|
|
92
|
+
headers,
|
|
93
|
+
...form ? { body: form } : body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
async parseResponse(res) {
|
|
97
|
+
console.debug(`[HttpClient] Parsing response for URL: ${res.url}`);
|
|
98
|
+
console.debug(
|
|
99
|
+
`[HttpClient] Response status: ${res.status} ${res.statusText}`
|
|
100
|
+
);
|
|
101
|
+
console.debug(`[HttpClient] Response data:`, await res.clone().text());
|
|
102
|
+
const json = await res.json();
|
|
103
|
+
if (json.code !== 0) {
|
|
104
|
+
throw new TikTokApiError(json.code, json.request_id);
|
|
105
|
+
}
|
|
106
|
+
return json.data;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// src/services/auth.ts
|
|
111
|
+
var Auth = class {
|
|
112
|
+
constructor(config) {
|
|
113
|
+
this.config = config;
|
|
114
|
+
}
|
|
115
|
+
getTokens(authCode) {
|
|
116
|
+
return this.post("/token/", {
|
|
117
|
+
client_id: this.config.clientKey,
|
|
118
|
+
client_secret: this.config.clientSecret,
|
|
119
|
+
grant_type: "authorization_code",
|
|
120
|
+
auth_code: decodeURIComponent(authCode),
|
|
121
|
+
redirect_uri: this.config.redirectUri
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
refreshTokens(refreshToken) {
|
|
125
|
+
return this.post("/refresh_token/", {
|
|
126
|
+
client_id: this.config.clientKey,
|
|
127
|
+
client_secret: this.config.clientSecret,
|
|
128
|
+
grant_type: "refresh_token",
|
|
129
|
+
refresh_token: refreshToken
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
revokeToken(accessToken) {
|
|
133
|
+
return this.post("/token/revoke/", {
|
|
134
|
+
client_id: this.config.clientKey,
|
|
135
|
+
client_secret: this.config.clientSecret,
|
|
136
|
+
token: accessToken
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
// for tt_users
|
|
140
|
+
getAuthorizationUrl(state) {
|
|
141
|
+
const params = new URLSearchParams({
|
|
142
|
+
client_key: this.config.clientKey,
|
|
143
|
+
response_type: "code",
|
|
144
|
+
scope: (this.config.scopes ?? []).join(","),
|
|
145
|
+
redirect_uri: this.config.redirectUri ?? "",
|
|
146
|
+
...state && { state }
|
|
147
|
+
});
|
|
148
|
+
return `https://www.tiktok.com/v2/auth/authorize/?${params.toString()}`;
|
|
149
|
+
}
|
|
150
|
+
async post(path, body) {
|
|
151
|
+
const response = await fetch(`${TIKTOK_AUTH_BASE_URL}${path}`, {
|
|
152
|
+
method: "POST",
|
|
153
|
+
headers: { "Content-Type": "application/json" },
|
|
154
|
+
body: JSON.stringify(body)
|
|
155
|
+
});
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
throw new TikTokApiError(response.status, response.statusText);
|
|
158
|
+
}
|
|
159
|
+
const json = await response.json();
|
|
160
|
+
if (json.code !== 0) {
|
|
161
|
+
throw new TikTokApiError(json.code, json.request_id);
|
|
162
|
+
}
|
|
163
|
+
return json.data;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// src/services/media.ts
|
|
168
|
+
var MediaService = class {
|
|
169
|
+
constructor(http) {
|
|
170
|
+
this.http = http;
|
|
171
|
+
}
|
|
172
|
+
upload(params, opts) {
|
|
173
|
+
const form = new FormData();
|
|
174
|
+
form.append("business_id", params.business_id);
|
|
175
|
+
form.append("file", params.file);
|
|
176
|
+
form.append("media_type", params.media_type);
|
|
177
|
+
return this.http.upload("/business/message/media/upload/", form, opts);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// src/services/messaging.ts
|
|
182
|
+
var Messaging = class {
|
|
183
|
+
constructor(http) {
|
|
184
|
+
this.http = http;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* List all conversations for a business account
|
|
188
|
+
*/
|
|
189
|
+
listConversations(params, opts) {
|
|
190
|
+
console.debug("[Messaging] listConversations called", { params });
|
|
191
|
+
return this.http.get("/business/message/conversation/list/", params, opts).then((response) => {
|
|
192
|
+
console.debug("[Messaging] listConversations succeeded");
|
|
193
|
+
return response;
|
|
194
|
+
}).catch((error) => {
|
|
195
|
+
console.error("[Messaging] listConversations failed", error);
|
|
196
|
+
throw error;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get messages in a specific conversation
|
|
201
|
+
*/
|
|
202
|
+
listMessages(params, opts) {
|
|
203
|
+
console.log("Conversation ID:", encodeURIComponent(params.conversation_id));
|
|
204
|
+
return this.http.get(
|
|
205
|
+
"/business/message/content/list/",
|
|
206
|
+
{
|
|
207
|
+
...params
|
|
208
|
+
},
|
|
209
|
+
opts
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Send a text message to a conversation
|
|
214
|
+
*/
|
|
215
|
+
sendTextMessage(body, opts) {
|
|
216
|
+
const { ...restBody } = body;
|
|
217
|
+
return this.http.post(
|
|
218
|
+
"/business/message/send/",
|
|
219
|
+
{ recipient_type: "CONVERSATION", ...restBody },
|
|
220
|
+
opts
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Send an image message to a conversation
|
|
225
|
+
*/
|
|
226
|
+
sendImageMessage(body, opts) {
|
|
227
|
+
return this.http.post(
|
|
228
|
+
"/business/message/send/",
|
|
229
|
+
{
|
|
230
|
+
...body
|
|
231
|
+
},
|
|
232
|
+
opts
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// src/services/webhook.ts
|
|
238
|
+
var WebhookService = class {
|
|
239
|
+
constructor(config) {
|
|
240
|
+
this.config = config;
|
|
241
|
+
}
|
|
242
|
+
create(params) {
|
|
243
|
+
return this.post("/business/webhook/update/", {
|
|
244
|
+
event_type: params.event_type,
|
|
245
|
+
callback_url: params.callback_url
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
get(params) {
|
|
249
|
+
return this.fetch("/business/webhook/list/", params.event_type);
|
|
250
|
+
}
|
|
251
|
+
delete(params) {
|
|
252
|
+
return this.post("/business/webhook/delete/", {
|
|
253
|
+
event_type: params.event_type
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
async post(path, body) {
|
|
257
|
+
console.log("called post");
|
|
258
|
+
const response = await fetch(`${TIKTOK_API_BASE_URL}${path}`, {
|
|
259
|
+
method: "POST",
|
|
260
|
+
headers: { "Content-Type": "application/json" },
|
|
261
|
+
body: JSON.stringify({
|
|
262
|
+
app_id: this.config.clientKey,
|
|
263
|
+
secret: this.config.clientSecret,
|
|
264
|
+
...body
|
|
265
|
+
})
|
|
266
|
+
});
|
|
267
|
+
console.log(response.status, await response.text());
|
|
268
|
+
return this.parseResponse(response);
|
|
269
|
+
}
|
|
270
|
+
async fetch(path, event_type) {
|
|
271
|
+
const url = new URL(`${TIKTOK_API_BASE_URL}${path}`);
|
|
272
|
+
url.searchParams.set("app_id", this.config.clientKey);
|
|
273
|
+
url.searchParams.set("secret", this.config.clientSecret);
|
|
274
|
+
url.searchParams.set("event_type", event_type);
|
|
275
|
+
const response = await globalThis.fetch(url.toString());
|
|
276
|
+
return this.parseResponse(response);
|
|
277
|
+
}
|
|
278
|
+
async parseResponse(response) {
|
|
279
|
+
if (!response.ok) {
|
|
280
|
+
throw new TikTokApiError(response.status, response.statusText);
|
|
281
|
+
}
|
|
282
|
+
const json = await response.json();
|
|
283
|
+
if (json.code !== 0) {
|
|
284
|
+
throw new TikTokApiError(json.code, json.request_id);
|
|
285
|
+
}
|
|
286
|
+
return json.data;
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// src/client.ts
|
|
291
|
+
var TikTokBusiness = class {
|
|
292
|
+
auth;
|
|
293
|
+
messaging;
|
|
294
|
+
media;
|
|
295
|
+
webhook;
|
|
296
|
+
constructor(config) {
|
|
297
|
+
const http = new HttpClient(config);
|
|
298
|
+
this.auth = new Auth(config);
|
|
299
|
+
this.messaging = new Messaging(http);
|
|
300
|
+
this.media = new MediaService(http);
|
|
301
|
+
this.webhook = new WebhookService(config);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
export {
|
|
305
|
+
TikTokApiError,
|
|
306
|
+
TikTokBusiness
|
|
307
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tiktok-business-messaging",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "TypeScript API Client for Tiktokbusiness API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
14
|
+
"dev": "tsup src/index.ts --format cjs,esm --watch --dts",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"test-watch": "vitest",
|
|
17
|
+
"lint": "tsc",
|
|
18
|
+
"ci": "pnpm run lint && pnpm run test && pnpm run build"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [],
|
|
21
|
+
"author": "Pshishir",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"packageManager": "pnpm@10.30.0",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^25.5.0",
|
|
26
|
+
"tsup": "^8.5.1",
|
|
27
|
+
"typescript": "^5.9.3",
|
|
28
|
+
"vitest": "^4.0.18"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"tsx": "^4.21.0"
|
|
32
|
+
}
|
|
33
|
+
}
|