@warriorteam/redai-zalo-sdk
Version:
Comprehensive TypeScript/JavaScript SDK for Zalo APIs - Official Account, ZNS, Consultation Service, Group Messaging, and Social APIs
1,248 lines (1,005 loc) β’ 56.7 kB
Markdown
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.11.1] - 2025-01-11
### π§ API IMPROVEMENTS
#### Method Signature Enhancement
- **IMPROVED**: `createOAAuthUrl()` method signature vα»i better parameter order
- Changed from: `(redirectUri, state?, usePkce?, pkce?)`
- Changed to: `(redirectUri, state?, pkce?, usePkce?)`
- **ENHANCED**: More intuitive API design vα»i PKCE config trΖ°α»c usePkce flag
- **UPDATED**: All examples vΓ documentation Δα» phΓΉ hợp vα»i signature mα»i
- **ADDED**: Comprehensive test coverage cho new signature
#### Developer Experience
- **IMPROVED**: Better IntelliSense support vα»i clearer parameter ordering
- **ENHANCED**: More logical API flow cho PKCE implementation
## [1.11.0] - 2025-01-11
### π SECURITY ENHANCEMENTS
#### PKCE Support for Official Account Authentication
- **ADDED**: PKCE (Proof Key for Code Exchange) support cho Official Account OAuth flow
- **ENHANCED**: `createOAAuthUrl()` method vα»i PKCE parameters vΓ auto-generated state
- **ADDED**: `generatePKCE()` method Δα» tαΊ‘o code_verifier vΓ code_challenge
- **UPDATED**: `getOAAccessToken()` method hỠtrợ code_verifier cho token exchange
- **ADDED**: `OAAuthResult` type vα»i url vΓ state information
- **ADDED**: `createSecureOAAuthUrl()` method vα»i full PKCE support
#### Security Best Practices
- **ENHANCED**: Auto-generated state vα»i prefix 'zalo_oa_' nαΊΏu khΓ΄ng Δược cung cαΊ₯p
- **ADDED**: Comprehensive PKCE documentation vΓ security guidelines
- **IMPROVED**: Type safety cho PKCE flow vα»i proper TypeScript interfaces
### π DOCUMENTATION
#### Authentication Guide Updates
- **UPDATED**: AUTHENTICATION.md vα»i PKCE implementation guide
- **ADDED**: Security benefits vΓ best practices cho PKCE
- **ADDED**: Complete examples cho PKCE flow
- **ADDED**: oa-auth-with-pkce.ts example file
### π§ TECHNICAL IMPROVEMENTS
#### API Compatibility
- **MAINTAINED**: Backward compatibility cho existing createOAAuthUrl() method
- **ADDED**: Deprecation notice cho old method signature
- **ENHANCED**: getAuthUrls() method vα»i PKCE support
## [1.10.1] - 2025-01-11
### π NEW FEATURES
#### Consultation Service Enhancement
- **ADDED**: `sendQuoteMessage()` - Gα»i tin nhαΊ―n tΖ° vαΊ₯n trΓch dαΊ«n (quote message)
- **ADDED**: `createQuoteMessage()` - Helper method Δα» tαΊ‘o quote message object
- **ADDED**: `apiGetWithHeaders()` - Method hα» trợ GET request vα»i custom headers
#### API Compliance
- **ENHANCED**: SendMessageResponse type vα»i ΔαΊ§y Δα»§ quota information types
- **ADDED**: Support cho tαΊ₯t cαΊ£ quota types: reply, sub_quota, purchase_quota, reward_quota
- **IMPROVED**: Quota information structure theo ΔΓΊng Zalo API specification
### π§ TECHNICAL IMPROVEMENTS
#### Message Types
- **UPDATED**: SendMessageResponse interface vα»i comprehensive quota fields
- **ADDED**: Support cho expired_date, owner_type, owner_id trong quota
- **ENHANCED**: Type safety cho quota_type vα»i union types
#### Validation & Error Handling
- **ADDED**: Validation cho text length (max 2000 characters)
- **ADDED**: Validation cho quote_message_id
- **IMPROVED**: Error messages vα»i detailed information
### π API Implementation
#### Consultation Quote Message API - 100% Compliant β
- **Endpoint**: `POST https://openapi.zalo.me/v3.0/oa/message/cs`
- **Headers**: access_token β
- **Body**: recipient, message vα»i text vΓ quote_message_id β
- **Response**: message_id, user_id, quota information β
### π― Usage Examples
#### Send Quote Message
```typescript
const zalo = new ZaloSDK();
// Gα»i tin nhαΊ―n trΓch dαΊ«n
const response = await zalo.consultation.sendQuoteMessage(
accessToken,
{ user_id: "186729651760683225" },
"ChΓ o bαΊ‘n, Shop cΓ³ Δα»a chα» lΓ 182 LΓͺ ΔαΊ‘i HΓ nh, P15, Q10, HCM",
"48687128d04c9410cd5f" // quote_message_id
);
console.log("Message ID:", response.message_id);
console.log("Quota Type:", response.quota?.quota_type);
```
#### Create Quote Message Object
```typescript
const quoteMessage = zalo.consultation.createQuoteMessage(
"CαΊ£m Ζ‘n bαΊ‘n ΔΓ£ liΓͺn hα»!",
"48687128d04c9410cd5f",
"Nα»i dung tin nhαΊ―n gα»c"
);
```
#### Handle Different Quota Types
```typescript
if (response.quota) {
switch (response.quota.quota_type) {
case "reply":
console.log(`Free replies: ${response.quota.remain}/${response.quota.total}`);
break;
case "sub_quota":
console.log(`Package quota: ${response.quota.remain}/${response.quota.total}`);
console.log(`Expires: ${response.quota.expired_date}`);
break;
case "purchase_quota":
case "reward_quota":
console.log(`Owner: ${response.quota.owner_type} (${response.quota.owner_id})`);
break;
}
} else {
console.log("Charged message (no free quota)");
}
```
## [1.10.0] - 2025-01-11
### π NEW FEATURES
#### Video Upload Service Enhancements
- **ADDED**: `uploadVideoAndWaitForCompletion()` - Combined method for upload and status checking
- **ADDED**: `VideoUploadStatus` enum for better status code handling
- **ADDED**: `apiGetWithHeaders()` method in ZaloClient for custom header requests
#### API Compliance Improvements
- **FIXED**: Video status check API to use token in header (not query params) per Zalo docs
- **UPDATED**: Status codes documentation to match official Zalo API specification
- **IMPROVED**: Video upload validation with proper MIME type checking
#### Architecture Cleanup
- **REMOVED**: Centralized endpoints from ZaloClient - each service now manages its own URLs
- **SIMPLIFIED**: ZaloClient to focus only on HTTP methods, not endpoint management
- **STANDARDIZED**: All services to use full URLs for better maintainability
### π§ TECHNICAL IMPROVEMENTS
#### Code Organization
- **REFACTORED**: Endpoint management - moved from centralized to service-specific
- **UPDATED**: All services to use full URLs instead of relative paths
- **CLEANED**: ZaloClient from unused endpoint definitions
#### Type Safety
- **ENHANCED**: Video upload types with proper status enum
- **IMPROVED**: Response handling for video upload APIs
- **ADDED**: Better error handling for video upload operations
### π API Compliance Status
#### Video Upload APIs - 100% Compliant β
- **Upload Video**: `POST /v2.0/article/upload_video/preparevideo` - β
Fully compliant
- **Check Status**: `GET /v2.0/article/upload_video/verify` - β
Fixed header usage
- **Article Verify**: `POST /v2.0/article/verify` - β
Fully compliant
#### Breaking Changes
- **ZaloClient**: Removed `endpoints` property and `getEndpointUrl()` method
- **VideoUploadService**: `checkVideoStatus()` now uses headers correctly
### π― Usage Examples
#### New Combined Video Upload
```typescript
const zalo = new ZaloSDK();
// Upload and wait for completion in one call
const result = await zalo.videoUpload.uploadVideoAndWaitForCompletion(
accessToken,
videoBuffer,
"video.mp4",
5 * 60 * 1000, // 5 minutes timeout
5 * 1000 // 5 seconds polling
);
console.log("Video ID:", result.video_id);
console.log("Status:", result.status); // Use VideoUploadStatus enum
```
#### Status Code Handling
```typescript
import { VideoUploadStatus } from '@warriorteam/redai-zalo-sdk';
if (status.status === VideoUploadStatus.SUCCESS) {
console.log("Video ready:", status.video_id);
} else if (status.status === VideoUploadStatus.PROCESSING) {
console.log("Still processing:", status.convert_percent + "%");
}
```
## [1.9.14] - 2025-01-11
### π¨ BREAKING CHANGES
#### Group Get Info API Fix
- **FIXED**: `GroupDetailResponse` interface to match Zalo API docs 100%
- **CHANGED**: Return type from partial data to full API response
- **FIXED**: `group_info` field names: `group_name` β `name`, `group_avatar` β `avatar`, `member_count` β `total_member`
- **ADDED**: Missing fields in `group_info`: `status`, `max_member`
- **REMOVED**: Non-existent fields: `admin_count`, `created_time`
- **FIXED**: `asset_info` structure: added `asset_id`, `auto_renew`, removed `status`
- **FIXED**: `group_setting` field name: `join_approval` β `join_appr`
- **REMOVED**: Non-existent field: `lock_view_member`
#### API Structure Corrections
- **FIXED**: All nested objects to match actual Zalo API response exactly
- **IMPROVED**: Type safety with accurate field definitions and documentation
- **STANDARDIZED**: Field types to match API specification (e.g., `max_member` as string)
### π Migration Guide
**Before (v1.9.13) - BROKEN:**
```typescript
// β Wrong return type and field names
const groupData = await groupService.getGroupInfo(accessToken, groupId);
// Returns partial data only
if (groupData) {
console.log(groupData.group_info.group_name); // β Wrong field name
console.log(groupData.group_info.group_avatar); // β Wrong field name
console.log(groupData.group_info.member_count); // β Wrong field name
console.log(groupData.group_info.admin_count); // β Field doesn't exist
console.log(groupData.asset_info.status); // β Field doesn't exist
console.log(groupData.group_setting.join_approval); // β Wrong field name
}
```
**After (v1.9.14) - CORRECT:**
```typescript
// β
Correct return type and field names
const response = await groupService.getGroupInfo(accessToken, groupId);
// β
Access response structure correctly
if (response.error === 0 && response.data) {
const { group_info, asset_info, group_setting } = response.data;
// β
group_info with correct field names
console.log('Name:', group_info.name); // β
Correct field name
console.log('Avatar:', group_info.avatar); // β
Correct field name
console.log('Group ID:', group_info.group_id);
console.log('Link:', group_info.group_link);
console.log('Description:', group_info.group_description);
console.log('Status:', group_info.status); // β
New field
console.log('Total Members:', group_info.total_member); // β
Correct field name
console.log('Max Members:', group_info.max_member); // β
New field (string)
console.log('Auto Delete:', group_info.auto_delete_date);
// β
asset_info with correct structure
console.log('Asset Type:', asset_info.asset_type); // gmf10, gmf50, gmf100
console.log('Asset ID:', asset_info.asset_id); // β
New field
console.log('Valid Through:', asset_info.valid_through);
console.log('Auto Renew:', asset_info.auto_renew); // β
New field (string)
// β
group_setting with correct field names
console.log('Lock Send Msg:', group_setting.lock_send_msg);
console.log('Join Approval:', group_setting.join_appr); // β
Correct field name
console.log('Enable Msg History:', group_setting.enable_msg_history);
console.log('Enable Link Join:', group_setting.enable_link_join);
}
```
### π§ Technical Details
- **API**: `GET https://openapi.zalo.me/v3.0/oa/group/getgroup`
- **Response**: Now returns full Zalo API response structure
- **Field Names**: All field names now match Zalo API exactly
- **Field Types**: Correct types for all fields (e.g., `max_member` as string, `auto_renew` as string)
## [1.9.13] - 2025-01-11
### π― STABLE RELEASE
#### All Group Management APIs Now 100% Compliant with Zalo Docs
- **VERIFIED**: All group management APIs have been audited and fixed to match Zalo API documentation exactly
- **STANDARDIZED**: Consistent response structures across all group APIs
- **OPTIMIZED**: Proper field names, types, and validation throughout
- **UNIFIED**: Common interfaces where APIs share similar structures
#### APIs Fixed in This Release Series (v1.9.4 - v1.9.13):
1. β
**Group Invite Members** - Fixed field names and response structure
2. β
**Group Pending Members List** - Fixed member interface and field names
3. β
**Group Accept/Reject Pending** - Fixed response structure
4. β
**Group Members List** - Fixed member interface and query parameters
5. β
**Group Add/Remove Admins** - Fixed request interface and field mapping
6. β
**Group List of OA** - Fixed response structure and group item interface
7. β
**Group Quota Check** - Fixed asset interface and response type
8. β
**Group Recent Chats** - Fixed endpoint, interface, and response structure
9. β
**Group Conversation** - Fixed interface and unified with recent chats
#### Quality Assurance
- **100% API COMPLIANCE**: All APIs now match official Zalo documentation exactly
- **TYPE SAFETY**: Comprehensive TypeScript interfaces with accurate field definitions
- **VALIDATION**: Proper input validation for all parameters
- **ERROR HANDLING**: Consistent error handling across all group management operations
- **DOCUMENTATION**: Complete API documentation with examples
### π Ready for Production
This release represents a complete overhaul of the Group Management service to ensure 100% compliance with Zalo's official API documentation. All APIs have been thoroughly tested and verified.
## [1.9.12] - 2025-01-11
### π¨ BREAKING CHANGES
#### Group Conversation API Fix
- **FIXED**: `getGroupConversation()` method to match Zalo API docs 100%
- **ADDED**: `GroupConversationResponse` interface using `RecentChatMessage` structure
- **CHANGED**: Default count parameter from 20 β 5 (matching Zalo API docs)
- **REMOVED**: Non-existent parameters: `fromTime`, `toTime`
- **FIXED**: Query parameters to not convert numbers to strings
- **CHANGED**: Return type to return full API response instead of custom wrapper
- **DEPRECATED**: `GroupConversationMessage` interface (use `RecentChatMessage` instead)
#### API Structure Corrections
- **DISCOVERED**: Conversation API returns identical structure to listrecentchat API
- **UNIFIED**: Both APIs now use same `RecentChatMessage` interface
- **FIXED**: Message object structure to match actual Zalo API response
- **IMPROVED**: Type safety with accurate field definitions
- **ADDED**: Comprehensive validation for all input parameters
### π Migration Guide
**Before (v1.9.11) - BROKEN:**
```typescript
// β Wrong parameters, return type, and structure
const result = await groupService.getGroupConversation(
accessToken,
groupId,
0,
20, // β Wrong default count
fromTime, // β Parameter doesn't exist
toTime // β Parameter doesn't exist
);
console.log(result.messages); // β Wrong structure
console.log(result.total); // β Field doesn't exist
result.messages.forEach(msg => {
console.log(msg.sender_id); // β Wrong field name
console.log(msg.sender_name); // β Wrong field name
console.log(msg.content); // β Wrong field name
console.log(msg.message_type); // β Wrong field name
console.log(msg.sent_time); // β Wrong field name and type
});
```
**After (v1.9.12) - CORRECT:**
```typescript
// β
Correct parameters and return type
const response = await groupService.getGroupConversation(
accessToken,
groupId,
0, // offset
5 // count (correct default)
);
// β
Access response structure correctly (same as listrecentchat)
if (response.error === 0 && response.data) {
response.data.forEach(message => {
console.log('Source:', message.src); // β
0=OA, 1=User
console.log('Time:', message.time); // β
Timestamp (number)
console.log('Type:', message.type); // β
text, photo, etc.
console.log('Message:', message.message); // β
Content
console.log('Message ID:', message.message_id); // β
Message ID
console.log('From ID:', message.from_id); // β
Sender ID
console.log('From Name:', message.from_display_name); // β
Sender name
console.log('From Avatar:', message.from_avatar); // β
Sender avatar
console.log('Group ID:', message.group_id); // β
Group ID
// β
Optional fields based on message type
if (message.type === 'photo' || message.type === 'GIF') {
console.log('Thumbnail:', message.thumb);
console.log('URL:', message.url);
if (message.description) {
console.log('Description:', message.description);
}
}
if (message.type === 'voice') {
console.log('Audio URL:', message.url);
}
if (message.type === 'location') {
console.log('Location:', message.location);
}
});
}
```
### π§ Technical Details
- **API**: `GET https://openapi.zalo.me/v3.0/oa/group/conversation`
- **Default Count**: Changed from 20 to 5 (matching Zalo API docs)
- **Query Params**: Only `group_id`, `offset`, `count` (removed non-existent params)
- **Response**: Returns identical structure to listrecentchat API
- **Message Fields**: All field names and types now match Zalo API exactly
## [1.9.11] - 2025-01-11
### π¨ BREAKING CHANGES
#### Group Recent Chats API Fix
- **FIXED**: API endpoint from `/recent` β `/listrecentchat` to match Zalo API docs
- **ADDED**: `RecentChatMessage` interface for correct message structure
- **ADDED**: `RecentChatsResponse` interface for proper API response
- **CHANGED**: Default count parameter from 20 β 5 (matching Zalo API docs)
- **FIXED**: Query parameters to not convert numbers to strings
- **CHANGED**: Return type to return full API response instead of custom wrapper
- **DEPRECATED**: `GroupRecentChat` interface (use `RecentChatMessage` instead)
#### API Structure Corrections
- **FIXED**: Message object structure to match actual Zalo API response (flat structure)
- **ADDED**: All missing fields: `src`, `time`, `type`, `message`, `from_id`, `from_display_name`, `from_avatar`
- **ADDED**: Optional fields: `links`, `thumb`, `url`, `description`, `location`
- **IMPROVED**: Type safety with accurate field definitions
- **FIXED**: Query parameter types (offset and count as numbers, not strings)
### π Migration Guide
**Before (v1.9.10) - BROKEN:**
```typescript
// β Wrong endpoint, return type, and structure
const result = await groupService.getRecentChats(accessToken, 0, 20);
console.log(result.chats); // β Wrong structure
console.log(result.total); // β Field doesn't exist in Zalo API
result.chats.forEach(chat => {
console.log(chat.group_name); // β Field doesn't exist
console.log(chat.last_message.content); // β Nested structure wrong
console.log(chat.unread_count); // β Field doesn't exist
});
```
**After (v1.9.11) - CORRECT:**
```typescript
// β
Correct endpoint, return type, and default count
const response = await groupService.getRecentChats(accessToken, 0, 5);
// β
Access response structure correctly
if (response.error === 0 && response.data) {
response.data.forEach(message => {
console.log('Source:', message.src); // β
0=OA, 1=User
console.log('Time:', message.time); // β
Timestamp
console.log('Type:', message.type); // β
text, photo, etc.
console.log('Message:', message.message); // β
Content
console.log('Message ID:', message.message_id); // β
Message ID
console.log('From ID:', message.from_id); // β
Sender ID
console.log('From Name:', message.from_display_name); // β
Sender name
console.log('From Avatar:', message.from_avatar); // β
Sender avatar
console.log('Group ID:', message.group_id); // β
Group ID
// β
Optional fields based on message type
if (message.links) console.log('Links:', message.links);
if (message.thumb) console.log('Thumbnail:', message.thumb);
if (message.url) console.log('URL:', message.url);
if (message.description) console.log('Description:', message.description);
if (message.location) console.log('Location:', message.location);
});
}
```
### π§ Technical Details
- **API**: `GET https://openapi.zalo.me/v3.0/oa/group/listrecentchat`
- **Default Count**: Changed from 20 to 5 (matching Zalo API docs)
- **Query Params**: Fixed to use proper number types instead of strings
- **Response**: Now returns actual Zalo API response structure
- **Message Fields**: All field names and types now match Zalo API exactly
## [1.9.10] - 2025-01-11
### π¨ BREAKING CHANGES
#### Group Quota API Fix
- **FIXED**: `getGroupQuota()` method to use correct response interface
- **CHANGED**: Return type from `GroupQuota` β `GroupQuotaMessageResponse`
- **FIXED**: `GroupQuotaAsset` interface to match Zalo API docs 100%
- **ADDED**: Missing `auto_renew` field in asset information
- **REMOVED**: Non-existent `remain` field from asset interface
- **IMPROVED**: Field ordering and documentation to match Zalo API exactly
#### API Structure Corrections
- **FIXED**: Asset object structure to match actual Zalo API response
- **REMOVED**: Incorrect `GroupQuota` interface with wrong data structure
- **IMPROVED**: Type safety with accurate field definitions
- **ADDED**: Comprehensive field documentation
### π Migration Guide
**Before (v1.9.9) - BROKEN:**
```typescript
// β Wrong return type and missing fields
const quota = await groupService.getGroupQuota(accessToken, "gmf10", "sub_quota");
// Returns GroupQuota with wrong structure
if (quota.data) {
console.log(quota.data.max_groups); // β Field doesn't exist
console.log(quota.data.asset_info); // β Wrong field name
quota.data.asset_info?.forEach(asset => {
console.log(asset.remain); // β Field doesn't exist
// β Missing auto_renew field
});
}
```
**After (v1.9.10) - CORRECT:**
```typescript
// β
Correct return type matching Zalo API
const response = await groupService.getGroupQuota(accessToken, "gmf10", "sub_quota");
// β
Access response structure correctly
if (response.error === 0 && response.data) {
response.data.forEach(asset => {
console.log('Product Type:', asset.product_type); // β
gmf10, gmf50, gmf100
console.log('Quota Type:', asset.quota_type); // β
sub_quota, purchase_quota, reward_quota
console.log('Asset ID:', asset.asset_id); // β
For group creation
console.log('Valid Through:', asset.valid_through); // β
Expiry date
console.log('Auto Renew:', asset.auto_renew); // β
New field
console.log('Status:', asset.status); // β
available/used
console.log('Used ID:', asset.used_id); // β
group_id if used
});
}
```
### π§ Technical Details
- **API**: `POST https://openapi.zalo.me/v3.0/oa/quota/group`
- **Request**: Maintains correct structure (no changes)
- **Response**: Now returns actual Zalo API response structure
- **Asset Fields**: All field names and types now match Zalo API exactly
## [1.9.9] - 2025-01-11
### π¨ BREAKING CHANGES
#### Group Get Groups of OA API Fix
- **FIXED**: `GroupsOfOAResponse` interface to match Zalo API docs 100%
- **ADDED**: `OAGroupItem` interface for correct group structure
- **CHANGED**: Default count parameter from 20 β 5 (matching Zalo API docs)
- **FIXED**: Query parameters to not convert numbers to strings
- **ADDED**: Missing `group_count` field in response
- **FIXED**: Group item field names to match Zalo API exactly
- **CHANGED**: Return type to return full API response instead of partial data
#### API Structure Corrections
- **FIXED**: Group object structure to match actual Zalo API response
- **ADDED**: All missing fields: `group_link`, `status`, `group_count`
- **FIXED**: Field names: `group_name` β `name`, `group_avatar` β `avatar`, `member_count` β `total_member`
- **IMPROVED**: Type safety with accurate field definitions
- **FIXED**: Query parameter types (offset and count as numbers, not strings)
### π Migration Guide
**Before (v1.9.8) - BROKEN:**
```typescript
// β Wrong return type and default count
const result = await groupService.getGroupsOfOA(accessToken, 0, 20);
console.log(result.groups); // β Partial data with wrong structure
console.log(result.total); // β Missing many fields
// β Groups had wrong field names
result.groups.forEach(group => {
console.log(group.group_name); // β Field doesn't exist
console.log(group.group_avatar); // β Field doesn't exist
console.log(group.member_count); // β Field doesn't exist
});
```
**After (v1.9.9) - CORRECT:**
```typescript
// β
Correct return type and default count
const response = await groupService.getGroupsOfOA(accessToken, 0, 5);
// β
Access full response structure
if (response.error === 0 && response.data) {
console.log('Total groups:', response.data.total);
console.log('Group count returned:', response.data.group_count);
console.log('Offset:', response.data.offset);
console.log('Count:', response.data.count);
// β
Groups have correct field names
response.data.groups.forEach(group => {
console.log('Name:', group.name); // β
Correct field name
console.log('Avatar:', group.avatar); // β
Correct field name
console.log('Group ID:', group.group_id); // β
Correct field name
console.log('Group Link:', group.group_link); // β
New field
console.log('Description:', group.group_description);
console.log('Total Members:', group.total_member); // β
Correct field name
console.log('Status:', group.status); // β
New field
});
}
```
### π§ Technical Details
- **API**: `GET https://openapi.zalo.me/v3.0/oa/group/getgroupsofoa`
- **Default Count**: Changed from 20 to 5 (matching Zalo API docs)
- **Query Params**: Fixed to use proper number types instead of strings
- **Response**: Now returns full Zalo API response structure
- **Group Fields**: All field names now match Zalo API exactly
## [1.9.8] - 2025-01-11
### π¨ BREAKING CHANGES
#### Group Admin Management API Fix
- **FIXED**: `GroupAdminActionRequest` interface field name from `admin_uids` β `member_user_ids`
- **FIXED**: Field mapping in `addAdmins()` and `removeAdmins()` methods
- **ADDED**: Method overloads for easier usage with direct array parameters
- **IMPROVED**: Input validation for admin management operations
#### API Structure Corrections
- **FIXED**: Request field name to match Zalo API docs 100%
- **IMPROVED**: Method signatures with overloads for better developer experience
- **ADDED**: Comprehensive validation for member user IDs
### π Migration Guide
**Before (v1.9.7) - BROKEN:**
```typescript
// β Wrong field name
await groupService.addAdmins(accessToken, groupId, {
admin_uids: ["user1", "user2"] // β Wrong field name
});
await groupService.removeAdmins(accessToken, groupId, {
admin_uids: ["user1", "user2"] // β Wrong field name
});
```
**After (v1.9.8) - CORRECT:**
```typescript
// β
Method 1: Object parameter (correct field name)
await groupService.addAdmins(accessToken, groupId, {
member_user_ids: ["user1", "user2"] // β
Correct field name
});
// β
Method 2: Direct array parameter (easier)
await groupService.addAdmins(accessToken, groupId, [
"user1", "user2"
]);
// β
Same for removeAdmins
await groupService.removeAdmins(accessToken, groupId, [
"user1", "user2"
]);
```
### π§ Technical Details
- **APIs**:
- `POST https://openapi.zalo.me/v3.0/oa/group/addadmins`
- `POST https://openapi.zalo.me/v3.0/oa/group/removeadmins`
- **Request**: Now uses correct `member_user_ids` field name
- **Response**: Maintains correct structure `{ error: number; message: string }`
- **Validation**: Added validation for non-empty member user IDs list
## [1.9.7] - 2025-01-11
### π¨ BREAKING CHANGES
#### Group List Members API Fix
- **FIXED**: `GroupMembersResponse` member interface to match Zalo API docs 100%
- **ADDED**: `GroupMemberInfo` interface for correct member structure
- **CHANGED**: Member field name from `display_name` β `name`
- **REMOVED**: Non-existent fields: `role`, `joined_time`
- **FIXED**: Query parameters to not convert numbers to strings
#### API Structure Corrections
- **FIXED**: Member object structure to match actual Zalo API response
- **REMOVED**: Fictional fields that don't exist in Zalo API (`role`, `joined_time`)
- **IMPROVED**: Type safety with accurate field definitions
- **FIXED**: Query parameter types (offset and count as numbers, not strings)
### π Migration Guide
**Before (v1.9.6) - BROKEN:**
```typescript
// β Wrong field names and extra fields
const members = response.data?.members || [];
members.forEach(member => {
console.log(member.display_name); // β Field doesn't exist
console.log(member.role); // β Field doesn't exist
console.log(member.joined_time); // β Field doesn't exist
});
```
**After (v1.9.7) - CORRECT:**
```typescript
// β
Correct field names matching Zalo API
const members = response.data?.members || [];
members.forEach(member => {
console.log(member.name); // β
Correct field name
console.log(member.user_id); // β
Optional for users
console.log(member.oa_id); // β
Optional for OA
console.log(member.avatar); // β
Avatar URL
});
```
### π§ Technical Details
- **API**: `GET https://openapi.zalo.me/v3.0/oa/group/listmember`
- **Query Params**: Fixed to use proper number types instead of strings
- **Response**: Now accurately reflects actual Zalo API response structure
- **Member Fields**: Only `user_id`, `oa_id`, `name`, `avatar` as per official documentation
## [1.9.6] - 2025-01-11
### π¨ BREAKING CHANGES
#### Group Accept Pending Members API Fix
- **FIXED**: `GroupAcceptPendingMembersResponse` interface to match Zalo API docs 100%
- **REMOVED**: Non-existent `data` field with `accepted_count` and `failed_members`
- **SIMPLIFIED**: Response structure to only include `error` and `message` as per official docs
#### API Structure Corrections
- **FIXED**: Response interface to match actual Zalo API response
- **REMOVED**: Fictional data fields that don't exist in Zalo API
- **IMPROVED**: Type accuracy with correct response structure
### π Migration Guide
**Before (v1.9.5) - BROKEN:**
```typescript
// β Wrong response structure with non-existent fields
const result = await groupService.acceptPendingMembers(accessToken, groupId, userIds);
console.log(result.data?.accepted_count); // β Field doesn't exist
console.log(result.data?.failed_members); // β Field doesn't exist
```
**After (v1.9.6) - CORRECT:**
```typescript
// β
Correct response structure matching Zalo API
const result = await groupService.acceptPendingMembers(accessToken, groupId, userIds);
console.log(result.error); // β
0 for success
console.log(result.message); // β
"Success"
// β
Check success
if (result.error === 0) {
console.log('β
Accept pending members thΓ nh cΓ΄ng!');
} else {
console.log('β Accept pending members thαΊ₯t bαΊ‘i:', result.message);
}
```
### π§ Technical Details
- **API**: `POST https://openapi.zalo.me/v3.0/oa/group/acceptpendinginvite`
- **Request**: Maintains correct structure (no changes)
- **Response**: Now accurately reflects actual Zalo API response
- **Validation**: Maintains existing validation and error handling
## [1.9.5] - 2025-01-11
### π¨ BREAKING CHANGES
#### Group Pending Members API Fix
- **FIXED**: `GroupPendingMember` interface to match Zalo API docs 100%
- **CHANGED**: Field name from `display_name` β `name`
- **REMOVED**: Non-existent fields: `avatar`, `request_time`, `request_message`
- **SIMPLIFIED**: Interface to only include fields actually returned by Zalo API
#### API Structure Corrections
- **FIXED**: Member object structure to match actual Zalo API response
- **REMOVED**: Fictional fields that don't exist in Zalo API
- **IMPROVED**: Type safety with accurate field definitions
### π Migration Guide
**Before (v1.9.4) - BROKEN:**
```typescript
// β Wrong field names and extra fields
const members = response.data?.members || [];
members.forEach(member => {
console.log(member.display_name); // β Field doesn't exist
console.log(member.avatar); // β Field doesn't exist
console.log(member.request_time); // β Field doesn't exist
});
```
**After (v1.9.5) - CORRECT:**
```typescript
// β
Correct field names matching Zalo API
const members = response.data?.members || [];
members.forEach(member => {
console.log(member.name); // β
Correct field name
console.log(member.user_id); // β
Correct field name
});
```
### π§ Technical Details
- **API**: `GET https://openapi.zalo.me/v3.0/oa/group/listpendinginvite`
- **Response**: Now accurately reflects actual Zalo API response structure
- **Fields**: Only `name` and `user_id` as per official documentation
## [1.9.4] - 2025-01-11
### π¨ BREAKING CHANGES
#### Group Invite Members API Fix
- **FIXED**: `GroupMemberInviteRequest` interface field name from `member_uids` β `member_user_ids`
- **FIXED**: Method implementation to use correct field mapping
- **CHANGED**: Return type from custom object to official Zalo API response structure
- **ADDED**: `GroupInviteResult` interface for proper API response typing
- **ADDED**: Method overload for easier usage with direct array parameter
#### API Structure Corrections
- **FIXED**: Request field name to match Zalo API docs 100%
- **FIXED**: Response structure to return actual API response instead of custom wrapper
- **IMPROVED**: Method signature with overloads for better developer experience
### π Migration Guide
**Before (v1.9.3) - BROKEN:**
```typescript
// β Wrong field name and return type
await groupService.inviteMembers(accessToken, groupId, {
member_uids: ["user1", "user2"] // β Wrong field name
});
// Returns: { success: boolean; invited_count: number } β Custom structure
```
**After (v1.9.4) - CORRECT:**
```typescript
// β
Method 1: Object parameter (correct field name)
const result = await groupService.inviteMembers(accessToken, groupId, {
member_user_ids: ["user1", "user2"] // β
Correct field name
});
// β
Method 2: Direct array parameter (easier)
const result = await groupService.inviteMembers(accessToken, groupId, [
"user1", "user2"
]);
// β
Returns actual Zalo API response
console.log(result.error); // 0 for success
console.log(result.message); // "Success"
```
### π§ Technical Details
- **API**: `POST https://openapi.zalo.me/v3.0/oa/group/invite`
- **Request**: Now uses correct `member_user_ids` field name
- **Response**: Returns actual Zalo API response structure
- **Validation**: Maintains existing validation (max 50 members, non-empty list)
## [1.9.3] - 2025-01-11
### β¨ NEW FEATURES
#### Group Asset Update API - COMPLETELY NEW
- **ADDED**: `updateGroupAsset()` method for updating group packages and extending expiration
- **ADDED**: `GroupAssetUpdateRequest` interface for asset update requests
- **ADDED**: Support for both parameter styles: `(accessToken, groupId, assetId)` and `(accessToken, updateData)`
- **ADDED**: Complete validation and error handling
#### New API Capabilities
- **β
Increase member limit**: Upgrade from gmf10 β gmf50 β gmf100
- **β
Extend group expiration**: Renew expired groups
- **β
Package management**: Full control over group asset lifecycle
### π Usage Examples
```typescript
// Method 1: Separate parameters
await groupService.updateGroupAsset(accessToken, groupId, newAssetId);
// Method 2: Object parameter
await groupService.updateGroupAsset(accessToken, {
group_id: "513c4f117a479319ca56",
asset_id: "new_asset_id_for_gmf50"
});
// Access response data
const result = await groupService.updateGroupAsset(accessToken, groupId, assetId);
console.log('New max members:', result.data.group_info.max_member);
console.log('Asset type:', result.data.asset_info.asset_type);
console.log('Valid through:', result.data.asset_info.valid_through);
```
### π§ Technical Details
- **API**: `POST https://openapi.zalo.me/v3.0/oa/group/updateasset`
- **Response**: Same structure as `GroupUpdateResult` with complete group info
- **Validation**: Full input validation for group_id and asset_id
- **Error Handling**: Comprehensive error messages and SDK error wrapping
## [1.9.2] - 2025-01-11
### π¨ BREAKING CHANGES
#### Group Update API Complete Restructure
- **FIXED**: `GroupUpdateRequest` interface to match Zalo API docs 100%
- **ADDED**: Missing fields: `group_avatar`, `lock_send_msg`, `join_appr`, `enable_msg_history`, `enable_link_join`
- **FIXED**: Field name from `description` β `group_description`
- **ADDED**: `GroupUpdateResult` interface for complete API response structure
- **CHANGED**: `updateGroupInfo()` return type from simple success flag to full response data
#### New Response Structure
- **ADDED**: Complete response structure with `group_info`, `asset_info`, `group_setting`
- **ADDED**: Helper methods: `extractGroupInfo()`, `extractGroupSettings()`, `extractAssetInfo()`
- **DEPRECATED**: `updateGroupAvatar()` method (use `updateGroupInfo()` with `group_avatar` field)
### π Migration Guide
**Before (v1.9.1) - BROKEN:**
```typescript
// β Missing many fields, wrong field names
await groupService.updateGroupInfo(token, groupId, {
group_name: "New Name",
description: "New Description" // β Wrong field name
});
```
**After (v1.9.2) - CORRECT:**
```typescript
// β
Complete API support
const result = await groupService.updateGroupInfo(token, groupId, {
group_name: "New Name",
group_description: "New Description", // β
Correct field name
group_avatar: "https://...", // β
Now supported
lock_send_msg: true, // β
Now supported
join_appr: false, // β
Now supported
enable_msg_history: true, // β
Now supported
enable_link_join: false // β
Now supported
});
// Access full response data
console.log('Group Info:', result.data.group_info);
console.log('Group Settings:', result.data.group_setting);
console.log('Asset Info:', result.data.asset_info);
// Or use helper methods
const groupInfo = groupService.extractGroupInfo(result);
const settings = groupService.extractGroupSettings(result);
```
## [1.9.1] - 2025-01-11
### π§ FIXED
#### Group Management API Response Structure
- **FIXED**: `GroupCreateResult` interface to match Zalo API response 100%
- **CHANGED**: Response structure from flat object to proper wrapper with `data`, `error`, `message`
- **ADDED**: `GroupCreateData` interface for the actual group data
- **ADDED**: `extractGroupData()` helper method to extract data from response
#### Response Structure Corrections
- **FIXED**: Field names to match Zalo API exactly:
- `group_name` β `name`
- `member_count` β `total_member`
- Added missing fields: `group_link`, `group_description`, `status`
- Removed non-existent field: `created_time`
### π Migration Guide
**Before (v1.9.0) - BROKEN:**
```typescript
const result = await groupService.createGroup(token, groupData);
// result.group_name β Field doesn't exist
// result.member_count β Field doesn't exist
```
**After (v1.9.1) - CORRECT:**
```typescript
const result = await groupService.createGroup(token, groupData);
// Full response: result.data, result.error, result.message
const groupData = result.data; // or use extractGroupData(result)
// groupData.name β
Correct field name
// groupData.total_member β
Correct field name
// groupData.group_link β
Available
// groupData.status β
Available
```
## [1.9.0] - 2025-01-11
### π¨ BREAKING CHANGES
#### ZNS Template API Complete Restructure
- **FIXED**: `ZNSUpdateTemplateRequest` interface to match Zalo API docs 100%
- **FIXED**: `ZNSCreateTemplateRequest` interface to match Zalo API docs 100%
- **CHANGED**: Field names from camelCase to snake_case (e.g., `templateId` β `template_id`)
- **ADDED**: Required fields: `template_type`, `tag`, `layout`, `tracking_id`
- **REMOVED**: Non-existent fields: `templateContent`, `timeout`, `previewUrl`
- **FIXED**: `params` structure to match Zalo API specification
#### New ZNS Template Structure
- **ADDED**: `ZNSTemplateLayout` interface with proper header/body/footer components
- **ADDED**: `ZNSLayoutComponent` interface supporting all Zalo component types
- **ADDED**: `ZNSTemplateParam` interface with correct param type structure
- **ADDED**: `ZNSTemplateCreateEditResponse` interface for API responses
#### New Enums and Constants
- **ADDED**: `ZNSTemplateType` enum (1-5 for different template types)
- **ADDED**: `ZNSTemplateTag` enum ("1"-"3" for Transaction/Customer Care/Promotion)
- **ADDED**: `ZNSButtonType` enum (1-8 for different button types)
- **ADDED**: `ZNSParamType` enum ("1"-"15" for different parameter types)
- **ADDED**: `ZNS_TEMPLATE_TYPES`, `ZNS_TEMPLATE_TAGS`, `ZNS_BUTTON_TYPES`, `ZNS_PARAM_TYPES` constants
- **ADDED**: `ZNSValidation` helper functions for validation
### π Migration Guide
**Before (v1.8.1) - BROKEN:**
```typescript
const templateData: ZNSUpdateTemplateRequest = {
templateId: "123",
templateName: "Test",
templateContent: "Hello", // β Field doesn't exist in Zalo API
templateTag: "1" // β Wrong field name
};
```
**After (v1.9.0) - CORRECT:**
```typescript
const templateData: ZNSUpdateTemplateRequest = {
template_id: "123",
template_name: "Test Template",
template_type: ZNSTemplateType.CUSTOM,
tag: ZNSTemplateTag.TRANSACTION,
layout: {
body: {
components: [
{ TITLE: { value: "Hello World" } }
]
}
},
tracking_id: "track_001"
};
```
### π§ Technical Details
- **API Compliance**: Now 100% matches official Zalo API documentation
- **Validation**: Added comprehensive validation functions
- **Type Safety**: Improved TypeScript types with proper enums
- **Constants**: Added helper constants for easier development
## [1.8.1] - 2025-01-10
### π§ FIXED
#### Upload Image API Corrections
- **Fixed**: `UploadImageResult` interface to match Zalo API docs exactly
- **Removed**: Unnecessary fields (`url`, `type`, `size`) from response interface
- **Fixed**: File size limit from 5MB β 1MB (as per Zalo docs)
- **Fixed**: Supported formats from "JPG, PNG, GIF" β "JPG, PNG" only
- **Updated**: Comments and documentation to reflect correct specifications
### π Migration Guide
If you're using `uploadImage()` API, note these changes:
```typescript
// Before (v1.8.0)
const result = await messageService.uploadImage(token, imageData, 'image.gif'); // β GIF not supported
// result.url, result.type, result.size // β These fields no longer exist
// After (v1.8.1)
const result = await messageService.uploadImage(token, imageData, 'image.png'); // β
Only JPG/PNG
// result.attachment_id // β
Only this field exists
```
### π§ Technical Details
- **API Endpoint**: `https://openapi.zalo.me/v2.0/oa/upload/image` (unchanged)
- **Max File Size**: 1MB (corrected from 5MB)
- **Supported Formats**: JPG, PNG (removed GIF support)
- **Response Structure**: Now 100% matches official Zalo API documentation
## [1.8.0] - 2025-01-10
### π¨ BREAKING CHANGES
#### Article List API Structure Fixed
- **Fixed**: `ArticleListData` interface to match Zalo API docs exactly
- **Changed**: `articles` field renamed to `medias` (as per Zalo API response)
- **Removed**: `offset` and `count` fields (not in Zalo API response)
- **Fixed**: `ArticleListItem` interface to match Zalo API response structure
#### Article List Item Fields Updated
- **Changed**: `create_time` β `create_date` (matches Zalo API)
- **Changed**: `update_time` β `update_date` (matches Zalo API)
- **Added**: `thumb` field (article thumbnail URL)
- **Removed**: `description`, `comment`, `total_like`, `total_comment` (not in list API response)
- **Removed**: All optional fields (`author`, `cover`, `body`, etc.) - these belong to detail API
### π Migration Guide
If you're using `getArticleList()` API, update your code:
```typescript
// Before (v1.7.x)
const response = await articleService.getArticleList(token, request);
const articles = response.data.articles; // β Wrong field name
const article = articles[0];
console.log(article.create_time); // β Wrong field name
// After (v1.8.0)
const response = await articleService.getArticleList(token, request);
const articles = response.data.medias; // β
Correct field name
const article = articles[0];
console.log(article.create_date); // β
Correct field name
console.log(article.thumb); // β
New field available
```
### π§ Technical Details
- **API Endpoint**: `https://openapi.zalo.me/v2.0/article/getslice` (unchanged)
- **Response Structure**: Now 100% matches official Zalo API documentation
- **Type Safety**: Enhanced TypeScript definitions for better development experience
## [1.6.0] - 2025-01-09
### Added
#### π POST Method for User List Retrieval
- **New Method**: `postUserList()` in UserService for POST-based user list retrieval
- **Same Endpoint**: Uses identical URL as `getUserList()` but with POST method
- **Request Body**: Sends data via request body instead of URL parameters
- **Full Feature Parity**: Supports all same parameters as GET method
- **Type Safety**: Same TypeScript interfaces and response types
#### π Enhanced Documentation & Examples
- **Comprehensive Example**: New `examples/user-list-post-example.ts` with detailed usage scenarios
- **Method Comparison**: Examples comparing GET vs POST approaches
- **Pagination Support**: Complete pagination examples using POST method
- **Filter Demonstrations**: Examples with tags, followers, and interaction periods
### Technical Details
#### POST Method Features
- **Endpoint**: `https://openapi.zalo.me/v3.0/oa/user/getlist` (same as GET)
- **Method**: POST with JSON body data
- **Parameters Support**:
- `offset`: Starting position for pagination
- `count`: Number of users to retrieve (max 50)
- `tag_name`: Filter by specific tag
- `last_interaction_period`: Filter by interaction timeframe
- `is_follower`: Filter by follower status
- **Response Format**: Identical to GET method (`UserListResponse`)
- **Error Handling**: Same comprehensive error handling as GET method
#### Use Cases for POST Method
- **Large Parameter Sets**: Better handling of complex filter combinations
- **Request Body Preference**: When POST body is preferred over URL parameters
- **API Consistency**: Matching backend API patterns that expect POST
- **Future Extensibility**: Easier to extend with additional body parameters
### Usage Examples
#### Basic POST Method Usage
```typescript
import { ZaloSDK } from "@warriorteam/redai-zalo-sdk";
const zalo = new ZaloSDK({
appId: "your-app-id",
appSecret: "your-app-secret"
});
// POST method for user list
const result = await zalo.user.postUserList(accessToken, {
offset: 0,
count: 20,
tag_name: "VIP_CUSTOMER",
is_follower: true
});
```
#### Comparing GET vs POST Methods
```typescript
// GET method (existing)
const getResult = await zalo.user.getUserList(accessToken, request);
// POST method (new)
const postResult = await zalo.user.postUserList(accessToken, request);
// Results are identical
console.log(getResult.total === postResult.total); // true
```
### Files Added/Modified
#### New Files
- `examples/user-list-post-example.ts` - Comprehensive POST method examples
#### Modified Files
- `src/services/user.service.ts` - Added `postUserList()` method and `postList` endpoint
### Breaking Changes
None. This release maintains full backward compatibility.
### Migration Guide
No migration required. The new `postUserList()` method is an additional option alongside the existing `getUserList()` method.
Both methods provide identical functionality:
- Use `getUserList()` for GET-based requests (existing behavior)
- Use `postUserList()` for POST-based requests (new option)
---
## [1.5.0] - 2025-01-09
### Added
#### π OA Quality Information API
- **New Method**: `getOAQuality()` in ZNSService for retrieving OA ZNS sending quality information
- **Quality Metrics**: Support for current (48-hour) and 7-day quality assessment periods
- **Quality Levels**: HIGH, MEDIUM, LOW, UNDEFINED quality level indicators
- **Type Safety**: New `ZNSOAQualityInfo` interface for comprehensive type support
#### π§ Enhanced URL Encoding
- **Improved Encoding**: Added `encodeURIComponent` for JSON data in UserService API calls
- **Better Compatibility**: Enhanced URL parameter handling for special characters
- **API Reliability**: Improved data transmission reliability for complex JSON structures
### Technical Details
#### OA Quality Information Features
- **Real-time Quality**: Get current OA ZNS sending quality (48-hour window)
- **Historical Quality**: Get 7-day quality assessment for trend analysis
- **Quality Indicators**:
- `HIGH`: Excellent sending quality
- `MEDIUM`: Average sending quality
- `LOW`: Poor sending quality
- `UNDEFINED`: Quality not determined (no ZNS sent in assessment period)
#### URL Encoding Improvements
- **JSON Parameter Encoding**: Proper encoding of JSON data in URL parameters
- **Special Character Support**: Enhanced handling of special characters in API requests
- **Cross-platform Compatibility**: Improved compatibility across different environments
### Usage Examples
#### OA Quality Information
```typescript
import { ZaloSDK } from "@warriorteam/redai-zalo-sdk";
const zalo = new ZaloSDK({
appId: "you