@bprotsyk/aso-core 2.1.142 → 2.1.180
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/lib/app/app.d.ts +75 -0
- package/lib/app/app.js +28 -1
- package/lib/index.d.ts +1 -1
- package/lib/keitaro/keitaro-conversions.d.ts +52 -0
- package/lib/keitaro/keitaro-conversions.js +27 -0
- package/lib/keitaro/keitaro-utils.d.ts +2 -0
- package/lib/keitaro/keitaro-utils.js +108 -0
- package/lib/keitaro-utils.d.ts +2 -0
- package/lib/keitaro-utils.js +136 -0
- package/lib/utils/comparing.d.ts +31 -0
- package/lib/utils/comparing.js +104 -0
- package/package.json +1 -1
- package/src/app/app.ts +38 -1
- package/src/index.ts +1 -1
- package/lib/templates/words.txt +0 -212
package/lib/app/app.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { AppType } from "./app-type";
|
|
2
2
|
import mongoose, { Document } from "mongoose";
|
|
3
|
+
export interface IAppsflyerPostback {
|
|
4
|
+
campaign_id: number;
|
|
5
|
+
id: number;
|
|
6
|
+
method: "GET" | "POST";
|
|
7
|
+
statuses: string[];
|
|
8
|
+
url: string;
|
|
9
|
+
notes?: string;
|
|
10
|
+
}
|
|
3
11
|
export interface IApp extends Document {
|
|
4
12
|
id: number;
|
|
5
13
|
enabled: boolean;
|
|
@@ -34,6 +42,7 @@ export interface IApp extends Document {
|
|
|
34
42
|
ech?: boolean;
|
|
35
43
|
imageFormat: EImageFormat;
|
|
36
44
|
policyPath: string;
|
|
45
|
+
postbacks?: IAppsflyerPostback[];
|
|
37
46
|
}
|
|
38
47
|
export declare enum EImageFormat {
|
|
39
48
|
PNG = "png",
|
|
@@ -211,6 +220,28 @@ export declare const AppSchema: mongoose.Schema<any, mongoose.Model<any, any, an
|
|
|
211
220
|
integrationVersion: IntegrationVersion;
|
|
212
221
|
removeInfo: any;
|
|
213
222
|
policyPath: string;
|
|
223
|
+
postbacks: mongoose.Types.DocumentArray<{
|
|
224
|
+
id: number;
|
|
225
|
+
campaign_id: number;
|
|
226
|
+
method: "GET" | "POST";
|
|
227
|
+
statuses: string[];
|
|
228
|
+
url: string;
|
|
229
|
+
notes?: string | null | undefined;
|
|
230
|
+
}, mongoose.Types.Subdocument<mongoose.mongo.BSON.ObjectId, any, {
|
|
231
|
+
id: number;
|
|
232
|
+
campaign_id: number;
|
|
233
|
+
method: "GET" | "POST";
|
|
234
|
+
statuses: string[];
|
|
235
|
+
url: string;
|
|
236
|
+
notes?: string | null | undefined;
|
|
237
|
+
}> & {
|
|
238
|
+
id: number;
|
|
239
|
+
campaign_id: number;
|
|
240
|
+
method: "GET" | "POST";
|
|
241
|
+
statuses: string[];
|
|
242
|
+
url: string;
|
|
243
|
+
notes?: string | null | undefined;
|
|
244
|
+
}>;
|
|
214
245
|
name?: string | null | undefined;
|
|
215
246
|
pushesEnabled?: boolean | null | undefined;
|
|
216
247
|
publicationHistory?: any;
|
|
@@ -255,6 +286,28 @@ export declare const AppSchema: mongoose.Schema<any, mongoose.Model<any, any, an
|
|
|
255
286
|
integrationVersion: IntegrationVersion;
|
|
256
287
|
removeInfo: any;
|
|
257
288
|
policyPath: string;
|
|
289
|
+
postbacks: mongoose.Types.DocumentArray<{
|
|
290
|
+
id: number;
|
|
291
|
+
campaign_id: number;
|
|
292
|
+
method: "GET" | "POST";
|
|
293
|
+
statuses: string[];
|
|
294
|
+
url: string;
|
|
295
|
+
notes?: string | null | undefined;
|
|
296
|
+
}, mongoose.Types.Subdocument<mongoose.mongo.BSON.ObjectId, any, {
|
|
297
|
+
id: number;
|
|
298
|
+
campaign_id: number;
|
|
299
|
+
method: "GET" | "POST";
|
|
300
|
+
statuses: string[];
|
|
301
|
+
url: string;
|
|
302
|
+
notes?: string | null | undefined;
|
|
303
|
+
}> & {
|
|
304
|
+
id: number;
|
|
305
|
+
campaign_id: number;
|
|
306
|
+
method: "GET" | "POST";
|
|
307
|
+
statuses: string[];
|
|
308
|
+
url: string;
|
|
309
|
+
notes?: string | null | undefined;
|
|
310
|
+
}>;
|
|
258
311
|
name?: string | null | undefined;
|
|
259
312
|
pushesEnabled?: boolean | null | undefined;
|
|
260
313
|
publicationHistory?: any;
|
|
@@ -299,6 +352,28 @@ export declare const AppSchema: mongoose.Schema<any, mongoose.Model<any, any, an
|
|
|
299
352
|
integrationVersion: IntegrationVersion;
|
|
300
353
|
removeInfo: any;
|
|
301
354
|
policyPath: string;
|
|
355
|
+
postbacks: mongoose.Types.DocumentArray<{
|
|
356
|
+
id: number;
|
|
357
|
+
campaign_id: number;
|
|
358
|
+
method: "GET" | "POST";
|
|
359
|
+
statuses: string[];
|
|
360
|
+
url: string;
|
|
361
|
+
notes?: string | null | undefined;
|
|
362
|
+
}, mongoose.Types.Subdocument<mongoose.mongo.BSON.ObjectId, any, {
|
|
363
|
+
id: number;
|
|
364
|
+
campaign_id: number;
|
|
365
|
+
method: "GET" | "POST";
|
|
366
|
+
statuses: string[];
|
|
367
|
+
url: string;
|
|
368
|
+
notes?: string | null | undefined;
|
|
369
|
+
}> & {
|
|
370
|
+
id: number;
|
|
371
|
+
campaign_id: number;
|
|
372
|
+
method: "GET" | "POST";
|
|
373
|
+
statuses: string[];
|
|
374
|
+
url: string;
|
|
375
|
+
notes?: string | null | undefined;
|
|
376
|
+
}>;
|
|
302
377
|
name?: string | null | undefined;
|
|
303
378
|
pushesEnabled?: boolean | null | undefined;
|
|
304
379
|
publicationHistory?: any;
|
package/lib/app/app.js
CHANGED
|
@@ -215,5 +215,32 @@ exports.AppSchema = new mongoose_1.Schema({
|
|
|
215
215
|
policyPath: {
|
|
216
216
|
type: String,
|
|
217
217
|
default: "none"
|
|
218
|
-
}
|
|
218
|
+
},
|
|
219
|
+
postbacks: [{
|
|
220
|
+
campaign_id: {
|
|
221
|
+
type: Number,
|
|
222
|
+
required: true
|
|
223
|
+
},
|
|
224
|
+
id: {
|
|
225
|
+
type: Number,
|
|
226
|
+
required: true
|
|
227
|
+
},
|
|
228
|
+
method: {
|
|
229
|
+
type: String,
|
|
230
|
+
enum: ["GET", "POST"],
|
|
231
|
+
required: true
|
|
232
|
+
},
|
|
233
|
+
statuses: [{
|
|
234
|
+
type: String,
|
|
235
|
+
required: true
|
|
236
|
+
}],
|
|
237
|
+
url: {
|
|
238
|
+
type: String,
|
|
239
|
+
required: true
|
|
240
|
+
},
|
|
241
|
+
notes: {
|
|
242
|
+
type: String,
|
|
243
|
+
required: false
|
|
244
|
+
}
|
|
245
|
+
}]
|
|
219
246
|
});
|
package/lib/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { IPush } from "./general/push";
|
|
|
2
2
|
export { IOffer, IPartner, IOfferType } from "./offers/offer";
|
|
3
3
|
export { IOffersSection, OffersSectionSchema, DefaultSectionId } from "./offers/section";
|
|
4
4
|
export { IAppOffersSection, ISectionsList, SectionsListSchema, IOfferState } from "./offers/list";
|
|
5
|
-
export { IAdjustEventIds, IntegrationVersion, IApp, AppSchema, PlugType, IAppKeitaroData, IExternalParams, IPlatformParams, EPlatform, AppStatus, IPublicationHistory, IRemovalInfo, EDirectType } from "./app/app";
|
|
5
|
+
export { IAdjustEventIds, IntegrationVersion, IApp, AppSchema, PlugType, IAppKeitaroData, IExternalParams, IPlatformParams, EPlatform, AppStatus, IPublicationHistory, IRemovalInfo, EDirectType, IAppsflyerPostback } from "./app/app";
|
|
6
6
|
export { IAppListItem } from "./app/app-list-item";
|
|
7
7
|
export { AppType } from "./app/app-type";
|
|
8
8
|
export { AlternativeLayoutType, AlternativeSourceType, AlternativeLogicType, AlternativeNetworkTool, AlternativeStorageType, AlternativeNavigation, AlternativeOnBackPressed, AlternativeOnActivityResult, IAppIntegration as IFlashIntegration } from "./app/app-integration";
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { KeitaroClicksInterval } from './keitaro-clicks';
|
|
2
|
+
export declare enum KeitaroOperator {
|
|
3
|
+
EQUALS = "EQUALS",
|
|
4
|
+
NOT_EQUAL = "NOT_EQUAL",
|
|
5
|
+
EQUALS_OR_GREATER_THAN = "EQUALS_OR_GREATER_THAN",
|
|
6
|
+
EQUALS_OR_LESS_THAN = "EQUALS_OR_LESS_THAN",
|
|
7
|
+
GREATER_THAN = "GREATER_THAN",
|
|
8
|
+
LESS_THAN = "LESS_THAN",
|
|
9
|
+
MATCH_REGEXP = "MATCH_REGEXP",
|
|
10
|
+
NOT_MATCH_REGEXP = "NOT_MATCH_REGEXP",
|
|
11
|
+
BEGINS_WITH = "BEGINS_WITH",
|
|
12
|
+
ENDS_WITH = "ENDS_WITH",
|
|
13
|
+
CONTAINS = "CONTAINS",
|
|
14
|
+
NOT_CONTAIN = "NOT_CONTAIN",
|
|
15
|
+
IN_LIST = "IN_LIST",
|
|
16
|
+
NOT_IN_LIST = "NOT_IN_LIST",
|
|
17
|
+
BETWEEN = "BETWEEN",
|
|
18
|
+
IS_SET = "IS_SET",
|
|
19
|
+
IS_NOT_SET = "IS_NOT_SET",
|
|
20
|
+
IS_TRUE = "IS_TRUE",
|
|
21
|
+
IS_FALSE = "IS_FALSE",
|
|
22
|
+
HAS_LABEL = "HAS_LABEL",
|
|
23
|
+
NOT_HAS_LABEL = "NOT_HAS_LABEL"
|
|
24
|
+
}
|
|
25
|
+
export interface IKeitaroConversionRangeRequest {
|
|
26
|
+
from?: string;
|
|
27
|
+
to?: string;
|
|
28
|
+
timezone?: string;
|
|
29
|
+
interval?: KeitaroClicksInterval;
|
|
30
|
+
}
|
|
31
|
+
export interface IKeitaroConversionFilterRequest {
|
|
32
|
+
name: string;
|
|
33
|
+
operator: KeitaroOperator;
|
|
34
|
+
expression?: string | number | Array<string | number>;
|
|
35
|
+
}
|
|
36
|
+
export interface IKeitaroConversionSortRequest {
|
|
37
|
+
name: string;
|
|
38
|
+
order: 'ASC' | 'DESC';
|
|
39
|
+
}
|
|
40
|
+
export interface IKeitaroConversionsRequest {
|
|
41
|
+
range: IKeitaroConversionRangeRequest;
|
|
42
|
+
limit?: number;
|
|
43
|
+
offset?: number;
|
|
44
|
+
columns?: string[];
|
|
45
|
+
filters?: IKeitaroConversionFilterRequest[];
|
|
46
|
+
sort?: IKeitaroConversionSortRequest[];
|
|
47
|
+
}
|
|
48
|
+
export interface IKeitaroConversionsResponse {
|
|
49
|
+
rows: any[];
|
|
50
|
+
total: number;
|
|
51
|
+
meta: string[];
|
|
52
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KeitaroOperator = void 0;
|
|
4
|
+
var KeitaroOperator;
|
|
5
|
+
(function (KeitaroOperator) {
|
|
6
|
+
KeitaroOperator["EQUALS"] = "EQUALS";
|
|
7
|
+
KeitaroOperator["NOT_EQUAL"] = "NOT_EQUAL";
|
|
8
|
+
KeitaroOperator["EQUALS_OR_GREATER_THAN"] = "EQUALS_OR_GREATER_THAN";
|
|
9
|
+
KeitaroOperator["EQUALS_OR_LESS_THAN"] = "EQUALS_OR_LESS_THAN";
|
|
10
|
+
KeitaroOperator["GREATER_THAN"] = "GREATER_THAN";
|
|
11
|
+
KeitaroOperator["LESS_THAN"] = "LESS_THAN";
|
|
12
|
+
KeitaroOperator["MATCH_REGEXP"] = "MATCH_REGEXP";
|
|
13
|
+
KeitaroOperator["NOT_MATCH_REGEXP"] = "NOT_MATCH_REGEXP";
|
|
14
|
+
KeitaroOperator["BEGINS_WITH"] = "BEGINS_WITH";
|
|
15
|
+
KeitaroOperator["ENDS_WITH"] = "ENDS_WITH";
|
|
16
|
+
KeitaroOperator["CONTAINS"] = "CONTAINS";
|
|
17
|
+
KeitaroOperator["NOT_CONTAIN"] = "NOT_CONTAIN";
|
|
18
|
+
KeitaroOperator["IN_LIST"] = "IN_LIST";
|
|
19
|
+
KeitaroOperator["NOT_IN_LIST"] = "NOT_IN_LIST";
|
|
20
|
+
KeitaroOperator["BETWEEN"] = "BETWEEN";
|
|
21
|
+
KeitaroOperator["IS_SET"] = "IS_SET";
|
|
22
|
+
KeitaroOperator["IS_NOT_SET"] = "IS_NOT_SET";
|
|
23
|
+
KeitaroOperator["IS_TRUE"] = "IS_TRUE";
|
|
24
|
+
KeitaroOperator["IS_FALSE"] = "IS_FALSE";
|
|
25
|
+
KeitaroOperator["HAS_LABEL"] = "HAS_LABEL";
|
|
26
|
+
KeitaroOperator["NOT_HAS_LABEL"] = "NOT_HAS_LABEL";
|
|
27
|
+
})(KeitaroOperator = exports.KeitaroOperator || (exports.KeitaroOperator = {}));
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.displayComparisonResults = void 0;
|
|
4
|
+
function displayComparisonResults(comparison) {
|
|
5
|
+
const isValidStatus = (status) => ['sale', 'rebill'].includes(status);
|
|
6
|
+
let wpTotalSum = comparison.wpConversions
|
|
7
|
+
.reduce((sum, conv) => sum + conv.sum, 0);
|
|
8
|
+
let keitaroTotalSum = comparison.keitaroConversions
|
|
9
|
+
.filter(conv => isValidStatus(conv.status))
|
|
10
|
+
.reduce((sum, conv) => sum + (parseFloat(conv.revenue) || 0), 0);
|
|
11
|
+
// Get all Keitaro rejected conversions
|
|
12
|
+
const allKeitaroRejected = [
|
|
13
|
+
...comparison.rejected.keitaro,
|
|
14
|
+
...comparison.matches
|
|
15
|
+
.filter(match => match.keitaro.status === 'rejected')
|
|
16
|
+
.map(match => match.keitaro)
|
|
17
|
+
];
|
|
18
|
+
// Group mismatches by project/offer
|
|
19
|
+
const wpMismatchesByProject = new Map();
|
|
20
|
+
const keitaroMismatchesByOffer = new Map();
|
|
21
|
+
const rejectedByOffer = new Map();
|
|
22
|
+
comparison.mismatches.onlyInWP.forEach(conv => {
|
|
23
|
+
if (!wpMismatchesByProject.has(conv.project)) {
|
|
24
|
+
wpMismatchesByProject.set(conv.project, { count: 0, sum: 0, conversions: [] });
|
|
25
|
+
}
|
|
26
|
+
const projectStats = wpMismatchesByProject.get(conv.project);
|
|
27
|
+
projectStats.count++;
|
|
28
|
+
projectStats.sum += conv.sum;
|
|
29
|
+
projectStats.conversions.push(conv);
|
|
30
|
+
});
|
|
31
|
+
// Include all conversions in mismatches, including rejected ones
|
|
32
|
+
[...comparison.mismatches.onlyInKeitaro, ...allKeitaroRejected].forEach(conv => {
|
|
33
|
+
if (!keitaroMismatchesByOffer.has(conv.offer)) {
|
|
34
|
+
keitaroMismatchesByOffer.set(conv.offer, { count: 0, sum: 0, conversions: [] });
|
|
35
|
+
}
|
|
36
|
+
const offerStats = keitaroMismatchesByOffer.get(conv.offer);
|
|
37
|
+
offerStats.count++;
|
|
38
|
+
offerStats.sum += parseFloat(conv.revenue) || 0;
|
|
39
|
+
offerStats.conversions.push(conv);
|
|
40
|
+
});
|
|
41
|
+
// Group rejected conversions by offer
|
|
42
|
+
allKeitaroRejected.forEach(conv => {
|
|
43
|
+
if (!rejectedByOffer.has(conv.offer)) {
|
|
44
|
+
rejectedByOffer.set(conv.offer, { count: 0, sum: 0, conversions: [] });
|
|
45
|
+
}
|
|
46
|
+
const offerStats = rejectedByOffer.get(conv.offer);
|
|
47
|
+
offerStats.count++;
|
|
48
|
+
offerStats.sum += parseFloat(conv.revenue) || 0;
|
|
49
|
+
offerStats.conversions.push(conv);
|
|
50
|
+
});
|
|
51
|
+
console.log('\n\x1b[1mЗагальна статистика:\x1b[0m');
|
|
52
|
+
console.log(`Всього конверсій у WP: ${comparison.wpConversions.length} (Сума: \x1b[33m$${wpTotalSum.toFixed(2)}\x1b[0m)`);
|
|
53
|
+
console.log(`Всього конверсій у Keitaro: ${comparison.keitaroConversions.filter(conv => isValidStatus(conv.status)).length} (Сума: \x1b[33m$${keitaroTotalSum.toFixed(2)}\x1b[0m)`);
|
|
54
|
+
console.log(`Співпадінь: ${comparison.matches.length}`);
|
|
55
|
+
console.log(`Відхилених конверсій: ${allKeitaroRejected.length}`);
|
|
56
|
+
const totalDiff = Math.abs(wpTotalSum - keitaroTotalSum);
|
|
57
|
+
console.log(`\x1b[1mЗагальна різниця:\x1b[0m \x1b[33m$${totalDiff.toFixed(2)}\x1b[0m`);
|
|
58
|
+
// Always show rejected conversions first if there are any
|
|
59
|
+
if (allKeitaroRejected.length > 0) {
|
|
60
|
+
const rejectedTotal = allKeitaroRejected.reduce((sum, conv) => sum + (parseFloat(conv.revenue) || 0), 0);
|
|
61
|
+
console.log('\n\x1b[1mВідхилені конверсії в Keitaro за офферами:\x1b[0m');
|
|
62
|
+
console.log(`Загальна кількість: ${allKeitaroRejected.length}`);
|
|
63
|
+
console.log(`Загальна сума: \x1b[33m$${rejectedTotal.toFixed(2)}\x1b[0m`);
|
|
64
|
+
for (const [offer, stats] of rejectedByOffer.entries()) {
|
|
65
|
+
console.log(`\n\x1b[33m${offer}:\x1b[0m`);
|
|
66
|
+
console.log(`Кількість: ${stats.count}, Сума: $${stats.sum.toFixed(2)}`);
|
|
67
|
+
stats.conversions.forEach(conv => {
|
|
68
|
+
const matchMark = comparison.matches.some(match => match.keitaro.sub_id === conv.sub_id) ? '[matched] ' : '';
|
|
69
|
+
console.log(` ${matchMark}${conv.sub_id} | ${conv.sale_datetime.split(' ')[0]} | $${conv.revenue}`);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
console.log('\n\x1b[1mВідсутні в Keitaro за проектами:\x1b[0m');
|
|
74
|
+
for (const [project, stats] of wpMismatchesByProject) {
|
|
75
|
+
console.log(`\n\x1b[31m${project}:\x1b[0m`);
|
|
76
|
+
console.log(`Кількість: ${stats.count}, Сума: $${stats.sum.toFixed(2)}`);
|
|
77
|
+
stats.conversions.forEach(conv => {
|
|
78
|
+
console.log(` ${conv.clickId} | ${conv.regDate.toISOString().split('T')[0]} | $${conv.sum}`);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
console.log('\n\x1b[1mВідсутні в WP за офферами:\x1b[0m');
|
|
82
|
+
for (const [offer, stats] of keitaroMismatchesByOffer) {
|
|
83
|
+
console.log(`\n\x1b[32m${offer}:\x1b[0m`);
|
|
84
|
+
console.log(`Кількість: ${stats.count}, Сума: $${stats.sum.toFixed(2)}`);
|
|
85
|
+
stats.conversions.forEach(conv => {
|
|
86
|
+
const statusMark = conv.status === 'rejected' ? '(rejected) ' : '';
|
|
87
|
+
const matchMark = comparison.matches.some(match => match.keitaro.sub_id === conv.sub_id) ? '[matched] ' : '';
|
|
88
|
+
console.log(` ${statusMark}${matchMark}${conv.sub_id} | ${conv.sale_datetime.split(' ')[0]} | $${conv.revenue}`);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
let mismatchWPSum = comparison.mismatches.onlyInWP
|
|
92
|
+
.reduce((sum, conv) => sum + conv.sum, 0);
|
|
93
|
+
let mismatchKeitaroSum = [...comparison.mismatches.onlyInKeitaro, ...allKeitaroRejected]
|
|
94
|
+
.reduce((sum, conv) => sum + (parseFloat(conv.revenue) || 0), 0);
|
|
95
|
+
console.log('\n\x1b[1mПідсумок розбіжностей:\x1b[0m');
|
|
96
|
+
console.log(`Сума відсутніх в Keitaro: \x1b[31m$${mismatchWPSum.toFixed(2)}\x1b[0m`);
|
|
97
|
+
console.log(`Сума відсутніх в WP: \x1b[32m$${mismatchKeitaroSum.toFixed(2)}\x1b[0m`);
|
|
98
|
+
console.log(`Різниця у відсутніх конверсіях: \x1b[33m$${Math.abs(mismatchWPSum - mismatchKeitaroSum).toFixed(2)}\x1b[0m`);
|
|
99
|
+
// Debug information
|
|
100
|
+
const matchingRejected = comparison.matches.filter(match => match.keitaro.status === 'rejected').length;
|
|
101
|
+
console.log('\n\x1b[1mДебаг інформація:\x1b[0m');
|
|
102
|
+
console.log(`Кількість конверсій відсутніх в WP: ${comparison.mismatches.onlyInKeitaro.length + comparison.rejected.keitaro.length + matchingRejected}`);
|
|
103
|
+
console.log(`З них:`);
|
|
104
|
+
console.log(`- Активних: ${comparison.mismatches.onlyInKeitaro.length}`);
|
|
105
|
+
console.log(`- Відхилених (унікальних): ${comparison.rejected.keitaro.length}`);
|
|
106
|
+
console.log(`- Відхилених (що є в WP): ${matchingRejected}`);
|
|
107
|
+
}
|
|
108
|
+
exports.displayComparisonResults = displayComparisonResults;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.displayComparisonResults = void 0;
|
|
27
|
+
const keitaro_service_1 = require("./network/keitaro/keitaro-service");
|
|
28
|
+
const keitaro_conversions_1 = require("./keitaro/keitaro-conversions");
|
|
29
|
+
const path = __importStar(require("path"));
|
|
30
|
+
const comparing_1 = require("./utils/comparing");
|
|
31
|
+
function displayComparisonResults(comparison) {
|
|
32
|
+
const isValidStatus = (status) => ['sale', 'rebill'].includes(status);
|
|
33
|
+
// Считаем общие суммы
|
|
34
|
+
let wpTotalSum = comparison.wpConversions
|
|
35
|
+
.reduce((sum, conv) => sum + conv.sum, 0);
|
|
36
|
+
let keitaroTotalSum = comparison.keitaroConversions
|
|
37
|
+
.filter((conv) => isValidStatus(conv.status))
|
|
38
|
+
.reduce((sum, conv) => sum + (parseFloat(conv.revenue) || 0), 0);
|
|
39
|
+
// Находим разницу
|
|
40
|
+
const totalDiff = keitaroTotalSum - wpTotalSum;
|
|
41
|
+
console.log('\n\x1b[1mЗагальна статистика:\x1b[0m');
|
|
42
|
+
console.log(`WP сума: $${wpTotalSum.toFixed(2)}`);
|
|
43
|
+
console.log(`Keitaro сума: $${keitaroTotalSum.toFixed(2)}`);
|
|
44
|
+
console.log(`Різниця: $${totalDiff.toFixed(2)}`);
|
|
45
|
+
// Получаем конверсии, которые есть в Keitaro, но нет в WP
|
|
46
|
+
const extraKeitaroConversions = comparison.mismatches.onlyInKeitaro
|
|
47
|
+
.filter(conv => isValidStatus(conv.status))
|
|
48
|
+
.sort((a, b) => (parseFloat(b.revenue) || 0) - (parseFloat(a.revenue) || 0));
|
|
49
|
+
// Группируем по офферам для удобства
|
|
50
|
+
const byOffer = new Map();
|
|
51
|
+
extraKeitaroConversions.forEach(conv => {
|
|
52
|
+
const offer = conv.offer || 'Unknown Offer';
|
|
53
|
+
if (!byOffer.has(offer)) {
|
|
54
|
+
byOffer.set(offer, { count: 0, sum: 0, conversions: [] });
|
|
55
|
+
}
|
|
56
|
+
const stats = byOffer.get(offer);
|
|
57
|
+
const revenue = parseFloat(conv.revenue) || 0;
|
|
58
|
+
stats.count++;
|
|
59
|
+
stats.sum += revenue;
|
|
60
|
+
stats.conversions.push(conv);
|
|
61
|
+
});
|
|
62
|
+
console.log('\n\x1b[1mКонверсії, які є в Keitaro, але відсутні в WP:\x1b[0m');
|
|
63
|
+
let totalExtraSum = 0;
|
|
64
|
+
for (const [offer, stats] of byOffer) {
|
|
65
|
+
console.log(`\n\x1b[33m${offer}:\x1b[0m`);
|
|
66
|
+
console.log(`Кількість: ${stats.count}, Сума: $${stats.sum.toFixed(2)}`);
|
|
67
|
+
stats.conversions.forEach(conv => {
|
|
68
|
+
if (conv.sub_id && conv.revenue) {
|
|
69
|
+
console.log(` ${conv.sub_id} | ${conv.revenue}`);
|
|
70
|
+
totalExtraSum += parseFloat(conv.revenue) || 0;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
console.log('\n\x1b[1mПідсумок:\x1b[0m');
|
|
75
|
+
console.log(`Загальна сума "зайвих" конверсій: \x1b[33m$${totalExtraSum.toFixed(2)}\x1b[0m`);
|
|
76
|
+
console.log('Це конверсії, які потрібно видалити з Keitaro, щоб суми співпадали з WP');
|
|
77
|
+
}
|
|
78
|
+
exports.displayComparisonResults = displayComparisonResults;
|
|
79
|
+
// Self-executing function to fetch and compare conversions
|
|
80
|
+
(async () => {
|
|
81
|
+
const request = {
|
|
82
|
+
range: {
|
|
83
|
+
from: '2025-01-01',
|
|
84
|
+
to: '2025-01-31',
|
|
85
|
+
timezone: 'Europe/Kyiv'
|
|
86
|
+
},
|
|
87
|
+
filters: [
|
|
88
|
+
{
|
|
89
|
+
name: 'status',
|
|
90
|
+
operator: keitaro_conversions_1.KeitaroOperator.IN_LIST,
|
|
91
|
+
expression: ['sale', 'rejected']
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'affiliate_network_id',
|
|
95
|
+
operator: keitaro_conversions_1.KeitaroOperator.EQUALS,
|
|
96
|
+
expression: 2
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
columns: [
|
|
100
|
+
'sign', 'version', 'campaign', 'campaign_group', 'landing_group', 'offer_group',
|
|
101
|
+
'landing', 'offer', 'affiliate_network', 'ts', 'stream', 'source',
|
|
102
|
+
'x_requested_with', 'referrer', 'search_engine', 'keyword', 'click_id',
|
|
103
|
+
'sub_id', 'visitor_code', 'campaign_id', 'campaign_group_id', 'offer_group_id',
|
|
104
|
+
'landing_group_id', 'landing_id', 'offer_id', 'affiliate_network_id', 'ts_id',
|
|
105
|
+
'stream_id', 'ad_campaign_id', 'external_id', 'creative_id',
|
|
106
|
+
'sub_id_1', 'sub_id_2', 'sub_id_3', 'sub_id_4', 'sub_id_5',
|
|
107
|
+
'sub_id_6', 'sub_id_7', 'sub_id_8', 'sub_id_9', 'sub_id_10',
|
|
108
|
+
'sub_id_11', 'sub_id_12', 'sub_id_13', 'sub_id_14', 'sub_id_15',
|
|
109
|
+
'sub_id_16', 'sub_id_17', 'sub_id_18', 'sub_id_19', 'sub_id_20',
|
|
110
|
+
'sub_id_21', 'sub_id_22', 'sub_id_23', 'sub_id_24', 'sub_id_25',
|
|
111
|
+
'sub_id_26', 'sub_id_27', 'sub_id_28', 'sub_id_29', 'sub_id_30',
|
|
112
|
+
'connection_type', 'operator', 'isp', 'country_code', 'country_flag',
|
|
113
|
+
'country', 'region', 'region_code', 'city', 'language', 'device_type',
|
|
114
|
+
'user_agent', 'os_icon', 'os', 'os_version', 'browser', 'browser_version',
|
|
115
|
+
'device_model', 'browser_icon', 'ip', 'ip_mask1', 'ip_mask2', 'ipv4only',
|
|
116
|
+
'ipv6only', 'cost', 'postback_datetime', 'sale_datetime', 'click_datetime',
|
|
117
|
+
'sale_period', 'status', 'previous_status', 'original_status',
|
|
118
|
+
'previous_conversion_id', 'params', 'conversion_id', 'tid',
|
|
119
|
+
'profitability', 'revenue', 'profit'
|
|
120
|
+
],
|
|
121
|
+
sort: [{
|
|
122
|
+
name: 'sale_datetime',
|
|
123
|
+
order: 'DESC'
|
|
124
|
+
}]
|
|
125
|
+
};
|
|
126
|
+
try {
|
|
127
|
+
const conversions = await keitaro_service_1.KeitaroService.getConversions(request);
|
|
128
|
+
const wpFilePath = path.join(process.cwd(), 'src', 'utils', 'wp-january.csv');
|
|
129
|
+
const comparison = await (0, comparing_1.compareConversions)(wpFilePath, conversions);
|
|
130
|
+
// Use our improved display function
|
|
131
|
+
displayComparisonResults(comparison);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console.error('Error fetching conversions:', error);
|
|
135
|
+
}
|
|
136
|
+
})();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface WPConversion {
|
|
2
|
+
status: string;
|
|
3
|
+
regDate: Date;
|
|
4
|
+
qualDate: Date;
|
|
5
|
+
approveOrRejectDate: Date;
|
|
6
|
+
project: string;
|
|
7
|
+
offerName: string;
|
|
8
|
+
sum: number;
|
|
9
|
+
currency: string;
|
|
10
|
+
geo: string;
|
|
11
|
+
subId?: string;
|
|
12
|
+
clickId: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ConversionComparison {
|
|
15
|
+
wpConversions: WPConversion[];
|
|
16
|
+
keitaroConversions: any[];
|
|
17
|
+
matches: {
|
|
18
|
+
wp: WPConversion;
|
|
19
|
+
keitaro: any;
|
|
20
|
+
}[];
|
|
21
|
+
mismatches: {
|
|
22
|
+
onlyInWP: WPConversion[];
|
|
23
|
+
onlyInKeitaro: any[];
|
|
24
|
+
};
|
|
25
|
+
rejected: {
|
|
26
|
+
wp: WPConversion[];
|
|
27
|
+
keitaro: any[];
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare function parseWPConversions(filePath: string): Promise<WPConversion[]>;
|
|
31
|
+
export declare function compareConversions(wpFilePath: string, keitaroConversions: any[]): Promise<ConversionComparison>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.compareConversions = exports.parseWPConversions = void 0;
|
|
27
|
+
const sync_1 = require("csv-parse/sync");
|
|
28
|
+
const fs = __importStar(require("fs"));
|
|
29
|
+
function parseDate(dateStr) {
|
|
30
|
+
const [month, day, year] = dateStr.split('/').map(num => parseInt(num));
|
|
31
|
+
return new Date(2000 + year, month - 1, day);
|
|
32
|
+
}
|
|
33
|
+
async function parseWPConversions(filePath) {
|
|
34
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
35
|
+
const records = (0, sync_1.parse)(fileContent, {
|
|
36
|
+
delimiter: ';',
|
|
37
|
+
columns: true,
|
|
38
|
+
skip_empty_lines: true
|
|
39
|
+
});
|
|
40
|
+
return records.map((record) => ({
|
|
41
|
+
status: record.Status,
|
|
42
|
+
regDate: parseDate(record.RegDate),
|
|
43
|
+
qualDate: parseDate(record.QualDate),
|
|
44
|
+
approveOrRejectDate: parseDate(record.Approve_or_Reject_Date),
|
|
45
|
+
project: record.Project,
|
|
46
|
+
offerName: record.Offer_Name,
|
|
47
|
+
sum: parseFloat(record.Sum),
|
|
48
|
+
currency: record.Currency,
|
|
49
|
+
geo: record.Geo,
|
|
50
|
+
subId: record.Sub_ID || undefined,
|
|
51
|
+
clickId: record.Click_ID
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
exports.parseWPConversions = parseWPConversions;
|
|
55
|
+
async function compareConversions(wpFilePath, keitaroConversions) {
|
|
56
|
+
const wpConversions = await parseWPConversions(wpFilePath);
|
|
57
|
+
const matches = [];
|
|
58
|
+
const onlyInWP = [];
|
|
59
|
+
const onlyInKeitaro = [];
|
|
60
|
+
const rejectedWP = [];
|
|
61
|
+
const rejectedKeitaro = [];
|
|
62
|
+
// Create a map of Keitaro conversions by their sub_id to match with WP's Click_ID
|
|
63
|
+
const keitaroMap = new Map(keitaroConversions.map(conv => [conv.sub_id, conv]));
|
|
64
|
+
// First, find matches and conversions only in WP
|
|
65
|
+
for (const wpConv of wpConversions) {
|
|
66
|
+
// Skip rejected conversions
|
|
67
|
+
if (wpConv.status === 'Удержан') {
|
|
68
|
+
rejectedWP.push(wpConv);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const keitaroMatch = keitaroMap.get(wpConv.clickId);
|
|
72
|
+
if (keitaroMatch) {
|
|
73
|
+
matches.push({ wp: wpConv, keitaro: keitaroMatch });
|
|
74
|
+
keitaroMap.delete(wpConv.clickId);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
onlyInWP.push(wpConv);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Remaining conversions in keitaroMap are only in Keitaro
|
|
81
|
+
// We need to check their status before adding them to onlyInKeitaro
|
|
82
|
+
for (const [_, conv] of keitaroMap) {
|
|
83
|
+
if (conv.status === 'rejected') {
|
|
84
|
+
rejectedKeitaro.push(conv);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
onlyInKeitaro.push(conv);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
wpConversions: wpConversions.filter(conv => conv.status !== 'Удержан'),
|
|
92
|
+
keitaroConversions: keitaroConversions.filter(conv => conv.status !== 'rejected'),
|
|
93
|
+
matches,
|
|
94
|
+
mismatches: {
|
|
95
|
+
onlyInWP,
|
|
96
|
+
onlyInKeitaro
|
|
97
|
+
},
|
|
98
|
+
rejected: {
|
|
99
|
+
wp: rejectedWP,
|
|
100
|
+
keitaro: rejectedKeitaro
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
exports.compareConversions = compareConversions;
|
package/package.json
CHANGED
package/src/app/app.ts
CHANGED
|
@@ -4,6 +4,15 @@ import mongoose, { Document, Model, model, PipelineStage, Schema, UpdateQuery, U
|
|
|
4
4
|
import { AlternativeFullscreen } from "app/app-integration";
|
|
5
5
|
const util = require("util")
|
|
6
6
|
|
|
7
|
+
export interface IAppsflyerPostback {
|
|
8
|
+
campaign_id: number
|
|
9
|
+
id: number
|
|
10
|
+
method: "GET" | "POST"
|
|
11
|
+
statuses: string[]
|
|
12
|
+
url: string
|
|
13
|
+
notes?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
7
16
|
export interface IApp extends Document {
|
|
8
17
|
id: number
|
|
9
18
|
enabled: boolean,
|
|
@@ -42,6 +51,7 @@ export interface IApp extends Document {
|
|
|
42
51
|
ech?: boolean,
|
|
43
52
|
imageFormat: EImageFormat
|
|
44
53
|
policyPath: string
|
|
54
|
+
postbacks?: IAppsflyerPostback[]
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
export enum EImageFormat {
|
|
@@ -390,7 +400,34 @@ export const AppSchema = new Schema({
|
|
|
390
400
|
policyPath: {
|
|
391
401
|
type: String,
|
|
392
402
|
default: "none"
|
|
393
|
-
}
|
|
403
|
+
},
|
|
404
|
+
postbacks: [{
|
|
405
|
+
campaign_id: {
|
|
406
|
+
type: Number,
|
|
407
|
+
required: true
|
|
408
|
+
},
|
|
409
|
+
id: {
|
|
410
|
+
type: Number,
|
|
411
|
+
required: true
|
|
412
|
+
},
|
|
413
|
+
method: {
|
|
414
|
+
type: String,
|
|
415
|
+
enum: ["GET", "POST"],
|
|
416
|
+
required: true
|
|
417
|
+
},
|
|
418
|
+
statuses: [{
|
|
419
|
+
type: String,
|
|
420
|
+
required: true
|
|
421
|
+
}],
|
|
422
|
+
url: {
|
|
423
|
+
type: String,
|
|
424
|
+
required: true
|
|
425
|
+
},
|
|
426
|
+
notes: {
|
|
427
|
+
type: String,
|
|
428
|
+
required: false
|
|
429
|
+
}
|
|
430
|
+
}]
|
|
394
431
|
})
|
|
395
432
|
|
|
396
433
|
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { IOffer, IPartner, IOfferType } from "./offers/offer"
|
|
|
4
4
|
export { IOffersSection, OffersSectionSchema, DefaultSectionId } from "./offers/section"
|
|
5
5
|
export { IAppOffersSection, ISectionsList, SectionsListSchema, IOfferState } from "./offers/list"
|
|
6
6
|
|
|
7
|
-
export { IAdjustEventIds, IntegrationVersion, IApp, AppSchema, PlugType, IAppKeitaroData, IExternalParams, IPlatformParams, EPlatform, AppStatus, IPublicationHistory, IRemovalInfo, EDirectType } from "./app/app"
|
|
7
|
+
export { IAdjustEventIds, IntegrationVersion, IApp, AppSchema, PlugType, IAppKeitaroData, IExternalParams, IPlatformParams, EPlatform, AppStatus, IPublicationHistory, IRemovalInfo, EDirectType, IAppsflyerPostback } from "./app/app"
|
|
8
8
|
export { IAppListItem } from "./app/app-list-item"
|
|
9
9
|
export { AppType } from "./app/app-type"
|
|
10
10
|
export { AlternativeLayoutType, AlternativeSourceType, AlternativeLogicType, AlternativeNetworkTool, AlternativeStorageType, AlternativeNavigation, AlternativeOnBackPressed, AlternativeOnActivityResult, IAppIntegration as IFlashIntegration } from "./app/app-integration"
|
package/lib/templates/words.txt
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
apple
|
|
2
|
-
banana
|
|
3
|
-
cherry
|
|
4
|
-
dragon
|
|
5
|
-
eagle
|
|
6
|
-
forest
|
|
7
|
-
garden
|
|
8
|
-
hammer
|
|
9
|
-
island
|
|
10
|
-
jungle
|
|
11
|
-
kitten
|
|
12
|
-
lemon
|
|
13
|
-
mountain
|
|
14
|
-
ocean
|
|
15
|
-
pencil
|
|
16
|
-
queen
|
|
17
|
-
river
|
|
18
|
-
sunset
|
|
19
|
-
tiger
|
|
20
|
-
umbrella
|
|
21
|
-
village
|
|
22
|
-
window
|
|
23
|
-
xylophone
|
|
24
|
-
yellow
|
|
25
|
-
zebra
|
|
26
|
-
adventure
|
|
27
|
-
beautiful
|
|
28
|
-
creative
|
|
29
|
-
delicious
|
|
30
|
-
excellent
|
|
31
|
-
fantastic
|
|
32
|
-
gorgeous
|
|
33
|
-
handsome
|
|
34
|
-
incredible
|
|
35
|
-
joyful
|
|
36
|
-
knowledge
|
|
37
|
-
luminous
|
|
38
|
-
magnificent
|
|
39
|
-
natural
|
|
40
|
-
outstanding
|
|
41
|
-
perfect
|
|
42
|
-
quality
|
|
43
|
-
radiant
|
|
44
|
-
spectacular
|
|
45
|
-
tremendous
|
|
46
|
-
ultimate
|
|
47
|
-
valuable
|
|
48
|
-
wonderful
|
|
49
|
-
amazing
|
|
50
|
-
brilliant
|
|
51
|
-
charming
|
|
52
|
-
delightful
|
|
53
|
-
elegant
|
|
54
|
-
fascinating
|
|
55
|
-
graceful
|
|
56
|
-
harmonious
|
|
57
|
-
inspiring
|
|
58
|
-
jubilant
|
|
59
|
-
kindhearted
|
|
60
|
-
loving
|
|
61
|
-
mysterious
|
|
62
|
-
nurturing
|
|
63
|
-
optimistic
|
|
64
|
-
peaceful
|
|
65
|
-
quintessential
|
|
66
|
-
radiant
|
|
67
|
-
serene
|
|
68
|
-
tranquil
|
|
69
|
-
uplifting
|
|
70
|
-
vibrant
|
|
71
|
-
whimsical
|
|
72
|
-
abundant
|
|
73
|
-
blessed
|
|
74
|
-
cheerful
|
|
75
|
-
dazzling
|
|
76
|
-
energetic
|
|
77
|
-
friendly
|
|
78
|
-
generous
|
|
79
|
-
hopeful
|
|
80
|
-
imaginative
|
|
81
|
-
jovial
|
|
82
|
-
knowledgeable
|
|
83
|
-
lively
|
|
84
|
-
majestic
|
|
85
|
-
noble
|
|
86
|
-
outstanding
|
|
87
|
-
passionate
|
|
88
|
-
quintessential
|
|
89
|
-
remarkable
|
|
90
|
-
splendid
|
|
91
|
-
treasured
|
|
92
|
-
unique
|
|
93
|
-
valuable
|
|
94
|
-
warmhearted
|
|
95
|
-
authentic
|
|
96
|
-
benevolent
|
|
97
|
-
charismatic
|
|
98
|
-
determined
|
|
99
|
-
enthusiastic
|
|
100
|
-
faithful
|
|
101
|
-
gracious
|
|
102
|
-
humble
|
|
103
|
-
inspiring
|
|
104
|
-
joyful
|
|
105
|
-
kind
|
|
106
|
-
loyal
|
|
107
|
-
magnificent
|
|
108
|
-
nurturing
|
|
109
|
-
optimistic
|
|
110
|
-
patient
|
|
111
|
-
quintessential
|
|
112
|
-
resilient
|
|
113
|
-
sincere
|
|
114
|
-
trustworthy
|
|
115
|
-
understanding
|
|
116
|
-
virtuous
|
|
117
|
-
wise
|
|
118
|
-
zealous
|
|
119
|
-
abundant
|
|
120
|
-
blessed
|
|
121
|
-
cheerful
|
|
122
|
-
dazzling
|
|
123
|
-
energetic
|
|
124
|
-
friendly
|
|
125
|
-
generous
|
|
126
|
-
hopeful
|
|
127
|
-
imaginative
|
|
128
|
-
jovial
|
|
129
|
-
knowledgeable
|
|
130
|
-
lively
|
|
131
|
-
majestic
|
|
132
|
-
noble
|
|
133
|
-
outstanding
|
|
134
|
-
passionate
|
|
135
|
-
quintessential
|
|
136
|
-
remarkable
|
|
137
|
-
splendid
|
|
138
|
-
treasured
|
|
139
|
-
unique
|
|
140
|
-
valuable
|
|
141
|
-
warmhearted
|
|
142
|
-
authentic
|
|
143
|
-
benevolent
|
|
144
|
-
charismatic
|
|
145
|
-
determined
|
|
146
|
-
enthusiastic
|
|
147
|
-
faithful
|
|
148
|
-
gracious
|
|
149
|
-
humble
|
|
150
|
-
inspiring
|
|
151
|
-
joyful
|
|
152
|
-
kind
|
|
153
|
-
loyal
|
|
154
|
-
magnificent
|
|
155
|
-
nurturing
|
|
156
|
-
optimistic
|
|
157
|
-
patient
|
|
158
|
-
quintessential
|
|
159
|
-
resilient
|
|
160
|
-
sincere
|
|
161
|
-
trustworthy
|
|
162
|
-
understanding
|
|
163
|
-
virtuous
|
|
164
|
-
wise
|
|
165
|
-
zealous
|
|
166
|
-
abundant
|
|
167
|
-
blessed
|
|
168
|
-
cheerful
|
|
169
|
-
dazzling
|
|
170
|
-
energetic
|
|
171
|
-
friendly
|
|
172
|
-
generous
|
|
173
|
-
hopeful
|
|
174
|
-
imaginative
|
|
175
|
-
jovial
|
|
176
|
-
knowledgeable
|
|
177
|
-
lively
|
|
178
|
-
majestic
|
|
179
|
-
noble
|
|
180
|
-
outstanding
|
|
181
|
-
passionate
|
|
182
|
-
quintessential
|
|
183
|
-
remarkable
|
|
184
|
-
splendid
|
|
185
|
-
treasured
|
|
186
|
-
unique
|
|
187
|
-
valuable
|
|
188
|
-
warmhearted
|
|
189
|
-
authentic
|
|
190
|
-
benevolent
|
|
191
|
-
charismatic
|
|
192
|
-
determined
|
|
193
|
-
enthusiastic
|
|
194
|
-
faithful
|
|
195
|
-
gracious
|
|
196
|
-
humble
|
|
197
|
-
inspiring
|
|
198
|
-
joyful
|
|
199
|
-
kind
|
|
200
|
-
loyal
|
|
201
|
-
magnificent
|
|
202
|
-
nurturing
|
|
203
|
-
optimistic
|
|
204
|
-
patient
|
|
205
|
-
quintessential
|
|
206
|
-
resilient
|
|
207
|
-
sincere
|
|
208
|
-
trustworthy
|
|
209
|
-
understanding
|
|
210
|
-
virtuous
|
|
211
|
-
wise
|
|
212
|
-
zealous
|