@redonvn/zalo-ads-sdk 0.2.4 → 0.2.13
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/index.d.mts +625 -66
- package/dist/index.d.ts +625 -66
- package/dist/index.js +1686 -144
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1657 -144
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -618,7 +618,10 @@ var ZaloAuthManager = class {
|
|
|
618
618
|
let adsProfile;
|
|
619
619
|
let formProfile;
|
|
620
620
|
if (domain === "ads.zalo.me") {
|
|
621
|
-
|
|
621
|
+
const explicitPlaySession = this.extraCookies?.PLAY_SESSION || cookies["PLAY_SESSION"];
|
|
622
|
+
if (!explicitPlaySession) {
|
|
623
|
+
await this.followOAuthRedirects("https://ads.zalo.me/api/login", cookies, true);
|
|
624
|
+
}
|
|
622
625
|
const whoamiRes = await this.fetchImpl("https://ads.zalo.me/api/whoami", {
|
|
623
626
|
method: "GET",
|
|
624
627
|
headers: {
|
|
@@ -628,12 +631,17 @@ var ZaloAuthManager = class {
|
|
|
628
631
|
}
|
|
629
632
|
});
|
|
630
633
|
this.mergeSetCookies(whoamiRes, cookies);
|
|
634
|
+
if (explicitPlaySession) {
|
|
635
|
+
cookies["PLAY_SESSION"] = explicitPlaySession;
|
|
636
|
+
}
|
|
631
637
|
const whoamiJson = await whoamiRes.json().catch(() => null);
|
|
632
638
|
if (whoamiJson?.data) {
|
|
633
639
|
adsProfile = whoamiJson.data;
|
|
634
640
|
if (adsProfile.id) {
|
|
635
641
|
cookies["ads_user_id"] = String(adsProfile.id);
|
|
636
642
|
}
|
|
643
|
+
} else if (this.extraCookies?.ads_user_id) {
|
|
644
|
+
cookies["ads_user_id"] = this.extraCookies.ads_user_id;
|
|
637
645
|
}
|
|
638
646
|
} else if (domain === "page.ads.zalo.me") {
|
|
639
647
|
await this.followOAuthRedirects("https://page.ads.zalo.me/api/login", cookies, false);
|
|
@@ -1038,6 +1046,469 @@ async function resolveCookies(auth, context) {
|
|
|
1038
1046
|
return { ...auth?.cookies ?? {}, ...await auth?.getCookies?.(context) ?? {} };
|
|
1039
1047
|
}
|
|
1040
1048
|
|
|
1049
|
+
// src/dtos/ads.dto.ts
|
|
1050
|
+
var ZaloAdsFlowChildMessageDto = class {
|
|
1051
|
+
/** Mẫu Zalo đã xác minh dùng 8 cho tin nhắn văn bản trong nhánh. */
|
|
1052
|
+
type;
|
|
1053
|
+
/** Nội dung phản hồi trong nhánh. */
|
|
1054
|
+
text;
|
|
1055
|
+
};
|
|
1056
|
+
var ZaloAdsFlowButtonDto = class {
|
|
1057
|
+
/** 3 = Gửi tin nhắn vào nhánh hội thoại; 10 = Mở website; 12 = Thực hiện cuộc gọi. */
|
|
1058
|
+
type;
|
|
1059
|
+
/** Nhãn nút hiển thị cho người dùng. */
|
|
1060
|
+
text;
|
|
1061
|
+
/** URL website; bắt buộc với type 10. */
|
|
1062
|
+
url;
|
|
1063
|
+
/** Cờ của nút website; dùng true với type 10. */
|
|
1064
|
+
editable;
|
|
1065
|
+
/** Số hotline, bắt buộc khi type là 12. */
|
|
1066
|
+
phone;
|
|
1067
|
+
/** Nhánh tin nhắn, bắt buộc khi type là 3. Với type 10 hoặc 12, để null hoặc bỏ qua. */
|
|
1068
|
+
messages;
|
|
1069
|
+
};
|
|
1070
|
+
var ZaloAdsFlowMessageDto = class {
|
|
1071
|
+
/** 1 = Tin nhắn văn bản; 2 = Tin nhắn kèm hình ảnh. */
|
|
1072
|
+
type;
|
|
1073
|
+
/** Nội dung tin nhắn, tối đa 1961 ký tự. Hỗ trợ biến: {{user_name}}, {{page_name}}, {{user_gender}}, {{user_id}}, {{page_id}}, {{date}}, {{datetime}}. */
|
|
1074
|
+
text;
|
|
1075
|
+
/** URL media hoặc null với tin nhắn văn bản thuần. Bắt buộc và không được null khi type = 2. */
|
|
1076
|
+
url;
|
|
1077
|
+
/** Các nút đi kèm tin nhắn. */
|
|
1078
|
+
buttons;
|
|
1079
|
+
};
|
|
1080
|
+
var ZaloAdsCreateFlowRequest = class {
|
|
1081
|
+
/** ID Official Account sở hữu kịch bản. */
|
|
1082
|
+
oaId;
|
|
1083
|
+
/** Luôn null khi tạo kịch bản mới. */
|
|
1084
|
+
id;
|
|
1085
|
+
/** Tên kịch bản, tối đa 33 ký tự theo giao diện Zalo Ads. */
|
|
1086
|
+
name;
|
|
1087
|
+
/** Các tin nhắn được gửi theo thứ tự trong kịch bản. */
|
|
1088
|
+
messages;
|
|
1089
|
+
};
|
|
1090
|
+
var ZaloAdsCreateFlowResponse = class {
|
|
1091
|
+
/** 0 khi tạo thành công; 1 khi lỗi (ví dụ: đã tới hạn mức 20 kịch bản). */
|
|
1092
|
+
code;
|
|
1093
|
+
/** Thông báo lỗi từ Zalo Ads khi code khác 0. */
|
|
1094
|
+
msg;
|
|
1095
|
+
/** ID kịch bản mới, truyền vào data.ZaloFlowId khi cập nhật quảng cáo Tin nhắn. */
|
|
1096
|
+
data;
|
|
1097
|
+
};
|
|
1098
|
+
var ZaloAdsActionType = {
|
|
1099
|
+
DAT_NGAY: 1,
|
|
1100
|
+
MUA_NGAY: 2,
|
|
1101
|
+
THU_NGAY: 3,
|
|
1102
|
+
TAI_NGAY: 4,
|
|
1103
|
+
XEM_THEM: 5,
|
|
1104
|
+
QUAN_TAM_OA: 6,
|
|
1105
|
+
NGHE_NGAY: 7,
|
|
1106
|
+
CHOI_NGAY: 8,
|
|
1107
|
+
DANG_KY: 9,
|
|
1108
|
+
GOI_DIEN: 10
|
|
1109
|
+
};
|
|
1110
|
+
var ZaloAdsAgeRangeId = {
|
|
1111
|
+
UNDER_18: 10282,
|
|
1112
|
+
FROM_18_TO_24: 10283,
|
|
1113
|
+
FROM_25_TO_34: 10284,
|
|
1114
|
+
FROM_35_TO_44: 10285,
|
|
1115
|
+
FROM_45_TO_54: 10286,
|
|
1116
|
+
FROM_55_TO_64: 10287,
|
|
1117
|
+
OVER_64: 10288
|
|
1118
|
+
};
|
|
1119
|
+
var ZaloAdsOsId = {
|
|
1120
|
+
ANDROID: 0,
|
|
1121
|
+
IOS: 1,
|
|
1122
|
+
PC: 3
|
|
1123
|
+
};
|
|
1124
|
+
var ZaloAdsDeliveryType = {
|
|
1125
|
+
STANDARD: 1,
|
|
1126
|
+
ACCELERATED: 2
|
|
1127
|
+
};
|
|
1128
|
+
var ZaloAdsUpdateVideoAdRequest = class {
|
|
1129
|
+
data;
|
|
1130
|
+
est;
|
|
1131
|
+
isBoostAd;
|
|
1132
|
+
stt;
|
|
1133
|
+
};
|
|
1134
|
+
var ZaloAdsUpdateVideoAdResponse = class {
|
|
1135
|
+
code;
|
|
1136
|
+
msg;
|
|
1137
|
+
data;
|
|
1138
|
+
};
|
|
1139
|
+
var ZaloAdsCreatePostCampaignRequest = class {
|
|
1140
|
+
/** Cấu hình chiến dịch Bài viết (object hoặc chuỗi JSON). */
|
|
1141
|
+
data;
|
|
1142
|
+
};
|
|
1143
|
+
var ZaloAdsCreatePostCampaignResponse = class {
|
|
1144
|
+
code;
|
|
1145
|
+
/** Chuỗi ID quảng cáo mới trong response Zalo Ads. */
|
|
1146
|
+
msg;
|
|
1147
|
+
data;
|
|
1148
|
+
};
|
|
1149
|
+
var ZaloAdsMessageAdUpdateDataDto = class {
|
|
1150
|
+
AdId;
|
|
1151
|
+
CampaignId;
|
|
1152
|
+
MemberId;
|
|
1153
|
+
/** Phiên bản lạc quan lấy từ bản quảng cáo hiện tại. */
|
|
1154
|
+
RowVersion;
|
|
1155
|
+
AdName;
|
|
1156
|
+
/** 20 (hoặc 19) là quảng cáo Tin nhắn. */
|
|
1157
|
+
Type;
|
|
1158
|
+
Settings;
|
|
1159
|
+
Status;
|
|
1160
|
+
StartDate;
|
|
1161
|
+
EndDate;
|
|
1162
|
+
/** ID kịch bản, lấy từ createFlow hoặc listFlowsByOfficialAccount. */
|
|
1163
|
+
ZaloFlowId;
|
|
1164
|
+
ZaloPageId;
|
|
1165
|
+
ZaloFormId;
|
|
1166
|
+
ConversionId;
|
|
1167
|
+
Report;
|
|
1168
|
+
};
|
|
1169
|
+
var ZaloAdsUpdateMessageAdRequest = class {
|
|
1170
|
+
/** Payload quảng cáo đầy đủ (object hoặc chuỗi JSON); service tự tuần tự hóa thành field form data. */
|
|
1171
|
+
data;
|
|
1172
|
+
/** Tham số UI phụ trợ; capture Zalo Ads có giá trị literal "undefined". */
|
|
1173
|
+
est;
|
|
1174
|
+
/** 0 là quảng cáo thông thường, 1 là boost ad. */
|
|
1175
|
+
isBoostAd;
|
|
1176
|
+
/** Trạng thái thao tác của giao diện; mẫu đã ghi nhận 0. */
|
|
1177
|
+
stt;
|
|
1178
|
+
};
|
|
1179
|
+
var ZaloAdsUpdateMessageAdResponse = class {
|
|
1180
|
+
msg;
|
|
1181
|
+
code;
|
|
1182
|
+
data;
|
|
1183
|
+
};
|
|
1184
|
+
|
|
1185
|
+
// src/validators/campaigns.validator.ts
|
|
1186
|
+
var VALID_CAMPAIGN_TYPES = {
|
|
1187
|
+
WEBSITE: 6,
|
|
1188
|
+
FORM: 17,
|
|
1189
|
+
MESSAGE: 18,
|
|
1190
|
+
OA: 3,
|
|
1191
|
+
VIDEO: 14,
|
|
1192
|
+
COMMERCE: 22,
|
|
1193
|
+
ARTICLE: 4,
|
|
1194
|
+
DISPLAY: 20
|
|
1195
|
+
};
|
|
1196
|
+
var VALID_AD_TYPES = {
|
|
1197
|
+
WEBSITE: 5,
|
|
1198
|
+
FORM: 18,
|
|
1199
|
+
MESSAGE: 30,
|
|
1200
|
+
OA: 2,
|
|
1201
|
+
VIDEO: 11,
|
|
1202
|
+
COMMERCE: 28,
|
|
1203
|
+
// Server trả về 28 (không phải 23)
|
|
1204
|
+
ARTICLE: 3,
|
|
1205
|
+
DISPLAY: 21
|
|
1206
|
+
};
|
|
1207
|
+
var ALLOWED_CTAS_PER_AD_TYPE = {
|
|
1208
|
+
// Website (Type 5): 9 CTAs
|
|
1209
|
+
[VALID_AD_TYPES.WEBSITE]: [
|
|
1210
|
+
ZaloAdsActionType.DAT_NGAY,
|
|
1211
|
+
ZaloAdsActionType.MUA_NGAY,
|
|
1212
|
+
ZaloAdsActionType.THU_NGAY,
|
|
1213
|
+
ZaloAdsActionType.TAI_NGAY,
|
|
1214
|
+
ZaloAdsActionType.XEM_THEM,
|
|
1215
|
+
ZaloAdsActionType.NGHE_NGAY,
|
|
1216
|
+
ZaloAdsActionType.CHOI_NGAY,
|
|
1217
|
+
ZaloAdsActionType.DANG_KY,
|
|
1218
|
+
ZaloAdsActionType.GOI_DIEN
|
|
1219
|
+
],
|
|
1220
|
+
// Video (Type 11): 8 CTAs
|
|
1221
|
+
[VALID_AD_TYPES.VIDEO]: [
|
|
1222
|
+
ZaloAdsActionType.DAT_NGAY,
|
|
1223
|
+
ZaloAdsActionType.MUA_NGAY,
|
|
1224
|
+
ZaloAdsActionType.THU_NGAY,
|
|
1225
|
+
ZaloAdsActionType.TAI_NGAY,
|
|
1226
|
+
ZaloAdsActionType.XEM_THEM,
|
|
1227
|
+
ZaloAdsActionType.NGHE_NGAY,
|
|
1228
|
+
ZaloAdsActionType.CHOI_NGAY,
|
|
1229
|
+
ZaloAdsActionType.DANG_KY
|
|
1230
|
+
],
|
|
1231
|
+
// Article (Type 3): 7 CTAs
|
|
1232
|
+
[VALID_AD_TYPES.ARTICLE]: [
|
|
1233
|
+
ZaloAdsActionType.DAT_NGAY,
|
|
1234
|
+
ZaloAdsActionType.MUA_NGAY,
|
|
1235
|
+
ZaloAdsActionType.THU_NGAY,
|
|
1236
|
+
ZaloAdsActionType.TAI_NGAY,
|
|
1237
|
+
ZaloAdsActionType.XEM_THEM,
|
|
1238
|
+
ZaloAdsActionType.CHOI_NGAY,
|
|
1239
|
+
ZaloAdsActionType.DANG_KY
|
|
1240
|
+
],
|
|
1241
|
+
// Form (Type 18): 5 CTAs
|
|
1242
|
+
[VALID_AD_TYPES.FORM]: [
|
|
1243
|
+
ZaloAdsActionType.DAT_NGAY,
|
|
1244
|
+
ZaloAdsActionType.MUA_NGAY,
|
|
1245
|
+
ZaloAdsActionType.THU_NGAY,
|
|
1246
|
+
ZaloAdsActionType.XEM_THEM,
|
|
1247
|
+
ZaloAdsActionType.DANG_KY
|
|
1248
|
+
],
|
|
1249
|
+
// Commerce (Type 28): 3 CTAs
|
|
1250
|
+
[VALID_AD_TYPES.COMMERCE]: [
|
|
1251
|
+
ZaloAdsActionType.DAT_NGAY,
|
|
1252
|
+
ZaloAdsActionType.MUA_NGAY,
|
|
1253
|
+
ZaloAdsActionType.XEM_THEM
|
|
1254
|
+
],
|
|
1255
|
+
// OA (Type 2): Cố định Quan tâm OA (6)
|
|
1256
|
+
[VALID_AD_TYPES.OA]: [ZaloAdsActionType.QUAN_TAM_OA]
|
|
1257
|
+
};
|
|
1258
|
+
function validateCreateCampaignPayload(payload, options = {}) {
|
|
1259
|
+
const errors = [];
|
|
1260
|
+
const warnings = [];
|
|
1261
|
+
if (!payload.name || typeof payload.name !== "string" || !payload.name.trim()) {
|
|
1262
|
+
errors.push("name l\xE0 b\u1EAFt bu\u1ED9c v\xE0 kh\xF4ng \u0111\u01B0\u1EE3c \u0111\u1EC3 tr\u1ED1ng.");
|
|
1263
|
+
} else if (payload.name.length > 255) {
|
|
1264
|
+
errors.push("name kh\xF4ng \u0111\u01B0\u1EE3c v\u01B0\u1EE3t qu\xE1 255 k\xFD t\u1EF1.");
|
|
1265
|
+
}
|
|
1266
|
+
const validTypes = Object.values(VALID_CAMPAIGN_TYPES);
|
|
1267
|
+
if (payload.campaignType === void 0 || payload.campaignType === null) {
|
|
1268
|
+
errors.push("campaignType l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
1269
|
+
} else if (!validTypes.includes(payload.campaignType)) {
|
|
1270
|
+
errors.push(
|
|
1271
|
+
`campaignType: ${payload.campaignType} kh\xF4ng h\u1EE3p l\u1EC7. C\xE1c lo\u1EA1i h\u1ED7 tr\u1EE3: 6 (Website), 17 (Form), 18 (Message), 3 (OA), 14 (Video), 22 (Commerce), 4 (Article), 20 (Display).`
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1274
|
+
if (payload.campaignType !== VALID_CAMPAIGN_TYPES.DISPLAY) {
|
|
1275
|
+
if (!payload.pageId || payload.pageId <= 0) {
|
|
1276
|
+
errors.push("pageId c\u1EE7a Official Account l\xE0 b\u1EAFt bu\u1ED9c \u0111\u1ED1i v\u1EDBi lo\u1EA1i chi\u1EBFn d\u1ECBch n\xE0y (ph\u1EA3i > 0).");
|
|
1277
|
+
}
|
|
1278
|
+
} else {
|
|
1279
|
+
if (payload.pageId !== -1 && payload.pageId !== 0 && !payload.pageId) {
|
|
1280
|
+
warnings.push("Qu\u1EA3ng c\xE1o Display th\u01B0\u1EDDng s\u1EED d\u1EE5ng pageId = -1.");
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
switch (payload.campaignType) {
|
|
1284
|
+
case VALID_CAMPAIGN_TYPES.WEBSITE:
|
|
1285
|
+
if (!payload.targetUrl || typeof payload.targetUrl !== "string" || !payload.targetUrl.trim()) {
|
|
1286
|
+
errors.push("targetUrl l\xE0 b\u1EAFt bu\u1ED9c \u0111\u1ED1i v\u1EDBi Qu\u1EA3ng c\xE1o Website.");
|
|
1287
|
+
} else if (!/^https?:\/\/.+/i.test(payload.targetUrl)) {
|
|
1288
|
+
errors.push("targetUrl ph\u1EA3i l\xE0 m\u1ED9t \u0111\u01B0\u1EDDng link h\u1EE3p l\u1EC7 b\u1EAFt \u0111\u1EA7u b\u1EB1ng http:// ho\u1EB7c https://.");
|
|
1289
|
+
}
|
|
1290
|
+
break;
|
|
1291
|
+
case VALID_CAMPAIGN_TYPES.ARTICLE:
|
|
1292
|
+
if (!payload.articleId || payload.articleId <= 0) {
|
|
1293
|
+
errors.push("articleId l\xE0 b\u1EAFt bu\u1ED9c \u0111\u1ED1i v\u1EDBi Qu\u1EA3ng c\xE1o B\xE0i Vi\u1EBFt (ph\u1EA3i > 0, l\u1EA5y t\u1EEB getOwnedArticles).");
|
|
1294
|
+
}
|
|
1295
|
+
break;
|
|
1296
|
+
case VALID_CAMPAIGN_TYPES.COMMERCE:
|
|
1297
|
+
if (payload.productId === void 0 || payload.productId === null) {
|
|
1298
|
+
warnings.push("Qu\u1EA3ng c\xE1o Commerce n\xEAn ch\u1EC9 \u0111\u1ECBnh productId (ho\u1EB7c -1/0 khi d\xF9ng Instant Page s\u1EA3n ph\u1EA9m).");
|
|
1299
|
+
}
|
|
1300
|
+
break;
|
|
1301
|
+
case VALID_CAMPAIGN_TYPES.FORM:
|
|
1302
|
+
case VALID_CAMPAIGN_TYPES.MESSAGE:
|
|
1303
|
+
break;
|
|
1304
|
+
}
|
|
1305
|
+
if (payload.budgetType !== void 0 && payload.budgetlimit !== void 0) {
|
|
1306
|
+
if (payload.budgetType === 2) {
|
|
1307
|
+
if (payload.budgetlimit > 0 && payload.budgetlimit < 6e5) {
|
|
1308
|
+
errors.push("Ng\xE2n s\xE1ch cho To\xE0n l\u1ECBch ch\u1EA1y t\u1ED1i thi\u1EC3u l\xE0 600,000 VN\u0110.");
|
|
1309
|
+
}
|
|
1310
|
+
} else if (payload.budgetType === 1) {
|
|
1311
|
+
if (payload.budgetlimit > 0 && payload.budgetlimit < 50) {
|
|
1312
|
+
warnings.push("S\u1ED1 l\u01B0\u1EE3ng t\u01B0\u01A1ng t\xE1c m\u1ED7i ng\xE0y t\u1ED1i thi\u1EC3u \u0111\u01B0\u1EE3c khuy\u1EBFn ngh\u1ECB t\u1EEB 50 l\u01B0\u1EE3t.");
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
if (payload.campaignId && payload.campaignId > 0) {
|
|
1317
|
+
if (payload.adId !== 0) {
|
|
1318
|
+
warnings.push("Khi th\xEAm Ad m\u1EDBi v\xE0o chi\u1EBFn d\u1ECBch hi\u1EC7n c\xF3 (Multi-Ad), adId n\xEAn \u0111\u1EC3 b\u1EB1ng 0.");
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
return {
|
|
1322
|
+
isValid: errors.length === 0,
|
|
1323
|
+
errors,
|
|
1324
|
+
warnings
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
function validateUpdateAdPayload(payload, options = {}) {
|
|
1328
|
+
const errors = [];
|
|
1329
|
+
const warnings = [];
|
|
1330
|
+
if (!payload.AdId || payload.AdId <= 0) {
|
|
1331
|
+
errors.push("AdId l\xE0 b\u1EAFt bu\u1ED9c (ph\u1EA3i > 0).");
|
|
1332
|
+
}
|
|
1333
|
+
if (!payload.CampaignId || payload.CampaignId <= 0) {
|
|
1334
|
+
errors.push("CampaignId l\xE0 b\u1EAFt bu\u1ED9c (ph\u1EA3i > 0).");
|
|
1335
|
+
}
|
|
1336
|
+
if (!payload.AdName || !payload.AdName.trim()) {
|
|
1337
|
+
errors.push("AdName l\xE0 b\u1EAFt bu\u1ED9c v\xE0 kh\xF4ng \u0111\u01B0\u1EE3c \u0111\u1EC3 tr\u1ED1ng.");
|
|
1338
|
+
} else if (payload.AdName.length > 255) {
|
|
1339
|
+
errors.push(`AdName qu\xE1 d\xE0i (${payload.AdName.length}/255 k\xFD t\u1EF1).`);
|
|
1340
|
+
}
|
|
1341
|
+
const adInfo = payload.Settings?.AdInfo;
|
|
1342
|
+
if (!adInfo) {
|
|
1343
|
+
errors.push("Settings.AdInfo l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
1344
|
+
} else {
|
|
1345
|
+
if (!adInfo.Caption || !adInfo.Caption.trim()) {
|
|
1346
|
+
errors.push("Settings.AdInfo.Caption l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
1347
|
+
} else {
|
|
1348
|
+
const captionLen = adInfo.Caption.trim().length;
|
|
1349
|
+
if (captionLen < 35) {
|
|
1350
|
+
errors.push(`Settings.AdInfo.Caption qu\xE1 ng\u1EAFn (${captionLen}/35 k\xFD t\u1EF1 t\u1ED1i thi\u1EC3u).`);
|
|
1351
|
+
} else if (captionLen > 90) {
|
|
1352
|
+
errors.push(`Settings.AdInfo.Caption qu\xE1 d\xE0i (${captionLen}/90 k\xFD t\u1EF1 t\u1ED1i \u0111a).`);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
if (adInfo.MoreInfo && adInfo.MoreInfo.trim()) {
|
|
1356
|
+
const moreInfoLen = adInfo.MoreInfo.trim().length;
|
|
1357
|
+
if (moreInfoLen < 90) {
|
|
1358
|
+
warnings.push(`Ch\xFA th\xEDch m\u1EDF r\u1ED9ng n\xEAn \u0111\u1EA1t t\u1ED1i thi\u1EC3u 90 k\xFD t\u1EF1 (hi\u1EC7n t\u1EA1i: ${moreInfoLen}).`);
|
|
1359
|
+
} else if (moreInfoLen > 2e3) {
|
|
1360
|
+
errors.push(`Ch\xFA th\xEDch m\u1EDF r\u1ED9ng v\u01B0\u1EE3t qu\xE1 gi\u1EDBi h\u1EA1n t\u1ED1i \u0111a 2000 k\xFD t\u1EF1 (hi\u1EC7n t\u1EA1i: ${moreInfoLen}).`);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
if (adInfo.Action !== void 0 && payload.Type) {
|
|
1364
|
+
const allowedCtas = ALLOWED_CTAS_PER_AD_TYPE[payload.Type];
|
|
1365
|
+
if (allowedCtas && !allowedCtas.includes(adInfo.Action)) {
|
|
1366
|
+
warnings.push(
|
|
1367
|
+
`M\xE3 h\xE0nh \u0111\u1ED9ng Action: ${adInfo.Action} c\xF3 th\u1EC3 kh\xF4ng n\u1EB1m trong danh m\u1EE5c n\xFAt CTA h\u1ED7 tr\u1EE3 cho Ad Type ${payload.Type}.`
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
const price = payload.Settings?.Price;
|
|
1373
|
+
if (!price) {
|
|
1374
|
+
errors.push("Settings.Price l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
1375
|
+
} else {
|
|
1376
|
+
if (payload.Type === VALID_AD_TYPES.WEBSITE) {
|
|
1377
|
+
if (price.Type !== 1) {
|
|
1378
|
+
errors.push(
|
|
1379
|
+
"\u0110\u1ED1i v\u1EDBi Qu\u1EA3ng c\xE1o Website (Type 5), Price.Type b\u1EAFt bu\u1ED9c ph\u1EA3i b\u1EB1ng 1 (CPC). N\u1EBFu truy\u1EC1n 2 s\u1EBD b\u1ECB m\xE1y ch\u1EE7 l\u1ED7i."
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
if (price.LimitType === 1) {
|
|
1383
|
+
errors.push(
|
|
1384
|
+
"Qu\u1EA3ng c\xE1o Website kh\xF4ng h\u1ED7 tr\u1EE3 LimitType: 1 (t\u1ED5ng ng\xE2n s\xE1ch). B\u1EAFt bu\u1ED9c ph\u1EA3i d\xF9ng LimitType: 0 (ng\xE2n s\xE1ch ng\xE0y)."
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
} else if (payload.Type === VALID_AD_TYPES.COMMERCE || payload.Type === VALID_AD_TYPES.OA || payload.Type === VALID_AD_TYPES.ARTICLE || payload.Type === VALID_AD_TYPES.VIDEO || payload.Type === VALID_AD_TYPES.FORM || payload.Type === VALID_AD_TYPES.MESSAGE) {
|
|
1388
|
+
if (price.Type !== 2) {
|
|
1389
|
+
errors.push(
|
|
1390
|
+
`\u0110\u1ED1i v\u1EDBi Ad Type ${payload.Type}, Price.Type b\u1EAFt bu\u1ED9c ph\u1EA3i b\u1EB1ng 2. N\u1EBFu truy\u1EC1n 1, Zalo Ads s\u1EBD b\xE1o l\u1ED7i code 14 (\u0110\u1EB7t gi\xE1 theo L\u01B0\u1EE3t hi\u1EC3n th\u1ECB kh\xF4ng h\u1ED7 tr\u1EE3).`
|
|
1391
|
+
);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
if (price.Limit !== void 0 && price.Limit > 0 && price.Limit < 50) {
|
|
1395
|
+
warnings.push("S\u1ED1 l\u01B0\u1EE3ng l\u01B0\u1EE3t t\u01B0\u01A1ng t\xE1c \u0111\u1EB7t m\u1ED7i ng\xE0y t\u1ED1i thi\u1EC3u l\xE0 50 l\u01B0\u1EE3t.");
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
const targeting = payload.Settings?.Targeting;
|
|
1399
|
+
if (targeting) {
|
|
1400
|
+
if (targeting.AgeRanges && targeting.AgeRanges.length > 0) {
|
|
1401
|
+
const validAges = Object.values(ZaloAdsAgeRangeId);
|
|
1402
|
+
const invalidAges = targeting.AgeRanges.filter((a) => !validAges.includes(a));
|
|
1403
|
+
if (invalidAges.length > 0) {
|
|
1404
|
+
errors.push(`M\xE3 kho\u1EA3ng tu\u1ED5i kh\xF4ng h\u1EE3p l\u1EC7: ${invalidAges.join(", ")}. H\u1EE3p l\u1EC7: ${validAges.join(", ")}.`);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
if (targeting.OsTypes && targeting.OsTypes.length > 0) {
|
|
1408
|
+
const validOs = Object.values(ZaloAdsOsId);
|
|
1409
|
+
const invalidOs = targeting.OsTypes.filter((os) => !validOs.includes(os));
|
|
1410
|
+
if (invalidOs.length > 0) {
|
|
1411
|
+
errors.push(`M\xE3 h\u1EC7 \u0111i\u1EC1u h\xE0nh kh\xF4ng h\u1EE3p l\u1EC7: ${invalidOs.join(", ")}. H\u1EE3p l\u1EC7: 0 (Android), 1 (iOS), 3 (PC).`);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
const deliveryType = payload.Settings?.DeliveryType;
|
|
1415
|
+
if (deliveryType !== void 0 && deliveryType !== 1 && deliveryType !== 2) {
|
|
1416
|
+
warnings.push("DeliveryType n\xEAn l\xE0 1 (Ti\xEAu chu\u1EA9n) ho\u1EB7c 2 (T\u0103ng t\u1ED1c).");
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
if (payload.Type === VALID_AD_TYPES.FORM) {
|
|
1420
|
+
if (!payload.ZaloFormId || !payload.ZaloFormId.trim()) {
|
|
1421
|
+
errors.push("ZaloFormId l\xE0 b\u1EAFt bu\u1ED9c \u0111\u1ED1i v\u1EDBi Qu\u1EA3ng c\xE1o Form (Ad Type 18).");
|
|
1422
|
+
}
|
|
1423
|
+
} else if (payload.Type === VALID_AD_TYPES.MESSAGE) {
|
|
1424
|
+
if (!payload.Settings?.Image?.LogoUrl) {
|
|
1425
|
+
errors.push("Settings.Image.LogoUrl l\xE0 b\u1EAFt bu\u1ED9c \u0111\u1ED1i v\u1EDBi Qu\u1EA3ng c\xE1o Tin Nh\u1EAFn (Ad Type 30).");
|
|
1426
|
+
}
|
|
1427
|
+
if (!payload.ZaloFlowId && !payload.MiniPageId) {
|
|
1428
|
+
warnings.push("Qu\u1EA3ng c\xE1o Tin Nh\u1EAFn n\xEAn g\u1EAFn ZaloFlowId ho\u1EB7c MiniPageId (Template 4 k\u1ECBch b\u1EA3n tin nh\u1EAFn).");
|
|
1429
|
+
}
|
|
1430
|
+
} else if (payload.Type === VALID_AD_TYPES.COMMERCE) {
|
|
1431
|
+
if (!payload.MiniPageId || payload.MiniPageId <= 0) {
|
|
1432
|
+
errors.push("MiniPageId l\xE0 b\u1EAFt bu\u1ED9c \u0111\u1ED1i v\u1EDBi Qu\u1EA3ng c\xE1o Commerce (Ad Type 28, Template 1/2/3).");
|
|
1433
|
+
}
|
|
1434
|
+
} else if (payload.Type === VALID_AD_TYPES.VIDEO) {
|
|
1435
|
+
if (!payload.ZaloVideoId && !payload.Settings?.Image?.VideoUrl) {
|
|
1436
|
+
errors.push("ZaloVideoId ho\u1EB7c Settings.Image.VideoUrl l\xE0 b\u1EAFt bu\u1ED9c \u0111\u1ED1i v\u1EDBi Qu\u1EA3ng c\xE1o Video (Ad Type 11).");
|
|
1437
|
+
}
|
|
1438
|
+
if (payload.Settings?.Image?.VideoUrl && !payload.Settings.Image.VideoUrl.includes("zascdn.me")) {
|
|
1439
|
+
warnings.push("VideoUrl n\xEAn l\xE0 \u0111\u01B0\u1EDDng d\u1EABn CDN ch\xEDnh th\u1EE9c c\u1EE7a Zalo Ads (*.zascdn.me).");
|
|
1440
|
+
}
|
|
1441
|
+
} else if (payload.Type === VALID_AD_TYPES.ARTICLE) {
|
|
1442
|
+
if (!payload.ZaloFeedId && !payload.articleId) {
|
|
1443
|
+
errors.push("ZaloFeedId ho\u1EB7c articleId l\xE0 b\u1EAFt bu\u1ED9c \u0111\u1ED1i v\u1EDBi Qu\u1EA3ng c\xE1o B\xE0i Vi\u1EBFt (Ad Type 3).");
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
return {
|
|
1447
|
+
isValid: errors.length === 0,
|
|
1448
|
+
errors,
|
|
1449
|
+
warnings
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
function validateRenameCampaignParams(params) {
|
|
1453
|
+
const errors = [];
|
|
1454
|
+
const warnings = [];
|
|
1455
|
+
const id = params.campid || params.campaignId;
|
|
1456
|
+
const name = params.newname || params.newName || params.name;
|
|
1457
|
+
if (!id || id <= 0) {
|
|
1458
|
+
errors.push("ID chi\u1EBFn d\u1ECBch (campid/campaignId) l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i > 0.");
|
|
1459
|
+
}
|
|
1460
|
+
if (!name || !name.trim()) {
|
|
1461
|
+
errors.push("T\xEAn m\u1EDBi c\u1EE7a chi\u1EBFn d\u1ECBch (newname/name) l\xE0 b\u1EAFt bu\u1ED9c v\xE0 kh\xF4ng \u0111\u01B0\u1EE3c \u0111\u1EC3 tr\u1ED1ng.");
|
|
1462
|
+
} else if (name.length > 255) {
|
|
1463
|
+
errors.push(`T\xEAn chi\u1EBFn d\u1ECBch qu\xE1 d\xE0i (${name.length}/255 k\xFD t\u1EF1).`);
|
|
1464
|
+
}
|
|
1465
|
+
return {
|
|
1466
|
+
isValid: errors.length === 0,
|
|
1467
|
+
errors,
|
|
1468
|
+
warnings
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
function validateChangeCampaignStatusParams(params) {
|
|
1472
|
+
const errors = [];
|
|
1473
|
+
const warnings = [];
|
|
1474
|
+
if (!params.campaignId || params.campaignId <= 0) {
|
|
1475
|
+
errors.push("campaignId l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i > 0.");
|
|
1476
|
+
}
|
|
1477
|
+
if (![1, 2, -2].includes(params.status)) {
|
|
1478
|
+
errors.push(`status: ${params.status} kh\xF4ng h\u1EE3p l\u1EC7. C\xE1c tr\u1EA1ng th\xE1i h\u1EE3p l\u1EC7: 1 (B\u1EADt), 2 (T\u1EA1m d\u1EEBng), -2 (X\xF3a).`);
|
|
1479
|
+
}
|
|
1480
|
+
if (params.status === 2 && params.isDraft) {
|
|
1481
|
+
warnings.push(
|
|
1482
|
+
"Chi\u1EBFn d\u1ECBch nh\xE1p ch\u01B0a c\xF3 qu\u1EA3ng c\xE1o ho\u1EA1t \u0111\u1ED9ng khi g\u1ECDi status: 2 (T\u1EA1m d\u1EEBng) s\u1EBD b\u1ECB m\xE1y ch\u1EE7 n\xE9m l\u1ED7i HTTP 500 Internal Server Error. \u0110\u1EC3 d\u1ECDn d\u1EB9p h\xE3y d\xF9ng status: -2 (X\xF3a)."
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
return {
|
|
1486
|
+
isValid: errors.length === 0,
|
|
1487
|
+
errors,
|
|
1488
|
+
warnings
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
function validateDuplicateAdParams(params) {
|
|
1492
|
+
const errors = [];
|
|
1493
|
+
const warnings = [];
|
|
1494
|
+
const id = params.adid || params.adId;
|
|
1495
|
+
if (!id || id <= 0) {
|
|
1496
|
+
errors.push("ID m\u1EABu qu\u1EA3ng c\xE1o g\u1ED1c c\u1EA7n nh\xE2n b\u1EA3n (adid/adId) l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i > 0.");
|
|
1497
|
+
}
|
|
1498
|
+
return {
|
|
1499
|
+
isValid: errors.length === 0,
|
|
1500
|
+
errors,
|
|
1501
|
+
warnings
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
var ZaloCampaignsValidator = {
|
|
1505
|
+
createCampaign: validateCreateCampaignPayload,
|
|
1506
|
+
updateAd: validateUpdateAdPayload,
|
|
1507
|
+
renameCampaign: validateRenameCampaignParams,
|
|
1508
|
+
changeStatus: validateChangeCampaignStatusParams,
|
|
1509
|
+
duplicateAd: validateDuplicateAdParams
|
|
1510
|
+
};
|
|
1511
|
+
|
|
1041
1512
|
// src/services/campaigns.service.ts
|
|
1042
1513
|
var OP_ZaloAdsUpdateVideoAd = {
|
|
1043
1514
|
operationId: "ZaloAds_updateVideoAd",
|
|
@@ -1158,7 +1629,9 @@ var OP_ZaloAdsGetAdById = {
|
|
|
1158
1629
|
"binaryFields": [],
|
|
1159
1630
|
"pathFields": [],
|
|
1160
1631
|
"queryFields": [
|
|
1161
|
-
"adId"
|
|
1632
|
+
"adId",
|
|
1633
|
+
"v",
|
|
1634
|
+
"newWard"
|
|
1162
1635
|
],
|
|
1163
1636
|
"headerFields": [],
|
|
1164
1637
|
"cookieFields": [],
|
|
@@ -1230,7 +1703,9 @@ var OP_ZaloAdsGetZaapiGetadsfromcampaignid = {
|
|
|
1230
1703
|
"pathFields": [],
|
|
1231
1704
|
"queryFields": [
|
|
1232
1705
|
"campaignId",
|
|
1233
|
-
"campId"
|
|
1706
|
+
"campId",
|
|
1707
|
+
"dataType",
|
|
1708
|
+
"v"
|
|
1234
1709
|
],
|
|
1235
1710
|
"headerFields": [],
|
|
1236
1711
|
"cookieFields": [],
|
|
@@ -1531,10 +2006,48 @@ var OP_ZaloAdsDeleteCampaign = {
|
|
|
1531
2006
|
"status"
|
|
1532
2007
|
]
|
|
1533
2008
|
};
|
|
2009
|
+
var OP_ZaloAdsDuplicateAd = {
|
|
2010
|
+
operationId: "zalo_ads_duplicate_ad",
|
|
2011
|
+
method: "POST",
|
|
2012
|
+
endpoint: "/zaapi/DuplicateAd",
|
|
2013
|
+
baseUrl: "https://ads.zalo.me",
|
|
2014
|
+
binaryFields: [],
|
|
2015
|
+
pathFields: [],
|
|
2016
|
+
queryFields: [],
|
|
2017
|
+
headerFields: [],
|
|
2018
|
+
cookieFields: [],
|
|
2019
|
+
bodyFields: ["adid"]
|
|
2020
|
+
};
|
|
2021
|
+
var OP_ZaloAdsCreateSpellCheckV3Job = {
|
|
2022
|
+
operationId: "zalo_ads_create_spell_check_v3_job",
|
|
2023
|
+
method: "POST",
|
|
2024
|
+
endpoint: "/api/spell-check/v3",
|
|
2025
|
+
baseUrl: "https://ads.zalo.me",
|
|
2026
|
+
binaryFields: [],
|
|
2027
|
+
pathFields: [],
|
|
2028
|
+
queryFields: [],
|
|
2029
|
+
headerFields: [],
|
|
2030
|
+
cookieFields: [],
|
|
2031
|
+
bodyFields: ["title", "caption"]
|
|
2032
|
+
};
|
|
2033
|
+
var OP_ZaloAdsGetSpellCheckV3Job = {
|
|
2034
|
+
operationId: "zalo_ads_get_spell_check_v3_job",
|
|
2035
|
+
method: "GET",
|
|
2036
|
+
endpoint: "/api/spell-check/v3/{jobId}",
|
|
2037
|
+
baseUrl: "https://ads.zalo.me",
|
|
2038
|
+
binaryFields: [],
|
|
2039
|
+
pathFields: ["jobId"],
|
|
2040
|
+
queryFields: [],
|
|
2041
|
+
headerFields: [],
|
|
2042
|
+
cookieFields: [],
|
|
2043
|
+
bodyFields: []
|
|
2044
|
+
};
|
|
1534
2045
|
var ZaloCampaignsService = class {
|
|
1535
2046
|
constructor(client) {
|
|
1536
2047
|
this.client = client;
|
|
1537
2048
|
}
|
|
2049
|
+
validator = ZaloCampaignsValidator;
|
|
2050
|
+
static validator = ZaloCampaignsValidator;
|
|
1538
2051
|
updateVideoAd(request, options) {
|
|
1539
2052
|
return this.client.requestFormUrlEncoded(OP_ZaloAdsUpdateVideoAd, this.client.toTransportInput(OP_ZaloAdsUpdateVideoAd, request), options);
|
|
1540
2053
|
}
|
|
@@ -1591,10 +2104,19 @@ var ZaloCampaignsService = class {
|
|
|
1591
2104
|
return this.createPostCampaign(request, options);
|
|
1592
2105
|
}
|
|
1593
2106
|
getAdById(request = {}, options) {
|
|
1594
|
-
|
|
2107
|
+
const normalized = {
|
|
2108
|
+
adId: request.adId,
|
|
2109
|
+
v: request.v ?? 2,
|
|
2110
|
+
newWard: request.newWard ?? true
|
|
2111
|
+
};
|
|
2112
|
+
return this.client.requestWithoutBody(OP_ZaloAdsGetAdById, this.client.toTransportInput(OP_ZaloAdsGetAdById, normalized), options);
|
|
1595
2113
|
}
|
|
1596
2114
|
getAdChangeLog(request, options) {
|
|
1597
|
-
|
|
2115
|
+
const normalized = {
|
|
2116
|
+
adId: request.adId,
|
|
2117
|
+
type: request.type ?? 1
|
|
2118
|
+
};
|
|
2119
|
+
return this.client.requestWithoutBody(OP_ZaloAdsGetAdChangeLog, this.client.toTransportInput(OP_ZaloAdsGetAdChangeLog, normalized), options);
|
|
1598
2120
|
}
|
|
1599
2121
|
getApiPlaceGetalldistricts(request = {}, options) {
|
|
1600
2122
|
return this.client.requestWithoutBody(OP_ZaloAdsGetApiPlaceGetalldistricts, this.client.toTransportInput(OP_ZaloAdsGetApiPlaceGetalldistricts, request), options);
|
|
@@ -1606,7 +2128,16 @@ var ZaloCampaignsService = class {
|
|
|
1606
2128
|
return this.client.requestWithoutBody(OP_ZaloAdsGetZaapiCheckcamphasmanyads, this.client.toTransportInput(OP_ZaloAdsGetZaapiCheckcamphasmanyads, request), options);
|
|
1607
2129
|
}
|
|
1608
2130
|
getZaapiGetadsfromcampaignid(request = {}, options) {
|
|
1609
|
-
|
|
2131
|
+
const normalized = {
|
|
2132
|
+
campaignId: request.campaignId ?? request.campId,
|
|
2133
|
+
dataType: request.dataType ?? 1,
|
|
2134
|
+
v: request.v ?? 2
|
|
2135
|
+
};
|
|
2136
|
+
return this.client.requestWithoutBody(OP_ZaloAdsGetZaapiGetadsfromcampaignid, this.client.toTransportInput(OP_ZaloAdsGetZaapiGetadsfromcampaignid, normalized), options);
|
|
2137
|
+
}
|
|
2138
|
+
/** Alias lấy danh sách quảng cáo của chiến dịch kèm metadata extend và extra.appliedRuleInfo */
|
|
2139
|
+
getAdsFromCampaignId(request = {}, options) {
|
|
2140
|
+
return this.getZaapiGetadsfromcampaignid(request, options);
|
|
1610
2141
|
}
|
|
1611
2142
|
getZaapiGetallpages(request = {}, options) {
|
|
1612
2143
|
return this.client.requestWithoutBody(OP_ZaloAdsGetZaapiGetallpages, this.client.toTransportInput(OP_ZaloAdsGetZaapiGetallpages, request), options);
|
|
@@ -1636,7 +2167,11 @@ var ZaloCampaignsService = class {
|
|
|
1636
2167
|
return this.client.requestWithoutBody(OP_ZaloAdsPostZaapiDeletead, this.client.toTransportInput(OP_ZaloAdsPostZaapiDeletead, request), options);
|
|
1637
2168
|
}
|
|
1638
2169
|
renameCampaign(request, options) {
|
|
1639
|
-
|
|
2170
|
+
const normalized = {
|
|
2171
|
+
campid: request.campid ?? request.campaignId,
|
|
2172
|
+
newname: request.newname ?? request.campaignName ?? request.name
|
|
2173
|
+
};
|
|
2174
|
+
return this.client.requestFormUrlEncoded(OP_ZaloAdsRenameCampaign, this.client.toTransportInput(OP_ZaloAdsRenameCampaign, normalized), options);
|
|
1640
2175
|
}
|
|
1641
2176
|
updateEcommerceAd(request, options) {
|
|
1642
2177
|
return this.client.requestFormUrlEncoded(OP_ZaloAdsUpdateEcommerceAd, this.client.toTransportInput(OP_ZaloAdsUpdateEcommerceAd, request), options);
|
|
@@ -1680,6 +2215,62 @@ var ZaloCampaignsService = class {
|
|
|
1680
2215
|
getSpellCheckV2(...args) {
|
|
1681
2216
|
return this.getApiSpellCheckV2(...args);
|
|
1682
2217
|
}
|
|
2218
|
+
/**
|
|
2219
|
+
* Khởi tạo tác vụ kiểm tra chính tả và vi phạm quy chuẩn quảng cáo AI V3 (POST /api/spell-check/v3).
|
|
2220
|
+
*/
|
|
2221
|
+
createSpellCheckV3Job(request, options) {
|
|
2222
|
+
return this.client.requestJson(
|
|
2223
|
+
OP_ZaloAdsCreateSpellCheckV3Job,
|
|
2224
|
+
this.client.toTransportInput(OP_ZaloAdsCreateSpellCheckV3Job, request),
|
|
2225
|
+
options
|
|
2226
|
+
);
|
|
2227
|
+
}
|
|
2228
|
+
/**
|
|
2229
|
+
* Lấy kết quả kiểm tra chính tả AI V3 theo jobId (GET /api/spell-check/v3/{jobId}).
|
|
2230
|
+
*/
|
|
2231
|
+
getSpellCheckV3Job(request, options) {
|
|
2232
|
+
return this.client.requestWithoutBody(
|
|
2233
|
+
OP_ZaloAdsGetSpellCheckV3Job,
|
|
2234
|
+
this.client.toTransportInput(OP_ZaloAdsGetSpellCheckV3Job, request),
|
|
2235
|
+
options
|
|
2236
|
+
);
|
|
2237
|
+
}
|
|
2238
|
+
/**
|
|
2239
|
+
* Helper kiểm tra chính tả AI V3 trọn gói: Gửi kiểm tra và tự động polling cho đến khi hoàn thành (hoặc timeout).
|
|
2240
|
+
* @param request Tiêu đề (title) và chú thích (caption) cần kiểm tra.
|
|
2241
|
+
* @param pollIntervalMs Chu kỳ polling kiểm tra trạng thái (mặc định: 600ms).
|
|
2242
|
+
* @param timeoutMs Thời gian chờ tối đa (mặc định: 15.000ms = 15 giây).
|
|
2243
|
+
*/
|
|
2244
|
+
async checkSpellV3AndWait(request, pollIntervalMs = 600, timeoutMs = 15e3, options) {
|
|
2245
|
+
const jobRes = await this.createSpellCheckV3Job(request, options);
|
|
2246
|
+
const jobId = jobRes?.data?.jobId;
|
|
2247
|
+
if (!jobId) {
|
|
2248
|
+
throw new Error(`[SpellCheckV3] Kh\xF4ng nh\u1EADn \u0111\u01B0\u1EE3c jobId t\u1EEB Zalo Ads (code: ${jobRes?.code})`);
|
|
2249
|
+
}
|
|
2250
|
+
const startTime = Date.now();
|
|
2251
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
2252
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
2253
|
+
const res = await this.getSpellCheckV3Job({ jobId }, options);
|
|
2254
|
+
if (res?.data?.status === "COMPLETED") {
|
|
2255
|
+
return res;
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
throw new Error(`[SpellCheckV3] Qu\xE1 th\u1EDDi gian ch\u1EDD (${timeoutMs}ms) cho t\xE1c v\u1EE5 ki\u1EC3m tra ch\xEDnh t\u1EA3 jobId: ${jobId}`);
|
|
2259
|
+
}
|
|
2260
|
+
/**
|
|
2261
|
+
* Nhân bản (sao chép / duplicate) một mẫu quảng cáo đã tồn tại trên Zalo Ads (POST /zaapi/DuplicateAd).
|
|
2262
|
+
* Giúp tạo nhanh một bản sao Ad mới trong cùng chiến dịch để thực hiện A/B testing.
|
|
2263
|
+
*/
|
|
2264
|
+
duplicateAd(request, options) {
|
|
2265
|
+
const payload = {
|
|
2266
|
+
adid: request.adid ?? request.adId
|
|
2267
|
+
};
|
|
2268
|
+
return this.client.requestFormUrlEncoded(
|
|
2269
|
+
OP_ZaloAdsDuplicateAd,
|
|
2270
|
+
this.client.toTransportInput(OP_ZaloAdsDuplicateAd, payload),
|
|
2271
|
+
options
|
|
2272
|
+
);
|
|
2273
|
+
}
|
|
1683
2274
|
};
|
|
1684
2275
|
|
|
1685
2276
|
// src/services/account.service.ts
|
|
@@ -1834,6 +2425,365 @@ var ZaloAccountService = class {
|
|
|
1834
2425
|
}
|
|
1835
2426
|
};
|
|
1836
2427
|
|
|
2428
|
+
// src/validators/audiences.validator.ts
|
|
2429
|
+
function validateCreateSavedAudienceRequest(input) {
|
|
2430
|
+
const errors = [];
|
|
2431
|
+
if (!input || typeof input !== "object") {
|
|
2432
|
+
return { isValid: false, errors: ["Input ph\u1EA3i l\xE0 m\u1ED9t object."], data: void 0 };
|
|
2433
|
+
}
|
|
2434
|
+
const raw = input;
|
|
2435
|
+
const name = typeof raw.name === "string" ? raw.name.trim() : "";
|
|
2436
|
+
if (!name) {
|
|
2437
|
+
errors.push("T\xEAn nh\xF3m \u0111\u1ED1i t\u01B0\u1EE3ng 'name' l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
2438
|
+
} else if (name.length < 2 || name.length > 255) {
|
|
2439
|
+
errors.push("T\xEAn nh\xF3m \u0111\u1ED1i t\u01B0\u1EE3ng 'name' ph\u1EA3i t\u1EEB 2 \u0111\u1EBFn 255 k\xFD t\u1EF1.");
|
|
2440
|
+
}
|
|
2441
|
+
let targetingObj;
|
|
2442
|
+
if (typeof raw.targeting === "string") {
|
|
2443
|
+
try {
|
|
2444
|
+
targetingObj = JSON.parse(raw.targeting);
|
|
2445
|
+
} catch {
|
|
2446
|
+
errors.push("Tr\u01B0\u1EDDng 'targeting' l\xE0 chu\u1ED7i JSON kh\xF4ng h\u1EE3p l\u1EC7.");
|
|
2447
|
+
targetingObj = {};
|
|
2448
|
+
}
|
|
2449
|
+
} else if (raw.targeting && typeof raw.targeting === "object") {
|
|
2450
|
+
targetingObj = raw.targeting;
|
|
2451
|
+
} else {
|
|
2452
|
+
errors.push("Tr\u01B0\u1EDDng 'targeting' l\xE0 b\u1EAFt bu\u1ED9c (chu\u1ED7i JSON ho\u1EB7c Object c\u1EA5u h\xECnh targeting).");
|
|
2453
|
+
targetingObj = {};
|
|
2454
|
+
}
|
|
2455
|
+
const gender = typeof targetingObj.Gender === "number" ? targetingObj.Gender : 0;
|
|
2456
|
+
if (![0, 1, 2].includes(gender)) {
|
|
2457
|
+
errors.push("Tr\u01B0\u1EDDng 'Gender' ph\u1EA3i l\xE0 0 (T\u1EA5t c\u1EA3), 1 (Nam), ho\u1EB7c 2 (N\u1EEF).");
|
|
2458
|
+
}
|
|
2459
|
+
const VALID_AGE_RANGES = [10282, 10283, 10284, 10285, 10286, 10287, 10288];
|
|
2460
|
+
const newAgeRanges = Array.isArray(targetingObj.NewAgeRanges) && targetingObj.NewAgeRanges.length > 0 ? targetingObj.NewAgeRanges.map(Number) : VALID_AGE_RANGES;
|
|
2461
|
+
for (const age of newAgeRanges) {
|
|
2462
|
+
if (!VALID_AGE_RANGES.includes(age)) {
|
|
2463
|
+
errors.push(`M\xE3 \u0111\u1ED9 tu\u1ED5i '${age}' kh\xF4ng h\u1EE3p l\u1EC7. C\xE1c m\xE3 cho ph\xE9p: ${VALID_AGE_RANGES.join(", ")}.`);
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
const VALID_OS = [0, 1, 2, 3];
|
|
2467
|
+
const os = Array.isArray(targetingObj.Os) && targetingObj.Os.length > 0 ? targetingObj.Os.map(Number) : [0, 1, 3];
|
|
2468
|
+
for (const o of os) {
|
|
2469
|
+
if (!VALID_OS.includes(o)) {
|
|
2470
|
+
errors.push(`M\xE3 h\u1EC7 \u0111i\u1EC1u h\xE0nh '${o}' kh\xF4ng h\u1EE3p l\u1EC7 (0: Android, 1: iOS, 2: Windows Phone, 3: PC).`);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
let location = targetingObj.Location;
|
|
2474
|
+
if (!location || typeof location !== "object") {
|
|
2475
|
+
location = {
|
|
2476
|
+
Addresses: [],
|
|
2477
|
+
Cities: [],
|
|
2478
|
+
WardTargeting: { Includes: [], Excludes: [] }
|
|
2479
|
+
};
|
|
2480
|
+
} else {
|
|
2481
|
+
location = {
|
|
2482
|
+
Addresses: Array.isArray(location.Addresses) ? location.Addresses : [],
|
|
2483
|
+
Cities: Array.isArray(location.Cities) ? location.Cities : [],
|
|
2484
|
+
WardTargeting: location.WardTargeting && typeof location.WardTargeting === "object" ? location.WardTargeting : { Includes: [], Excludes: [] }
|
|
2485
|
+
};
|
|
2486
|
+
if (location.WardTargeting.Includes && !Array.isArray(location.WardTargeting.Includes)) {
|
|
2487
|
+
errors.push("Tr\u01B0\u1EDDng 'Location.WardTargeting.Includes' ph\u1EA3i l\xE0 m\u1ED9t m\u1EA3ng khu v\u1EF1c.");
|
|
2488
|
+
} else if (Array.isArray(location.WardTargeting.Includes)) {
|
|
2489
|
+
for (const inc of location.WardTargeting.Includes) {
|
|
2490
|
+
if (![1, 2, 3].includes(Number(inc.Id))) {
|
|
2491
|
+
errors.push(`M\xE3 v\xF9ng mi\u1EC1n Id='${inc.Id}' kh\xF4ng h\u1EE3p l\u1EC7 (1: Mi\u1EC1n B\u1EAFc, 2: Mi\u1EC1n Trung, 3: Mi\u1EC1n Nam).`);
|
|
2492
|
+
}
|
|
2493
|
+
if (inc.Provinces && !Array.isArray(inc.Provinces)) {
|
|
2494
|
+
errors.push(`Tr\u01B0\u1EDDng 'Provinces' trong v\xF9ng mi\u1EC1n Id=${inc.Id} ph\u1EA3i l\xE0 m\u1ED9t m\u1EA3ng t\u1EC9nh/th\xE0nh.`);
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
const finalTargeting = {
|
|
2500
|
+
...targetingObj,
|
|
2501
|
+
CustomAudienceId: 0,
|
|
2502
|
+
Gender: gender,
|
|
2503
|
+
NewAgeRanges: newAgeRanges,
|
|
2504
|
+
Os: os,
|
|
2505
|
+
Location: location
|
|
2506
|
+
};
|
|
2507
|
+
return {
|
|
2508
|
+
isValid: errors.length === 0,
|
|
2509
|
+
errors,
|
|
2510
|
+
data: errors.length === 0 ? {
|
|
2511
|
+
name,
|
|
2512
|
+
caType: 1,
|
|
2513
|
+
targeting: JSON.stringify(finalTargeting)
|
|
2514
|
+
} : void 0
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2517
|
+
function validateCreatePhoneAudienceRequest(input) {
|
|
2518
|
+
const errors = [];
|
|
2519
|
+
if (!input || typeof input !== "object") {
|
|
2520
|
+
return { isValid: false, errors: ["Input ph\u1EA3i l\xE0 m\u1ED9t object."], data: void 0 };
|
|
2521
|
+
}
|
|
2522
|
+
const raw = input;
|
|
2523
|
+
const name = typeof raw.name === "string" ? raw.name.trim() : "";
|
|
2524
|
+
if (!name || name.length < 2 || name.length > 255) {
|
|
2525
|
+
errors.push("T\xEAn nh\xF3m \u0111\u1ED1i t\u01B0\u1EE3ng 'name' ph\u1EA3i t\u1EEB 2 \u0111\u1EBFn 255 k\xFD t\u1EF1.");
|
|
2526
|
+
}
|
|
2527
|
+
let targetingObj = {};
|
|
2528
|
+
if (typeof raw.targeting === "string") {
|
|
2529
|
+
try {
|
|
2530
|
+
targetingObj = JSON.parse(raw.targeting);
|
|
2531
|
+
} catch {
|
|
2532
|
+
errors.push("Tr\u01B0\u1EDDng 'targeting' l\xE0 chu\u1ED7i JSON kh\xF4ng h\u1EE3p l\u1EC7.");
|
|
2533
|
+
}
|
|
2534
|
+
} else if (raw.targeting && typeof raw.targeting === "object") {
|
|
2535
|
+
targetingObj = raw.targeting;
|
|
2536
|
+
} else {
|
|
2537
|
+
errors.push("Tr\u01B0\u1EDDng 'targeting' l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
2538
|
+
}
|
|
2539
|
+
const path = typeof targetingObj.Path === "string" ? targetingObj.Path.trim() : "";
|
|
2540
|
+
if (!path) {
|
|
2541
|
+
errors.push("Tr\u01B0\u1EDDng 'Path' (URL file .txt sau khi upload) l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
2542
|
+
}
|
|
2543
|
+
const total = typeof targetingObj.Total === "number" ? targetingObj.Total : 0;
|
|
2544
|
+
if (total <= 0) {
|
|
2545
|
+
errors.push("Tr\u01B0\u1EDDng 'Total' (s\u1ED1 l\u01B0\u1EE3ng S\u0110T trong file) ph\u1EA3i l\u1EDBn h\u01A1n 0.");
|
|
2546
|
+
}
|
|
2547
|
+
return {
|
|
2548
|
+
isValid: errors.length === 0,
|
|
2549
|
+
errors,
|
|
2550
|
+
data: errors.length === 0 ? {
|
|
2551
|
+
name,
|
|
2552
|
+
caType: 3,
|
|
2553
|
+
targeting: JSON.stringify({
|
|
2554
|
+
Name: String(targetingObj.Name || "phones.txt"),
|
|
2555
|
+
Path: path,
|
|
2556
|
+
Total: total,
|
|
2557
|
+
ZaloId: Number(targetingObj.ZaloId || 0),
|
|
2558
|
+
OriginAudience: 0
|
|
2559
|
+
})
|
|
2560
|
+
} : void 0
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
function validateCreateLookalikeAudienceRequest(input) {
|
|
2564
|
+
const errors = [];
|
|
2565
|
+
if (!input || typeof input !== "object") {
|
|
2566
|
+
return { isValid: false, errors: ["Input ph\u1EA3i l\xE0 m\u1ED9t object."], data: void 0 };
|
|
2567
|
+
}
|
|
2568
|
+
const raw = input;
|
|
2569
|
+
const name = typeof raw.name === "string" ? raw.name.trim() : "";
|
|
2570
|
+
if (!name || name.length < 2 || name.length > 255) {
|
|
2571
|
+
errors.push("T\xEAn nh\xF3m \u0111\u1ED1i t\u01B0\u1EE3ng 'name' ph\u1EA3i t\u1EEB 2 \u0111\u1EBFn 255 k\xFD t\u1EF1.");
|
|
2572
|
+
}
|
|
2573
|
+
let targetingObj = {};
|
|
2574
|
+
if (typeof raw.targeting === "string") {
|
|
2575
|
+
try {
|
|
2576
|
+
targetingObj = JSON.parse(raw.targeting);
|
|
2577
|
+
} catch {
|
|
2578
|
+
errors.push("Tr\u01B0\u1EDDng 'targeting' l\xE0 chu\u1ED7i JSON kh\xF4ng h\u1EE3p l\u1EC7.");
|
|
2579
|
+
}
|
|
2580
|
+
} else if (raw.targeting && typeof raw.targeting === "object") {
|
|
2581
|
+
targetingObj = raw.targeting;
|
|
2582
|
+
} else {
|
|
2583
|
+
errors.push("Tr\u01B0\u1EDDng 'targeting' l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
2584
|
+
}
|
|
2585
|
+
const originAudience = Number(targetingObj.OriginAudience);
|
|
2586
|
+
if (!originAudience || originAudience <= 0) {
|
|
2587
|
+
errors.push("Tr\u01B0\u1EDDng 'OriginAudience' (ID nh\xF3m \u0111\u1ED1i t\u01B0\u1EE3ng S\u0110T g\u1ED1c) l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
2588
|
+
}
|
|
2589
|
+
const total = Number(targetingObj.Total);
|
|
2590
|
+
if (![1e6, 15e5, 2e6].includes(total)) {
|
|
2591
|
+
errors.push("Tr\u01B0\u1EDDng 'Total' c\u1EE7a Lookalike ch\u1EC9 ch\u1EA5p nh\u1EADn c\xE1c m\u1ED1c: 1.000.000, 1.500.000 ho\u1EB7c 2.000.000.");
|
|
2592
|
+
}
|
|
2593
|
+
return {
|
|
2594
|
+
isValid: errors.length === 0,
|
|
2595
|
+
errors,
|
|
2596
|
+
data: errors.length === 0 ? {
|
|
2597
|
+
name,
|
|
2598
|
+
caType: 6,
|
|
2599
|
+
targeting: JSON.stringify({
|
|
2600
|
+
OriginAudience: originAudience,
|
|
2601
|
+
Total: total,
|
|
2602
|
+
Name: String(targetingObj.Name || "Lookalike Audience"),
|
|
2603
|
+
Path: String(targetingObj.Path || "")
|
|
2604
|
+
})
|
|
2605
|
+
} : void 0
|
|
2606
|
+
};
|
|
2607
|
+
}
|
|
2608
|
+
function validateCreateFormEventAudienceRequest(input) {
|
|
2609
|
+
const errors = [];
|
|
2610
|
+
if (!input || typeof input !== "object") {
|
|
2611
|
+
return { isValid: false, errors: ["Input ph\u1EA3i l\xE0 m\u1ED9t object."], data: void 0 };
|
|
2612
|
+
}
|
|
2613
|
+
const raw = input;
|
|
2614
|
+
const name = typeof raw.name === "string" ? raw.name.trim() : "";
|
|
2615
|
+
if (!name || name.length < 2 || name.length > 255) {
|
|
2616
|
+
errors.push("T\xEAn nh\xF3m \u0111\u1ED1i t\u01B0\u1EE3ng 'name' ph\u1EA3i t\u1EEB 2 \u0111\u1EBFn 255 k\xFD t\u1EF1.");
|
|
2617
|
+
}
|
|
2618
|
+
let targetingObj = {};
|
|
2619
|
+
if (typeof raw.targeting === "string") {
|
|
2620
|
+
try {
|
|
2621
|
+
targetingObj = JSON.parse(raw.targeting);
|
|
2622
|
+
} catch {
|
|
2623
|
+
errors.push("Tr\u01B0\u1EDDng 'targeting' l\xE0 chu\u1ED7i JSON kh\xF4ng h\u1EE3p l\u1EC7.");
|
|
2624
|
+
}
|
|
2625
|
+
} else if (raw.targeting && typeof raw.targeting === "object") {
|
|
2626
|
+
targetingObj = raw.targeting;
|
|
2627
|
+
}
|
|
2628
|
+
const eventType = Number(targetingObj.EventType);
|
|
2629
|
+
if (![1, 2, 3, 4].includes(eventType)) {
|
|
2630
|
+
errors.push("Tr\u01B0\u1EDDng 'EventType' ph\u1EA3i l\xE0 1 (\u0110\xE3 xem ch\u01B0a g\u1EEDi), 2 (\u0110\xE3 xem & g\u1EEDi), 3 (\u0110\xE3 xem), ho\u1EB7c 4 (M\u1EDF chat t\u1EEB Form).");
|
|
2631
|
+
}
|
|
2632
|
+
const oaId = typeof targetingObj.OaId === "string" ? targetingObj.OaId.trim() : String(targetingObj.OaId || "");
|
|
2633
|
+
if (!oaId) {
|
|
2634
|
+
errors.push("Tr\u01B0\u1EDDng 'OaId' (d\u1EA1ng chu\u1ED7i c\u1EE7a pageId ho\u1EB7c noisedId) l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
2635
|
+
}
|
|
2636
|
+
const numDays = Number(targetingObj.NumDays);
|
|
2637
|
+
if (isNaN(numDays) || numDays < 1 || numDays > 90) {
|
|
2638
|
+
errors.push("Tr\u01B0\u1EDDng 'NumDays' ph\u1EA3i n\u1EB1m trong kho\u1EA3ng t\u1EEB 1 \u0111\u1EBFn 90 ng\xE0y.");
|
|
2639
|
+
}
|
|
2640
|
+
const formIds = Array.isArray(targetingObj.FormIds) ? targetingObj.FormIds : [];
|
|
2641
|
+
if (formIds.length === 0) {
|
|
2642
|
+
errors.push("Tr\u01B0\u1EDDng 'FormIds' ph\u1EA3i ch\u1EE9a \xEDt nh\u1EA5t 1 ID Form.");
|
|
2643
|
+
}
|
|
2644
|
+
return {
|
|
2645
|
+
isValid: errors.length === 0,
|
|
2646
|
+
errors,
|
|
2647
|
+
data: errors.length === 0 ? {
|
|
2648
|
+
name,
|
|
2649
|
+
caType: 12,
|
|
2650
|
+
targeting: JSON.stringify({
|
|
2651
|
+
EventType: eventType,
|
|
2652
|
+
OaId: oaId,
|
|
2653
|
+
NumDays: numDays,
|
|
2654
|
+
FormIds: formIds
|
|
2655
|
+
})
|
|
2656
|
+
} : void 0
|
|
2657
|
+
};
|
|
2658
|
+
}
|
|
2659
|
+
function validateCreateMessageOaEventAudienceRequest(input) {
|
|
2660
|
+
const errors = [];
|
|
2661
|
+
if (!input || typeof input !== "object") {
|
|
2662
|
+
return { isValid: false, errors: ["Input ph\u1EA3i l\xE0 m\u1ED9t object."], data: void 0 };
|
|
2663
|
+
}
|
|
2664
|
+
const raw = input;
|
|
2665
|
+
const name = typeof raw.name === "string" ? raw.name.trim() : "";
|
|
2666
|
+
if (!name || name.length < 2 || name.length > 255) {
|
|
2667
|
+
errors.push("T\xEAn nh\xF3m \u0111\u1ED1i t\u01B0\u1EE3ng 'name' ph\u1EA3i t\u1EEB 2 \u0111\u1EBFn 255 k\xFD t\u1EF1.");
|
|
2668
|
+
}
|
|
2669
|
+
let targetingObj = {};
|
|
2670
|
+
if (typeof raw.targeting === "string") {
|
|
2671
|
+
try {
|
|
2672
|
+
targetingObj = JSON.parse(raw.targeting);
|
|
2673
|
+
} catch {
|
|
2674
|
+
errors.push("Tr\u01B0\u1EDDng 'targeting' l\xE0 chu\u1ED7i JSON kh\xF4ng h\u1EE3p l\u1EC7.");
|
|
2675
|
+
}
|
|
2676
|
+
} else if (raw.targeting && typeof raw.targeting === "object") {
|
|
2677
|
+
targetingObj = raw.targeting;
|
|
2678
|
+
}
|
|
2679
|
+
const eventId = Number(targetingObj.EventId ?? targetingObj.EventType ?? 1);
|
|
2680
|
+
const oaId = typeof targetingObj.OaId === "string" ? targetingObj.OaId.trim() : String(targetingObj.OaId || "");
|
|
2681
|
+
if (!oaId) {
|
|
2682
|
+
errors.push("Tr\u01B0\u1EDDng 'OaId' (chu\u1ED7i pageId ho\u1EB7c noisedId c\u1EE7a OA) l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
2683
|
+
}
|
|
2684
|
+
const numDays = Number(targetingObj.NumDays);
|
|
2685
|
+
if (isNaN(numDays) || numDays < 1 || numDays > 90) {
|
|
2686
|
+
errors.push("Tr\u01B0\u1EDDng 'NumDays' ph\u1EA3i n\u1EB1m trong kho\u1EA3ng t\u1EEB 1 \u0111\u1EBFn 90 ng\xE0y.");
|
|
2687
|
+
}
|
|
2688
|
+
return {
|
|
2689
|
+
isValid: errors.length === 0,
|
|
2690
|
+
errors,
|
|
2691
|
+
data: errors.length === 0 ? {
|
|
2692
|
+
name,
|
|
2693
|
+
caType: 11,
|
|
2694
|
+
targeting: JSON.stringify({
|
|
2695
|
+
EventId: eventId,
|
|
2696
|
+
OaId: oaId,
|
|
2697
|
+
NumDays: numDays
|
|
2698
|
+
})
|
|
2699
|
+
} : void 0
|
|
2700
|
+
};
|
|
2701
|
+
}
|
|
2702
|
+
function validateCreateMessagePersonalEventAudienceRequest(input) {
|
|
2703
|
+
const errors = [];
|
|
2704
|
+
if (!input || typeof input !== "object") {
|
|
2705
|
+
return { isValid: false, errors: ["Input ph\u1EA3i l\xE0 m\u1ED9t object."], data: void 0 };
|
|
2706
|
+
}
|
|
2707
|
+
const raw = input;
|
|
2708
|
+
const name = typeof raw.name === "string" ? raw.name.trim() : "";
|
|
2709
|
+
if (!name || name.length < 2 || name.length > 255) {
|
|
2710
|
+
errors.push("T\xEAn nh\xF3m \u0111\u1ED1i t\u01B0\u1EE3ng 'name' ph\u1EA3i t\u1EEB 2 \u0111\u1EBFn 255 k\xFD t\u1EF1.");
|
|
2711
|
+
}
|
|
2712
|
+
let targetingObj = {};
|
|
2713
|
+
if (typeof raw.targeting === "string") {
|
|
2714
|
+
try {
|
|
2715
|
+
targetingObj = JSON.parse(raw.targeting);
|
|
2716
|
+
} catch {
|
|
2717
|
+
errors.push("Tr\u01B0\u1EDDng 'targeting' l\xE0 chu\u1ED7i JSON kh\xF4ng h\u1EE3p l\u1EC7.");
|
|
2718
|
+
}
|
|
2719
|
+
} else if (raw.targeting && typeof raw.targeting === "object") {
|
|
2720
|
+
targetingObj = raw.targeting;
|
|
2721
|
+
}
|
|
2722
|
+
const eventId = Number(targetingObj.EventId ?? targetingObj.EventType ?? 1);
|
|
2723
|
+
if (![1, 2, 3].includes(eventId)) {
|
|
2724
|
+
errors.push("Tr\u01B0\u1EDDng 'EventId' ph\u1EA3i l\xE0 1 (\u0110\xE3 li\xEAn h\u1EC7), 2 (\u0110\xE3 xem kh\xF4ng li\xEAn h\u1EC7), ho\u1EB7c 3 (Xem carousel kh\xF4ng li\xEAn h\u1EC7).");
|
|
2725
|
+
}
|
|
2726
|
+
const numDays = Number(targetingObj.NumDays);
|
|
2727
|
+
if (isNaN(numDays) || numDays < 1 || numDays > 90) {
|
|
2728
|
+
errors.push("Tr\u01B0\u1EDDng 'NumDays' ph\u1EA3i n\u1EB1m trong kho\u1EA3ng t\u1EEB 1 \u0111\u1EBFn 90 ng\xE0y.");
|
|
2729
|
+
}
|
|
2730
|
+
const pageIds = Array.isArray(targetingObj.PageIds) ? targetingObj.PageIds.map(String) : [];
|
|
2731
|
+
if (pageIds.length === 0) {
|
|
2732
|
+
errors.push("Tr\u01B0\u1EDDng 'PageIds' ph\u1EA3i ch\u1EE9a \xEDt nh\u1EA5t 1 ID trang c\xE1 nh\xE2n.");
|
|
2733
|
+
}
|
|
2734
|
+
return {
|
|
2735
|
+
isValid: errors.length === 0,
|
|
2736
|
+
errors,
|
|
2737
|
+
data: errors.length === 0 ? {
|
|
2738
|
+
name,
|
|
2739
|
+
caType: 14,
|
|
2740
|
+
targeting: JSON.stringify({
|
|
2741
|
+
EventId: eventId,
|
|
2742
|
+
NumDays: numDays,
|
|
2743
|
+
PageIds: pageIds
|
|
2744
|
+
})
|
|
2745
|
+
} : void 0
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
function validateEstimateAudienceRequest(input) {
|
|
2749
|
+
const errors = [];
|
|
2750
|
+
if (!input || typeof input !== "object") {
|
|
2751
|
+
return { isValid: false, errors: ["Input ph\u1EA3i l\xE0 m\u1ED9t object."], data: void 0 };
|
|
2752
|
+
}
|
|
2753
|
+
const raw = input;
|
|
2754
|
+
const gender = typeof raw.gender === "number" ? raw.gender : 0;
|
|
2755
|
+
if (![0, 1, 2].includes(gender)) {
|
|
2756
|
+
errors.push("Tr\u01B0\u1EDDng 'gender' ph\u1EA3i l\xE0 0, 1 ho\u1EB7c 2.");
|
|
2757
|
+
}
|
|
2758
|
+
const ageRanges = Array.isArray(raw.ageRanges) && raw.ageRanges.length > 0 ? raw.ageRanges.map(Number) : [10283, 10284, 10285, 10286, 10287];
|
|
2759
|
+
const platforms = Array.isArray(raw.platforms) && raw.platforms.length > 0 ? raw.platforms.map(Number) : [0, 1, 3];
|
|
2760
|
+
const priceType = typeof raw.priceType === "number" ? raw.priceType : 2;
|
|
2761
|
+
const wardsTargeting = typeof raw.wardsTargeting === "string" && raw.wardsTargeting.trim() ? raw.wardsTargeting.trim() : JSON.stringify({ Includes: [], Excludes: [] });
|
|
2762
|
+
return {
|
|
2763
|
+
isValid: errors.length === 0,
|
|
2764
|
+
errors,
|
|
2765
|
+
data: errors.length === 0 ? {
|
|
2766
|
+
gender,
|
|
2767
|
+
ageRanges,
|
|
2768
|
+
platforms,
|
|
2769
|
+
locations: typeof raw.locations === "string" ? raw.locations : "",
|
|
2770
|
+
provinces: Array.isArray(raw.provinces) ? raw.provinces.map(Number) : [],
|
|
2771
|
+
wardsTargeting,
|
|
2772
|
+
priceType,
|
|
2773
|
+
number: typeof raw.number === "string" ? raw.number : String(raw.number || "0")
|
|
2774
|
+
} : void 0
|
|
2775
|
+
};
|
|
2776
|
+
}
|
|
2777
|
+
var ZaloAudiencesValidator = {
|
|
2778
|
+
createSavedAudience: validateCreateSavedAudienceRequest,
|
|
2779
|
+
createPhoneAudience: validateCreatePhoneAudienceRequest,
|
|
2780
|
+
createLookalikeAudience: validateCreateLookalikeAudienceRequest,
|
|
2781
|
+
createFormEventAudience: validateCreateFormEventAudienceRequest,
|
|
2782
|
+
createMessageOaEventAudience: validateCreateMessageOaEventAudienceRequest,
|
|
2783
|
+
createMessagePersonalEventAudience: validateCreateMessagePersonalEventAudienceRequest,
|
|
2784
|
+
estimateAudience: validateEstimateAudienceRequest
|
|
2785
|
+
};
|
|
2786
|
+
|
|
1837
2787
|
// src/services/audiences.service.ts
|
|
1838
2788
|
var OP_ZaloAdsCreateCustomAudience = {
|
|
1839
2789
|
"operationId": "ZaloAds_createCustomAudience",
|
|
@@ -1934,6 +2884,7 @@ var ZaloAudiencesService = class {
|
|
|
1934
2884
|
constructor(client) {
|
|
1935
2885
|
this.client = client;
|
|
1936
2886
|
}
|
|
2887
|
+
static validator = ZaloAudiencesValidator;
|
|
1937
2888
|
postApiAdEstimateaudience(request, options) {
|
|
1938
2889
|
return this.client.requestJson(OP_ZaloAdsPostApiAdEstimateaudience, this.client.toTransportInput(OP_ZaloAdsPostApiAdEstimateaudience, request), options);
|
|
1939
2890
|
}
|
|
@@ -1986,7 +2937,22 @@ var ZaloAudiencesService = class {
|
|
|
1986
2937
|
}
|
|
1987
2938
|
/** Tạo mới Nhóm đối tượng phát sinh sự kiện trên Kịch bản Tin nhắn cá nhân (`caType = 14` - `MESSAGE_PERSONAL_EVENT_AUDIENCE`). */
|
|
1988
2939
|
createMessagePersonalEventAudience(request, options) {
|
|
1989
|
-
|
|
2940
|
+
let targetingObj;
|
|
2941
|
+
if (typeof request.targeting === "string") {
|
|
2942
|
+
try {
|
|
2943
|
+
targetingObj = JSON.parse(request.targeting);
|
|
2944
|
+
} catch {
|
|
2945
|
+
targetingObj = null;
|
|
2946
|
+
}
|
|
2947
|
+
} else if (request.targeting && typeof request.targeting === "object") {
|
|
2948
|
+
targetingObj = { ...request.targeting };
|
|
2949
|
+
}
|
|
2950
|
+
if (targetingObj && typeof targetingObj === "object") {
|
|
2951
|
+
if (targetingObj.EventId === void 0 && targetingObj.EventType !== void 0) {
|
|
2952
|
+
targetingObj.EventId = targetingObj.EventType;
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
const targetingStr = targetingObj ? JSON.stringify(targetingObj) : typeof request.targeting === "string" ? request.targeting : JSON.stringify(request.targeting);
|
|
1990
2956
|
return this.createCustomAudience({ name: request.name, caType: 14, targeting: targetingStr }, options);
|
|
1991
2957
|
}
|
|
1992
2958
|
/** Tạo mới Nhóm đối tượng từ Trung tâm Lead - Zalo Form (`caType = 10` - `FORM_AUDIENCE`). */
|
|
@@ -2206,6 +3172,25 @@ var OP_ZaloAdsUpsertCustomReport = {
|
|
|
2206
3172
|
"isDefault"
|
|
2207
3173
|
]
|
|
2208
3174
|
};
|
|
3175
|
+
var OP_ZaloAdsDownloadReport = {
|
|
3176
|
+
operationId: "zalo_ads_download_report",
|
|
3177
|
+
method: "POST",
|
|
3178
|
+
endpoint: "/api/collect",
|
|
3179
|
+
baseUrl: "https://ads.zalo.me",
|
|
3180
|
+
binaryFields: [],
|
|
3181
|
+
pathFields: [],
|
|
3182
|
+
queryFields: [],
|
|
3183
|
+
headerFields: [],
|
|
3184
|
+
cookieFields: [],
|
|
3185
|
+
bodyFields: [
|
|
3186
|
+
"t",
|
|
3187
|
+
"adIds",
|
|
3188
|
+
"status",
|
|
3189
|
+
"dateFilter",
|
|
3190
|
+
"reportKind",
|
|
3191
|
+
"et"
|
|
3192
|
+
]
|
|
3193
|
+
};
|
|
2209
3194
|
var ZaloReportsService = class {
|
|
2210
3195
|
constructor(client) {
|
|
2211
3196
|
this.client = client;
|
|
@@ -2268,6 +3253,18 @@ var ZaloReportsService = class {
|
|
|
2268
3253
|
getCampaignReportLegacy(...args) {
|
|
2269
3254
|
return this.getZaapiCampaignReportcampaign(...args);
|
|
2270
3255
|
}
|
|
3256
|
+
/** Xuất và tải trực tiếp tệp tin Excel (.xlsx / Blob) báo cáo hiệu quả quảng cáo (POST /api/collect) */
|
|
3257
|
+
downloadReport(request, options) {
|
|
3258
|
+
const payload = {
|
|
3259
|
+
t: request.t ?? "cd.download.report",
|
|
3260
|
+
status: request.status ?? 2,
|
|
3261
|
+
dateFilter: request.dateFilter ?? 0,
|
|
3262
|
+
reportKind: request.reportKind ?? 100001,
|
|
3263
|
+
et: request.et ?? Math.floor(Date.now() / 1e3),
|
|
3264
|
+
adIds: request.adIds
|
|
3265
|
+
};
|
|
3266
|
+
return this.client.requestJson(OP_ZaloAdsDownloadReport, this.client.toTransportInput(OP_ZaloAdsDownloadReport, payload), options);
|
|
3267
|
+
}
|
|
2271
3268
|
};
|
|
2272
3269
|
|
|
2273
3270
|
// src/services/conversions.service.ts
|
|
@@ -2534,12 +3531,242 @@ var ZaloAutomatedRulesService = class {
|
|
|
2534
3531
|
changeAutomatedRuleStatus(request, options) {
|
|
2535
3532
|
return this.client.requestJson(OP_ZaloAdsChangeAutomatedRuleStatus, this.client.toTransportInput(OP_ZaloAdsChangeAutomatedRuleStatus, request), options);
|
|
2536
3533
|
}
|
|
2537
|
-
getAutomatedRuleHistory(request, options) {
|
|
2538
|
-
return this.client.requestWithoutBody(OP_ZaloAdsGetAutomatedRuleHistory, this.client.toTransportInput(OP_ZaloAdsGetAutomatedRuleHistory, request), options);
|
|
3534
|
+
getAutomatedRuleHistory(request, options) {
|
|
3535
|
+
return this.client.requestWithoutBody(OP_ZaloAdsGetAutomatedRuleHistory, this.client.toTransportInput(OP_ZaloAdsGetAutomatedRuleHistory, request), options);
|
|
3536
|
+
}
|
|
3537
|
+
deleteAutomatedRule(request, options) {
|
|
3538
|
+
return this.client.requestJson(OP_ZaloAdsDeleteAutomatedRule, this.client.toTransportInput(OP_ZaloAdsDeleteAutomatedRule, request), options);
|
|
3539
|
+
}
|
|
3540
|
+
};
|
|
3541
|
+
|
|
3542
|
+
// src/validators/flows.validator.ts
|
|
3543
|
+
var MAX_INT_32 = 2147483647;
|
|
3544
|
+
function is32BitPositiveInteger(val) {
|
|
3545
|
+
if (typeof val === "number") {
|
|
3546
|
+
return Number.isInteger(val) && val > 0 && val <= MAX_INT_32;
|
|
3547
|
+
}
|
|
3548
|
+
if (typeof val === "string" && val.trim() !== "") {
|
|
3549
|
+
const num = Number(val);
|
|
3550
|
+
return Number.isInteger(num) && num > 0 && num <= MAX_INT_32;
|
|
3551
|
+
}
|
|
3552
|
+
return false;
|
|
3553
|
+
}
|
|
3554
|
+
function isPositiveInteger(val) {
|
|
3555
|
+
if (typeof val === "number") return Number.isInteger(val) && val > 0;
|
|
3556
|
+
if (typeof val === "string" && val.trim() !== "") {
|
|
3557
|
+
const num = Number(val);
|
|
3558
|
+
return Number.isInteger(num) && num > 0;
|
|
3559
|
+
}
|
|
3560
|
+
return false;
|
|
3561
|
+
}
|
|
3562
|
+
function isNonNegativeInteger(val) {
|
|
3563
|
+
if (typeof val === "number") return Number.isInteger(val) && val >= 0;
|
|
3564
|
+
if (typeof val === "string" && val.trim() !== "") {
|
|
3565
|
+
const num = Number(val);
|
|
3566
|
+
return Number.isInteger(num) && num >= 0;
|
|
3567
|
+
}
|
|
3568
|
+
return false;
|
|
3569
|
+
}
|
|
3570
|
+
function toPositiveInteger(val) {
|
|
3571
|
+
return typeof val === "number" ? Math.floor(val) : parseInt(String(val), 10);
|
|
3572
|
+
}
|
|
3573
|
+
function validateFlowButtons(buttons, errors, prefix) {
|
|
3574
|
+
if (!Array.isArray(buttons)) {
|
|
3575
|
+
errors.push(`${prefix}: Tr\u01B0\u1EDDng 'buttons' ph\u1EA3i l\xE0 m\u1ED9t m\u1EA3ng.`);
|
|
3576
|
+
return [];
|
|
3577
|
+
}
|
|
3578
|
+
const cleanButtons = [];
|
|
3579
|
+
buttons.forEach((btn, idx) => {
|
|
3580
|
+
const btnPrefix = `${prefix}.buttons[${idx}]`;
|
|
3581
|
+
if (!btn || typeof btn !== "object") {
|
|
3582
|
+
errors.push(`${btnPrefix}: N\xFAt b\u1EA5m ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng.`);
|
|
3583
|
+
return;
|
|
3584
|
+
}
|
|
3585
|
+
const b = btn;
|
|
3586
|
+
if (typeof b.text !== "string" || b.text.trim() === "") {
|
|
3587
|
+
errors.push(`${btnPrefix}: Nh\xE3n n\xFAt 'text' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 kh\xF4ng \u0111\u01B0\u1EE3c \u0111\u1EC3 tr\u1ED1ng.`);
|
|
3588
|
+
}
|
|
3589
|
+
const btnType = Number(b.type);
|
|
3590
|
+
if (![3, 10, 12].includes(btnType)) {
|
|
3591
|
+
errors.push(`${btnPrefix}: Lo\u1EA1i n\xFAt 'type' ph\u1EA3i l\xE0 3 (nh\xE1nh h\u1ED9i tho\u1EA1i), 10 (m\u1EDF website), ho\u1EB7c 12 (g\u1ECDi hotline).`);
|
|
3592
|
+
}
|
|
3593
|
+
const cleanBtn = {
|
|
3594
|
+
type: btnType,
|
|
3595
|
+
text: String(b.text ?? "").trim(),
|
|
3596
|
+
editable: b.editable !== false
|
|
3597
|
+
};
|
|
3598
|
+
if (btnType === 10) {
|
|
3599
|
+
if (typeof b.url !== "string" || b.url.trim() === "") {
|
|
3600
|
+
errors.push(`${btnPrefix}: N\xFAt lo\u1EA1i 10 (m\u1EDF website) b\u1EAFt bu\u1ED9c ph\u1EA3i c\xF3 tr\u01B0\u1EDDng 'url' h\u1EE3p l\u1EC7.`);
|
|
3601
|
+
} else {
|
|
3602
|
+
cleanBtn.url = b.url.trim();
|
|
3603
|
+
}
|
|
3604
|
+
} else if (btnType === 12) {
|
|
3605
|
+
if (typeof b.phone !== "string" || b.phone.trim() === "") {
|
|
3606
|
+
errors.push(`${btnPrefix}: N\xFAt lo\u1EA1i 12 (g\u1ECDi hotline) b\u1EAFt bu\u1ED9c ph\u1EA3i c\xF3 tr\u01B0\u1EDDng 'phone' h\u1EE3p l\u1EC7.`);
|
|
3607
|
+
} else {
|
|
3608
|
+
cleanBtn.phone = b.phone.trim();
|
|
3609
|
+
}
|
|
3610
|
+
} else if (btnType === 3) {
|
|
3611
|
+
if (!Array.isArray(b.messages) || b.messages.length === 0) {
|
|
3612
|
+
errors.push(`${btnPrefix}: N\xFAt lo\u1EA1i 3 (nh\xE1nh h\u1ED9i tho\u1EA1i) b\u1EAFt bu\u1ED9c ph\u1EA3i c\xF3 m\u1EA3ng 'messages' ch\u1EE9a \xEDt nh\u1EA5t 1 tin nh\u1EAFn con ph\u1EA3n h\u1ED3i.`);
|
|
3613
|
+
} else {
|
|
3614
|
+
const cleanChildMsgs = [];
|
|
3615
|
+
b.messages.forEach((child, cIdx) => {
|
|
3616
|
+
const childPrefix = `${btnPrefix}.messages[${cIdx}]`;
|
|
3617
|
+
if (!child || typeof child !== "object") {
|
|
3618
|
+
errors.push(`${childPrefix}: Tin nh\u1EAFn con ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng.`);
|
|
3619
|
+
return;
|
|
3620
|
+
}
|
|
3621
|
+
const c = child;
|
|
3622
|
+
if (typeof c.text !== "string" || c.text.trim() === "") {
|
|
3623
|
+
errors.push(`${childPrefix}: N\u1ED9i dung tin nh\u1EAFn con 'text' kh\xF4ng \u0111\u01B0\u1EE3c \u0111\u1EC3 tr\u1ED1ng.`);
|
|
3624
|
+
} else if (c.text.length > 1961) {
|
|
3625
|
+
errors.push(`${childPrefix}: N\u1ED9i dung tin nh\u1EAFn con v\u01B0\u1EE3t qu\xE1 1961 k\xFD t\u1EF1 (hi\u1EC7n c\xF3 ${c.text.length} k\xFD t\u1EF1).`);
|
|
3626
|
+
}
|
|
3627
|
+
cleanChildMsgs.push({
|
|
3628
|
+
type: 8,
|
|
3629
|
+
text: String(c.text ?? "").trim()
|
|
3630
|
+
});
|
|
3631
|
+
});
|
|
3632
|
+
cleanBtn.messages = cleanChildMsgs;
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
cleanButtons.push(cleanBtn);
|
|
3636
|
+
});
|
|
3637
|
+
return cleanButtons;
|
|
3638
|
+
}
|
|
3639
|
+
function validateFlowMessages(messages, errors) {
|
|
3640
|
+
if (!Array.isArray(messages)) {
|
|
3641
|
+
errors.push("Tr\u01B0\u1EDDng 'messages' ph\u1EA3i l\xE0 m\u1ED9t m\u1EA3ng ch\u1EE9a tin nh\u1EAFn k\u1ECBch b\u1EA3n.");
|
|
3642
|
+
return [];
|
|
3643
|
+
}
|
|
3644
|
+
if (messages.length !== 1) {
|
|
3645
|
+
errors.push(`Zalo Ads ch\u1EC9 cho ph\xE9p k\u1ECBch b\u1EA3n ch\u1EE9a DUY NH\u1EA4T 1 tin nh\u1EAFn ch\xE0o m\u1EEBng c\u1EA5p cao (hi\u1EC7n c\xF3 ${messages.length} tin nh\u1EAFn). C\xE1c nh\xE1nh h\u1ED9i tho\u1EA1i ti\u1EBFp theo ph\u1EA3i c\u1EA5u h\xECnh b\xEAn trong n\xFAt b\u1EA5m lo\u1EA1i 3 (type=3).`);
|
|
3646
|
+
return [];
|
|
3647
|
+
}
|
|
3648
|
+
const msg = messages[0];
|
|
3649
|
+
if (!msg || typeof msg !== "object") {
|
|
3650
|
+
errors.push("Tin nh\u1EAFn k\u1ECBch b\u1EA3n ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng.");
|
|
3651
|
+
return [];
|
|
3652
|
+
}
|
|
3653
|
+
const m = msg;
|
|
3654
|
+
const msgType = Number(m.type);
|
|
3655
|
+
if (![1, 2].includes(msgType)) {
|
|
3656
|
+
errors.push("Lo\u1EA1i tin nh\u1EAFn 'type' ph\u1EA3i l\xE0 1 (v\u0103n b\u1EA3n) ho\u1EB7c 2 (k\xE8m h\xECnh \u1EA3nh).");
|
|
3657
|
+
}
|
|
3658
|
+
if (typeof m.text !== "string" || m.text.trim() === "") {
|
|
3659
|
+
errors.push("N\u1ED9i dung tin nh\u1EAFn 'text' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 kh\xF4ng \u0111\u01B0\u1EE3c \u0111\u1EC3 tr\u1ED1ng.");
|
|
3660
|
+
} else if (m.text.length > 1961) {
|
|
3661
|
+
errors.push(`N\u1ED9i dung tin nh\u1EAFn v\u01B0\u1EE3t qu\xE1 1961 k\xFD t\u1EF1 (hi\u1EC7n c\xF3 ${m.text.length} k\xFD t\u1EF1).`);
|
|
3662
|
+
}
|
|
3663
|
+
const cleanMsg = {
|
|
3664
|
+
type: msgType,
|
|
3665
|
+
text: String(m.text ?? "").trim()
|
|
3666
|
+
};
|
|
3667
|
+
if (msgType === 2) {
|
|
3668
|
+
if (typeof m.url !== "string" || m.url.trim() === "") {
|
|
3669
|
+
errors.push("Tin nh\u1EAFn lo\u1EA1i 2 (k\xE8m \u1EA3nh) b\u1EAFt bu\u1ED9c ph\u1EA3i c\xF3 tr\u01B0\u1EDDng 'url' ch\u1EE9a \u0111\u01B0\u1EDDng d\u1EABn h\xECnh \u1EA3nh.");
|
|
3670
|
+
} else {
|
|
3671
|
+
cleanMsg.url = m.url.trim();
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
if (m.buttons !== void 0 && m.buttons !== null) {
|
|
3675
|
+
cleanMsg.buttons = validateFlowButtons(m.buttons, errors, "messages[0]");
|
|
3676
|
+
}
|
|
3677
|
+
return [cleanMsg];
|
|
3678
|
+
}
|
|
3679
|
+
function validateListFlowsByOfficialAccountRequest(input) {
|
|
3680
|
+
const errors = [];
|
|
3681
|
+
if (!input || typeof input !== "object") {
|
|
3682
|
+
return { isValid: false, errors: ["D\u1EEF li\u1EC7u y\xEAu c\u1EA7u l\u1EA5y danh s\xE1ch k\u1ECBch b\u1EA3n ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng."] };
|
|
3683
|
+
}
|
|
3684
|
+
const obj = input;
|
|
3685
|
+
if (!is32BitPositiveInteger(obj.oaId)) {
|
|
3686
|
+
errors.push("Tr\u01B0\u1EDDng 'oaId' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 m\xE3 pageId n\u1ED9i b\u1ED9 ki\u1EC3u s\u1ED1 nguy\xEAn 32-bit (<= 2,147,483,647). Kh\xF4ng truy\u1EC1n chu\u1ED7i noisedId c\xF4ng khai (18 ch\u1EEF s\u1ED1) v\xEC backend Play Framework s\u1EBD t\u1EEB ch\u1ED1i.");
|
|
3687
|
+
}
|
|
3688
|
+
if (!isPositiveInteger(obj.limit)) {
|
|
3689
|
+
errors.push("Tr\u01B0\u1EDDng 'limit' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
3690
|
+
}
|
|
3691
|
+
if (obj.offset !== void 0 && obj.offset !== null && !isNonNegativeInteger(obj.offset)) {
|
|
3692
|
+
errors.push("Tr\u01B0\u1EDDng 'offset' n\u1EBFu c\xF3 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn kh\xF4ng \xE2m (>= 0).");
|
|
3693
|
+
}
|
|
3694
|
+
if (obj.flowId !== void 0 && obj.flowId !== null && !isPositiveInteger(obj.flowId)) {
|
|
3695
|
+
errors.push("Tr\u01B0\u1EDDng 'flowId' n\u1EBFu c\xF3 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
3696
|
+
}
|
|
3697
|
+
const cleanData = {
|
|
3698
|
+
oaId: toPositiveInteger(obj.oaId),
|
|
3699
|
+
limit: toPositiveInteger(obj.limit)
|
|
3700
|
+
};
|
|
3701
|
+
if (obj.offset !== void 0 && obj.offset !== null) cleanData.offset = toPositiveInteger(obj.offset);
|
|
3702
|
+
if (obj.flowId !== void 0 && obj.flowId !== null) cleanData.flowId = toPositiveInteger(obj.flowId);
|
|
3703
|
+
return {
|
|
3704
|
+
isValid: errors.length === 0,
|
|
3705
|
+
errors,
|
|
3706
|
+
data: errors.length === 0 ? cleanData : void 0
|
|
3707
|
+
};
|
|
3708
|
+
}
|
|
3709
|
+
function validateCreateFlowRequest(input) {
|
|
3710
|
+
const errors = [];
|
|
3711
|
+
if (!input || typeof input !== "object") {
|
|
3712
|
+
return { isValid: false, errors: ["D\u1EEF li\u1EC7u y\xEAu c\u1EA7u t\u1EA1o k\u1ECBch b\u1EA3n ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng."] };
|
|
2539
3713
|
}
|
|
2540
|
-
|
|
2541
|
-
|
|
3714
|
+
const obj = input;
|
|
3715
|
+
if (!is32BitPositiveInteger(obj.oaId)) {
|
|
3716
|
+
errors.push("Tr\u01B0\u1EDDng 'oaId' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 m\xE3 pageId n\u1ED9i b\u1ED9 ki\u1EC3u s\u1ED1 nguy\xEAn 32-bit (<= 2,147,483,647).");
|
|
3717
|
+
}
|
|
3718
|
+
if (typeof obj.name !== "string" || obj.name.trim() === "") {
|
|
3719
|
+
errors.push("Tr\u01B0\u1EDDng 'name' (t\xEAn k\u1ECBch b\u1EA3n) l\xE0 b\u1EAFt bu\u1ED9c v\xE0 kh\xF4ng \u0111\u01B0\u1EE3c \u0111\u1EC3 tr\u1ED1ng.");
|
|
3720
|
+
} else if (obj.name.trim().length > 50) {
|
|
3721
|
+
errors.push(`T\xEAn k\u1ECBch b\u1EA3n kh\xF4ng \u0111\u01B0\u1EE3c v\u01B0\u1EE3t qu\xE1 50 k\xFD t\u1EF1 (hi\u1EC7n c\xF3 ${obj.name.trim().length} k\xFD t\u1EF1).`);
|
|
3722
|
+
}
|
|
3723
|
+
const cleanMessages = validateFlowMessages(obj.messages, errors);
|
|
3724
|
+
const cleanData = {
|
|
3725
|
+
oaId: toPositiveInteger(obj.oaId),
|
|
3726
|
+
id: null,
|
|
3727
|
+
name: String(obj.name ?? "").trim(),
|
|
3728
|
+
messages: cleanMessages
|
|
3729
|
+
};
|
|
3730
|
+
return {
|
|
3731
|
+
isValid: errors.length === 0,
|
|
3732
|
+
errors,
|
|
3733
|
+
data: errors.length === 0 ? cleanData : void 0
|
|
3734
|
+
};
|
|
3735
|
+
}
|
|
3736
|
+
function validateUpdateFlowRequest(input) {
|
|
3737
|
+
const errors = [];
|
|
3738
|
+
if (!input || typeof input !== "object") {
|
|
3739
|
+
return { isValid: false, errors: ["D\u1EEF li\u1EC7u y\xEAu c\u1EA7u c\u1EADp nh\u1EADt k\u1ECBch b\u1EA3n ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng."] };
|
|
2542
3740
|
}
|
|
3741
|
+
const obj = input;
|
|
3742
|
+
if (!isPositiveInteger(obj.id)) {
|
|
3743
|
+
errors.push("Tr\u01B0\u1EDDng 'id' (ID Flow c\u1EA7n c\u1EADp nh\u1EADt) l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
3744
|
+
}
|
|
3745
|
+
if (!is32BitPositiveInteger(obj.oaId)) {
|
|
3746
|
+
errors.push("Tr\u01B0\u1EDDng 'oaId' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 m\xE3 pageId n\u1ED9i b\u1ED9 ki\u1EC3u s\u1ED1 nguy\xEAn 32-bit (<= 2,147,483,647).");
|
|
3747
|
+
}
|
|
3748
|
+
if (typeof obj.name !== "string" || obj.name.trim() === "") {
|
|
3749
|
+
errors.push("Tr\u01B0\u1EDDng 'name' (t\xEAn k\u1ECBch b\u1EA3n) l\xE0 b\u1EAFt bu\u1ED9c v\xE0 kh\xF4ng \u0111\u01B0\u1EE3c \u0111\u1EC3 tr\u1ED1ng.");
|
|
3750
|
+
} else if (obj.name.trim().length > 50) {
|
|
3751
|
+
errors.push(`T\xEAn k\u1ECBch b\u1EA3n kh\xF4ng \u0111\u01B0\u1EE3c v\u01B0\u1EE3t qu\xE1 50 k\xFD t\u1EF1 (hi\u1EC7n c\xF3 ${obj.name.trim().length} k\xFD t\u1EF1).`);
|
|
3752
|
+
}
|
|
3753
|
+
const cleanMessages = validateFlowMessages(obj.messages, errors);
|
|
3754
|
+
const cleanData = {
|
|
3755
|
+
id: toPositiveInteger(obj.id),
|
|
3756
|
+
oaId: toPositiveInteger(obj.oaId),
|
|
3757
|
+
name: String(obj.name ?? "").trim(),
|
|
3758
|
+
messages: cleanMessages
|
|
3759
|
+
};
|
|
3760
|
+
return {
|
|
3761
|
+
isValid: errors.length === 0,
|
|
3762
|
+
errors,
|
|
3763
|
+
data: errors.length === 0 ? cleanData : void 0
|
|
3764
|
+
};
|
|
3765
|
+
}
|
|
3766
|
+
var ZaloFlowsValidator = {
|
|
3767
|
+
listFlowsByOfficialAccount: validateListFlowsByOfficialAccountRequest,
|
|
3768
|
+
createFlow: validateCreateFlowRequest,
|
|
3769
|
+
updateFlow: validateUpdateFlowRequest
|
|
2543
3770
|
};
|
|
2544
3771
|
|
|
2545
3772
|
// src/services/flows.service.ts
|
|
@@ -2583,6 +3810,12 @@ var ZaloFlowsService = class {
|
|
|
2583
3810
|
constructor(client) {
|
|
2584
3811
|
this.client = client;
|
|
2585
3812
|
}
|
|
3813
|
+
/**
|
|
3814
|
+
* Bộ công cụ kiểm tra tính hợp lệ dữ liệu (Standalone Validators) cho Message Flows APIs.
|
|
3815
|
+
* Dùng để validate dữ liệu đầu vào trước khi lưu DB hoặc gọi Zalo Ads.
|
|
3816
|
+
*/
|
|
3817
|
+
static validator = ZaloFlowsValidator;
|
|
3818
|
+
validator = ZaloFlowsValidator;
|
|
2586
3819
|
listFlowsByOfficialAccount(request, options) {
|
|
2587
3820
|
return this.client.requestWithoutBody(OP_ZaloAdsListFlowsByOfficialAccount, this.client.toTransportInput(OP_ZaloAdsListFlowsByOfficialAccount, request), options);
|
|
2588
3821
|
}
|
|
@@ -3496,7 +4729,301 @@ var ZaloChatWidgetService = class {
|
|
|
3496
4729
|
}
|
|
3497
4730
|
};
|
|
3498
4731
|
|
|
4732
|
+
// src/validators/forms.validator.ts
|
|
4733
|
+
function isValidUrl(val) {
|
|
4734
|
+
if (typeof val !== "string" || val.trim() === "") return false;
|
|
4735
|
+
try {
|
|
4736
|
+
const url = new URL(val);
|
|
4737
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
4738
|
+
} catch {
|
|
4739
|
+
return false;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
function validateCreateFormRequest(input) {
|
|
4743
|
+
const errors = [];
|
|
4744
|
+
if (!input || typeof input !== "object") {
|
|
4745
|
+
return {
|
|
4746
|
+
isValid: false,
|
|
4747
|
+
errors: ["D\u1EEF li\u1EC7u y\xEAu c\u1EA7u t\u1EA1o Form ph\u1EA3i l\xE0 m\u1ED9t object."],
|
|
4748
|
+
data: void 0
|
|
4749
|
+
};
|
|
4750
|
+
}
|
|
4751
|
+
let raw = input;
|
|
4752
|
+
if (typeof input.data === "string") {
|
|
4753
|
+
try {
|
|
4754
|
+
raw = JSON.parse(input.data);
|
|
4755
|
+
} catch {
|
|
4756
|
+
errors.push("Tr\u01B0\u1EDDng 'data' kh\xF4ng ph\u1EA3i l\xE0 chu\u1ED7i JSON h\u1EE3p l\u1EC7.");
|
|
4757
|
+
return { isValid: false, errors, data: void 0 };
|
|
4758
|
+
}
|
|
4759
|
+
} else if (typeof input.data === "object" && input.data !== null) {
|
|
4760
|
+
raw = input.data;
|
|
4761
|
+
}
|
|
4762
|
+
const oaIdRaw = raw.oaId;
|
|
4763
|
+
let cleanOaId = "";
|
|
4764
|
+
if (typeof oaIdRaw === "string" && /^[0-9]+$/.test(oaIdRaw.trim())) {
|
|
4765
|
+
cleanOaId = oaIdRaw.trim();
|
|
4766
|
+
} else if (typeof oaIdRaw === "number" && Number.isInteger(oaIdRaw) && oaIdRaw > 0) {
|
|
4767
|
+
cleanOaId = String(oaIdRaw);
|
|
4768
|
+
} else {
|
|
4769
|
+
errors.push("Tr\u01B0\u1EDDng 'oaId' b\u1EAFt bu\u1ED9c ph\u1EA3i l\xE0 chu\u1ED7i s\u1ED1 nguy\xEAn d\u01B0\u01A1ng \u0111\u1EA1i di\u1EC7n cho pageId n\u1ED9i b\u1ED9 c\u1EE7a OA (v\xED d\u1EE5: '429753475').");
|
|
4770
|
+
}
|
|
4771
|
+
const title = typeof raw.title === "string" ? raw.title.trim() : "";
|
|
4772
|
+
if (!title) {
|
|
4773
|
+
errors.push("Tr\u01B0\u1EDDng 'title' (ti\xEAu \u0111\u1EC1 form) l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
4774
|
+
} else if (title.length < 2 || title.length > 40) {
|
|
4775
|
+
errors.push(`Tr\u01B0\u1EDDng 'title' ph\u1EA3i c\xF3 \u0111\u1ED9 d\xE0i t\u1EEB 2 \u0111\u1EBFn 40 k\xFD t\u1EF1 (hi\u1EC7n t\u1EA1i: ${title.length} k\xFD t\u1EF1).`);
|
|
4776
|
+
}
|
|
4777
|
+
const formName = typeof raw.formName === "string" ? raw.formName.trim() : "";
|
|
4778
|
+
if (!formName) {
|
|
4779
|
+
errors.push("Tr\u01B0\u1EDDng 'formName' (t\xEAn qu\u1EA3n tr\u1ECB form) l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
4780
|
+
} else if (formName.length < 2 || formName.length > 100) {
|
|
4781
|
+
errors.push(`Tr\u01B0\u1EDDng 'formName' ph\u1EA3i c\xF3 \u0111\u1ED9 d\xE0i t\u1EEB 2 \u0111\u1EBFn 100 k\xFD t\u1EF1 (hi\u1EC7n t\u1EA1i: ${formName.length} k\xFD t\u1EF1).`);
|
|
4782
|
+
}
|
|
4783
|
+
let cleanDescription = void 0;
|
|
4784
|
+
if (raw.description !== void 0 && raw.description !== null) {
|
|
4785
|
+
const desc = String(raw.description).trim();
|
|
4786
|
+
if (desc.length > 600) {
|
|
4787
|
+
errors.push(`Tr\u01B0\u1EDDng 'description' kh\xF4ng \u0111\u01B0\u1EE3c v\u01B0\u1EE3t qu\xE1 600 k\xFD t\u1EF1 (hi\u1EC7n t\u1EA1i: ${desc.length} k\xFD t\u1EF1).`);
|
|
4788
|
+
} else {
|
|
4789
|
+
cleanDescription = desc;
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
const coverPhoto = typeof raw.coverPhoto === "string" ? raw.coverPhoto.trim() : "";
|
|
4793
|
+
if (!coverPhoto) {
|
|
4794
|
+
errors.push("Tr\u01B0\u1EDDng 'coverPhoto' (\u1EA3nh b\xECa form) l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
4795
|
+
} else if (!isValidUrl(coverPhoto)) {
|
|
4796
|
+
errors.push(`Tr\u01B0\u1EDDng 'coverPhoto' ph\u1EA3i l\xE0 URL h\u1EE3p l\u1EC7 (http:// ho\u1EB7c https://).`);
|
|
4797
|
+
}
|
|
4798
|
+
const submitTitle = typeof raw.submitTitle === "string" && raw.submitTitle.trim() !== "" ? raw.submitTitle.trim() : "G\u1EEDi th\xF4ng tin th\xE0nh c\xF4ng";
|
|
4799
|
+
if (submitTitle.length > 90) {
|
|
4800
|
+
errors.push(`Tr\u01B0\u1EDDng 'submitTitle' kh\xF4ng \u0111\u01B0\u1EE3c v\u01B0\u1EE3t qu\xE1 90 k\xFD t\u1EF1 (hi\u1EC7n t\u1EA1i: ${submitTitle.length} k\xFD t\u1EF1).`);
|
|
4801
|
+
}
|
|
4802
|
+
const thanks = typeof raw.thanks === "string" && raw.thanks.trim() !== "" ? raw.thanks.trim() : "C\u1EA3m \u01A1n b\u1EA1n \u0111\xE3 g\u1EEDi th\xF4ng tin. Ch\xFAng t\xF4i s\u1EBD li\xEAn h\u1EC7 trong th\u1EDDi gian s\u1EDBm nh\u1EA5t.";
|
|
4803
|
+
if (thanks.length > 255) {
|
|
4804
|
+
errors.push(`Tr\u01B0\u1EDDng 'thanks' kh\xF4ng \u0111\u01B0\u1EE3c v\u01B0\u1EE3t qu\xE1 255 k\xFD t\u1EF1 (hi\u1EC7n t\u1EA1i: ${thanks.length} k\xFD t\u1EF1).`);
|
|
4805
|
+
}
|
|
4806
|
+
const gotoActionType = raw.gotoActionType === 0 ? 0 : 1;
|
|
4807
|
+
const gotoText = typeof raw.gotoText === "string" && raw.gotoText.trim() !== "" ? raw.gotoText.trim() : gotoActionType === 1 ? "Xem trang Web" : "\u0110\xF3ng l\u1EA1i";
|
|
4808
|
+
let cleanWebsite = void 0;
|
|
4809
|
+
if (gotoActionType === 1) {
|
|
4810
|
+
if (!raw.website || !isValidUrl(raw.website)) {
|
|
4811
|
+
errors.push("Khi 'gotoActionType' = 1 (m\u1EDF website), tr\u01B0\u1EDDng 'website' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 URL h\u1EE3p l\u1EC7.");
|
|
4812
|
+
} else {
|
|
4813
|
+
cleanWebsite = String(raw.website).trim();
|
|
4814
|
+
}
|
|
4815
|
+
}
|
|
4816
|
+
const useCustomConsent = Boolean(raw.useCustomConsent);
|
|
4817
|
+
let cleanConsentName = void 0;
|
|
4818
|
+
let cleanConsentUrl = void 0;
|
|
4819
|
+
if (useCustomConsent) {
|
|
4820
|
+
const cName = typeof raw.consentName === "string" ? raw.consentName.trim() : "";
|
|
4821
|
+
if (!cName) {
|
|
4822
|
+
errors.push("Khi 'useCustomConsent' = true, tr\u01B0\u1EDDng 'consentName' (ti\xEAu \u0111\u1EC1 \u0111i\u1EC1u kho\u1EA3n) l\xE0 b\u1EAFt bu\u1ED9c.");
|
|
4823
|
+
} else if (cName.length > 255) {
|
|
4824
|
+
errors.push(`Tr\u01B0\u1EDDng 'consentName' kh\xF4ng \u0111\u01B0\u1EE3c v\u01B0\u1EE3t qu\xE1 255 k\xFD t\u1EF1 (hi\u1EC7n t\u1EA1i: ${cName.length} k\xFD t\u1EF1).`);
|
|
4825
|
+
} else {
|
|
4826
|
+
cleanConsentName = cName;
|
|
4827
|
+
}
|
|
4828
|
+
if (!raw.consentUrl || !isValidUrl(raw.consentUrl)) {
|
|
4829
|
+
errors.push("Khi 'useCustomConsent' = true, tr\u01B0\u1EDDng 'consentUrl' ph\u1EA3i l\xE0 URL h\u1EE3p l\u1EC7 ch\u1EE9a ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt.");
|
|
4830
|
+
} else {
|
|
4831
|
+
cleanConsentUrl = String(raw.consentUrl).trim();
|
|
4832
|
+
}
|
|
4833
|
+
}
|
|
4834
|
+
if (!Array.isArray(raw.questions) || raw.questions.length === 0) {
|
|
4835
|
+
errors.push("Bi\u1EC3u m\u1EABu b\u1EAFt bu\u1ED9c ph\u1EA3i c\xF3 \xEDt nh\u1EA5t 1 c\xE2u h\u1ECFi trong m\u1EA3ng 'questions'.");
|
|
4836
|
+
}
|
|
4837
|
+
const cleanQuestions = [];
|
|
4838
|
+
let hasPhonePrimary = false;
|
|
4839
|
+
let hasPhoneSecondary = false;
|
|
4840
|
+
if (Array.isArray(raw.questions)) {
|
|
4841
|
+
raw.questions.forEach((q, idx) => {
|
|
4842
|
+
const qPrefix = `C\xE2u h\u1ECFi [${idx}]`;
|
|
4843
|
+
if (!q || typeof q !== "object") {
|
|
4844
|
+
errors.push(`${qPrefix} ph\u1EA3i l\xE0 m\u1ED9t object.`);
|
|
4845
|
+
return;
|
|
4846
|
+
}
|
|
4847
|
+
const qTitle = typeof q.title === "string" ? q.title.trim() : "";
|
|
4848
|
+
if (qTitle.length < 2 || qTitle.length > 180) {
|
|
4849
|
+
errors.push(`${qPrefix}: Ti\xEAu \u0111\u1EC1 'title' ph\u1EA3i c\xF3 \u0111\u1ED9 d\xE0i t\u1EEB 2 \u0111\u1EBFn 180 k\xFD t\u1EF1.`);
|
|
4850
|
+
}
|
|
4851
|
+
if (!Array.isArray(q.answers) || q.answers.length === 0) {
|
|
4852
|
+
errors.push(`${qPrefix}: Ph\u1EA3i c\xF3 \xEDt nh\u1EA5t 1 \u0111\xE1p \xE1n trong m\u1EA3ng 'answers'.`);
|
|
4853
|
+
return;
|
|
4854
|
+
}
|
|
4855
|
+
const cleanAnswers = [];
|
|
4856
|
+
q.answers.forEach((ans, aIdx) => {
|
|
4857
|
+
const aType = Number(ans?.type);
|
|
4858
|
+
if (![2, 7, 13].includes(aType)) {
|
|
4859
|
+
errors.push(`${qPrefix} \u0111\xE1p \xE1n [${aIdx}]: 'type' ph\u1EA3i l\xE0 7 (H\u1ECD t\xEAn), 13 (S\u0110T chu\u1EA9n), ho\u1EB7c 2 (S\u0110T ph\u1EE5).`);
|
|
4860
|
+
}
|
|
4861
|
+
if (aType === 13) hasPhonePrimary = true;
|
|
4862
|
+
if (aType === 2) hasPhoneSecondary = true;
|
|
4863
|
+
cleanAnswers.push({
|
|
4864
|
+
type: aType,
|
|
4865
|
+
placeHolder: ans?.placeHolder ? String(ans.placeHolder).trim() : void 0,
|
|
4866
|
+
value: ans?.value ? String(ans.value).trim() : null,
|
|
4867
|
+
priority: typeof ans?.priority === "number" ? ans.priority : 500
|
|
4868
|
+
});
|
|
4869
|
+
});
|
|
4870
|
+
cleanQuestions.push({
|
|
4871
|
+
title: qTitle,
|
|
4872
|
+
type: typeof q.type === "number" ? q.type : 1,
|
|
4873
|
+
isRequired: q.isRequired !== void 0 ? Boolean(q.isRequired) : true,
|
|
4874
|
+
editDisabled: Boolean(q.editDisabled),
|
|
4875
|
+
priority: typeof q.priority === "number" ? q.priority : (idx + 1) * 100,
|
|
4876
|
+
answers: cleanAnswers
|
|
4877
|
+
});
|
|
4878
|
+
});
|
|
4879
|
+
}
|
|
4880
|
+
if (hasPhoneSecondary) {
|
|
4881
|
+
cleanQuestions.forEach((q) => {
|
|
4882
|
+
if (q.answers.some((a) => a.type === 13)) {
|
|
4883
|
+
q.editDisabled = true;
|
|
4884
|
+
}
|
|
4885
|
+
});
|
|
4886
|
+
}
|
|
4887
|
+
const cleanSections = [];
|
|
4888
|
+
if (Array.isArray(raw.sections)) {
|
|
4889
|
+
raw.sections.forEach((sec, sIdx) => {
|
|
4890
|
+
const sPrefix = `Section [${sIdx}]`;
|
|
4891
|
+
const sType = Number(sec?.type);
|
|
4892
|
+
if (isNaN(sType) || sType < 0 || sType > 4) {
|
|
4893
|
+
errors.push(`${sPrefix}: 'type' ph\u1EA3i l\xE0 s\u1ED1 t\u1EEB 0 \u0111\u1EBFn 4 (0=t\u1ED5ng quan, 1=chi ti\u1EBFt, 2=b\u1EA3ng gi\xE1, 3=\u01B0u \u0111i\u1EC3m, 4=\u01B0u \u0111\xE3i).`);
|
|
4894
|
+
}
|
|
4895
|
+
const sTitle = typeof sec?.title === "string" ? sec.title.trim() : "";
|
|
4896
|
+
if (sTitle.length > 60) {
|
|
4897
|
+
errors.push(`${sPrefix}: 'title' kh\xF4ng \u0111\u01B0\u1EE3c v\u01B0\u1EE3t qu\xE1 60 k\xFD t\u1EF1.`);
|
|
4898
|
+
}
|
|
4899
|
+
cleanSections.push({
|
|
4900
|
+
type: sType,
|
|
4901
|
+
title: sTitle,
|
|
4902
|
+
disable: Boolean(sec?.disable),
|
|
4903
|
+
expand: Boolean(sec?.expand),
|
|
4904
|
+
content: sec?.content ? sec.content : {}
|
|
4905
|
+
});
|
|
4906
|
+
});
|
|
4907
|
+
}
|
|
4908
|
+
const cleanData = {
|
|
4909
|
+
oaId: cleanOaId,
|
|
4910
|
+
title,
|
|
4911
|
+
formName,
|
|
4912
|
+
description: cleanDescription,
|
|
4913
|
+
coverPhoto,
|
|
4914
|
+
submitTitle,
|
|
4915
|
+
thanks,
|
|
4916
|
+
gotoActionType,
|
|
4917
|
+
gotoText,
|
|
4918
|
+
website: cleanWebsite,
|
|
4919
|
+
reviewSubmit: raw.reviewSubmit !== void 0 ? Boolean(raw.reviewSubmit) : true,
|
|
4920
|
+
questions: cleanQuestions,
|
|
4921
|
+
sections: cleanSections.length > 0 ? cleanSections : void 0,
|
|
4922
|
+
useCustomConsent,
|
|
4923
|
+
consentName: cleanConsentName,
|
|
4924
|
+
consentUrl: cleanConsentUrl
|
|
4925
|
+
};
|
|
4926
|
+
return {
|
|
4927
|
+
isValid: errors.length === 0,
|
|
4928
|
+
errors,
|
|
4929
|
+
data: errors.length === 0 ? cleanData : void 0
|
|
4930
|
+
};
|
|
4931
|
+
}
|
|
4932
|
+
function validateListFormsByOfficialAccountRequest(input) {
|
|
4933
|
+
const errors = [];
|
|
4934
|
+
if (!input || typeof input !== "object") {
|
|
4935
|
+
return { isValid: false, errors: ["Input ph\u1EA3i l\xE0 m\u1ED9t object."], data: void 0 };
|
|
4936
|
+
}
|
|
4937
|
+
const raw = input;
|
|
4938
|
+
let cleanOaId = "";
|
|
4939
|
+
if (typeof raw.oaId === "string" && /^[0-9]+$/.test(raw.oaId.trim())) {
|
|
4940
|
+
cleanOaId = raw.oaId.trim();
|
|
4941
|
+
} else if (typeof raw.oaId === "number" && Number.isInteger(raw.oaId) && raw.oaId > 0) {
|
|
4942
|
+
cleanOaId = String(raw.oaId);
|
|
4943
|
+
} else {
|
|
4944
|
+
errors.push("Tr\u01B0\u1EDDng 'oaId' b\u1EAFt bu\u1ED9c ph\u1EA3i l\xE0 chu\u1ED7i s\u1ED1 nguy\xEAn d\u01B0\u01A1ng \u0111\u1EA1i di\u1EC7n cho pageId c\u1EE7a OA.");
|
|
4945
|
+
}
|
|
4946
|
+
return {
|
|
4947
|
+
isValid: errors.length === 0,
|
|
4948
|
+
errors,
|
|
4949
|
+
data: errors.length === 0 ? { oaId: cleanOaId, isAll: raw.isAll !== void 0 ? Boolean(raw.isAll) : true } : void 0
|
|
4950
|
+
};
|
|
4951
|
+
}
|
|
4952
|
+
function validateListFormLeadsRequest(input) {
|
|
4953
|
+
const errors = [];
|
|
4954
|
+
if (!input || typeof input !== "object") {
|
|
4955
|
+
return { isValid: false, errors: ["Input ph\u1EA3i l\xE0 m\u1ED9t object."], data: void 0 };
|
|
4956
|
+
}
|
|
4957
|
+
const raw = input;
|
|
4958
|
+
const oaId = typeof raw.oaId === "string" ? raw.oaId.trim() : "";
|
|
4959
|
+
if (!oaId) {
|
|
4960
|
+
errors.push("Tr\u01B0\u1EDDng 'oaId' l\xE0 b\u1EAFt bu\u1ED9c (m\xE3 ch\u1EEF t\u1EEB getAuthSession, v\xED d\u1EE5 'J4UB9MRE497YDQKKK').");
|
|
4961
|
+
} else if (/^[0-9]+$/.test(oaId)) {
|
|
4962
|
+
errors.push("Tr\u01B0\u1EDDng 'oaId' tr\xEAn Lead Center ph\u1EA3i l\xE0 m\xE3 ch\u1EEF alphanumeric t\u1EEB getAuthSession, kh\xF4ng d\xF9ng chu\u1ED7i s\u1ED1 pageId (s\u1EBD b\u1ECB l\u1ED7i 403 Forbidden).");
|
|
4963
|
+
}
|
|
4964
|
+
const type = Number(raw.type);
|
|
4965
|
+
if (![1, 5].includes(type)) {
|
|
4966
|
+
errors.push("Tr\u01B0\u1EDDng 'type' ph\u1EA3i l\xE0 1 (ZALOFORM) ho\u1EB7c 5 (COMMERCE).");
|
|
4967
|
+
}
|
|
4968
|
+
const limit = typeof raw.limit === "number" && raw.limit > 0 ? Math.floor(raw.limit) : 20;
|
|
4969
|
+
const offset = typeof raw.offset === "number" && raw.offset >= 0 ? Math.floor(raw.offset) : 0;
|
|
4970
|
+
const status = typeof raw.status === "string" && raw.status.trim() !== "" ? raw.status.trim() : "2,6,1,0,7,4,5,3";
|
|
4971
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
4972
|
+
const fromTime = typeof raw.fromTime === "number" && raw.fromTime >= 0 ? Math.floor(raw.fromTime) : now - 90 * 86400;
|
|
4973
|
+
const toTime = typeof raw.toTime === "number" && raw.toTime >= 0 ? Math.floor(raw.toTime) : now;
|
|
4974
|
+
if (toTime < fromTime) {
|
|
4975
|
+
errors.push("M\u1ED1c th\u1EDDi gian k\u1EBFt th\xFAc 'toTime' ph\u1EA3i l\u1EDBn h\u01A1n ho\u1EB7c b\u1EB1ng 'fromTime'.");
|
|
4976
|
+
}
|
|
4977
|
+
return {
|
|
4978
|
+
isValid: errors.length === 0,
|
|
4979
|
+
errors,
|
|
4980
|
+
data: errors.length === 0 ? {
|
|
4981
|
+
oaId,
|
|
4982
|
+
type,
|
|
4983
|
+
limit,
|
|
4984
|
+
offset,
|
|
4985
|
+
status,
|
|
4986
|
+
fromTime,
|
|
4987
|
+
toTime,
|
|
4988
|
+
formIds: raw.formIds ? String(raw.formIds).trim() : void 0,
|
|
4989
|
+
keyword: raw.keyword ? String(raw.keyword).trim() : void 0,
|
|
4990
|
+
reason: raw.reason ? String(raw.reason).trim() : void 0,
|
|
4991
|
+
labelIds: raw.labelIds ? String(raw.labelIds).trim() : void 0,
|
|
4992
|
+
isCommerce: raw.isCommerce !== void 0 ? Boolean(raw.isCommerce) : false
|
|
4993
|
+
} : void 0
|
|
4994
|
+
};
|
|
4995
|
+
}
|
|
4996
|
+
var ZaloFormsValidator = {
|
|
4997
|
+
createForm: validateCreateFormRequest,
|
|
4998
|
+
listFormsByOfficialAccount: validateListFormsByOfficialAccountRequest,
|
|
4999
|
+
listFormLeads: validateListFormLeadsRequest
|
|
5000
|
+
};
|
|
5001
|
+
|
|
3499
5002
|
// src/services/forms.service.ts
|
|
5003
|
+
var OP_ZaloAdsCreateForm2 = {
|
|
5004
|
+
operationId: "zalo_ads_create_form",
|
|
5005
|
+
method: "POST",
|
|
5006
|
+
endpoint: "/api/form/create",
|
|
5007
|
+
baseUrl: "https://ads.zalo.me",
|
|
5008
|
+
binaryFields: [],
|
|
5009
|
+
pathFields: [],
|
|
5010
|
+
queryFields: [],
|
|
5011
|
+
headerFields: [],
|
|
5012
|
+
cookieFields: [],
|
|
5013
|
+
bodyFields: ["data"]
|
|
5014
|
+
};
|
|
5015
|
+
var OP_ZaloAdsListFormsByOfficialAccount2 = {
|
|
5016
|
+
operationId: "zalo_ads_list_forms_by_official_account",
|
|
5017
|
+
method: "GET",
|
|
5018
|
+
endpoint: "/api/form/getByOaId",
|
|
5019
|
+
baseUrl: "https://ads.zalo.me",
|
|
5020
|
+
binaryFields: [],
|
|
5021
|
+
pathFields: [],
|
|
5022
|
+
queryFields: ["oaId", "isAll"],
|
|
5023
|
+
headerFields: [],
|
|
5024
|
+
cookieFields: [],
|
|
5025
|
+
bodyFields: []
|
|
5026
|
+
};
|
|
3500
5027
|
var OP_ZaloFormsDisableFormLeadNotification = {
|
|
3501
5028
|
"operationId": "ZaloForms_disableFormLeadNotification",
|
|
3502
5029
|
"method": "POST",
|
|
@@ -3570,7 +5097,8 @@ var OP_ZaloFormsListConversationLeads = {
|
|
|
3570
5097
|
"keyword",
|
|
3571
5098
|
"fromTime",
|
|
3572
5099
|
"labelIds",
|
|
3573
|
-
"sceneIds"
|
|
5100
|
+
"sceneIds",
|
|
5101
|
+
"reason"
|
|
3574
5102
|
],
|
|
3575
5103
|
"headerFields": [],
|
|
3576
5104
|
"cookieFields": [],
|
|
@@ -3594,7 +5122,8 @@ var OP_ZaloFormsListFormLeads = {
|
|
|
3594
5122
|
"keyword",
|
|
3595
5123
|
"fromTime",
|
|
3596
5124
|
"labelIds",
|
|
3597
|
-
"isCommerce"
|
|
5125
|
+
"isCommerce",
|
|
5126
|
+
"reason"
|
|
3598
5127
|
],
|
|
3599
5128
|
"headerFields": [],
|
|
3600
5129
|
"cookieFields": [],
|
|
@@ -3676,10 +5205,59 @@ var OP_ZaloFormsGetAuthSession = {
|
|
|
3676
5205
|
"cookieFields": [],
|
|
3677
5206
|
"bodyFields": []
|
|
3678
5207
|
};
|
|
5208
|
+
var OP_ZaloFormsTrackManageFormEvent = {
|
|
5209
|
+
operationId: "zalo_forms_track_manage_form_event",
|
|
5210
|
+
method: "GET",
|
|
5211
|
+
endpoint: "/client/manage/api/manageForm/tracking",
|
|
5212
|
+
baseUrl: "https://form.ads.zalo.me",
|
|
5213
|
+
binaryFields: [],
|
|
5214
|
+
pathFields: [],
|
|
5215
|
+
queryFields: ["eventId", "oaId", "formId"],
|
|
5216
|
+
headerFields: [],
|
|
5217
|
+
cookieFields: [],
|
|
5218
|
+
bodyFields: []
|
|
5219
|
+
};
|
|
3679
5220
|
var ZaloFormsService = class {
|
|
3680
5221
|
constructor(client) {
|
|
3681
5222
|
this.client = client;
|
|
3682
5223
|
}
|
|
5224
|
+
validator = ZaloFormsValidator;
|
|
5225
|
+
static validator = ZaloFormsValidator;
|
|
5226
|
+
/**
|
|
5227
|
+
* Tạo biểu mẫu mới gắn với Official Account (POST https://ads.zalo.me/api/form/create).
|
|
5228
|
+
* Hỗ trợ truyền thẳng Typed Payload (tự động validate qua ZaloFormsValidator và mã hóa urlencoded)
|
|
5229
|
+
* hoặc object dạng `{ data: JSON.stringify(...) }`.
|
|
5230
|
+
*/
|
|
5231
|
+
async createForm(request, options) {
|
|
5232
|
+
let payloadStr;
|
|
5233
|
+
if (typeof request.data === "string") {
|
|
5234
|
+
payloadStr = request.data;
|
|
5235
|
+
} else {
|
|
5236
|
+
const val = ZaloFormsValidator.createForm(request);
|
|
5237
|
+
if (!val.isValid || !val.data) {
|
|
5238
|
+
throw new Error(`[ZaloFormsValidator] D\u1EEF li\u1EC7u t\u1EA1o form kh\xF4ng h\u1EE3p l\u1EC7:
|
|
5239
|
+
- ${val.errors.join("\n - ")}`);
|
|
5240
|
+
}
|
|
5241
|
+
payloadStr = JSON.stringify(val.data);
|
|
5242
|
+
}
|
|
5243
|
+
return this.client.requestFormUrlEncoded(
|
|
5244
|
+
OP_ZaloAdsCreateForm2,
|
|
5245
|
+
this.client.toTransportInput(OP_ZaloAdsCreateForm2, { data: payloadStr }),
|
|
5246
|
+
options
|
|
5247
|
+
);
|
|
5248
|
+
}
|
|
5249
|
+
/**
|
|
5250
|
+
* Lấy danh sách biểu mẫu Zalo Ads theo Official Account (GET https://ads.zalo.me/api/form/getByOaId).
|
|
5251
|
+
*/
|
|
5252
|
+
listFormsByOfficialAccount(request, options) {
|
|
5253
|
+
const val = ZaloFormsValidator.listFormsByOfficialAccount(request);
|
|
5254
|
+
const cleanInput = val.isValid && val.data ? val.data : request;
|
|
5255
|
+
return this.client.requestWithoutBody(
|
|
5256
|
+
OP_ZaloAdsListFormsByOfficialAccount2,
|
|
5257
|
+
this.client.toTransportInput(OP_ZaloAdsListFormsByOfficialAccount2, cleanInput),
|
|
5258
|
+
options
|
|
5259
|
+
);
|
|
5260
|
+
}
|
|
3683
5261
|
getAuthSession(options) {
|
|
3684
5262
|
return this.client.requestWithoutBody(OP_ZaloFormsGetAuthSession, void 0, options);
|
|
3685
5263
|
}
|
|
@@ -3713,10 +5291,20 @@ var ZaloFormsService = class {
|
|
|
3713
5291
|
updateFormLeadNotificationInterval(request, options) {
|
|
3714
5292
|
return this.client.requestJson(OP_ZaloFormsUpdateFormLeadNotificationInterval, this.client.toTransportInput(OP_ZaloFormsUpdateFormLeadNotificationInterval, request), options);
|
|
3715
5293
|
}
|
|
5294
|
+
/**
|
|
5295
|
+
* Ghi nhận sự kiện tương tác trên Lead Center (GET /client/manage/api/manageForm/tracking).
|
|
5296
|
+
*/
|
|
5297
|
+
trackManageFormEvent(request, options) {
|
|
5298
|
+
return this.client.requestWithoutBody(
|
|
5299
|
+
OP_ZaloFormsTrackManageFormEvent,
|
|
5300
|
+
this.client.toTransportInput(OP_ZaloFormsTrackManageFormEvent, request),
|
|
5301
|
+
options
|
|
5302
|
+
);
|
|
5303
|
+
}
|
|
3716
5304
|
};
|
|
3717
5305
|
|
|
3718
5306
|
// src/validators/instant-pages.validator.ts
|
|
3719
|
-
function
|
|
5307
|
+
function isPositiveInteger2(val) {
|
|
3720
5308
|
if (typeof val === "number") return Number.isInteger(val) && val > 0;
|
|
3721
5309
|
if (typeof val === "string" && val.trim() !== "") {
|
|
3722
5310
|
const num = Number(val);
|
|
@@ -3724,7 +5312,7 @@ function isPositiveInteger(val) {
|
|
|
3724
5312
|
}
|
|
3725
5313
|
return false;
|
|
3726
5314
|
}
|
|
3727
|
-
function
|
|
5315
|
+
function toPositiveInteger2(val) {
|
|
3728
5316
|
return typeof val === "number" ? Math.floor(val) : parseInt(String(val), 10);
|
|
3729
5317
|
}
|
|
3730
5318
|
function validateCloneInstantPageRequest(input) {
|
|
@@ -3733,13 +5321,13 @@ function validateCloneInstantPageRequest(input) {
|
|
|
3733
5321
|
return { isValid: false, errors: ["D\u1EEF li\u1EC7u y\xEAu c\u1EA7u sao ch\xE9p Instant Page ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng (object)."] };
|
|
3734
5322
|
}
|
|
3735
5323
|
const obj = input;
|
|
3736
|
-
if (!
|
|
5324
|
+
if (!isPositiveInteger2(obj.instantPageId)) {
|
|
3737
5325
|
errors.push("Tr\u01B0\u1EDDng 'instantPageId' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
3738
5326
|
}
|
|
3739
5327
|
return {
|
|
3740
5328
|
isValid: errors.length === 0,
|
|
3741
5329
|
errors,
|
|
3742
|
-
data: errors.length === 0 ? { instantPageId:
|
|
5330
|
+
data: errors.length === 0 ? { instantPageId: toPositiveInteger2(obj.instantPageId) } : void 0
|
|
3743
5331
|
};
|
|
3744
5332
|
}
|
|
3745
5333
|
function validateGetInstantPageDetailRequest(input) {
|
|
@@ -3748,13 +5336,13 @@ function validateGetInstantPageDetailRequest(input) {
|
|
|
3748
5336
|
return { isValid: false, errors: ["D\u1EEF li\u1EC7u y\xEAu c\u1EA7u l\u1EA5y chi ti\u1EBFt Instant Page ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng (object)."] };
|
|
3749
5337
|
}
|
|
3750
5338
|
const obj = input;
|
|
3751
|
-
if (!
|
|
5339
|
+
if (!isPositiveInteger2(obj.instantPageId)) {
|
|
3752
5340
|
errors.push("Tr\u01B0\u1EDDng 'instantPageId' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
3753
5341
|
}
|
|
3754
5342
|
return {
|
|
3755
5343
|
isValid: errors.length === 0,
|
|
3756
5344
|
errors,
|
|
3757
|
-
data: errors.length === 0 ? { instantPageId:
|
|
5345
|
+
data: errors.length === 0 ? { instantPageId: toPositiveInteger2(obj.instantPageId) } : void 0
|
|
3758
5346
|
};
|
|
3759
5347
|
}
|
|
3760
5348
|
function validateGetInstantPageSharedUserRequest(input) {
|
|
@@ -3763,18 +5351,18 @@ function validateGetInstantPageSharedUserRequest(input) {
|
|
|
3763
5351
|
return { isValid: false, errors: ["D\u1EEF li\u1EC7u y\xEAu c\u1EA7u l\u1EA5y th\xF4ng tin ng\u01B0\u1EDDi d\xF9ng chia s\u1EBB ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng (object)."] };
|
|
3764
5352
|
}
|
|
3765
5353
|
const obj = input;
|
|
3766
|
-
if (!
|
|
5354
|
+
if (!isPositiveInteger2(obj.instantPageId)) {
|
|
3767
5355
|
errors.push("Tr\u01B0\u1EDDng 'instantPageId' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
3768
5356
|
}
|
|
3769
|
-
if (!
|
|
5357
|
+
if (!isPositiveInteger2(obj.userId)) {
|
|
3770
5358
|
errors.push("Tr\u01B0\u1EDDng 'userId' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
3771
5359
|
}
|
|
3772
5360
|
return {
|
|
3773
5361
|
isValid: errors.length === 0,
|
|
3774
5362
|
errors,
|
|
3775
5363
|
data: errors.length === 0 ? {
|
|
3776
|
-
instantPageId:
|
|
3777
|
-
userId:
|
|
5364
|
+
instantPageId: toPositiveInteger2(obj.instantPageId),
|
|
5365
|
+
userId: toPositiveInteger2(obj.userId)
|
|
3778
5366
|
} : void 0
|
|
3779
5367
|
};
|
|
3780
5368
|
}
|
|
@@ -3784,15 +5372,15 @@ function validateListInstantPageEditHistoryRequest(input) {
|
|
|
3784
5372
|
return { isValid: false, errors: ["D\u1EEF li\u1EC7u y\xEAu c\u1EA7u l\u1EA5y l\u1ECBch s\u1EED ch\u1EC9nh s\u1EEDa ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng (object)."] };
|
|
3785
5373
|
}
|
|
3786
5374
|
const obj = input;
|
|
3787
|
-
if (!
|
|
5375
|
+
if (!isPositiveInteger2(obj.instantPageId)) {
|
|
3788
5376
|
errors.push("Tr\u01B0\u1EDDng 'instantPageId' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
3789
5377
|
}
|
|
3790
5378
|
let limit = void 0;
|
|
3791
5379
|
if (obj.limit !== void 0 && obj.limit !== null) {
|
|
3792
|
-
if (!
|
|
5380
|
+
if (!isPositiveInteger2(obj.limit)) {
|
|
3793
5381
|
errors.push("Tr\u01B0\u1EDDng 'limit' n\u1EBFu c\xF3 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn >= 1.");
|
|
3794
5382
|
} else {
|
|
3795
|
-
limit =
|
|
5383
|
+
limit = toPositiveInteger2(obj.limit);
|
|
3796
5384
|
}
|
|
3797
5385
|
}
|
|
3798
5386
|
let offset = void 0;
|
|
@@ -3808,7 +5396,7 @@ function validateListInstantPageEditHistoryRequest(input) {
|
|
|
3808
5396
|
isValid: errors.length === 0,
|
|
3809
5397
|
errors,
|
|
3810
5398
|
data: errors.length === 0 ? {
|
|
3811
|
-
instantPageId:
|
|
5399
|
+
instantPageId: toPositiveInteger2(obj.instantPageId),
|
|
3812
5400
|
...limit !== void 0 ? { limit } : {},
|
|
3813
5401
|
...offset !== void 0 ? { offset } : {}
|
|
3814
5402
|
} : void 0
|
|
@@ -3820,13 +5408,13 @@ function validateListInstantPageFormRecordsRequest(input) {
|
|
|
3820
5408
|
return { isValid: false, errors: ["D\u1EEF li\u1EC7u y\xEAu c\u1EA7u t\u1EA3i b\u1EA3n ghi form ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng (object)."] };
|
|
3821
5409
|
}
|
|
3822
5410
|
const obj = input;
|
|
3823
|
-
if (!
|
|
5411
|
+
if (!isPositiveInteger2(obj.instantPageId)) {
|
|
3824
5412
|
errors.push("Tr\u01B0\u1EDDng 'instantPageId' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
3825
5413
|
}
|
|
3826
5414
|
return {
|
|
3827
5415
|
isValid: errors.length === 0,
|
|
3828
5416
|
errors,
|
|
3829
|
-
data: errors.length === 0 ? { instantPageId:
|
|
5417
|
+
data: errors.length === 0 ? { instantPageId: toPositiveInteger2(obj.instantPageId) } : void 0
|
|
3830
5418
|
};
|
|
3831
5419
|
}
|
|
3832
5420
|
function validateListInstantPageSharesRequest(input) {
|
|
@@ -3835,13 +5423,13 @@ function validateListInstantPageSharesRequest(input) {
|
|
|
3835
5423
|
return { isValid: false, errors: ["D\u1EEF li\u1EC7u y\xEAu c\u1EA7u l\u1EA5y danh s\xE1ch ng\u01B0\u1EDDi \u0111\u01B0\u1EE3c chia s\u1EBB ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng (object)."] };
|
|
3836
5424
|
}
|
|
3837
5425
|
const obj = input;
|
|
3838
|
-
if (!
|
|
5426
|
+
if (!isPositiveInteger2(obj.instantPageId)) {
|
|
3839
5427
|
errors.push("Tr\u01B0\u1EDDng 'instantPageId' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
3840
5428
|
}
|
|
3841
5429
|
return {
|
|
3842
5430
|
isValid: errors.length === 0,
|
|
3843
5431
|
errors,
|
|
3844
|
-
data: errors.length === 0 ? { instantPageId:
|
|
5432
|
+
data: errors.length === 0 ? { instantPageId: toPositiveInteger2(obj.instantPageId) } : void 0
|
|
3845
5433
|
};
|
|
3846
5434
|
}
|
|
3847
5435
|
function validateListInstantPageUsersRequest(input) {
|
|
@@ -3867,10 +5455,10 @@ function validateSearchInstantPagesRequest(input) {
|
|
|
3867
5455
|
const obj = input;
|
|
3868
5456
|
const data = {};
|
|
3869
5457
|
if (obj.id !== void 0 && obj.id !== null && obj.id !== "") {
|
|
3870
|
-
if (!
|
|
5458
|
+
if (!isPositiveInteger2(obj.id)) {
|
|
3871
5459
|
errors.push("B\u1ED9 l\u1ECDc 'id' n\u1EBFu c\xF3 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
3872
5460
|
} else {
|
|
3873
|
-
data.id =
|
|
5461
|
+
data.id = toPositiveInteger2(obj.id);
|
|
3874
5462
|
}
|
|
3875
5463
|
}
|
|
3876
5464
|
if (obj.name !== void 0 && obj.name !== null) {
|
|
@@ -3901,10 +5489,10 @@ function validateUpdateInstantPageRequest(input) {
|
|
|
3901
5489
|
}
|
|
3902
5490
|
const obj = input;
|
|
3903
5491
|
const instantPageId = obj.instantPageId ?? obj.id;
|
|
3904
|
-
if (!
|
|
5492
|
+
if (!isPositiveInteger2(instantPageId)) {
|
|
3905
5493
|
errors.push("Tr\u01B0\u1EDDng 'instantPageId' (ho\u1EB7c 'id') l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
3906
5494
|
}
|
|
3907
|
-
const numericId =
|
|
5495
|
+
const numericId = toPositiveInteger2(instantPageId);
|
|
3908
5496
|
const data = {
|
|
3909
5497
|
instantPageId: numericId,
|
|
3910
5498
|
id: numericId
|
|
@@ -3935,7 +5523,7 @@ function validateUpdateInstantPageRequest(input) {
|
|
|
3935
5523
|
if (obj.status !== void 0 && obj.status !== null) {
|
|
3936
5524
|
const st = Number(obj.status);
|
|
3937
5525
|
if (![0, 1, 2, 3].includes(st)) {
|
|
3938
|
-
errors.push("Tr\u01B0\u1EDDng 'status' ph\u1EA3i l\xE0 m\u1ED9t trong c\xE1c m\xE3: 0 (B\u1EA3n sao m\u1EDBi), 1 (
|
|
5526
|
+
errors.push("Tr\u01B0\u1EDDng 'status' ph\u1EA3i l\xE0 m\u1ED9t trong c\xE1c m\xE3: 0 (B\u1EA3n sao m\u1EDBi t\u1EA1o ch\u01B0a l\u01B0u), 1 (\u0110ang ho\u1EA1t \u0111\u1ED9ng / \u0110\xE3 xu\u1EA5t b\u1EA3n), 3 (\u0110\xE3 k\u1EBFt th\xFAc / L\u01B0u tr\u1EEF).");
|
|
3939
5527
|
} else {
|
|
3940
5528
|
data.status = st;
|
|
3941
5529
|
}
|
|
@@ -3998,7 +5586,7 @@ function validateListInstantPageLeadsRequest(input) {
|
|
|
3998
5586
|
errors.push("Tr\u01B0\u1EDDng 'ids' kh\xF4ng ch\u1EE9a b\u1EA5t k\u1EF3 ID h\u1EE3p l\u1EC7 n\xE0o.");
|
|
3999
5587
|
} else {
|
|
4000
5588
|
for (const id of idParts) {
|
|
4001
|
-
if (!
|
|
5589
|
+
if (!isPositiveInteger2(id)) {
|
|
4002
5590
|
errors.push(`M\xE3 trang '${id}' trong danh s\xE1ch 'ids' kh\xF4ng ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng h\u1EE3p l\u1EC7.`);
|
|
4003
5591
|
}
|
|
4004
5592
|
}
|
|
@@ -4022,7 +5610,7 @@ function validateUpsertInstantPageFormRequest(input) {
|
|
|
4022
5610
|
return { isValid: false, errors: ["D\u1EEF li\u1EC7u c\u1EA5u h\xECnh bi\u1EC3u m\u1EABu Instant Page ph\u1EA3i l\xE0 m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng."] };
|
|
4023
5611
|
}
|
|
4024
5612
|
const obj = input;
|
|
4025
|
-
if (!
|
|
5613
|
+
if (!isPositiveInteger2(obj.instantPageId)) {
|
|
4026
5614
|
errors.push("Tr\u01B0\u1EDDng 'instantPageId' l\xE0 b\u1EAFt bu\u1ED9c v\xE0 ph\u1EA3i l\xE0 s\u1ED1 nguy\xEAn d\u01B0\u01A1ng (> 0).");
|
|
4027
5615
|
}
|
|
4028
5616
|
if (!obj.oaId || typeof obj.oaId !== "string" || !/^\d+$/.test(obj.oaId.trim())) {
|
|
@@ -4075,7 +5663,7 @@ function validateUpsertInstantPageFormRequest(input) {
|
|
|
4075
5663
|
}
|
|
4076
5664
|
}
|
|
4077
5665
|
const data = {
|
|
4078
|
-
instantPageId:
|
|
5666
|
+
instantPageId: toPositiveInteger2(obj.instantPageId),
|
|
4079
5667
|
oaId: String(obj.oaId).trim(),
|
|
4080
5668
|
formName: String(obj.formName).trim(),
|
|
4081
5669
|
questions: obj.questions ?? [],
|
|
@@ -4843,110 +6431,6 @@ var ZaloAdsIntegrationManager = class {
|
|
|
4843
6431
|
}
|
|
4844
6432
|
};
|
|
4845
6433
|
|
|
4846
|
-
// src/dtos/ads.dto.ts
|
|
4847
|
-
var ZaloAdsFlowChildMessageDto = class {
|
|
4848
|
-
/** Mẫu Zalo đã xác minh dùng 8 cho tin nhắn văn bản trong nhánh. */
|
|
4849
|
-
type;
|
|
4850
|
-
/** Nội dung phản hồi trong nhánh. */
|
|
4851
|
-
text;
|
|
4852
|
-
};
|
|
4853
|
-
var ZaloAdsFlowButtonDto = class {
|
|
4854
|
-
/** 3 = Gửi tin nhắn vào nhánh hội thoại; 10 = Mở website; 12 = Thực hiện cuộc gọi. */
|
|
4855
|
-
type;
|
|
4856
|
-
/** Nhãn nút hiển thị cho người dùng. */
|
|
4857
|
-
text;
|
|
4858
|
-
/** URL website; bắt buộc với type 10. */
|
|
4859
|
-
url;
|
|
4860
|
-
/** Cờ của nút website; dùng true với type 10. */
|
|
4861
|
-
editable;
|
|
4862
|
-
/** Số hotline, bắt buộc khi type là 12. */
|
|
4863
|
-
phone;
|
|
4864
|
-
/** Nhánh tin nhắn, bắt buộc khi type là 3. Với type 10 hoặc 12, để null hoặc bỏ qua. */
|
|
4865
|
-
messages;
|
|
4866
|
-
};
|
|
4867
|
-
var ZaloAdsFlowMessageDto = class {
|
|
4868
|
-
/** 1 = Tin nhắn văn bản; 2 = Tin nhắn kèm hình ảnh. */
|
|
4869
|
-
type;
|
|
4870
|
-
/** Nội dung tin nhắn, tối đa 1961 ký tự. Hỗ trợ biến: {{user_name}}, {{page_name}}, {{user_gender}}, {{user_id}}, {{page_id}}, {{date}}, {{datetime}}. */
|
|
4871
|
-
text;
|
|
4872
|
-
/** URL media hoặc null với tin nhắn văn bản thuần. Bắt buộc và không được null khi type = 2. */
|
|
4873
|
-
url;
|
|
4874
|
-
/** Các nút đi kèm tin nhắn. */
|
|
4875
|
-
buttons;
|
|
4876
|
-
};
|
|
4877
|
-
var ZaloAdsCreateFlowRequest = class {
|
|
4878
|
-
/** ID Official Account sở hữu kịch bản. */
|
|
4879
|
-
oaId;
|
|
4880
|
-
/** Luôn null khi tạo kịch bản mới. */
|
|
4881
|
-
id;
|
|
4882
|
-
/** Tên kịch bản, tối đa 33 ký tự theo giao diện Zalo Ads. */
|
|
4883
|
-
name;
|
|
4884
|
-
/** Các tin nhắn được gửi theo thứ tự trong kịch bản. */
|
|
4885
|
-
messages;
|
|
4886
|
-
};
|
|
4887
|
-
var ZaloAdsCreateFlowResponse = class {
|
|
4888
|
-
/** 0 khi tạo thành công. */
|
|
4889
|
-
code;
|
|
4890
|
-
/** ID kịch bản mới, truyền vào data.ZaloFlowId khi cập nhật quảng cáo Tin nhắn. */
|
|
4891
|
-
data;
|
|
4892
|
-
};
|
|
4893
|
-
var ZaloAdsUpdateVideoAdRequest = class {
|
|
4894
|
-
data;
|
|
4895
|
-
est;
|
|
4896
|
-
isBoostAd;
|
|
4897
|
-
stt;
|
|
4898
|
-
};
|
|
4899
|
-
var ZaloAdsUpdateVideoAdResponse = class {
|
|
4900
|
-
code;
|
|
4901
|
-
msg;
|
|
4902
|
-
data;
|
|
4903
|
-
};
|
|
4904
|
-
var ZaloAdsCreatePostCampaignRequest = class {
|
|
4905
|
-
/** Cấu hình chiến dịch Bài viết (object hoặc chuỗi JSON). */
|
|
4906
|
-
data;
|
|
4907
|
-
};
|
|
4908
|
-
var ZaloAdsCreatePostCampaignResponse = class {
|
|
4909
|
-
code;
|
|
4910
|
-
/** Chuỗi ID quảng cáo mới trong response Zalo Ads. */
|
|
4911
|
-
msg;
|
|
4912
|
-
data;
|
|
4913
|
-
};
|
|
4914
|
-
var ZaloAdsMessageAdUpdateDataDto = class {
|
|
4915
|
-
AdId;
|
|
4916
|
-
CampaignId;
|
|
4917
|
-
MemberId;
|
|
4918
|
-
/** Phiên bản lạc quan lấy từ bản quảng cáo hiện tại. */
|
|
4919
|
-
RowVersion;
|
|
4920
|
-
AdName;
|
|
4921
|
-
/** 20 (hoặc 19) là quảng cáo Tin nhắn. */
|
|
4922
|
-
Type;
|
|
4923
|
-
Settings;
|
|
4924
|
-
Status;
|
|
4925
|
-
StartDate;
|
|
4926
|
-
EndDate;
|
|
4927
|
-
/** ID kịch bản, lấy từ createFlow hoặc listFlowsByOfficialAccount. */
|
|
4928
|
-
ZaloFlowId;
|
|
4929
|
-
ZaloPageId;
|
|
4930
|
-
ZaloFormId;
|
|
4931
|
-
ConversionId;
|
|
4932
|
-
Report;
|
|
4933
|
-
};
|
|
4934
|
-
var ZaloAdsUpdateMessageAdRequest = class {
|
|
4935
|
-
/** Payload quảng cáo đầy đủ (object hoặc chuỗi JSON); service tự tuần tự hóa thành field form data. */
|
|
4936
|
-
data;
|
|
4937
|
-
/** Tham số UI phụ trợ; capture Zalo Ads có giá trị literal "undefined". */
|
|
4938
|
-
est;
|
|
4939
|
-
/** 0 là quảng cáo thông thường, 1 là boost ad. */
|
|
4940
|
-
isBoostAd;
|
|
4941
|
-
/** Trạng thái thao tác của giao diện; mẫu đã ghi nhận 0. */
|
|
4942
|
-
stt;
|
|
4943
|
-
};
|
|
4944
|
-
var ZaloAdsUpdateMessageAdResponse = class {
|
|
4945
|
-
msg;
|
|
4946
|
-
code;
|
|
4947
|
-
data;
|
|
4948
|
-
};
|
|
4949
|
-
|
|
4950
6434
|
// src/dtos/instant-pages.dto.ts
|
|
4951
6435
|
var ZaloInstantPageBlockType = /* @__PURE__ */ ((ZaloInstantPageBlockType2) => {
|
|
4952
6436
|
ZaloInstantPageBlockType2[ZaloInstantPageBlockType2["CAROUSEL_SLIDER"] = 1] = "CAROUSEL_SLIDER";
|
|
@@ -4970,21 +6454,28 @@ var ZaloInstantPageBlockType = /* @__PURE__ */ ((ZaloInstantPageBlockType2) => {
|
|
|
4970
6454
|
return ZaloInstantPageBlockType2;
|
|
4971
6455
|
})(ZaloInstantPageBlockType || {});
|
|
4972
6456
|
export {
|
|
6457
|
+
ALLOWED_CTAS_PER_AD_TYPE,
|
|
4973
6458
|
ALL_ZALO_ADS_DOMAINS,
|
|
4974
6459
|
MemoryZaloSessionStore,
|
|
4975
6460
|
RedisZaloSessionStore,
|
|
4976
6461
|
TieredZaloSessionStore,
|
|
6462
|
+
VALID_AD_TYPES,
|
|
6463
|
+
VALID_CAMPAIGN_TYPES,
|
|
4977
6464
|
ZaloAccountService,
|
|
4978
6465
|
ZaloAdsIntegrationManager as ZaloAdsAccountPool,
|
|
6466
|
+
ZaloAdsActionType,
|
|
6467
|
+
ZaloAdsAgeRangeId,
|
|
4979
6468
|
ZaloAdsCreateFlowRequest,
|
|
4980
6469
|
ZaloAdsCreateFlowResponse,
|
|
4981
6470
|
ZaloAdsCreatePostCampaignRequest,
|
|
4982
6471
|
ZaloAdsCreatePostCampaignResponse,
|
|
6472
|
+
ZaloAdsDeliveryType,
|
|
4983
6473
|
ZaloAdsFlowButtonDto,
|
|
4984
6474
|
ZaloAdsFlowChildMessageDto,
|
|
4985
6475
|
ZaloAdsFlowMessageDto,
|
|
4986
6476
|
ZaloAdsIntegrationManager,
|
|
4987
6477
|
ZaloAdsMessageAdUpdateDataDto,
|
|
6478
|
+
ZaloAdsOsId,
|
|
4988
6479
|
ZaloAdsSdk,
|
|
4989
6480
|
ZaloAdsService,
|
|
4990
6481
|
ZaloAdsUpdateMessageAdRequest,
|
|
@@ -4992,13 +6483,17 @@ export {
|
|
|
4992
6483
|
ZaloAdsUpdateVideoAdRequest,
|
|
4993
6484
|
ZaloAdsUpdateVideoAdResponse,
|
|
4994
6485
|
ZaloAudiencesService,
|
|
6486
|
+
ZaloAudiencesValidator,
|
|
4995
6487
|
ZaloAuthManager,
|
|
4996
6488
|
ZaloAutomatedRulesService,
|
|
4997
6489
|
ZaloCampaignsService,
|
|
6490
|
+
ZaloCampaignsValidator,
|
|
4998
6491
|
ZaloChatWidgetService,
|
|
4999
6492
|
ZaloConversionsService,
|
|
5000
6493
|
ZaloFlowsService,
|
|
6494
|
+
ZaloFlowsValidator,
|
|
5001
6495
|
ZaloFormsService,
|
|
6496
|
+
ZaloFormsValidator,
|
|
5002
6497
|
ZaloInstantPageBlockType,
|
|
5003
6498
|
ZaloInstantPagesService,
|
|
5004
6499
|
ZaloInstantPagesValidator,
|
|
@@ -5015,9 +6510,24 @@ export {
|
|
|
5015
6510
|
defaultSharedMemorySessionStore,
|
|
5016
6511
|
deriveAccountKeyFromZpsid,
|
|
5017
6512
|
parseZaloCookieInput,
|
|
6513
|
+
validateChangeCampaignStatusParams,
|
|
5018
6514
|
validateCloneInstantPageRequest,
|
|
6515
|
+
validateCreateCampaignPayload,
|
|
6516
|
+
validateCreateFlowRequest,
|
|
6517
|
+
validateCreateFormEventAudienceRequest,
|
|
6518
|
+
validateCreateFormRequest,
|
|
6519
|
+
validateCreateLookalikeAudienceRequest,
|
|
6520
|
+
validateCreateMessageOaEventAudienceRequest,
|
|
6521
|
+
validateCreateMessagePersonalEventAudienceRequest,
|
|
6522
|
+
validateCreatePhoneAudienceRequest,
|
|
6523
|
+
validateCreateSavedAudienceRequest,
|
|
6524
|
+
validateDuplicateAdParams,
|
|
6525
|
+
validateEstimateAudienceRequest,
|
|
5019
6526
|
validateGetInstantPageDetailRequest,
|
|
5020
6527
|
validateGetInstantPageSharedUserRequest,
|
|
6528
|
+
validateListFlowsByOfficialAccountRequest,
|
|
6529
|
+
validateListFormLeadsRequest,
|
|
6530
|
+
validateListFormsByOfficialAccountRequest,
|
|
5021
6531
|
validateListInstantPageEditHistoryRequest,
|
|
5022
6532
|
validateListInstantPageFormRecordsRequest,
|
|
5023
6533
|
validateListInstantPageLeadsRequest,
|
|
@@ -5025,7 +6535,10 @@ export {
|
|
|
5025
6535
|
validateListInstantPageUsersRequest,
|
|
5026
6536
|
validateListMiniPageTemplatesRequest,
|
|
5027
6537
|
validateListOfficialAccountsRequest,
|
|
6538
|
+
validateRenameCampaignParams,
|
|
5028
6539
|
validateSearchInstantPagesRequest,
|
|
6540
|
+
validateUpdateAdPayload,
|
|
6541
|
+
validateUpdateFlowRequest,
|
|
5029
6542
|
validateUpdateInstantPageRequest,
|
|
5030
6543
|
validateUpsertInstantPageFormRequest
|
|
5031
6544
|
};
|