@warriorteam/redai-zalo-sdk 1.12.0 → 1.12.2
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/ARCHITECTURE.md +265 -0
- package/CHANGELOG.md +0 -57
- package/README.md +1 -33
- package/SERVICES_ADDED.md +540 -0
- package/UPDATE_ARTICLE_STATUS.md +152 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -8
- package/dist/index.js.map +1 -1
- package/dist/types/webhook.d.ts +19 -25
- package/dist/types/webhook.d.ts.map +1 -1
- package/dist/types/webhook.js +1 -110
- package/dist/types/webhook.js.map +1 -1
- package/dist/utils/type-guards.d.ts +96 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +192 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/docs/ARTICLE_MANAGEMENT.md +395 -395
- package/docs/AUTHENTICATION.md +852 -853
- package/docs/CONSULTATION_SERVICE.md +512 -512
- package/docs/GROUP_MANAGEMENT.md +232 -232
- package/docs/USER_MANAGEMENT.md +481 -481
- package/docs/WEBHOOK_EVENTS.md +858 -858
- package/examples/article-status-update.ts +178 -178
- package/examples/oa-auth-with-pkce.ts +179 -179
- package/examples/user-list-post-example.ts +186 -186
- package/examples/video-upload-combined.example.ts +228 -228
- package/examples/zns-template-edit.example.ts +317 -317
- package/package.json +1 -1
- package/docs/WEBHOOK_MESSAGE_HELPERS.md +0 -230
- package/examples/webhook-message-classification.ts +0 -285
|
@@ -1,395 +1,395 @@
|
|
|
1
|
-
# Article Management Service
|
|
2
|
-
|
|
3
|
-
The `ArticleService` and `VideoUploadService` provide comprehensive article management capabilities for Zalo Official Account (OA).
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
### ArticleService
|
|
8
|
-
- ✅ Create normal articles with rich content
|
|
9
|
-
- ✅ Create video articles
|
|
10
|
-
- ✅ Update existing articles
|
|
11
|
-
- ✅ **Update article status only** (new feature)
|
|
12
|
-
- ✅ Get article details and lists
|
|
13
|
-
- ✅ Remove articles
|
|
14
|
-
- ✅ Track article creation/update progress
|
|
15
|
-
- ✅ Comprehensive validation
|
|
16
|
-
|
|
17
|
-
### VideoUploadService
|
|
18
|
-
- ✅ Upload video files for articles
|
|
19
|
-
- ✅ Check video processing status
|
|
20
|
-
- ✅ Wait for upload completion with polling
|
|
21
|
-
- ✅ Upload videos from URLs
|
|
22
|
-
- ✅ Get video information
|
|
23
|
-
|
|
24
|
-
## Prerequisites
|
|
25
|
-
|
|
26
|
-
1. **OA Requirements:**
|
|
27
|
-
- OA must have permission to create articles
|
|
28
|
-
- Access token must have "manage_article" scope
|
|
29
|
-
- OA must have active status and be verified
|
|
30
|
-
|
|
31
|
-
2. **Content Limits:**
|
|
32
|
-
- Title: max 150 characters
|
|
33
|
-
- Author: max 50 characters (normal articles only)
|
|
34
|
-
- Description: max 300 characters
|
|
35
|
-
- Image size: max 1MB per image
|
|
36
|
-
- Video size: max 50MB per video
|
|
37
|
-
- Video formats: MP4, AVI only
|
|
38
|
-
|
|
39
|
-
## Usage
|
|
40
|
-
|
|
41
|
-
### Initialize SDK
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
import { ZaloSDK } from 'redai-zalo-sdk';
|
|
45
|
-
|
|
46
|
-
const sdk = new ZaloSDK({
|
|
47
|
-
appId: 'your_app_id',
|
|
48
|
-
appSecret: 'your_app_secret',
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const article = sdk.article;
|
|
52
|
-
const videoUpload = sdk.videoUpload;
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### 1. Create Normal Article
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
import { ArticleType, ArticleStatus, CommentStatus, CoverType, BodyItemType } from 'redai-zalo-sdk';
|
|
59
|
-
|
|
60
|
-
const normalArticle = {
|
|
61
|
-
type: ArticleType.NORMAL,
|
|
62
|
-
title: "Welcome to Our Service",
|
|
63
|
-
author: "RedAI Team",
|
|
64
|
-
description: "This is a comprehensive guide to our new features",
|
|
65
|
-
cover: {
|
|
66
|
-
cover_type: CoverType.PHOTO,
|
|
67
|
-
photo_url: "https://example.com/cover.jpg",
|
|
68
|
-
status: ArticleStatus.SHOW
|
|
69
|
-
},
|
|
70
|
-
body: [
|
|
71
|
-
{
|
|
72
|
-
type: BodyItemType.TEXT,
|
|
73
|
-
content: "Welcome to our new service! Here's what you need to know..."
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
type: BodyItemType.IMAGE,
|
|
77
|
-
url: "https://example.com/image1.jpg"
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
type: BodyItemType.TEXT,
|
|
81
|
-
content: "For more information, please contact us."
|
|
82
|
-
}
|
|
83
|
-
],
|
|
84
|
-
tracking_link: "https://your-website.com/tracking",
|
|
85
|
-
status: ArticleStatus.SHOW,
|
|
86
|
-
comment: CommentStatus.SHOW
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const result = await article.createNormalArticle(accessToken, normalArticle);
|
|
90
|
-
console.log(`Article creation token: ${result.token}`);
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### 2. Create Video Article
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
const videoArticle = {
|
|
97
|
-
type: ArticleType.VIDEO,
|
|
98
|
-
title: "Product Demo Video",
|
|
99
|
-
description: "Watch our latest product demonstration",
|
|
100
|
-
video_id: "your_video_id", // Get from video upload
|
|
101
|
-
avatar: "https://example.com/thumbnail.jpg",
|
|
102
|
-
status: ArticleStatus.SHOW,
|
|
103
|
-
comment: CommentStatus.SHOW
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const result = await article.createVideoArticle(accessToken, videoArticle);
|
|
107
|
-
console.log(`Video article creation token: ${result.token}`);
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### 3. Upload Video for Articles
|
|
111
|
-
|
|
112
|
-
```typescript
|
|
113
|
-
// Upload video file
|
|
114
|
-
const videoFile = new File([videoBuffer], 'demo.mp4', { type: 'video/mp4' });
|
|
115
|
-
const uploadResult = await videoUpload.uploadVideo(accessToken, videoFile);
|
|
116
|
-
|
|
117
|
-
// Wait for processing completion
|
|
118
|
-
const finalResult = await videoUpload.waitForUploadCompletion(
|
|
119
|
-
accessToken,
|
|
120
|
-
uploadResult.token,
|
|
121
|
-
5 * 60 * 1000, // 5 minutes timeout
|
|
122
|
-
5 * 1000 // 5 seconds polling interval
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
console.log(`Video ID: ${finalResult.video_id}`);
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### 4. Upload Video from URL
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
const uploadResult = await videoUpload.uploadVideoFromUrl(
|
|
132
|
-
accessToken,
|
|
133
|
-
'https://example.com/video.mp4'
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
const finalResult = await videoUpload.waitForUploadCompletion(
|
|
137
|
-
accessToken,
|
|
138
|
-
uploadResult.token
|
|
139
|
-
);
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### 5. Check Article Creation Progress
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
const progress = await article.checkArticleProcess(accessToken, token);
|
|
146
|
-
|
|
147
|
-
switch (progress.status) {
|
|
148
|
-
case 'processing':
|
|
149
|
-
console.log('Article is still being processed...');
|
|
150
|
-
break;
|
|
151
|
-
case 'success':
|
|
152
|
-
console.log(`Article created with ID: ${progress.article_id}`);
|
|
153
|
-
break;
|
|
154
|
-
case 'failed':
|
|
155
|
-
console.error(`Article creation failed: ${progress.error_message}`);
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### 6. Get Article Details
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
const articleDetail = await article.getArticleDetail(accessToken, articleId);
|
|
164
|
-
console.log('Article details:', articleDetail);
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### 7. Get Article List
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
const articleList = await article.getArticleList(accessToken, {
|
|
171
|
-
offset: 0,
|
|
172
|
-
limit: 20,
|
|
173
|
-
type: 'normal' // or 'video'
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
console.log(`Found ${articleList.data.total} articles`);
|
|
177
|
-
articleList.data.articles.forEach(article => {
|
|
178
|
-
console.log(`- ${article.title} (${article.total_view} views)`);
|
|
179
|
-
});
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### 8. Update Article
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
const updateData = {
|
|
186
|
-
id: articleId,
|
|
187
|
-
type: ArticleType.NORMAL,
|
|
188
|
-
title: "Updated Article Title",
|
|
189
|
-
author: "Updated Author",
|
|
190
|
-
description: "Updated description",
|
|
191
|
-
cover: {
|
|
192
|
-
cover_type: CoverType.PHOTO,
|
|
193
|
-
photo_url: "https://example.com/new-cover.jpg",
|
|
194
|
-
status: ArticleStatus.SHOW
|
|
195
|
-
},
|
|
196
|
-
body: [
|
|
197
|
-
{
|
|
198
|
-
type: BodyItemType.TEXT,
|
|
199
|
-
content: "Updated content..."
|
|
200
|
-
}
|
|
201
|
-
],
|
|
202
|
-
status: ArticleStatus.SHOW
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
const updateResult = await article.updateNormalArticle(accessToken, updateData);
|
|
206
|
-
console.log(`Update token: ${updateResult.token}`);
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### 9. Remove Article
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
const removeResult = await article.removeArticle(accessToken, articleId);
|
|
213
|
-
console.log(removeResult.message);
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### 10. Check Video Status
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
const videoStatus = await videoUpload.checkVideoStatus(accessToken, token);
|
|
220
|
-
|
|
221
|
-
console.log(`Status: ${videoStatus.status}`);
|
|
222
|
-
console.log(`Progress: ${videoStatus.convert_percent}%`);
|
|
223
|
-
|
|
224
|
-
if (videoStatus.video_id) {
|
|
225
|
-
console.log(`Video ID: ${videoStatus.video_id}`);
|
|
226
|
-
}
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
## Error Handling
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
import { ZaloSDKError } from 'redai-zalo-sdk';
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
const result = await article.createNormalArticle(accessToken, articleData);
|
|
236
|
-
console.log('Success:', result);
|
|
237
|
-
} catch (error) {
|
|
238
|
-
if (error instanceof ZaloSDKError) {
|
|
239
|
-
console.error('Zalo API Error:', error.message, error.code);
|
|
240
|
-
console.error('Details:', error.details);
|
|
241
|
-
} else {
|
|
242
|
-
console.error('Unexpected error:', error);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
## Complete Example
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
import { ZaloSDK, ArticleType, CoverType, BodyItemType, ArticleStatus } from 'redai-zalo-sdk';
|
|
251
|
-
|
|
252
|
-
class ArticleManager {
|
|
253
|
-
private sdk: ZaloSDK;
|
|
254
|
-
|
|
255
|
-
constructor() {
|
|
256
|
-
this.sdk = new ZaloSDK({
|
|
257
|
-
appId: process.env.ZALO_APP_ID!,
|
|
258
|
-
appSecret: process.env.ZALO_APP_SECRET!,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
async createCompleteArticle(accessToken: string) {
|
|
263
|
-
try {
|
|
264
|
-
// 1. Upload video first (if needed)
|
|
265
|
-
const videoFile = new File([videoBuffer], 'demo.mp4');
|
|
266
|
-
const videoUpload = await this.sdk.videoUpload.uploadVideo(accessToken, videoFile);
|
|
267
|
-
const videoResult = await this.sdk.videoUpload.waitForUploadCompletion(
|
|
268
|
-
accessToken,
|
|
269
|
-
videoUpload.token
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
// 2. Create article with video
|
|
273
|
-
const article = await this.sdk.article.createNormalArticle(accessToken, {
|
|
274
|
-
type: ArticleType.NORMAL,
|
|
275
|
-
title: "Complete Guide",
|
|
276
|
-
author: "RedAI Team",
|
|
277
|
-
description: "A complete guide with video content",
|
|
278
|
-
cover: {
|
|
279
|
-
cover_type: CoverType.PHOTO,
|
|
280
|
-
photo_url: "https://example.com/cover.jpg",
|
|
281
|
-
status: ArticleStatus.SHOW
|
|
282
|
-
},
|
|
283
|
-
body: [
|
|
284
|
-
{
|
|
285
|
-
type: BodyItemType.TEXT,
|
|
286
|
-
content: "Introduction to our service..."
|
|
287
|
-
},
|
|
288
|
-
{
|
|
289
|
-
type: BodyItemType.VIDEO,
|
|
290
|
-
video_id: videoResult.video_id
|
|
291
|
-
},
|
|
292
|
-
{
|
|
293
|
-
type: BodyItemType.TEXT,
|
|
294
|
-
content: "Thank you for watching!"
|
|
295
|
-
}
|
|
296
|
-
],
|
|
297
|
-
status: ArticleStatus.SHOW
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
// 3. Wait for article creation
|
|
301
|
-
const articleProgress = await this.sdk.article.checkArticleProcess(
|
|
302
|
-
accessToken,
|
|
303
|
-
article.token
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
return articleProgress;
|
|
307
|
-
} catch (error) {
|
|
308
|
-
console.error('Article creation error:', error);
|
|
309
|
-
throw error;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
### Update Article Status
|
|
316
|
-
|
|
317
|
-
Update only the status of an existing article without modifying other content:
|
|
318
|
-
|
|
319
|
-
```typescript
|
|
320
|
-
import { ArticleStatus, CommentStatus } from 'redai-zalo-sdk';
|
|
321
|
-
|
|
322
|
-
// Update article status to 'show' (publish)
|
|
323
|
-
const publishResult = await article.updateArticleStatus(
|
|
324
|
-
accessToken,
|
|
325
|
-
'article_id',
|
|
326
|
-
ArticleStatus.SHOW
|
|
327
|
-
);
|
|
328
|
-
|
|
329
|
-
// Update article status to 'hide' and disable comments
|
|
330
|
-
const hideResult = await article.updateArticleStatus(
|
|
331
|
-
accessToken,
|
|
332
|
-
'article_id',
|
|
333
|
-
ArticleStatus.HIDE,
|
|
334
|
-
CommentStatus.HIDE
|
|
335
|
-
);
|
|
336
|
-
|
|
337
|
-
// Check update progress
|
|
338
|
-
const progress = await article.checkArticleProcess(
|
|
339
|
-
accessToken,
|
|
340
|
-
publishResult.token
|
|
341
|
-
);
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
**Method Signature:**
|
|
345
|
-
```typescript
|
|
346
|
-
updateArticleStatus(
|
|
347
|
-
accessToken: string,
|
|
348
|
-
articleId: string,
|
|
349
|
-
status: ArticleStatus,
|
|
350
|
-
comment?: CommentStatus
|
|
351
|
-
): Promise<ArticleUpdateResponse>
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
**Parameters:**
|
|
355
|
-
- `accessToken`: OA access token
|
|
356
|
-
- `articleId`: ID of the article to update
|
|
357
|
-
- `status`: New article status (`'show'` or `'hide'`)
|
|
358
|
-
- `comment`: Optional comment status (`'show'` or `'hide'`)
|
|
359
|
-
|
|
360
|
-
**How it works:**
|
|
361
|
-
1. Retrieves current article details using `getArticleDetail()`
|
|
362
|
-
2. Preserves all existing content (title, body, cover, etc.)
|
|
363
|
-
3. Updates only the status and optionally comment status
|
|
364
|
-
4. Calls the appropriate update method based on article type
|
|
365
|
-
|
|
366
|
-
**Benefits:**
|
|
367
|
-
- ✅ Simple status-only updates without content modification
|
|
368
|
-
- ✅ Preserves all existing article data
|
|
369
|
-
- ✅ Automatic type detection (normal vs video articles)
|
|
370
|
-
- ✅ Optional comment status control
|
|
371
|
-
- ✅ Same progress tracking as full updates
|
|
372
|
-
|
|
373
|
-
## Validation Rules
|
|
374
|
-
|
|
375
|
-
### Normal Articles
|
|
376
|
-
- Title: required, max 150 characters
|
|
377
|
-
- Author: required, max 50 characters
|
|
378
|
-
- Description: required, max 300 characters
|
|
379
|
-
- Cover: required, must specify type and corresponding URL/ID
|
|
380
|
-
- Body: required, at least one item
|
|
381
|
-
|
|
382
|
-
### Video Articles
|
|
383
|
-
- Title: required, max 150 characters
|
|
384
|
-
- Description: required, max 300 characters
|
|
385
|
-
- Video ID: required, must be valid uploaded video
|
|
386
|
-
- Avatar: required, thumbnail URL
|
|
387
|
-
|
|
388
|
-
### Video Files
|
|
389
|
-
- Formats: MP4, AVI only
|
|
390
|
-
- Size: max 50MB
|
|
391
|
-
- Processing: asynchronous, use polling to check status
|
|
392
|
-
|
|
393
|
-
## API Reference
|
|
394
|
-
|
|
395
|
-
For detailed API documentation, see the TypeScript definitions in the source code. All methods include comprehensive JSDoc comments with parameter descriptions and return types.
|
|
1
|
+
# Article Management Service
|
|
2
|
+
|
|
3
|
+
The `ArticleService` and `VideoUploadService` provide comprehensive article management capabilities for Zalo Official Account (OA).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
### ArticleService
|
|
8
|
+
- ✅ Create normal articles with rich content
|
|
9
|
+
- ✅ Create video articles
|
|
10
|
+
- ✅ Update existing articles
|
|
11
|
+
- ✅ **Update article status only** (new feature)
|
|
12
|
+
- ✅ Get article details and lists
|
|
13
|
+
- ✅ Remove articles
|
|
14
|
+
- ✅ Track article creation/update progress
|
|
15
|
+
- ✅ Comprehensive validation
|
|
16
|
+
|
|
17
|
+
### VideoUploadService
|
|
18
|
+
- ✅ Upload video files for articles
|
|
19
|
+
- ✅ Check video processing status
|
|
20
|
+
- ✅ Wait for upload completion with polling
|
|
21
|
+
- ✅ Upload videos from URLs
|
|
22
|
+
- ✅ Get video information
|
|
23
|
+
|
|
24
|
+
## Prerequisites
|
|
25
|
+
|
|
26
|
+
1. **OA Requirements:**
|
|
27
|
+
- OA must have permission to create articles
|
|
28
|
+
- Access token must have "manage_article" scope
|
|
29
|
+
- OA must have active status and be verified
|
|
30
|
+
|
|
31
|
+
2. **Content Limits:**
|
|
32
|
+
- Title: max 150 characters
|
|
33
|
+
- Author: max 50 characters (normal articles only)
|
|
34
|
+
- Description: max 300 characters
|
|
35
|
+
- Image size: max 1MB per image
|
|
36
|
+
- Video size: max 50MB per video
|
|
37
|
+
- Video formats: MP4, AVI only
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
### Initialize SDK
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { ZaloSDK } from 'redai-zalo-sdk';
|
|
45
|
+
|
|
46
|
+
const sdk = new ZaloSDK({
|
|
47
|
+
appId: 'your_app_id',
|
|
48
|
+
appSecret: 'your_app_secret',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const article = sdk.article;
|
|
52
|
+
const videoUpload = sdk.videoUpload;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 1. Create Normal Article
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { ArticleType, ArticleStatus, CommentStatus, CoverType, BodyItemType } from 'redai-zalo-sdk';
|
|
59
|
+
|
|
60
|
+
const normalArticle = {
|
|
61
|
+
type: ArticleType.NORMAL,
|
|
62
|
+
title: "Welcome to Our Service",
|
|
63
|
+
author: "RedAI Team",
|
|
64
|
+
description: "This is a comprehensive guide to our new features",
|
|
65
|
+
cover: {
|
|
66
|
+
cover_type: CoverType.PHOTO,
|
|
67
|
+
photo_url: "https://example.com/cover.jpg",
|
|
68
|
+
status: ArticleStatus.SHOW
|
|
69
|
+
},
|
|
70
|
+
body: [
|
|
71
|
+
{
|
|
72
|
+
type: BodyItemType.TEXT,
|
|
73
|
+
content: "Welcome to our new service! Here's what you need to know..."
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
type: BodyItemType.IMAGE,
|
|
77
|
+
url: "https://example.com/image1.jpg"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: BodyItemType.TEXT,
|
|
81
|
+
content: "For more information, please contact us."
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
tracking_link: "https://your-website.com/tracking",
|
|
85
|
+
status: ArticleStatus.SHOW,
|
|
86
|
+
comment: CommentStatus.SHOW
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const result = await article.createNormalArticle(accessToken, normalArticle);
|
|
90
|
+
console.log(`Article creation token: ${result.token}`);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 2. Create Video Article
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
const videoArticle = {
|
|
97
|
+
type: ArticleType.VIDEO,
|
|
98
|
+
title: "Product Demo Video",
|
|
99
|
+
description: "Watch our latest product demonstration",
|
|
100
|
+
video_id: "your_video_id", // Get from video upload
|
|
101
|
+
avatar: "https://example.com/thumbnail.jpg",
|
|
102
|
+
status: ArticleStatus.SHOW,
|
|
103
|
+
comment: CommentStatus.SHOW
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const result = await article.createVideoArticle(accessToken, videoArticle);
|
|
107
|
+
console.log(`Video article creation token: ${result.token}`);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 3. Upload Video for Articles
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// Upload video file
|
|
114
|
+
const videoFile = new File([videoBuffer], 'demo.mp4', { type: 'video/mp4' });
|
|
115
|
+
const uploadResult = await videoUpload.uploadVideo(accessToken, videoFile);
|
|
116
|
+
|
|
117
|
+
// Wait for processing completion
|
|
118
|
+
const finalResult = await videoUpload.waitForUploadCompletion(
|
|
119
|
+
accessToken,
|
|
120
|
+
uploadResult.token,
|
|
121
|
+
5 * 60 * 1000, // 5 minutes timeout
|
|
122
|
+
5 * 1000 // 5 seconds polling interval
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
console.log(`Video ID: ${finalResult.video_id}`);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 4. Upload Video from URL
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
const uploadResult = await videoUpload.uploadVideoFromUrl(
|
|
132
|
+
accessToken,
|
|
133
|
+
'https://example.com/video.mp4'
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const finalResult = await videoUpload.waitForUploadCompletion(
|
|
137
|
+
accessToken,
|
|
138
|
+
uploadResult.token
|
|
139
|
+
);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 5. Check Article Creation Progress
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const progress = await article.checkArticleProcess(accessToken, token);
|
|
146
|
+
|
|
147
|
+
switch (progress.status) {
|
|
148
|
+
case 'processing':
|
|
149
|
+
console.log('Article is still being processed...');
|
|
150
|
+
break;
|
|
151
|
+
case 'success':
|
|
152
|
+
console.log(`Article created with ID: ${progress.article_id}`);
|
|
153
|
+
break;
|
|
154
|
+
case 'failed':
|
|
155
|
+
console.error(`Article creation failed: ${progress.error_message}`);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 6. Get Article Details
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
const articleDetail = await article.getArticleDetail(accessToken, articleId);
|
|
164
|
+
console.log('Article details:', articleDetail);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 7. Get Article List
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const articleList = await article.getArticleList(accessToken, {
|
|
171
|
+
offset: 0,
|
|
172
|
+
limit: 20,
|
|
173
|
+
type: 'normal' // or 'video'
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
console.log(`Found ${articleList.data.total} articles`);
|
|
177
|
+
articleList.data.articles.forEach(article => {
|
|
178
|
+
console.log(`- ${article.title} (${article.total_view} views)`);
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 8. Update Article
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
const updateData = {
|
|
186
|
+
id: articleId,
|
|
187
|
+
type: ArticleType.NORMAL,
|
|
188
|
+
title: "Updated Article Title",
|
|
189
|
+
author: "Updated Author",
|
|
190
|
+
description: "Updated description",
|
|
191
|
+
cover: {
|
|
192
|
+
cover_type: CoverType.PHOTO,
|
|
193
|
+
photo_url: "https://example.com/new-cover.jpg",
|
|
194
|
+
status: ArticleStatus.SHOW
|
|
195
|
+
},
|
|
196
|
+
body: [
|
|
197
|
+
{
|
|
198
|
+
type: BodyItemType.TEXT,
|
|
199
|
+
content: "Updated content..."
|
|
200
|
+
}
|
|
201
|
+
],
|
|
202
|
+
status: ArticleStatus.SHOW
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const updateResult = await article.updateNormalArticle(accessToken, updateData);
|
|
206
|
+
console.log(`Update token: ${updateResult.token}`);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 9. Remove Article
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
const removeResult = await article.removeArticle(accessToken, articleId);
|
|
213
|
+
console.log(removeResult.message);
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 10. Check Video Status
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
const videoStatus = await videoUpload.checkVideoStatus(accessToken, token);
|
|
220
|
+
|
|
221
|
+
console.log(`Status: ${videoStatus.status}`);
|
|
222
|
+
console.log(`Progress: ${videoStatus.convert_percent}%`);
|
|
223
|
+
|
|
224
|
+
if (videoStatus.video_id) {
|
|
225
|
+
console.log(`Video ID: ${videoStatus.video_id}`);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Error Handling
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { ZaloSDKError } from 'redai-zalo-sdk';
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
const result = await article.createNormalArticle(accessToken, articleData);
|
|
236
|
+
console.log('Success:', result);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (error instanceof ZaloSDKError) {
|
|
239
|
+
console.error('Zalo API Error:', error.message, error.code);
|
|
240
|
+
console.error('Details:', error.details);
|
|
241
|
+
} else {
|
|
242
|
+
console.error('Unexpected error:', error);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Complete Example
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { ZaloSDK, ArticleType, CoverType, BodyItemType, ArticleStatus } from 'redai-zalo-sdk';
|
|
251
|
+
|
|
252
|
+
class ArticleManager {
|
|
253
|
+
private sdk: ZaloSDK;
|
|
254
|
+
|
|
255
|
+
constructor() {
|
|
256
|
+
this.sdk = new ZaloSDK({
|
|
257
|
+
appId: process.env.ZALO_APP_ID!,
|
|
258
|
+
appSecret: process.env.ZALO_APP_SECRET!,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async createCompleteArticle(accessToken: string) {
|
|
263
|
+
try {
|
|
264
|
+
// 1. Upload video first (if needed)
|
|
265
|
+
const videoFile = new File([videoBuffer], 'demo.mp4');
|
|
266
|
+
const videoUpload = await this.sdk.videoUpload.uploadVideo(accessToken, videoFile);
|
|
267
|
+
const videoResult = await this.sdk.videoUpload.waitForUploadCompletion(
|
|
268
|
+
accessToken,
|
|
269
|
+
videoUpload.token
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
// 2. Create article with video
|
|
273
|
+
const article = await this.sdk.article.createNormalArticle(accessToken, {
|
|
274
|
+
type: ArticleType.NORMAL,
|
|
275
|
+
title: "Complete Guide",
|
|
276
|
+
author: "RedAI Team",
|
|
277
|
+
description: "A complete guide with video content",
|
|
278
|
+
cover: {
|
|
279
|
+
cover_type: CoverType.PHOTO,
|
|
280
|
+
photo_url: "https://example.com/cover.jpg",
|
|
281
|
+
status: ArticleStatus.SHOW
|
|
282
|
+
},
|
|
283
|
+
body: [
|
|
284
|
+
{
|
|
285
|
+
type: BodyItemType.TEXT,
|
|
286
|
+
content: "Introduction to our service..."
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
type: BodyItemType.VIDEO,
|
|
290
|
+
video_id: videoResult.video_id
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
type: BodyItemType.TEXT,
|
|
294
|
+
content: "Thank you for watching!"
|
|
295
|
+
}
|
|
296
|
+
],
|
|
297
|
+
status: ArticleStatus.SHOW
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// 3. Wait for article creation
|
|
301
|
+
const articleProgress = await this.sdk.article.checkArticleProcess(
|
|
302
|
+
accessToken,
|
|
303
|
+
article.token
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
return articleProgress;
|
|
307
|
+
} catch (error) {
|
|
308
|
+
console.error('Article creation error:', error);
|
|
309
|
+
throw error;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Update Article Status
|
|
316
|
+
|
|
317
|
+
Update only the status of an existing article without modifying other content:
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
import { ArticleStatus, CommentStatus } from 'redai-zalo-sdk';
|
|
321
|
+
|
|
322
|
+
// Update article status to 'show' (publish)
|
|
323
|
+
const publishResult = await article.updateArticleStatus(
|
|
324
|
+
accessToken,
|
|
325
|
+
'article_id',
|
|
326
|
+
ArticleStatus.SHOW
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
// Update article status to 'hide' and disable comments
|
|
330
|
+
const hideResult = await article.updateArticleStatus(
|
|
331
|
+
accessToken,
|
|
332
|
+
'article_id',
|
|
333
|
+
ArticleStatus.HIDE,
|
|
334
|
+
CommentStatus.HIDE
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
// Check update progress
|
|
338
|
+
const progress = await article.checkArticleProcess(
|
|
339
|
+
accessToken,
|
|
340
|
+
publishResult.token
|
|
341
|
+
);
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Method Signature:**
|
|
345
|
+
```typescript
|
|
346
|
+
updateArticleStatus(
|
|
347
|
+
accessToken: string,
|
|
348
|
+
articleId: string,
|
|
349
|
+
status: ArticleStatus,
|
|
350
|
+
comment?: CommentStatus
|
|
351
|
+
): Promise<ArticleUpdateResponse>
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
**Parameters:**
|
|
355
|
+
- `accessToken`: OA access token
|
|
356
|
+
- `articleId`: ID of the article to update
|
|
357
|
+
- `status`: New article status (`'show'` or `'hide'`)
|
|
358
|
+
- `comment`: Optional comment status (`'show'` or `'hide'`)
|
|
359
|
+
|
|
360
|
+
**How it works:**
|
|
361
|
+
1. Retrieves current article details using `getArticleDetail()`
|
|
362
|
+
2. Preserves all existing content (title, body, cover, etc.)
|
|
363
|
+
3. Updates only the status and optionally comment status
|
|
364
|
+
4. Calls the appropriate update method based on article type
|
|
365
|
+
|
|
366
|
+
**Benefits:**
|
|
367
|
+
- ✅ Simple status-only updates without content modification
|
|
368
|
+
- ✅ Preserves all existing article data
|
|
369
|
+
- ✅ Automatic type detection (normal vs video articles)
|
|
370
|
+
- ✅ Optional comment status control
|
|
371
|
+
- ✅ Same progress tracking as full updates
|
|
372
|
+
|
|
373
|
+
## Validation Rules
|
|
374
|
+
|
|
375
|
+
### Normal Articles
|
|
376
|
+
- Title: required, max 150 characters
|
|
377
|
+
- Author: required, max 50 characters
|
|
378
|
+
- Description: required, max 300 characters
|
|
379
|
+
- Cover: required, must specify type and corresponding URL/ID
|
|
380
|
+
- Body: required, at least one item
|
|
381
|
+
|
|
382
|
+
### Video Articles
|
|
383
|
+
- Title: required, max 150 characters
|
|
384
|
+
- Description: required, max 300 characters
|
|
385
|
+
- Video ID: required, must be valid uploaded video
|
|
386
|
+
- Avatar: required, thumbnail URL
|
|
387
|
+
|
|
388
|
+
### Video Files
|
|
389
|
+
- Formats: MP4, AVI only
|
|
390
|
+
- Size: max 50MB
|
|
391
|
+
- Processing: asynchronous, use polling to check status
|
|
392
|
+
|
|
393
|
+
## API Reference
|
|
394
|
+
|
|
395
|
+
For detailed API documentation, see the TypeScript definitions in the source code. All methods include comprehensive JSDoc comments with parameter descriptions and return types.
|