@warriorteam/redai-zalo-sdk 1.26.1 ā 1.26.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/dist/services/consultation.service.d.ts +50 -17
- package/dist/services/consultation.service.d.ts.map +1 -1
- package/dist/services/consultation.service.js +120 -26
- package/dist/services/consultation.service.js.map +1 -1
- package/package.json +1 -1
- package/examples/article-status-update.ts +0 -178
- package/examples/broadcast-example.ts +0 -201
- package/examples/consultation-service-example.ts +0 -390
- package/examples/get-all-articles-example.ts +0 -171
- package/examples/group-message-list.ts +0 -288
- package/examples/oa-auth-with-pkce.ts +0 -179
- package/examples/promotion-examples.ts +0 -514
- package/examples/send-message-sequence.ts +0 -200
- package/examples/user-list-post-example.ts +0 -186
- package/examples/video-upload-combined.example.ts +0 -228
- package/examples/webhook-message-classification.ts +0 -285
- package/examples/zns-template-edit.example.ts +0 -317
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example: How to use the enhanced getAllArticles and getAllArticlesCombined methods
|
|
3
|
-
*
|
|
4
|
-
* This example demonstrates the new custom API methods that can fetch all articles
|
|
5
|
-
* automatically by handling pagination internally.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { ArticleService } from "../src/services/article.service";
|
|
9
|
-
import { ZaloClient } from "../src/clients/zalo-client";
|
|
10
|
-
|
|
11
|
-
// Initialize the services
|
|
12
|
-
const zaloClient = new ZaloClient();
|
|
13
|
-
const articleService = new ArticleService(zaloClient);
|
|
14
|
-
|
|
15
|
-
// Your access token
|
|
16
|
-
const ACCESS_TOKEN = "your-access-token-here";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Example 1: Get all normal articles with progress tracking
|
|
20
|
-
*/
|
|
21
|
-
async function getAllNormalArticlesExample() {
|
|
22
|
-
console.log("=== Getting All Normal Articles ===");
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
const result = await articleService.getAllArticles(ACCESS_TOKEN, "normal", {
|
|
26
|
-
batchSize: 50, // Fetch 50 articles per API call
|
|
27
|
-
maxArticles: 1000, // Stop after 1000 articles (0 = no limit)
|
|
28
|
-
onProgress: (progress) => {
|
|
29
|
-
console.log(`š Batch ${progress.currentBatch}: ${progress.totalFetched} articles fetched, hasMore: ${progress.hasMore}`);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
console.log(`ā
Completed! Total: ${result.totalFetched} normal articles in ${result.totalBatches} batches`);
|
|
34
|
-
console.log(`š Has more articles available: ${result.hasMore}`);
|
|
35
|
-
|
|
36
|
-
// Access the articles
|
|
37
|
-
result.articles.forEach((article, index) => {
|
|
38
|
-
console.log(`${index + 1}. ${article.title} (ID: ${article.id})`);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
return result;
|
|
42
|
-
} catch (error) {
|
|
43
|
-
console.error("ā Error getting normal articles:", error);
|
|
44
|
-
throw error;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Example 2: Get all video articles
|
|
50
|
-
*/
|
|
51
|
-
async function getAllVideoArticlesExample() {
|
|
52
|
-
console.log("\n=== Getting All Video Articles ===");
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const result = await articleService.getAllArticles(ACCESS_TOKEN, "video", {
|
|
56
|
-
batchSize: 30, // Smaller batch size for videos
|
|
57
|
-
maxArticles: 500, // Limit to 500 videos
|
|
58
|
-
onProgress: (progress) => {
|
|
59
|
-
console.log(`š„ Video Batch ${progress.currentBatch}: ${progress.totalFetched} videos fetched`);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
console.log(`ā
Completed! Total: ${result.totalFetched} video articles in ${result.totalBatches} batches`);
|
|
64
|
-
|
|
65
|
-
return result;
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.error("ā Error getting video articles:", error);
|
|
68
|
-
throw error;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Example 3: Get all articles (both normal and video) combined
|
|
74
|
-
*/
|
|
75
|
-
async function getAllArticlesCombinedExample() {
|
|
76
|
-
console.log("\n=== Getting All Articles Combined ===");
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
const result = await articleService.getAllArticlesCombined(ACCESS_TOKEN, {
|
|
80
|
-
batchSize: 50,
|
|
81
|
-
maxArticlesPerType: 500, // Max 500 per type (normal + video)
|
|
82
|
-
onProgress: (progress) => {
|
|
83
|
-
console.log(`š± ${progress.type.toUpperCase()}: Batch ${progress.currentBatch}, Total: ${progress.totalFetched}`);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
console.log(`ā
All articles fetched!`);
|
|
88
|
-
console.log(`š Total articles: ${result.totalFetched}`);
|
|
89
|
-
console.log(`š Normal articles: ${result.breakdown.normal.totalFetched}`);
|
|
90
|
-
console.log(`š„ Video articles: ${result.breakdown.video.totalFetched}`);
|
|
91
|
-
console.log(`š Total API calls: ${result.totalBatches}`);
|
|
92
|
-
|
|
93
|
-
// You can access all articles combined
|
|
94
|
-
console.log(`\nš All Articles (${result.articles.length}):`);
|
|
95
|
-
result.articles.slice(0, 5).forEach((article, index) => {
|
|
96
|
-
console.log(`${index + 1}. [${article.type?.toUpperCase()}] ${article.title}`);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
if (result.articles.length > 5) {
|
|
100
|
-
console.log(`... and ${result.articles.length - 5} more articles`);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return result;
|
|
104
|
-
} catch (error) {
|
|
105
|
-
console.error("ā Error getting combined articles:", error);
|
|
106
|
-
throw error;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Example 4: Get unlimited articles (fetch everything)
|
|
112
|
-
*/
|
|
113
|
-
async function getAllArticlesUnlimitedExample() {
|
|
114
|
-
console.log("\n=== Getting ALL Articles (No Limit) ===");
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
const result = await articleService.getAllArticles(ACCESS_TOKEN, "normal", {
|
|
118
|
-
batchSize: 100, // Max batch size
|
|
119
|
-
maxArticles: 0, // 0 = no limit, fetch everything
|
|
120
|
-
onProgress: (progress) => {
|
|
121
|
-
console.log(`š Batch ${progress.currentBatch}: ${progress.totalFetched} articles...`);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
console.log(`ā
Fetched EVERYTHING! Total: ${result.totalFetched} articles`);
|
|
126
|
-
console.log(`ā ļø Warning: This fetched all articles without limit!`);
|
|
127
|
-
|
|
128
|
-
return result;
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.error("ā Error getting unlimited articles:", error);
|
|
131
|
-
throw error;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Main function to run all examples
|
|
137
|
-
*/
|
|
138
|
-
async function runExamples() {
|
|
139
|
-
try {
|
|
140
|
-
// Example 1: Normal articles
|
|
141
|
-
await getAllNormalArticlesExample();
|
|
142
|
-
|
|
143
|
-
// Example 2: Video articles
|
|
144
|
-
await getAllVideoArticlesExample();
|
|
145
|
-
|
|
146
|
-
// Example 3: Combined articles
|
|
147
|
-
await getAllArticlesCombinedExample();
|
|
148
|
-
|
|
149
|
-
// Example 4: Unlimited (commented out for safety)
|
|
150
|
-
// await getAllArticlesUnlimitedExample();
|
|
151
|
-
|
|
152
|
-
console.log("\nš All examples completed successfully!");
|
|
153
|
-
|
|
154
|
-
} catch (error) {
|
|
155
|
-
console.error("š„ Example failed:", error);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Export for use in other files
|
|
160
|
-
export {
|
|
161
|
-
getAllNormalArticlesExample,
|
|
162
|
-
getAllVideoArticlesExample,
|
|
163
|
-
getAllArticlesCombinedExample,
|
|
164
|
-
getAllArticlesUnlimitedExample,
|
|
165
|
-
runExamples
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
// Run examples if this file is executed directly
|
|
169
|
-
if (require.main === module) {
|
|
170
|
-
runExamples();
|
|
171
|
-
}
|
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
GroupMessageService,
|
|
3
|
-
GroupMessageItem,
|
|
4
|
-
SendMessageListToGroupRequest,
|
|
5
|
-
SendMessageListToMultipleGroupsRequest,
|
|
6
|
-
GroupMessageProgressInfo,
|
|
7
|
-
MultipleGroupsProgressInfo
|
|
8
|
-
} from "../src/services/group-message.service";
|
|
9
|
-
import { ZaloClient } from "../src/clients/zalo-client";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* VĆ dỄ sį» dỄng API gį»i danh sĆ”ch tin nhįŗÆn tį»i 1 group vį»i callback tracking
|
|
13
|
-
*/
|
|
14
|
-
async function sendMessageListToGroupExample() {
|
|
15
|
-
// Khį»i tįŗ”o Zalo client vĆ group message service
|
|
16
|
-
const zaloClient = new ZaloClient();
|
|
17
|
-
const groupMessageService = new GroupMessageService(zaloClient);
|
|
18
|
-
|
|
19
|
-
// ThÓng tin cẄu hình
|
|
20
|
-
const accessToken = "YOUR_ACCESS_TOKEN";
|
|
21
|
-
const groupId = "GROUP_ID_TO_SEND_TO";
|
|
22
|
-
|
|
23
|
-
// Äį»nh nghÄ©a danh sĆ”ch tin nhįŗÆn thĆ“ng bĆ”o
|
|
24
|
-
const messages: GroupMessageItem[] = [
|
|
25
|
-
{
|
|
26
|
-
type: "text",
|
|
27
|
-
text: "š¢ THĆNG BĆO QUAN TRį»NG",
|
|
28
|
-
delay: 2000,
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
type: "text",
|
|
32
|
-
text: "š§ Hį» thį»ng sįŗ½ bįŗ£o trƬ từ 2:00 - 4:00 sĆ”ng ngĆ y mai (15/09/2024)",
|
|
33
|
-
delay: 1500,
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
type: "image",
|
|
37
|
-
imageUrl: "https://example.com/maintenance-schedule.jpg",
|
|
38
|
-
caption: "Lį»ch trƬnh bįŗ£o trƬ chi tiįŗæt",
|
|
39
|
-
delay: 2000,
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
type: "text",
|
|
43
|
-
text: "ā ļø Trong thį»i gian bįŗ£o trƬ, cĆ”c dį»ch vỄ sau sįŗ½ tįŗ”m ngʰng:\n⢠Website chĆnh\n⢠Mobile App\n⢠API Services",
|
|
44
|
-
delay: 1500,
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
type: "sticker",
|
|
48
|
-
stickerId: "MAINTENANCE_STICKER_ID",
|
|
49
|
-
delay: 1000,
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
type: "text",
|
|
53
|
-
text: "š Hotline hį» trợ khįŗ©n cįŗ„p: 1900-xxxx\nš§ Email: support@company.com",
|
|
54
|
-
},
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
// Callback function Äį» tracking tiįŗæn trƬnh
|
|
58
|
-
const onProgress = (progress: GroupMessageProgressInfo) => {
|
|
59
|
-
const percentage = Math.round(((progress.messageIndex + 1) / progress.totalMessages) * 100);
|
|
60
|
-
|
|
61
|
-
switch (progress.status) {
|
|
62
|
-
case 'started':
|
|
63
|
-
console.log(`š [${percentage}%] BįŗÆt Äįŗ§u gį»i tin nhįŗÆn ${progress.messageIndex + 1}/${progress.totalMessages} (${progress.messageType})`);
|
|
64
|
-
break;
|
|
65
|
-
|
|
66
|
-
case 'completed':
|
|
67
|
-
const duration = progress.endTime ? progress.endTime - progress.startTime : 0;
|
|
68
|
-
console.log(`ā
[${percentage}%] HoĆ n thĆ nh tin nhįŗÆn ${progress.messageIndex + 1}/${progress.totalMessages} - ${duration}ms`);
|
|
69
|
-
break;
|
|
70
|
-
|
|
71
|
-
case 'failed':
|
|
72
|
-
const failDuration = progress.endTime ? progress.endTime - progress.startTime : 0;
|
|
73
|
-
console.log(`ā [${percentage}%] Thįŗ„t bįŗ”i tin nhįŗÆn ${progress.messageIndex + 1}/${progress.totalMessages} - ${failDuration}ms - Lį»i: ${progress.error}`);
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Tįŗ”o request
|
|
79
|
-
const request: SendMessageListToGroupRequest = {
|
|
80
|
-
accessToken,
|
|
81
|
-
groupId,
|
|
82
|
-
messages,
|
|
83
|
-
defaultDelay: 1000, // Delay mįŗ·c Äį»nh 1 giĆ¢y giữa cĆ”c tin nhįŗÆn
|
|
84
|
-
onProgress, // Callback tracking
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
console.log("š¢ BįŗÆt Äįŗ§u gį»i danh sĆ”ch tin nhįŗÆn thĆ“ng bĆ”o tį»i group...");
|
|
89
|
-
console.log(`š„ Group ID: ${groupId}`);
|
|
90
|
-
console.log(`š Tį»ng sį» tin nhįŗÆn: ${messages.length}`);
|
|
91
|
-
console.log("ā".repeat(60));
|
|
92
|
-
|
|
93
|
-
// Gį»i danh sĆ”ch tin nhįŗÆn tį»i group
|
|
94
|
-
const result = await groupMessageService.sendMessageListToGroup(request);
|
|
95
|
-
|
|
96
|
-
console.log("ā".repeat(60));
|
|
97
|
-
console.log("š Kįŗ¾T QUįŗ¢:");
|
|
98
|
-
console.log(`š Tį»ng tin nhįŗÆn: ${result.totalMessages}`);
|
|
99
|
-
console.log(`ā
Thà nh cÓng: ${result.successfulMessages} (${Math.round((result.successfulMessages / result.totalMessages) * 100)}%)`);
|
|
100
|
-
console.log(`ā Thįŗ„t bįŗ”i: ${result.failedMessages} (${Math.round((result.failedMessages / result.totalMessages) * 100)}%)`);
|
|
101
|
-
console.log(`ā±ļø Tį»ng thį»i gian: ${result.totalDuration}ms (${Math.round(result.totalDuration / 1000)}s)`);
|
|
102
|
-
|
|
103
|
-
// In chi tiết từng tin nhắn
|
|
104
|
-
console.log("\nš CHI TIįŗ¾T TỪNG TIN NHįŗ®N:");
|
|
105
|
-
result.messageResults.forEach((messageResult, index) => {
|
|
106
|
-
const status = messageResult.success ? "ā
" : "ā";
|
|
107
|
-
console.log(`${status} [${index + 1}] ${messageResult.messageType} - ${messageResult.duration}ms`);
|
|
108
|
-
|
|
109
|
-
if (!messageResult.success && messageResult.error) {
|
|
110
|
-
console.log(` ā Lį»i: ${messageResult.error}`);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
} catch (error) {
|
|
115
|
-
console.error("ā Lį»i khi gį»i danh sĆ”ch tin nhįŗÆn tį»i group:", error);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* VĆ dỄ gį»i danh sĆ”ch tin nhįŗÆn tį»i nhiį»u groups
|
|
121
|
-
*/
|
|
122
|
-
async function sendMessageListToMultipleGroupsExample() {
|
|
123
|
-
const zaloClient = new ZaloClient();
|
|
124
|
-
const groupMessageService = new GroupMessageService(zaloClient);
|
|
125
|
-
|
|
126
|
-
const accessToken = "YOUR_ACCESS_TOKEN";
|
|
127
|
-
const groupIds = [
|
|
128
|
-
"GROUP_ID_1",
|
|
129
|
-
"GROUP_ID_2",
|
|
130
|
-
"GROUP_ID_3",
|
|
131
|
-
"GROUP_ID_4"
|
|
132
|
-
];
|
|
133
|
-
|
|
134
|
-
// Danh sÔch tin nhắn marketing
|
|
135
|
-
const messages: GroupMessageItem[] = [
|
|
136
|
-
{
|
|
137
|
-
type: "text",
|
|
138
|
-
text: "š FLASH SALE 24H - GIįŗ¢M GIĆ Sį»C!",
|
|
139
|
-
delay: 2000,
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
type: "image",
|
|
143
|
-
imageUrl: "https://example.com/flash-sale-banner.jpg",
|
|
144
|
-
caption: "š„ Giįŗ£m tį»i 70% + Freeship toĆ n quį»c",
|
|
145
|
-
delay: 3000,
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
type: "text",
|
|
149
|
-
text: "ā° Chį» còn 12 giį»! Sį» lượng có hįŗ”n!\nš Mua ngay: https://shop.example.com/flash-sale",
|
|
150
|
-
delay: 1500,
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
type: "sticker",
|
|
154
|
-
stickerId: "SALE_STICKER_ID",
|
|
155
|
-
},
|
|
156
|
-
];
|
|
157
|
-
|
|
158
|
-
// Callback tracking cho multiple groups
|
|
159
|
-
let completedGroups = 0;
|
|
160
|
-
const onProgress = (progress: MultipleGroupsProgressInfo) => {
|
|
161
|
-
switch (progress.status) {
|
|
162
|
-
case 'started':
|
|
163
|
-
console.log(`šÆ BįŗÆt Äįŗ§u gį»i tį»i Group ${progress.groupIndex + 1}/${progress.totalGroups} (${progress.groupId})`);
|
|
164
|
-
break;
|
|
165
|
-
|
|
166
|
-
case 'completed':
|
|
167
|
-
completedGroups++;
|
|
168
|
-
const successRate = progress.result ?
|
|
169
|
-
Math.round((progress.result.successfulMessages / progress.result.totalMessages) * 100) : 0;
|
|
170
|
-
console.log(`ā
Hoà n thà nh Group ${completedGroups}/${progress.totalGroups} - Thà nh cÓng: ${successRate}%`);
|
|
171
|
-
break;
|
|
172
|
-
|
|
173
|
-
case 'failed':
|
|
174
|
-
console.log(`ā Thįŗ„t bįŗ”i Group ${progress.groupIndex + 1}/${progress.totalGroups} - Lį»i: ${progress.error}`);
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const request: SendMessageListToMultipleGroupsRequest = {
|
|
180
|
-
accessToken,
|
|
181
|
-
groupIds,
|
|
182
|
-
messages,
|
|
183
|
-
defaultDelay: 1000, // Delay giữa tin nhắn
|
|
184
|
-
delayBetweenGroups: 3000, // Delay 3 giây giữa cÔc groups
|
|
185
|
-
onProgress,
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
console.log("šÆ BįŗÆt Äįŗ§u chiįŗæn dį»ch marketing tį»i nhiį»u groups...");
|
|
190
|
-
console.log(`š„ Tį»ng sį» groups: ${groupIds.length}`);
|
|
191
|
-
console.log(`š Tį»ng sį» tin nhįŗÆn mį»i group: ${messages.length}`);
|
|
192
|
-
console.log(`š Tį»ng sį» tin nhįŗÆn sįŗ½ gį»i: ${groupIds.length * messages.length}`);
|
|
193
|
-
console.log("ā".repeat(60));
|
|
194
|
-
|
|
195
|
-
const result = await groupMessageService.sendMessageListToMultipleGroups(request);
|
|
196
|
-
|
|
197
|
-
console.log("ā".repeat(60));
|
|
198
|
-
console.log("š Kįŗ¾T QUįŗ¢ Tį»NG QUAN:");
|
|
199
|
-
console.log(`š„ Tį»ng groups: ${result.totalGroups}`);
|
|
200
|
-
console.log(`ā
Groups thà nh cÓng: ${result.successfulGroups} (${Math.round((result.successfulGroups / result.totalGroups) * 100)}%)`);
|
|
201
|
-
console.log(`ā Groups thįŗ„t bįŗ”i: ${result.failedGroups} (${Math.round((result.failedGroups / result.totalGroups) * 100)}%)`);
|
|
202
|
-
console.log(`ā±ļø Tį»ng thį»i gian: ${result.totalDuration}ms (${Math.round(result.totalDuration / 1000)}s)`);
|
|
203
|
-
|
|
204
|
-
console.log("\nš THį»NG KĆ TIN NHįŗ®N:");
|
|
205
|
-
console.log(`ā
Tin nhắn thà nh cÓng: ${result.messageStats.totalSuccessfulMessages}`);
|
|
206
|
-
console.log(`ā Tin nhįŗÆn thįŗ„t bįŗ”i: ${result.messageStats.totalFailedMessages}`);
|
|
207
|
-
console.log(`š Tį»ng tin nhįŗÆn: ${result.messageStats.totalMessages}`);
|
|
208
|
-
console.log(`š Tį»· lį» thĆ nh cĆ“ng: ${Math.round((result.messageStats.totalSuccessfulMessages / result.messageStats.totalMessages) * 100)}%`);
|
|
209
|
-
|
|
210
|
-
// In chi tiết từng group
|
|
211
|
-
console.log("\nš CHI TIįŗ¾T TỪNG GROUP:");
|
|
212
|
-
result.groupResults.forEach((groupResult, index) => {
|
|
213
|
-
const status = groupResult.success ? "ā
" : "ā";
|
|
214
|
-
const successRate = groupResult.messageListResult ?
|
|
215
|
-
Math.round((groupResult.messageListResult.successfulMessages / groupResult.messageListResult.totalMessages) * 100) : 0;
|
|
216
|
-
|
|
217
|
-
console.log(`${status} Group ${index + 1}: ${groupResult.groupId} - ${groupResult.duration}ms - Thà nh cÓng: ${successRate}%`);
|
|
218
|
-
|
|
219
|
-
if (!groupResult.success && groupResult.error) {
|
|
220
|
-
console.log(` ā Lį»i: ${groupResult.error}`);
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
} catch (error) {
|
|
225
|
-
console.error("ā Lį»i khi gį»i danh sĆ”ch tin nhįŗÆn tį»i nhiį»u groups:", error);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* VĆ dỄ gį»i tin nhįŗÆn mention tį»i group
|
|
231
|
-
*/
|
|
232
|
-
async function sendMentionMessageListExample() {
|
|
233
|
-
const zaloClient = new ZaloClient();
|
|
234
|
-
const groupMessageService = new GroupMessageService(zaloClient);
|
|
235
|
-
|
|
236
|
-
const accessToken = "YOUR_ACCESS_TOKEN";
|
|
237
|
-
const groupId = "GROUP_ID";
|
|
238
|
-
|
|
239
|
-
const messages: GroupMessageItem[] = [
|
|
240
|
-
{
|
|
241
|
-
type: "text",
|
|
242
|
-
text: "šÆ Cuį»c hį»p quan trį»ng sįŗÆp diį»
n ra!",
|
|
243
|
-
delay: 1000,
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
type: "mention",
|
|
247
|
-
text: "@Admin @Manager Vui lòng chuįŗ©n bį» tĆ i liį»u cho cuį»c hį»p lĆŗc 14:00",
|
|
248
|
-
mentions: [
|
|
249
|
-
{ user_id: "ADMIN_USER_ID", offset: 0, length: 6 },
|
|
250
|
-
{ user_id: "MANAGER_USER_ID", offset: 7, length: 8 }
|
|
251
|
-
],
|
|
252
|
-
delay: 2000,
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
type: "text",
|
|
256
|
-
text: "š Agenda:\n1. BĆ”o cĆ”o thĆ”ng\n2. Kįŗæ hoįŗ”ch quý mį»i\n3. Q&A",
|
|
257
|
-
},
|
|
258
|
-
];
|
|
259
|
-
|
|
260
|
-
const onProgress = (progress: GroupMessageProgressInfo) => {
|
|
261
|
-
if (progress.status === 'completed') {
|
|
262
|
-
console.log(`ā
ÄĆ£ gį»i tin nhįŗÆn ${progress.messageIndex + 1}: ${progress.messageType}`);
|
|
263
|
-
} else if (progress.status === 'failed') {
|
|
264
|
-
console.log(`ā Gį»i thįŗ„t bįŗ”i tin nhįŗÆn ${progress.messageIndex + 1}: ${progress.error}`);
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
try {
|
|
269
|
-
const result = await groupMessageService.sendMessageListToGroup({
|
|
270
|
-
accessToken,
|
|
271
|
-
groupId,
|
|
272
|
-
messages,
|
|
273
|
-
defaultDelay: 1500,
|
|
274
|
-
onProgress,
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
console.log(`\nš¢ ÄĆ£ gį»i thĆ“ng bĆ”o cuį»c hį»p: ${result.successfulMessages}/${result.totalMessages} tin nhįŗÆn thĆ nh cĆ“ng`);
|
|
278
|
-
} catch (error) {
|
|
279
|
-
console.error("ā Lį»i gį»i thĆ“ng bĆ”o cuį»c hį»p:", error);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Export cĆ”c function Äį» sį» dỄng
|
|
284
|
-
export {
|
|
285
|
-
sendMessageListToGroupExample,
|
|
286
|
-
sendMessageListToMultipleGroupsExample,
|
|
287
|
-
sendMentionMessageListExample,
|
|
288
|
-
};
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example: Official Account Authentication with PKCE
|
|
3
|
-
* Demonstrates how to use the updated createOAAuthUrl method with PKCE support
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { ZaloSDK } from '../src';
|
|
7
|
-
|
|
8
|
-
// Initialize SDK
|
|
9
|
-
const sdk = new ZaloSDK({
|
|
10
|
-
appId: 'your_app_id',
|
|
11
|
-
appSecret: 'your_app_secret',
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
// Example 1: Basic OA Auth without PKCE
|
|
15
|
-
async function basicOAAuth() {
|
|
16
|
-
console.log('=== Basic OA Auth (without PKCE) ===');
|
|
17
|
-
|
|
18
|
-
const redirectUri = 'https://your-app.com/callback';
|
|
19
|
-
|
|
20
|
-
// Create auth URL - state will be auto-generated with 'zalo_oa_' prefix
|
|
21
|
-
const authResult = sdk.auth.createOAAuthUrl(redirectUri);
|
|
22
|
-
|
|
23
|
-
console.log('Authorization URL:', authResult.url);
|
|
24
|
-
console.log('Generated State:', authResult.state);
|
|
25
|
-
|
|
26
|
-
// You can also provide custom state
|
|
27
|
-
const customAuthResult = sdk.auth.createOAAuthUrl(redirectUri, 'my_custom_state');
|
|
28
|
-
console.log('Custom State URL:', customAuthResult.url);
|
|
29
|
-
console.log('Custom State:', customAuthResult.state);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Example 2: OA Auth with PKCE for enhanced security (Manual PKCE)
|
|
33
|
-
async function oaAuthWithPKCE() {
|
|
34
|
-
console.log('\n=== OA Auth with Manual PKCE ===');
|
|
35
|
-
|
|
36
|
-
const redirectUri = 'https://your-app.com/callback';
|
|
37
|
-
|
|
38
|
-
// Step 1: Generate PKCE configuration
|
|
39
|
-
const pkce = sdk.auth.generatePKCE();
|
|
40
|
-
console.log('Generated PKCE:');
|
|
41
|
-
console.log('- Code Verifier:', pkce.code_verifier);
|
|
42
|
-
console.log('- Code Challenge:', pkce.code_challenge);
|
|
43
|
-
console.log('- Challenge Method:', pkce.code_challenge_method);
|
|
44
|
-
|
|
45
|
-
// Step 2: Create auth URL with manual PKCE
|
|
46
|
-
const authResult = sdk.auth.createOAAuthUrl(redirectUri, undefined, pkce, true);
|
|
47
|
-
|
|
48
|
-
console.log('\nAuthorization URL with PKCE:', authResult.url);
|
|
49
|
-
console.log('Generated State:', authResult.state);
|
|
50
|
-
console.log('Used PKCE:', authResult.pkce);
|
|
51
|
-
|
|
52
|
-
// IMPORTANT: Store the code_verifier and state for later use
|
|
53
|
-
// You'll need these when exchanging the authorization code for access token
|
|
54
|
-
console.log('\nā ļø IMPORTANT: Store these values for token exchange:');
|
|
55
|
-
console.log('- Code Verifier:', pkce.code_verifier);
|
|
56
|
-
console.log('- State:', authResult.state);
|
|
57
|
-
|
|
58
|
-
return { pkce, state: authResult.state };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Example 2b: OA Auth with Auto-Generated PKCE
|
|
62
|
-
async function oaAuthWithAutoPKCE() {
|
|
63
|
-
console.log('\n=== OA Auth with Auto-Generated PKCE ===');
|
|
64
|
-
|
|
65
|
-
const redirectUri = 'https://your-app.com/callback';
|
|
66
|
-
|
|
67
|
-
// Create auth URL with auto-generated PKCE (pkce=undefined, usePkce=true)
|
|
68
|
-
const authResult = sdk.auth.createOAAuthUrl(redirectUri, undefined, undefined, true);
|
|
69
|
-
|
|
70
|
-
console.log('Authorization URL with Auto PKCE:', authResult.url);
|
|
71
|
-
console.log('Generated State:', authResult.state);
|
|
72
|
-
console.log('Auto-Generated PKCE:', authResult.pkce);
|
|
73
|
-
|
|
74
|
-
// IMPORTANT: Store the auto-generated PKCE and state
|
|
75
|
-
console.log('\nā ļø IMPORTANT: Store these auto-generated values:');
|
|
76
|
-
console.log('- Code Verifier:', authResult.pkce?.code_verifier);
|
|
77
|
-
console.log('- State:', authResult.state);
|
|
78
|
-
|
|
79
|
-
return authResult;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Example 3: Complete flow - Authorization + Token Exchange
|
|
83
|
-
async function completeOAFlow() {
|
|
84
|
-
console.log('\n=== Complete OA Flow with PKCE ===');
|
|
85
|
-
|
|
86
|
-
const redirectUri = 'https://your-app.com/callback';
|
|
87
|
-
|
|
88
|
-
// Step 1: Generate PKCE and create auth URL
|
|
89
|
-
const pkce = sdk.auth.generatePKCE();
|
|
90
|
-
const authResult = sdk.auth.createOAAuthUrl(redirectUri, 'my_oa_flow', pkce);
|
|
91
|
-
|
|
92
|
-
console.log('1. Redirect user to:', authResult.url);
|
|
93
|
-
console.log('2. Store state and code_verifier:', {
|
|
94
|
-
state: authResult.state,
|
|
95
|
-
code_verifier: pkce.code_verifier
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// Step 2: After user authorizes and returns with code
|
|
99
|
-
// (This would happen in your callback handler)
|
|
100
|
-
const simulateCallback = async (authorizationCode: string, returnedState: string) => {
|
|
101
|
-
console.log('\n3. User returned with authorization code');
|
|
102
|
-
|
|
103
|
-
// Verify state matches
|
|
104
|
-
if (returnedState !== authResult.state) {
|
|
105
|
-
throw new Error('State mismatch - possible CSRF attack');
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Step 3: Exchange code for access token with PKCE
|
|
109
|
-
try {
|
|
110
|
-
const tokenResult = await sdk.auth.getOAAccessToken({
|
|
111
|
-
app_id: 'your_app_id',
|
|
112
|
-
app_secret: 'your_app_secret',
|
|
113
|
-
code: authorizationCode,
|
|
114
|
-
redirect_uri: redirectUri,
|
|
115
|
-
code_verifier: pkce.code_verifier, // Include code_verifier for PKCE
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
console.log('4. Successfully obtained access token:', {
|
|
119
|
-
access_token: tokenResult.access_token.substring(0, 20) + '...',
|
|
120
|
-
expires_in: tokenResult.expires_in,
|
|
121
|
-
has_refresh_token: !!tokenResult.refresh_token
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
return tokenResult;
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.error('Failed to exchange code for token:', error);
|
|
127
|
-
throw error;
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
// Simulate the callback (in real app, this would be handled by your callback endpoint)
|
|
132
|
-
console.log('\n--- Simulating callback ---');
|
|
133
|
-
// await simulateCallback('simulated_auth_code', authResult.state);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Example 4: Using getAuthUrls method
|
|
137
|
-
async function getAuthUrlsExample() {
|
|
138
|
-
console.log('\n=== Get Auth URLs ===');
|
|
139
|
-
|
|
140
|
-
const redirectUri = 'https://your-app.com/callback';
|
|
141
|
-
const pkce = sdk.auth.generatePKCE();
|
|
142
|
-
|
|
143
|
-
const authUrls = sdk.auth.getAuthUrls(redirectUri, pkce);
|
|
144
|
-
|
|
145
|
-
console.log('All auth URLs:', {
|
|
146
|
-
oa_auth_url: authUrls.oa_auth_url,
|
|
147
|
-
social_auth_url: authUrls.social_auth_url,
|
|
148
|
-
token_url: authUrls.token_url,
|
|
149
|
-
refresh_url: authUrls.refresh_url
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Run examples
|
|
154
|
-
async function runExamples() {
|
|
155
|
-
try {
|
|
156
|
-
await basicOAAuth();
|
|
157
|
-
await oaAuthWithPKCE();
|
|
158
|
-
await oaAuthWithAutoPKCE();
|
|
159
|
-
await completeOAFlow();
|
|
160
|
-
await getAuthUrlsExample();
|
|
161
|
-
} catch (error) {
|
|
162
|
-
console.error('Example error:', error);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Export for use in other files
|
|
167
|
-
export {
|
|
168
|
-
basicOAAuth,
|
|
169
|
-
oaAuthWithPKCE,
|
|
170
|
-
oaAuthWithAutoPKCE,
|
|
171
|
-
completeOAFlow,
|
|
172
|
-
getAuthUrlsExample,
|
|
173
|
-
runExamples
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
// Run if this file is executed directly
|
|
177
|
-
if (require.main === module) {
|
|
178
|
-
runExamples();
|
|
179
|
-
}
|