@warriorteam/redai-zalo-sdk
Version:
Comprehensive TypeScript/JavaScript SDK for Zalo APIs - Official Account, ZNS, Consultation Service, Group Messaging, and Social APIs
396 lines (325 loc) • 10.3 kB
Markdown
# Article Management Service
The `ArticleService` and `VideoUploadService` provide comprehensive article management capabilities for Zalo Official Account (OA).
## Features
### ArticleService
- ✅ Create normal articles with rich content
- ✅ Create video articles
- ✅ Update existing articles
- ✅ **Update article status only** (new feature)
- ✅ Get article details and lists
- ✅ Remove articles
- ✅ Track article creation/update progress
- ✅ Comprehensive validation
### VideoUploadService
- ✅ Upload video files for articles
- ✅ Check video processing status
- ✅ Wait for upload completion with polling
- ✅ Upload videos from URLs
- ✅ Get video information
## Prerequisites
1. **OA Requirements:**
- OA must have permission to create articles
- Access token must have "manage_article" scope
- OA must have active status and be verified
2. **Content Limits:**
- Title: max 150 characters
- Author: max 50 characters (normal articles only)
- Description: max 300 characters
- Image size: max 1MB per image
- Video size: max 50MB per video
- Video formats: MP4, AVI only
## Usage
### Initialize SDK
```typescript
import { ZaloSDK } from 'redai-zalo-sdk';
const sdk = new ZaloSDK({
appId: 'your_app_id',
appSecret: 'your_app_secret',
});
const article = sdk.article;
const videoUpload = sdk.videoUpload;
```
### 1. Create Normal Article
```typescript
import { ArticleType, ArticleStatus, CommentStatus, CoverType, BodyItemType } from 'redai-zalo-sdk';
const normalArticle = {
type: ArticleType.NORMAL,
title: "Welcome to Our Service",
author: "RedAI Team",
description: "This is a comprehensive guide to our new features",
cover: {
cover_type: CoverType.PHOTO,
photo_url: "https://example.com/cover.jpg",
status: ArticleStatus.SHOW
},
body: [
{
type: BodyItemType.TEXT,
content: "Welcome to our new service! Here's what you need to know..."
},
{
type: BodyItemType.IMAGE,
url: "https://example.com/image1.jpg"
},
{
type: BodyItemType.TEXT,
content: "For more information, please contact us."
}
],
tracking_link: "https://your-website.com/tracking",
status: ArticleStatus.SHOW,
comment: CommentStatus.SHOW
};
const result = await article.createNormalArticle(accessToken, normalArticle);
console.log(`Article creation token: ${result.token}`);
```
### 2. Create Video Article
```typescript
const videoArticle = {
type: ArticleType.VIDEO,
title: "Product Demo Video",
description: "Watch our latest product demonstration",
video_id: "your_video_id", // Get from video upload
avatar: "https://example.com/thumbnail.jpg",
status: ArticleStatus.SHOW,
comment: CommentStatus.SHOW
};
const result = await article.createVideoArticle(accessToken, videoArticle);
console.log(`Video article creation token: ${result.token}`);
```
### 3. Upload Video for Articles
```typescript
// Upload video file
const videoFile = new File([videoBuffer], 'demo.mp4', { type: 'video/mp4' });
const uploadResult = await videoUpload.uploadVideo(accessToken, videoFile);
// Wait for processing completion
const finalResult = await videoUpload.waitForUploadCompletion(
accessToken,
uploadResult.token,
5 * 60 * 1000, // 5 minutes timeout
5 * 1000 // 5 seconds polling interval
);
console.log(`Video ID: ${finalResult.video_id}`);
```
### 4. Upload Video from URL
```typescript
const uploadResult = await videoUpload.uploadVideoFromUrl(
accessToken,
'https://example.com/video.mp4'
);
const finalResult = await videoUpload.waitForUploadCompletion(
accessToken,
uploadResult.token
);
```
### 5. Check Article Creation Progress
```typescript
const progress = await article.checkArticleProcess(accessToken, token);
switch (progress.status) {
case 'processing':
console.log('Article is still being processed...');
break;
case 'success':
console.log(`Article created with ID: ${progress.article_id}`);
break;
case 'failed':
console.error(`Article creation failed: ${progress.error_message}`);
break;
}
```
### 6. Get Article Details
```typescript
const articleDetail = await article.getArticleDetail(accessToken, articleId);
console.log('Article details:', articleDetail);
```
### 7. Get Article List
```typescript
const articleList = await article.getArticleList(accessToken, {
offset: 0,
limit: 20,
type: 'normal' // or 'video'
});
console.log(`Found ${articleList.data.total} articles`);
articleList.data.articles.forEach(article => {
console.log(`- ${article.title} (${article.total_view} views)`);
});
```
### 8. Update Article
```typescript
const updateData = {
id: articleId,
type: ArticleType.NORMAL,
title: "Updated Article Title",
author: "Updated Author",
description: "Updated description",
cover: {
cover_type: CoverType.PHOTO,
photo_url: "https://example.com/new-cover.jpg",
status: ArticleStatus.SHOW
},
body: [
{
type: BodyItemType.TEXT,
content: "Updated content..."
}
],
status: ArticleStatus.SHOW
};
const updateResult = await article.updateNormalArticle(accessToken, updateData);
console.log(`Update token: ${updateResult.token}`);
```
### 9. Remove Article
```typescript
const removeResult = await article.removeArticle(accessToken, articleId);
console.log(removeResult.message);
```
### 10. Check Video Status
```typescript
const videoStatus = await videoUpload.checkVideoStatus(accessToken, token);
console.log(`Status: ${videoStatus.status}`);
console.log(`Progress: ${videoStatus.convert_percent}%`);
if (videoStatus.video_id) {
console.log(`Video ID: ${videoStatus.video_id}`);
}
```
## Error Handling
```typescript
import { ZaloSDKError } from 'redai-zalo-sdk';
try {
const result = await article.createNormalArticle(accessToken, articleData);
console.log('Success:', result);
} catch (error) {
if (error instanceof ZaloSDKError) {
console.error('Zalo API Error:', error.message, error.code);
console.error('Details:', error.details);
} else {
console.error('Unexpected error:', error);
}
}
```
## Complete Example
```typescript
import { ZaloSDK, ArticleType, CoverType, BodyItemType, ArticleStatus } from 'redai-zalo-sdk';
class ArticleManager {
private sdk: ZaloSDK;
constructor() {
this.sdk = new ZaloSDK({
appId: process.env.ZALO_APP_ID!,
appSecret: process.env.ZALO_APP_SECRET!,
});
}
async createCompleteArticle(accessToken: string) {
try {
// 1. Upload video first (if needed)
const videoFile = new File([videoBuffer], 'demo.mp4');
const videoUpload = await this.sdk.videoUpload.uploadVideo(accessToken, videoFile);
const videoResult = await this.sdk.videoUpload.waitForUploadCompletion(
accessToken,
videoUpload.token
);
// 2. Create article with video
const article = await this.sdk.article.createNormalArticle(accessToken, {
type: ArticleType.NORMAL,
title: "Complete Guide",
author: "RedAI Team",
description: "A complete guide with video content",
cover: {
cover_type: CoverType.PHOTO,
photo_url: "https://example.com/cover.jpg",
status: ArticleStatus.SHOW
},
body: [
{
type: BodyItemType.TEXT,
content: "Introduction to our service..."
},
{
type: BodyItemType.VIDEO,
video_id: videoResult.video_id
},
{
type: BodyItemType.TEXT,
content: "Thank you for watching!"
}
],
status: ArticleStatus.SHOW
});
// 3. Wait for article creation
const articleProgress = await this.sdk.article.checkArticleProcess(
accessToken,
article.token
);
return articleProgress;
} catch (error) {
console.error('Article creation error:', error);
throw error;
}
}
}
```
### Update Article Status
Update only the status of an existing article without modifying other content:
```typescript
import { ArticleStatus, CommentStatus } from 'redai-zalo-sdk';
// Update article status to 'show' (publish)
const publishResult = await article.updateArticleStatus(
accessToken,
'article_id',
ArticleStatus.SHOW
);
// Update article status to 'hide' and disable comments
const hideResult = await article.updateArticleStatus(
accessToken,
'article_id',
ArticleStatus.HIDE,
CommentStatus.HIDE
);
// Check update progress
const progress = await article.checkArticleProcess(
accessToken,
publishResult.token
);
```
**Method Signature:**
```typescript
updateArticleStatus(
accessToken: string,
articleId: string,
status: ArticleStatus,
comment?: CommentStatus
): Promise<ArticleUpdateResponse>
```
**Parameters:**
- `accessToken`: OA access token
- `articleId`: ID of the article to update
- `status`: New article status (`'show'` or `'hide'`)
- `comment`: Optional comment status (`'show'` or `'hide'`)
**How it works:**
1. Retrieves current article details using `getArticleDetail()`
2. Preserves all existing content (title, body, cover, etc.)
3. Updates only the status and optionally comment status
4. Calls the appropriate update method based on article type
**Benefits:**
- ✅ Simple status-only updates without content modification
- ✅ Preserves all existing article data
- ✅ Automatic type detection (normal vs video articles)
- ✅ Optional comment status control
- ✅ Same progress tracking as full updates
## Validation Rules
### Normal Articles
- Title: required, max 150 characters
- Author: required, max 50 characters
- Description: required, max 300 characters
- Cover: required, must specify type and corresponding URL/ID
- Body: required, at least one item
### Video Articles
- Title: required, max 150 characters
- Description: required, max 300 characters
- Video ID: required, must be valid uploaded video
- Avatar: required, thumbnail URL
### Video Files
- Formats: MP4, AVI only
- Size: max 50MB
- Processing: asynchronous, use polling to check status
## API Reference
For detailed API documentation, see the TypeScript definitions in the source code. All methods include comprehensive JSDoc comments with parameter descriptions and return types.