@pixels-online/pixels-client-js-sdk 1.16.0 → 1.18.0
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.esm.js +307 -54
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +309 -53
- package/dist/index.js.map +1 -1
- package/dist/offerwall-sdk.umd.js +309 -53
- package/dist/offerwall-sdk.umd.js.map +1 -1
- package/dist/types/blockchain/user_wallet.d.ts +1 -2
- package/dist/types/offer.d.ts +22 -10
- package/dist/types/player.d.ts +13 -7
- package/dist/utils/conditions.d.ts +49 -8
- package/dist/utils/template.d.ts +18 -1
- package/package.json +1 -1
|
@@ -1284,19 +1284,47 @@
|
|
|
1284
1284
|
}
|
|
1285
1285
|
|
|
1286
1286
|
const keyPattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
1287
|
-
|
|
1287
|
+
/**
|
|
1288
|
+
* This replaces {keyName} keys from the template with corresponding values from the dynamic object.
|
|
1289
|
+
*/
|
|
1288
1290
|
function renderTemplate(template, dynamic) {
|
|
1289
1291
|
if (!template)
|
|
1290
1292
|
return '';
|
|
1291
|
-
return template.replace(keyPattern, (
|
|
1293
|
+
return template.replace(keyPattern, (_match, key) => {
|
|
1294
|
+
if (dynamic && typeof dynamic[key] === 'boolean') {
|
|
1295
|
+
return dynamic[key] ? '✓' : '✗';
|
|
1296
|
+
}
|
|
1292
1297
|
if (dynamic && dynamic[key] !== undefined) {
|
|
1293
1298
|
return String(dynamic[key]);
|
|
1294
1299
|
}
|
|
1295
1300
|
return '{?}'; // indicate missing key
|
|
1296
1301
|
});
|
|
1297
1302
|
}
|
|
1303
|
+
/**
|
|
1304
|
+
* This replaces {{keyName}} in dynamic condition keys with corresponding values from
|
|
1305
|
+
* the PlayerOffer.trackers
|
|
1306
|
+
*
|
|
1307
|
+
* eg. a condition high_score_pet-{{surfacerPlayerId}} with high_score_pet-12345
|
|
1308
|
+
*/
|
|
1309
|
+
function replaceDynamicConditionKey(key, trackers) {
|
|
1310
|
+
return key?.replace(/\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g, (match, p1) => {
|
|
1311
|
+
const value = trackers[p1];
|
|
1312
|
+
return value !== undefined ? String(value) : match;
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
/** this replaces all of the dynamic conditions.keys by calling replaceDynamicConditionKey */
|
|
1316
|
+
function replaceDynamicConditionKeys(conditions, trackers) {
|
|
1317
|
+
return conditions.map((condition) => ({
|
|
1318
|
+
...condition,
|
|
1319
|
+
key: replaceDynamicConditionKey(condition.key, trackers),
|
|
1320
|
+
}));
|
|
1321
|
+
}
|
|
1298
1322
|
|
|
1299
|
-
const meetsBaseConditions = ({ conditions, playerSnap, addDetails,
|
|
1323
|
+
const meetsBaseConditions = ({ conditions, playerSnap, addDetails,
|
|
1324
|
+
/** this exists if calling meetsBaseConditions from meetsCompletionConditions. but surfacing
|
|
1325
|
+
* check doesn't use this since we don't have a playerOffer at surfacing time
|
|
1326
|
+
*/
|
|
1327
|
+
playerOffer, }) => {
|
|
1300
1328
|
const conditionData = [];
|
|
1301
1329
|
let isValid = true;
|
|
1302
1330
|
if (conditions?.minDaysInGame) {
|
|
@@ -1663,7 +1691,11 @@
|
|
|
1663
1691
|
}
|
|
1664
1692
|
// Evaluate dynamic conditions
|
|
1665
1693
|
if (conditions?.dynamic?.conditions?.length) {
|
|
1666
|
-
const
|
|
1694
|
+
const resolvedConditions = replaceDynamicConditionKeys(conditions.dynamic.conditions, playerOffer?.trackers || {});
|
|
1695
|
+
const dynamicResult = meetsDynamicConditions(playerSnap.dynamic, {
|
|
1696
|
+
...conditions.dynamic,
|
|
1697
|
+
conditions: resolvedConditions,
|
|
1698
|
+
});
|
|
1667
1699
|
if (addDetails) {
|
|
1668
1700
|
conditionData.push({
|
|
1669
1701
|
isMet: dynamicResult,
|
|
@@ -1772,6 +1804,31 @@
|
|
|
1772
1804
|
return { isValid: false };
|
|
1773
1805
|
}
|
|
1774
1806
|
}
|
|
1807
|
+
if (conditions.allowedCountries?.length) {
|
|
1808
|
+
const playerCountry = playerSnap.ip?.countryCode;
|
|
1809
|
+
if (!playerCountry || !conditions.allowedCountries.includes(playerCountry)) {
|
|
1810
|
+
return { isValid: false };
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
if (conditions.restrictedCountries?.length) {
|
|
1814
|
+
const playerCountry = playerSnap.ip?.countryCode;
|
|
1815
|
+
if (!playerCountry) {
|
|
1816
|
+
return { isValid: false };
|
|
1817
|
+
}
|
|
1818
|
+
if (conditions.restrictedCountries.includes(playerCountry)) {
|
|
1819
|
+
return { isValid: false };
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
if (conditions.networkRestrictions?.length) {
|
|
1823
|
+
if (!playerSnap.ip) {
|
|
1824
|
+
return { isValid: false };
|
|
1825
|
+
}
|
|
1826
|
+
for (const restriction of conditions.networkRestrictions) {
|
|
1827
|
+
if (playerSnap.ip[restriction]) {
|
|
1828
|
+
return { isValid: false };
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1775
1832
|
return meetsBaseConditions({ conditions, playerSnap });
|
|
1776
1833
|
};
|
|
1777
1834
|
const hasConditions = (conditions) => {
|
|
@@ -1824,6 +1881,12 @@
|
|
|
1824
1881
|
return true;
|
|
1825
1882
|
if (surCond.links && Object.keys(surCond.links).length > 0)
|
|
1826
1883
|
return true;
|
|
1884
|
+
if (surCond.allowedCountries?.length)
|
|
1885
|
+
return true;
|
|
1886
|
+
if (surCond.restrictedCountries?.length)
|
|
1887
|
+
return true;
|
|
1888
|
+
if (surCond.networkRestrictions?.length)
|
|
1889
|
+
return true;
|
|
1827
1890
|
const compCond = conditions;
|
|
1828
1891
|
if (compCond.context)
|
|
1829
1892
|
return true;
|
|
@@ -1845,11 +1908,25 @@
|
|
|
1845
1908
|
return true;
|
|
1846
1909
|
return false;
|
|
1847
1910
|
};
|
|
1848
|
-
const
|
|
1911
|
+
const offerMeetsCompletionConditions = (offer, snapshot) => {
|
|
1912
|
+
return meetsCompletionConditions({
|
|
1913
|
+
completionConditions: offer.completionConditions || {},
|
|
1914
|
+
completionTrackers: offer.completionTrackers,
|
|
1915
|
+
playerSnap: snapshot,
|
|
1916
|
+
playerOffer: offer,
|
|
1917
|
+
addDetails: true,
|
|
1918
|
+
});
|
|
1919
|
+
};
|
|
1920
|
+
const meetsCompletionConditions = ({ completionConditions, completionTrackers, playerSnap, playerOffer, addDetails = false, maxClaimCount, }) => {
|
|
1849
1921
|
if (completionConditions) {
|
|
1850
1922
|
const conditions = completionConditions;
|
|
1923
|
+
// For multi-claim offers, scale cumulative requirements by (claimedCount + 1)
|
|
1924
|
+
const shouldScale = maxClaimCount === -1 || (maxClaimCount && maxClaimCount > 1);
|
|
1925
|
+
const claimMultiplier = shouldScale ? (playerOffer.claimedCount || 0) + 1 : 1;
|
|
1851
1926
|
const conditionData = [];
|
|
1852
1927
|
let isValid = true;
|
|
1928
|
+
let maxTotalClaimsFromScaling = Infinity;
|
|
1929
|
+
const updateMax = (limit) => (maxTotalClaimsFromScaling = Math.min(maxTotalClaimsFromScaling, limit));
|
|
1853
1930
|
if (completionConditions?.context?.id) {
|
|
1854
1931
|
const hasTrackedContext = completionTrackers?.context &&
|
|
1855
1932
|
completionConditions.context.id === completionTrackers.context;
|
|
@@ -1866,73 +1943,93 @@
|
|
|
1866
1943
|
}
|
|
1867
1944
|
else {
|
|
1868
1945
|
if (isDisqualify)
|
|
1869
|
-
return { isValid: false };
|
|
1946
|
+
return { isValid: false, availableClaimsNow: 0 };
|
|
1870
1947
|
}
|
|
1871
1948
|
}
|
|
1872
1949
|
if (conditions?.buyItem) {
|
|
1873
|
-
const
|
|
1950
|
+
const baseAmount = conditions.buyItem.amount || 1;
|
|
1951
|
+
const scaledAmount = baseAmount * claimMultiplier;
|
|
1952
|
+
const trackerValue = completionTrackers?.buyItem || 0;
|
|
1953
|
+
const isDisqualify = trackerValue < scaledAmount;
|
|
1954
|
+
if (shouldScale && baseAmount > 0) {
|
|
1955
|
+
updateMax(Math.floor(trackerValue / baseAmount));
|
|
1956
|
+
}
|
|
1874
1957
|
if (addDetails) {
|
|
1875
1958
|
conditionData.push({
|
|
1876
1959
|
isMet: !isDisqualify,
|
|
1877
1960
|
kind: 'buyItem',
|
|
1878
|
-
trackerAmount:
|
|
1879
|
-
text: `Buy ${
|
|
1961
|
+
trackerAmount: trackerValue,
|
|
1962
|
+
text: `Buy ${scaledAmount} ${conditions.buyItem.name}`,
|
|
1880
1963
|
});
|
|
1881
1964
|
if (isDisqualify)
|
|
1882
1965
|
isValid = false;
|
|
1883
1966
|
}
|
|
1884
1967
|
else {
|
|
1885
1968
|
if (isDisqualify)
|
|
1886
|
-
return { isValid: false };
|
|
1969
|
+
return { isValid: false, availableClaimsNow: 0 };
|
|
1887
1970
|
}
|
|
1888
1971
|
}
|
|
1889
1972
|
if (conditions?.spendCurrency) {
|
|
1890
|
-
const
|
|
1973
|
+
const baseAmount = conditions.spendCurrency.amount || 1;
|
|
1974
|
+
const scaledAmount = baseAmount * claimMultiplier;
|
|
1975
|
+
const trackerValue = completionTrackers?.spendCurrency || 0;
|
|
1976
|
+
const isDisqualify = trackerValue < scaledAmount;
|
|
1977
|
+
if (shouldScale && baseAmount > 0) {
|
|
1978
|
+
updateMax(Math.floor(trackerValue / baseAmount));
|
|
1979
|
+
}
|
|
1891
1980
|
if (addDetails) {
|
|
1892
1981
|
conditionData.push({
|
|
1893
1982
|
isMet: !isDisqualify,
|
|
1894
1983
|
kind: 'spendCurrency',
|
|
1895
|
-
trackerAmount:
|
|
1896
|
-
text: `Spend ${
|
|
1984
|
+
trackerAmount: trackerValue,
|
|
1985
|
+
text: `Spend ${scaledAmount} ${conditions.spendCurrency.name}`,
|
|
1897
1986
|
});
|
|
1898
1987
|
if (isDisqualify)
|
|
1899
1988
|
isValid = false;
|
|
1900
1989
|
}
|
|
1901
1990
|
else {
|
|
1902
1991
|
if (isDisqualify)
|
|
1903
|
-
return { isValid: false };
|
|
1992
|
+
return { isValid: false, availableClaimsNow: 0 };
|
|
1904
1993
|
}
|
|
1905
1994
|
}
|
|
1906
1995
|
if (conditions?.depositCurrency) {
|
|
1907
|
-
const
|
|
1908
|
-
|
|
1996
|
+
const baseAmount = conditions.depositCurrency.amount || 1;
|
|
1997
|
+
const scaledAmount = baseAmount * claimMultiplier;
|
|
1998
|
+
const trackerValue = completionTrackers?.depositCurrency || 0;
|
|
1999
|
+
const isDisqualify = trackerValue < scaledAmount;
|
|
2000
|
+
if (shouldScale && baseAmount > 0) {
|
|
2001
|
+
updateMax(Math.floor(trackerValue / baseAmount));
|
|
2002
|
+
}
|
|
1909
2003
|
if (addDetails) {
|
|
1910
2004
|
conditionData.push({
|
|
1911
2005
|
isMet: !isDisqualify,
|
|
1912
2006
|
kind: 'depositCurrency',
|
|
1913
|
-
trackerAmount:
|
|
1914
|
-
text: `Deposit ${
|
|
2007
|
+
trackerAmount: trackerValue,
|
|
2008
|
+
text: `Deposit ${scaledAmount} ${conditions.depositCurrency.name}`,
|
|
1915
2009
|
});
|
|
2010
|
+
if (isDisqualify)
|
|
2011
|
+
isValid = false;
|
|
1916
2012
|
}
|
|
1917
2013
|
else {
|
|
1918
2014
|
if (isDisqualify)
|
|
1919
|
-
return { isValid: false };
|
|
2015
|
+
return { isValid: false, availableClaimsNow: 0 };
|
|
1920
2016
|
}
|
|
1921
2017
|
}
|
|
1922
2018
|
if (conditions?.login) {
|
|
1923
|
-
const isMet =
|
|
2019
|
+
const isMet = new Date(playerSnap.snapshotLastUpdated || 0).getTime() >
|
|
2020
|
+
new Date(playerOffer.createdAt || 0).getTime();
|
|
1924
2021
|
if (addDetails) {
|
|
1925
2022
|
conditionData.push({
|
|
1926
2023
|
isMet,
|
|
1927
2024
|
kind: 'login',
|
|
1928
|
-
trackerAmount:
|
|
2025
|
+
trackerAmount: isMet ? 1 : 0,
|
|
1929
2026
|
text: `Login to the game`,
|
|
1930
2027
|
});
|
|
1931
2028
|
isValid = isMet;
|
|
1932
2029
|
}
|
|
1933
2030
|
else {
|
|
1934
2031
|
if (!isMet)
|
|
1935
|
-
return { isValid: false };
|
|
2032
|
+
return { isValid: false, availableClaimsNow: 0 };
|
|
1936
2033
|
}
|
|
1937
2034
|
}
|
|
1938
2035
|
if (conditions?.loginStreak) {
|
|
@@ -1952,7 +2049,7 @@
|
|
|
1952
2049
|
}
|
|
1953
2050
|
else {
|
|
1954
2051
|
if (isDisqualify)
|
|
1955
|
-
return { isValid: false };
|
|
2052
|
+
return { isValid: false, availableClaimsNow: 0 };
|
|
1956
2053
|
}
|
|
1957
2054
|
}
|
|
1958
2055
|
if (conditions?.social) {
|
|
@@ -1962,9 +2059,11 @@
|
|
|
1962
2059
|
const hasContent = Boolean(mode === 'accumulate'
|
|
1963
2060
|
? tSocial?.mode === 'accumulate'
|
|
1964
2061
|
: tSocial && tSocial.mode !== 'accumulate' && !!tSocial.videoId);
|
|
1965
|
-
|
|
1966
|
-
const
|
|
1967
|
-
const
|
|
2062
|
+
// Only scale social metrics in accumulate mode (attach mode is single content)
|
|
2063
|
+
const socialMultiplier = mode === 'accumulate' ? claimMultiplier : 1;
|
|
2064
|
+
const minLikes = (cSocial?.minLikes || 0) * socialMultiplier;
|
|
2065
|
+
const minViews = (cSocial?.minViews || 0) * socialMultiplier;
|
|
2066
|
+
const minComments = (cSocial?.minComments || 0) * socialMultiplier;
|
|
1968
2067
|
const likes = tSocial?.likes || 0;
|
|
1969
2068
|
const views = tSocial?.views || 0;
|
|
1970
2069
|
const comments = tSocial?.comments || 0;
|
|
@@ -1972,6 +2071,17 @@
|
|
|
1972
2071
|
if (likes < minLikes || views < minViews || comments < minComments) {
|
|
1973
2072
|
isDisqualify = true;
|
|
1974
2073
|
}
|
|
2074
|
+
if (shouldScale && mode === 'accumulate' && hasContent) {
|
|
2075
|
+
const baseLikes = cSocial?.minLikes || 0;
|
|
2076
|
+
const baseViews = cSocial?.minViews || 0;
|
|
2077
|
+
const baseComments = cSocial?.minComments || 0;
|
|
2078
|
+
if (baseLikes > 0)
|
|
2079
|
+
updateMax(Math.floor(likes / baseLikes));
|
|
2080
|
+
if (baseViews > 0)
|
|
2081
|
+
updateMax(Math.floor(views / baseViews));
|
|
2082
|
+
if (baseComments > 0)
|
|
2083
|
+
updateMax(Math.floor(comments / baseComments));
|
|
2084
|
+
}
|
|
1975
2085
|
if (addDetails) {
|
|
1976
2086
|
const platformMap = {
|
|
1977
2087
|
tiktok: 'TikTok',
|
|
@@ -2043,31 +2153,47 @@
|
|
|
2043
2153
|
}
|
|
2044
2154
|
else {
|
|
2045
2155
|
if (isDisqualify)
|
|
2046
|
-
return { isValid: false };
|
|
2156
|
+
return { isValid: false, availableClaimsNow: 0 };
|
|
2047
2157
|
}
|
|
2048
2158
|
}
|
|
2049
2159
|
// Linked completions - wait for N linked entities to complete
|
|
2050
2160
|
if (conditions?.linkedCompletions?.min) {
|
|
2161
|
+
const baseMin = conditions.linkedCompletions.min;
|
|
2051
2162
|
const currentCount = completionTrackers?.linkedCompletions || 0;
|
|
2052
|
-
const
|
|
2053
|
-
const isDisqualify = currentCount <
|
|
2163
|
+
const scaledMin = baseMin * claimMultiplier;
|
|
2164
|
+
const isDisqualify = currentCount < scaledMin;
|
|
2165
|
+
if (shouldScale && baseMin > 0) {
|
|
2166
|
+
updateMax(Math.floor(currentCount / baseMin));
|
|
2167
|
+
}
|
|
2054
2168
|
if (addDetails) {
|
|
2055
2169
|
conditionData.push({
|
|
2056
2170
|
isMet: !isDisqualify,
|
|
2057
2171
|
kind: 'linkedCompletions',
|
|
2058
2172
|
trackerAmount: currentCount,
|
|
2059
|
-
text: `Wait for ${
|
|
2173
|
+
text: `Wait for ${scaledMin} linked ${scaledMin === 1 ? 'entity' : 'entities'} to complete`,
|
|
2060
2174
|
});
|
|
2061
2175
|
if (isDisqualify)
|
|
2062
2176
|
isValid = false;
|
|
2063
2177
|
}
|
|
2064
2178
|
else {
|
|
2065
2179
|
if (isDisqualify)
|
|
2066
|
-
return { isValid: false };
|
|
2180
|
+
return { isValid: false, availableClaimsNow: 0 };
|
|
2067
2181
|
}
|
|
2068
2182
|
}
|
|
2069
2183
|
if (conditions?.dynamicTracker?.conditions?.length) {
|
|
2070
|
-
const
|
|
2184
|
+
const resolvedConditions = replaceDynamicConditionKeys(conditions.dynamicTracker.conditions, playerOffer?.trackers || {});
|
|
2185
|
+
// now we have the game-defined conditions with {{}} keys populated. feed these conditions into evaluator
|
|
2186
|
+
const dynamicResult = meetsDynamicConditions(completionTrackers?.dynamicTracker, {
|
|
2187
|
+
...conditions.dynamicTracker,
|
|
2188
|
+
conditions: resolvedConditions,
|
|
2189
|
+
}, claimMultiplier);
|
|
2190
|
+
if (shouldScale) {
|
|
2191
|
+
const dynamicMax = getMaxClaimsForDynamicGroup(completionTrackers?.dynamicTracker || {}, {
|
|
2192
|
+
...conditions.dynamicTracker,
|
|
2193
|
+
conditions: resolvedConditions,
|
|
2194
|
+
}, playerOffer.claimedCount || 0);
|
|
2195
|
+
updateMax(dynamicMax);
|
|
2196
|
+
}
|
|
2071
2197
|
if (addDetails) {
|
|
2072
2198
|
conditionData.push({
|
|
2073
2199
|
isMet: dynamicResult,
|
|
@@ -2079,19 +2205,29 @@
|
|
|
2079
2205
|
}
|
|
2080
2206
|
else {
|
|
2081
2207
|
if (!dynamicResult)
|
|
2082
|
-
return { isValid: false };
|
|
2208
|
+
return { isValid: false, availableClaimsNow: 0 };
|
|
2083
2209
|
}
|
|
2084
2210
|
}
|
|
2085
2211
|
const r = meetsBaseConditions({
|
|
2086
2212
|
conditions,
|
|
2087
2213
|
playerSnap,
|
|
2088
2214
|
addDetails: true,
|
|
2215
|
+
playerOffer,
|
|
2089
2216
|
});
|
|
2090
2217
|
isValid = isValid && r.isValid;
|
|
2091
2218
|
conditionData.push(...(r.conditionData || []));
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2219
|
+
if (maxClaimCount && maxClaimCount > 0) {
|
|
2220
|
+
updateMax(maxClaimCount);
|
|
2221
|
+
}
|
|
2222
|
+
const claimedCount = playerOffer.claimedCount || 0;
|
|
2223
|
+
let availableClaimsNow = !isValid
|
|
2224
|
+
? 0
|
|
2225
|
+
: maxTotalClaimsFromScaling === Infinity
|
|
2226
|
+
? -1
|
|
2227
|
+
: Math.max(0, maxTotalClaimsFromScaling - claimedCount);
|
|
2228
|
+
return { isValid, conditionData, availableClaimsNow };
|
|
2229
|
+
}
|
|
2230
|
+
return { isValid: true, conditionData: [], availableClaimsNow: -1 };
|
|
2095
2231
|
};
|
|
2096
2232
|
/**
|
|
2097
2233
|
* Checks if completion conditions were met before a specific expiry time.
|
|
@@ -2100,10 +2236,9 @@
|
|
|
2100
2236
|
* @param completionConditions - The completion conditions to check
|
|
2101
2237
|
* @param completionTrackers - The completion trackers (for buyItem, spendCurrency, etc.)
|
|
2102
2238
|
* @param playerSnap - The player snapshot with field timestamps
|
|
2103
|
-
* @param expiryTime - The expiry timestamp in milliseconds
|
|
2104
2239
|
* @returns true if all conditions were met before expiry, false otherwise
|
|
2105
2240
|
*/
|
|
2106
|
-
const meetsCompletionConditionsBeforeExpiry = ({ completionConditions, completionTrackers, playerSnap,
|
|
2241
|
+
const meetsCompletionConditionsBeforeExpiry = ({ completionConditions, completionTrackers, playerSnap, playerOffer, maxClaimCount, }) => {
|
|
2107
2242
|
if (!completionConditions)
|
|
2108
2243
|
return false;
|
|
2109
2244
|
// Check if there are actually any conditions to evaluate
|
|
@@ -2113,10 +2248,15 @@
|
|
|
2113
2248
|
const conditionsMet = meetsCompletionConditions({
|
|
2114
2249
|
completionConditions,
|
|
2115
2250
|
completionTrackers,
|
|
2251
|
+
playerOffer,
|
|
2116
2252
|
playerSnap,
|
|
2253
|
+
maxClaimCount,
|
|
2117
2254
|
});
|
|
2118
2255
|
if (!conditionsMet.isValid)
|
|
2119
2256
|
return false;
|
|
2257
|
+
if (!playerOffer.expiresAt)
|
|
2258
|
+
return true;
|
|
2259
|
+
const expiryTime = new Date(playerOffer.expiresAt).getTime();
|
|
2120
2260
|
const lastSnapshotUpdate = new Date(playerSnap.snapshotLastUpdated).getTime();
|
|
2121
2261
|
/**
|
|
2122
2262
|
* Checks if a field was updated after the expiry time.
|
|
@@ -2227,37 +2367,55 @@
|
|
|
2227
2367
|
* Checks if a dynamic object meets a set of dynamic field conditions.
|
|
2228
2368
|
* @param dynamicObj - The object with any key and string or number value.
|
|
2229
2369
|
* @param conditions - Array of conditions to check.
|
|
2370
|
+
* @param claimMultiplier - Multiplier to scale conditions (used for numeric comparisons).
|
|
2230
2371
|
* @returns true if all conditions are met, false otherwise.
|
|
2231
2372
|
*/
|
|
2232
2373
|
/**
|
|
2233
2374
|
* Evaluates a single dynamic condition against the dynamic object.
|
|
2234
2375
|
*/
|
|
2235
|
-
function evaluateDynamicCondition(dynamicObj, cond) {
|
|
2376
|
+
function evaluateDynamicCondition(dynamicObj, cond, claimMultiplier = 1) {
|
|
2377
|
+
if (!dynamicObj)
|
|
2378
|
+
return false;
|
|
2236
2379
|
const val = dynamicObj[cond.key];
|
|
2237
2380
|
if (val === undefined)
|
|
2238
2381
|
return false;
|
|
2239
2382
|
const isNumber = typeof val === 'number';
|
|
2240
|
-
const
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2383
|
+
const isBoolean = typeof val === 'boolean';
|
|
2384
|
+
if (isBoolean) {
|
|
2385
|
+
switch (cond.operator) {
|
|
2386
|
+
case '==':
|
|
2387
|
+
return val === Boolean(cond.compareTo);
|
|
2388
|
+
case '!=':
|
|
2389
|
+
return val !== Boolean(cond.compareTo);
|
|
2390
|
+
default:
|
|
2391
|
+
return false;
|
|
2392
|
+
}
|
|
2246
2393
|
}
|
|
2394
|
+
const compareTo = isNumber ? Number(cond.compareTo) : String(cond.compareTo);
|
|
2247
2395
|
if (isNumber && typeof compareTo === 'number') {
|
|
2396
|
+
const skipMultiplier = cond.operator === '==' || cond.operator === '!=';
|
|
2397
|
+
const scaledCompareTo = skipMultiplier ? compareTo : compareTo * claimMultiplier;
|
|
2248
2398
|
switch (cond.operator) {
|
|
2399
|
+
case '==':
|
|
2400
|
+
return val === scaledCompareTo;
|
|
2401
|
+
case '!=':
|
|
2402
|
+
return val !== scaledCompareTo;
|
|
2249
2403
|
case '>':
|
|
2250
|
-
return val >
|
|
2404
|
+
return val > scaledCompareTo;
|
|
2251
2405
|
case '>=':
|
|
2252
|
-
return val >=
|
|
2406
|
+
return val >= scaledCompareTo;
|
|
2253
2407
|
case '<':
|
|
2254
|
-
return val <
|
|
2408
|
+
return val < scaledCompareTo;
|
|
2255
2409
|
case '<=':
|
|
2256
|
-
return val <=
|
|
2410
|
+
return val <= scaledCompareTo;
|
|
2257
2411
|
}
|
|
2258
2412
|
}
|
|
2259
2413
|
else if (!isNumber && typeof compareTo === 'string') {
|
|
2260
2414
|
switch (cond.operator) {
|
|
2415
|
+
case '==':
|
|
2416
|
+
return val === compareTo;
|
|
2417
|
+
case '!=':
|
|
2418
|
+
return val !== compareTo;
|
|
2261
2419
|
case 'has':
|
|
2262
2420
|
return val.includes(compareTo);
|
|
2263
2421
|
case 'not_has':
|
|
@@ -2266,24 +2424,119 @@
|
|
|
2266
2424
|
}
|
|
2267
2425
|
return false;
|
|
2268
2426
|
}
|
|
2427
|
+
/**
|
|
2428
|
+
* Calculates the maximum number of claims supported by a single dynamic condition.
|
|
2429
|
+
*/
|
|
2430
|
+
function getMaxClaimsForDynamicCondition(dynamicObj, cond) {
|
|
2431
|
+
if (!dynamicObj)
|
|
2432
|
+
return 0;
|
|
2433
|
+
const val = dynamicObj[cond.key];
|
|
2434
|
+
if (val === undefined)
|
|
2435
|
+
return 0;
|
|
2436
|
+
if (typeof val === 'number') {
|
|
2437
|
+
const base = Number(cond.compareTo);
|
|
2438
|
+
if (isNaN(base)) {
|
|
2439
|
+
return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
|
|
2440
|
+
}
|
|
2441
|
+
switch (cond.operator) {
|
|
2442
|
+
case '>=':
|
|
2443
|
+
if (base === 0)
|
|
2444
|
+
return val >= 0 ? Infinity : 0;
|
|
2445
|
+
if (base < 0)
|
|
2446
|
+
return val >= base ? Infinity : 0;
|
|
2447
|
+
return Math.max(0, Math.floor(val / base));
|
|
2448
|
+
case '>':
|
|
2449
|
+
if (base === 0)
|
|
2450
|
+
return val > 0 ? Infinity : 0;
|
|
2451
|
+
if (base < 0)
|
|
2452
|
+
return val > base ? Infinity : 0;
|
|
2453
|
+
if (val <= 0)
|
|
2454
|
+
return 0;
|
|
2455
|
+
return Math.max(0, Math.ceil(val / base) - 1);
|
|
2456
|
+
case '==':
|
|
2457
|
+
return val === base ? Infinity : 0;
|
|
2458
|
+
case '!=':
|
|
2459
|
+
return val !== base ? Infinity : 0;
|
|
2460
|
+
case '<=':
|
|
2461
|
+
if (base === 0)
|
|
2462
|
+
return val <= 0 ? Infinity : 0;
|
|
2463
|
+
if (base > 0)
|
|
2464
|
+
return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
|
|
2465
|
+
if (val >= 0)
|
|
2466
|
+
return 0;
|
|
2467
|
+
return Math.max(0, Math.floor(val / base));
|
|
2468
|
+
case '<':
|
|
2469
|
+
if (base === 0)
|
|
2470
|
+
return val < 0 ? Infinity : 0;
|
|
2471
|
+
if (base > 0)
|
|
2472
|
+
return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
|
|
2473
|
+
if (val >= 0)
|
|
2474
|
+
return 0;
|
|
2475
|
+
return Math.max(0, Math.ceil(val / base) - 1);
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
// we don't scale the rest, they are always true or always false
|
|
2479
|
+
return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
|
|
2480
|
+
}
|
|
2481
|
+
/**
|
|
2482
|
+
* Calculates the maximum number of claims supported by a group of dynamic conditions.
|
|
2483
|
+
*/
|
|
2484
|
+
function getMaxClaimsForDynamicGroup(dynamicObj, dynamicGroup, currentClaimCount = 0) {
|
|
2485
|
+
const { conditions, links } = dynamicGroup;
|
|
2486
|
+
if (!conditions || conditions.length === 0)
|
|
2487
|
+
return Infinity;
|
|
2488
|
+
// AND only
|
|
2489
|
+
if (!links || links.length === 0 || links.every((l) => l === 'AND')) {
|
|
2490
|
+
let minClaims = Infinity;
|
|
2491
|
+
for (const cond of conditions) {
|
|
2492
|
+
const max = getMaxClaimsForDynamicCondition(dynamicObj, cond);
|
|
2493
|
+
if (max === 0)
|
|
2494
|
+
return 0;
|
|
2495
|
+
minClaims = Math.min(minClaims, max);
|
|
2496
|
+
}
|
|
2497
|
+
return minClaims;
|
|
2498
|
+
}
|
|
2499
|
+
// OR only
|
|
2500
|
+
if (links.every((l) => l === 'OR')) {
|
|
2501
|
+
let maxClaims = 0;
|
|
2502
|
+
for (const cond of conditions) {
|
|
2503
|
+
const max = getMaxClaimsForDynamicCondition(dynamicObj, cond);
|
|
2504
|
+
if (max === Infinity)
|
|
2505
|
+
return Infinity;
|
|
2506
|
+
maxClaims = Math.max(maxClaims, max);
|
|
2507
|
+
}
|
|
2508
|
+
return maxClaims;
|
|
2509
|
+
}
|
|
2510
|
+
// mixed:
|
|
2511
|
+
const maxIterations = 100;
|
|
2512
|
+
for (let n = currentClaimCount + 1; n <= currentClaimCount + maxIterations; n++) {
|
|
2513
|
+
if (!meetsDynamicConditions(dynamicObj, dynamicGroup, n)) {
|
|
2514
|
+
return n - 1;
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
return currentClaimCount + maxIterations;
|
|
2518
|
+
}
|
|
2269
2519
|
/**
|
|
2270
2520
|
* Evaluates a group of dynamic conditions with logical links (AND, OR, AND NOT).
|
|
2271
2521
|
* @param dynamicObj - The player's dynamic object with any key and string or number value.
|
|
2272
2522
|
* @param dynamicGroup - The group of conditions and links to check.
|
|
2523
|
+
* @param claimMultiplier - Multiplier to scale conditions (used for numeric comparisons).
|
|
2273
2524
|
* @returns true if the group evaluates to true, false otherwise.
|
|
2274
2525
|
*/
|
|
2275
|
-
function meetsDynamicConditions(dynamicObj, dynamicGroup) {
|
|
2526
|
+
function meetsDynamicConditions(dynamicObj, dynamicGroup, claimMultiplier = 1) {
|
|
2276
2527
|
const { conditions, links } = dynamicGroup;
|
|
2277
2528
|
if (!conditions || conditions.length === 0)
|
|
2278
2529
|
return true;
|
|
2530
|
+
if (!dynamicObj)
|
|
2531
|
+
return false;
|
|
2279
2532
|
// If no links, treat as AND between all conditions
|
|
2280
2533
|
if (!links || links.length === 0) {
|
|
2281
|
-
return conditions.every((cond) => evaluateDynamicCondition(dynamicObj, cond));
|
|
2534
|
+
return conditions.every((cond) => evaluateDynamicCondition(dynamicObj, cond, claimMultiplier));
|
|
2282
2535
|
}
|
|
2283
2536
|
// Evaluate the first condition
|
|
2284
|
-
let result = evaluateDynamicCondition(dynamicObj, conditions[0]);
|
|
2537
|
+
let result = evaluateDynamicCondition(dynamicObj, conditions[0], claimMultiplier);
|
|
2285
2538
|
for (let i = 0; i < links.length; i++) {
|
|
2286
|
-
const nextCond = evaluateDynamicCondition(dynamicObj, conditions[i + 1]);
|
|
2539
|
+
const nextCond = evaluateDynamicCondition(dynamicObj, conditions[i + 1], claimMultiplier);
|
|
2287
2540
|
const link = links[i];
|
|
2288
2541
|
if (link === 'AND') {
|
|
2289
2542
|
result = result && nextCond;
|
|
@@ -2371,6 +2624,8 @@
|
|
|
2371
2624
|
exports.OfferwallClient = OfferwallClient;
|
|
2372
2625
|
exports.PlayerOfferStatuses = PlayerOfferStatuses;
|
|
2373
2626
|
exports.SSEConnection = SSEConnection;
|
|
2627
|
+
exports.getMaxClaimsForDynamicCondition = getMaxClaimsForDynamicCondition;
|
|
2628
|
+
exports.getMaxClaimsForDynamicGroup = getMaxClaimsForDynamicGroup;
|
|
2374
2629
|
exports.hasConditions = hasConditions;
|
|
2375
2630
|
exports.meetsBaseConditions = meetsBaseConditions;
|
|
2376
2631
|
exports.meetsClaimableConditions = meetsClaimableConditions;
|
|
@@ -2379,6 +2634,7 @@
|
|
|
2379
2634
|
exports.meetsDynamicConditions = meetsDynamicConditions;
|
|
2380
2635
|
exports.meetsSurfacingConditions = meetsSurfacingConditions;
|
|
2381
2636
|
exports.offerListenerEvents = offerListenerEvents;
|
|
2637
|
+
exports.offerMeetsCompletionConditions = offerMeetsCompletionConditions;
|
|
2382
2638
|
exports.rewardKinds = rewardKinds;
|
|
2383
2639
|
exports.rewardSchema = rewardSchema;
|
|
2384
2640
|
|