@pixels-online/pixels-client-js-sdk 1.14.0 → 1.15.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 +358 -45
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +359 -44
- package/dist/index.js.map +1 -1
- package/dist/offerwall-sdk.umd.js +359 -44
- package/dist/offerwall-sdk.umd.js.map +1 -1
- package/dist/types/offer.d.ts +102 -19
- package/dist/types/player.d.ts +124 -32
- package/dist/types/reward.d.ts +10 -9
- package/dist/utils/conditions.d.ts +16 -0
- package/dist/utils/template.d.ts +2 -0
- package/package.json +1 -1
|
@@ -1175,6 +1175,19 @@
|
|
|
1175
1175
|
}
|
|
1176
1176
|
}
|
|
1177
1177
|
|
|
1178
|
+
const keyPattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
1179
|
+
// renders template by replacing {keyName} with values from dynamic
|
|
1180
|
+
function renderTemplate(template, dynamic) {
|
|
1181
|
+
if (!template)
|
|
1182
|
+
return '';
|
|
1183
|
+
return template.replace(keyPattern, (match, key) => {
|
|
1184
|
+
if (dynamic && dynamic[key] !== undefined) {
|
|
1185
|
+
return String(dynamic[key]);
|
|
1186
|
+
}
|
|
1187
|
+
return '{?}'; // indicate missing key
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1178
1191
|
const meetsBaseConditions = ({ conditions, playerSnap, addDetails, }) => {
|
|
1179
1192
|
const conditionData = [];
|
|
1180
1193
|
let isValid = true;
|
|
@@ -1500,6 +1513,92 @@
|
|
|
1500
1513
|
}
|
|
1501
1514
|
}
|
|
1502
1515
|
}
|
|
1516
|
+
// Validate link count conditions
|
|
1517
|
+
if (conditions?.links) {
|
|
1518
|
+
for (const [linkType, constraint] of Object.entries(conditions.links)) {
|
|
1519
|
+
const linkCount = playerSnap.entityLinks?.filter((link) => link.kind === linkType).length || 0;
|
|
1520
|
+
if (constraint.min !== undefined) {
|
|
1521
|
+
const isDisqualify = linkCount < constraint.min;
|
|
1522
|
+
if (addDetails) {
|
|
1523
|
+
conditionData.push({
|
|
1524
|
+
isMet: !isDisqualify,
|
|
1525
|
+
kind: 'links',
|
|
1526
|
+
trackerAmount: linkCount,
|
|
1527
|
+
text: `At least ${constraint.min} ${linkType} link(s)`,
|
|
1528
|
+
});
|
|
1529
|
+
if (isDisqualify)
|
|
1530
|
+
isValid = false;
|
|
1531
|
+
}
|
|
1532
|
+
else {
|
|
1533
|
+
if (isDisqualify)
|
|
1534
|
+
return { isValid: false };
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
if (constraint.max !== undefined) {
|
|
1538
|
+
const isDisqualify = linkCount > constraint.max;
|
|
1539
|
+
if (addDetails) {
|
|
1540
|
+
conditionData.push({
|
|
1541
|
+
isMet: !isDisqualify,
|
|
1542
|
+
kind: 'links',
|
|
1543
|
+
trackerAmount: linkCount,
|
|
1544
|
+
text: `At most ${constraint.max} ${linkType} link(s)`,
|
|
1545
|
+
});
|
|
1546
|
+
if (isDisqualify)
|
|
1547
|
+
isValid = false;
|
|
1548
|
+
}
|
|
1549
|
+
else {
|
|
1550
|
+
if (isDisqualify)
|
|
1551
|
+
return { isValid: false };
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
// Evaluate dynamic conditions
|
|
1557
|
+
if (conditions?.dynamic?.conditions?.length) {
|
|
1558
|
+
const dynamicResult = meetsDynamicConditions(playerSnap.dynamic || {}, conditions.dynamic);
|
|
1559
|
+
if (addDetails) {
|
|
1560
|
+
conditionData.push({
|
|
1561
|
+
isMet: dynamicResult,
|
|
1562
|
+
kind: 'dynamic',
|
|
1563
|
+
text: renderTemplate(conditions.dynamic.template, playerSnap.dynamic) ||
|
|
1564
|
+
'Dynamic conditions',
|
|
1565
|
+
});
|
|
1566
|
+
if (!dynamicResult)
|
|
1567
|
+
isValid = false;
|
|
1568
|
+
}
|
|
1569
|
+
else {
|
|
1570
|
+
if (!dynamicResult)
|
|
1571
|
+
return { isValid: false };
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
if (conditions?.identifiers?.platforms?.length) {
|
|
1575
|
+
const playerPlatforms = new Set(playerSnap.identifiers?.map((i) => i.platform.toLowerCase()) || []);
|
|
1576
|
+
const isAndBehaviour = conditions.identifiers.behaviour === 'AND';
|
|
1577
|
+
const platformsToCheck = conditions.identifiers.platforms;
|
|
1578
|
+
let isMet;
|
|
1579
|
+
let displayText;
|
|
1580
|
+
if (isAndBehaviour) {
|
|
1581
|
+
isMet = platformsToCheck.every((platform) => playerPlatforms.has(platform.toLowerCase()));
|
|
1582
|
+
displayText = `Link all: ${platformsToCheck.join(', ')}`;
|
|
1583
|
+
}
|
|
1584
|
+
else {
|
|
1585
|
+
isMet = platformsToCheck.some((platform) => playerPlatforms.has(platform.toLowerCase()));
|
|
1586
|
+
displayText = `Link any: ${platformsToCheck.join(', ')}`;
|
|
1587
|
+
}
|
|
1588
|
+
if (addDetails) {
|
|
1589
|
+
conditionData.push({
|
|
1590
|
+
isMet,
|
|
1591
|
+
kind: 'identifiers',
|
|
1592
|
+
text: displayText,
|
|
1593
|
+
});
|
|
1594
|
+
if (!isMet)
|
|
1595
|
+
isValid = false;
|
|
1596
|
+
}
|
|
1597
|
+
else {
|
|
1598
|
+
if (!isMet)
|
|
1599
|
+
return { isValid: false };
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1503
1602
|
return { isValid, conditionData: addDetails ? conditionData : undefined };
|
|
1504
1603
|
};
|
|
1505
1604
|
const meetsSurfacingConditions = ({ surfacingConditions, playerSnap, context, playerOffers, }) => {
|
|
@@ -1508,6 +1607,13 @@
|
|
|
1508
1607
|
// context is not in the list of surfacing contexts, so we don't want to surface this offer
|
|
1509
1608
|
return { isValid: false };
|
|
1510
1609
|
}
|
|
1610
|
+
if (surfacingConditions?.targetEntityTypes?.length) {
|
|
1611
|
+
const playerTarget = playerSnap.target || 'default';
|
|
1612
|
+
// check if entity type is allowed
|
|
1613
|
+
if (!surfacingConditions.targetEntityTypes.includes(playerTarget)) {
|
|
1614
|
+
return { isValid: false };
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1511
1617
|
const conditions = surfacingConditions;
|
|
1512
1618
|
if (conditions?.andTags?.length) {
|
|
1513
1619
|
// check if player has all of the tags
|
|
@@ -1546,12 +1652,6 @@
|
|
|
1546
1652
|
(!playerSnap.dateSignedUp || playerSnap.dateSignedUp > conditions.maxDateSignedUp)) {
|
|
1547
1653
|
return { isValid: false };
|
|
1548
1654
|
}
|
|
1549
|
-
// Check dynamic conditions if present
|
|
1550
|
-
if (conditions.dynamic?.conditions?.length) {
|
|
1551
|
-
if (!meetsDynamicConditions(playerSnap.dynamic || {}, conditions.dynamic)) {
|
|
1552
|
-
return { isValid: false };
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
1655
|
const completedOfferIds = new Set();
|
|
1556
1656
|
for (const pOffer of playerOffers?.values() || []) {
|
|
1557
1657
|
if (pOffer.status === 'claimed' || pOffer.status === 'claimable') {
|
|
@@ -1587,6 +1687,10 @@
|
|
|
1587
1687
|
return true;
|
|
1588
1688
|
if (conditions.minDaysInGame)
|
|
1589
1689
|
return true;
|
|
1690
|
+
if (conditions.dynamic?.conditions?.length)
|
|
1691
|
+
return true;
|
|
1692
|
+
if (conditions.identifiers?.platforms?.length)
|
|
1693
|
+
return true;
|
|
1590
1694
|
const surCond = conditions;
|
|
1591
1695
|
if (surCond.contexts?.length)
|
|
1592
1696
|
return true;
|
|
@@ -1606,6 +1710,12 @@
|
|
|
1606
1710
|
return true;
|
|
1607
1711
|
if (surCond.completedOffers?.length)
|
|
1608
1712
|
return true;
|
|
1713
|
+
if (surCond.programmatic)
|
|
1714
|
+
return true;
|
|
1715
|
+
if (surCond.targetEntityTypes?.length)
|
|
1716
|
+
return true;
|
|
1717
|
+
if (surCond.links && Object.keys(surCond.links).length > 0)
|
|
1718
|
+
return true;
|
|
1609
1719
|
const compCond = conditions;
|
|
1610
1720
|
if (compCond.context)
|
|
1611
1721
|
return true;
|
|
@@ -1621,6 +1731,8 @@
|
|
|
1621
1731
|
return true;
|
|
1622
1732
|
if (compCond.loginStreak)
|
|
1623
1733
|
return true;
|
|
1734
|
+
if (compCond.linkedCompletions)
|
|
1735
|
+
return true;
|
|
1624
1736
|
return false;
|
|
1625
1737
|
};
|
|
1626
1738
|
const meetsCompletionConditions = ({ completionConditions, completionTrackers, playerSnap, addDetails = false, }) => {
|
|
@@ -1734,51 +1846,107 @@
|
|
|
1734
1846
|
}
|
|
1735
1847
|
}
|
|
1736
1848
|
if (conditions?.social) {
|
|
1737
|
-
const hasAttachedContent = !!completionTrackers?.social?.videoId;
|
|
1738
1849
|
const tSocial = completionTrackers?.social;
|
|
1739
1850
|
const cSocial = completionConditions.social;
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1851
|
+
const mode = cSocial?.mode || 'attach';
|
|
1852
|
+
const hasContent = Boolean(mode === 'accumulate'
|
|
1853
|
+
? tSocial?.mode === 'accumulate'
|
|
1854
|
+
: tSocial && tSocial.mode !== 'accumulate' && !!tSocial.videoId);
|
|
1855
|
+
const minLikes = cSocial?.minLikes || 0;
|
|
1856
|
+
const minViews = cSocial?.minViews || 0;
|
|
1857
|
+
const minComments = cSocial?.minComments || 0;
|
|
1858
|
+
const likes = tSocial?.likes || 0;
|
|
1859
|
+
const views = tSocial?.views || 0;
|
|
1860
|
+
const comments = tSocial?.comments || 0;
|
|
1861
|
+
let isDisqualify = !hasContent;
|
|
1862
|
+
if (likes < minLikes || views < minViews || comments < minComments) {
|
|
1748
1863
|
isDisqualify = true;
|
|
1749
1864
|
}
|
|
1750
1865
|
if (addDetails) {
|
|
1751
|
-
// Build detailed text about requirements
|
|
1752
1866
|
const platformMap = {
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1867
|
+
tiktok: 'TikTok',
|
|
1868
|
+
instagram: 'Instagram',
|
|
1869
|
+
youtube: 'YouTube',
|
|
1756
1870
|
};
|
|
1757
|
-
const platformText = conditions.social.platforms
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1871
|
+
const platformText = conditions.social.platforms
|
|
1872
|
+
.map((platform) => platformMap[platform])
|
|
1873
|
+
.join(' | ');
|
|
1874
|
+
const requiredWords = cSocial?.requiredWords ?? [];
|
|
1875
|
+
if (mode === 'accumulate') {
|
|
1876
|
+
const matchCount = (tSocial?.mode === 'accumulate' && tSocial.matchCount) || 0;
|
|
1877
|
+
conditionData.push({
|
|
1878
|
+
isMet: hasContent,
|
|
1879
|
+
kind: 'social',
|
|
1880
|
+
trackerAmount: matchCount,
|
|
1881
|
+
text: hasContent
|
|
1882
|
+
? `Found ${matchCount} matching ${platformText} post${matchCount !== 1 ? 's' : ''}`
|
|
1883
|
+
: requiredWords.length > 0
|
|
1884
|
+
? `Post ${platformText} content with ${requiredWords.map((w) => `"${w}"`).join(', ')}`
|
|
1885
|
+
: `Post ${platformText} content`,
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
else {
|
|
1889
|
+
const title = (tSocial && tSocial.mode !== 'accumulate' && tSocial.title) || undefined;
|
|
1890
|
+
conditionData.push({
|
|
1891
|
+
isMet: hasContent,
|
|
1892
|
+
kind: 'social',
|
|
1893
|
+
trackerAmount: 0,
|
|
1894
|
+
text: !hasContent
|
|
1895
|
+
? requiredWords.length > 0
|
|
1896
|
+
? `Attach a ${platformText} post with ${requiredWords.map((w) => `"${w}"`).join(', ')} in the title`
|
|
1897
|
+
: `Attach a ${platformText} post`
|
|
1898
|
+
: `Attached: ${title}`,
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
if (minLikes > 0) {
|
|
1902
|
+
conditionData.push({
|
|
1903
|
+
isMet: hasContent && likes >= minLikes,
|
|
1904
|
+
kind: 'social',
|
|
1905
|
+
trackerAmount: likes,
|
|
1906
|
+
text: mode === 'accumulate'
|
|
1907
|
+
? `Combined ${minLikes} Likes`
|
|
1908
|
+
: `Reach ${minLikes} Likes`,
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
if (minViews > 0) {
|
|
1912
|
+
conditionData.push({
|
|
1913
|
+
isMet: hasContent && views >= minViews,
|
|
1914
|
+
kind: 'social',
|
|
1915
|
+
trackerAmount: views,
|
|
1916
|
+
text: mode === 'accumulate'
|
|
1917
|
+
? `Combined ${minViews} Views`
|
|
1918
|
+
: `Reach ${minViews} Views`,
|
|
1919
|
+
});
|
|
1771
1920
|
}
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
:
|
|
1921
|
+
if (minComments > 0) {
|
|
1922
|
+
conditionData.push({
|
|
1923
|
+
isMet: hasContent && comments >= minComments,
|
|
1924
|
+
kind: 'social',
|
|
1925
|
+
trackerAmount: comments,
|
|
1926
|
+
text: mode === 'accumulate'
|
|
1927
|
+
? `Combined ${minComments} Comments`
|
|
1928
|
+
: `Reach ${minComments} Comments`,
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
if (isDisqualify)
|
|
1932
|
+
isValid = false;
|
|
1933
|
+
}
|
|
1934
|
+
else {
|
|
1935
|
+
if (isDisqualify)
|
|
1936
|
+
return { isValid: false };
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
// Linked completions - wait for N linked entities to complete
|
|
1940
|
+
if (conditions?.linkedCompletions?.min) {
|
|
1941
|
+
const currentCount = completionTrackers?.linkedCompletions || 0;
|
|
1942
|
+
const requiredCount = conditions.linkedCompletions.min;
|
|
1943
|
+
const isDisqualify = currentCount < requiredCount;
|
|
1944
|
+
if (addDetails) {
|
|
1777
1945
|
conditionData.push({
|
|
1778
|
-
isMet:
|
|
1779
|
-
kind: '
|
|
1780
|
-
trackerAmount,
|
|
1781
|
-
text:
|
|
1946
|
+
isMet: !isDisqualify,
|
|
1947
|
+
kind: 'linkedCompletions',
|
|
1948
|
+
trackerAmount: currentCount,
|
|
1949
|
+
text: `Wait for ${requiredCount} linked ${requiredCount === 1 ? 'entity' : 'entities'} to complete`,
|
|
1782
1950
|
});
|
|
1783
1951
|
if (isDisqualify)
|
|
1784
1952
|
isValid = false;
|
|
@@ -1799,6 +1967,136 @@
|
|
|
1799
1967
|
}
|
|
1800
1968
|
return { isValid: true, conditionData: [] };
|
|
1801
1969
|
};
|
|
1970
|
+
/**
|
|
1971
|
+
* Checks if completion conditions were met before a specific expiry time.
|
|
1972
|
+
* Returns true if all relevant condition fields were updated before expiryTime.
|
|
1973
|
+
*
|
|
1974
|
+
* @param completionConditions - The completion conditions to check
|
|
1975
|
+
* @param completionTrackers - The completion trackers (for buyItem, spendCurrency, etc.)
|
|
1976
|
+
* @param playerSnap - The player snapshot with field timestamps
|
|
1977
|
+
* @param expiryTime - The expiry timestamp in milliseconds
|
|
1978
|
+
* @returns true if all conditions were met before expiry, false otherwise
|
|
1979
|
+
*/
|
|
1980
|
+
const meetsCompletionConditionsBeforeExpiry = ({ completionConditions, completionTrackers, playerSnap, expiryTime, }) => {
|
|
1981
|
+
if (!completionConditions)
|
|
1982
|
+
return false;
|
|
1983
|
+
// Check if there are actually any conditions to evaluate
|
|
1984
|
+
if (!hasConditions(completionConditions))
|
|
1985
|
+
return false;
|
|
1986
|
+
// First check if conditions are actually met
|
|
1987
|
+
const conditionsMet = meetsCompletionConditions({
|
|
1988
|
+
completionConditions,
|
|
1989
|
+
completionTrackers,
|
|
1990
|
+
playerSnap,
|
|
1991
|
+
});
|
|
1992
|
+
if (!conditionsMet.isValid)
|
|
1993
|
+
return false;
|
|
1994
|
+
const lastSnapshotUpdate = new Date(playerSnap.snapshotLastUpdated).getTime();
|
|
1995
|
+
/**
|
|
1996
|
+
* Checks if a field was updated after the expiry time.
|
|
1997
|
+
* Returns true if updated AFTER or AT expiry (violates grace period).
|
|
1998
|
+
* Returns false if updated BEFORE expiry (allows grace period).
|
|
1999
|
+
*/
|
|
2000
|
+
function wasUpdatedAfterExpiry(data) {
|
|
2001
|
+
let lastUpdated;
|
|
2002
|
+
if (typeof data === 'object' && data !== null && !(data instanceof Date)) {
|
|
2003
|
+
// Object with optional lastUpdated field
|
|
2004
|
+
lastUpdated = data.lastUpdated
|
|
2005
|
+
? new Date(data.lastUpdated).getTime()
|
|
2006
|
+
: lastSnapshotUpdate;
|
|
2007
|
+
}
|
|
2008
|
+
else if (data instanceof Date) {
|
|
2009
|
+
lastUpdated = data.getTime();
|
|
2010
|
+
}
|
|
2011
|
+
else if (typeof data === 'string' || typeof data === 'number') {
|
|
2012
|
+
lastUpdated = new Date(data).getTime();
|
|
2013
|
+
}
|
|
2014
|
+
else {
|
|
2015
|
+
// No data provided, use snapshot timestamp
|
|
2016
|
+
lastUpdated = lastSnapshotUpdate;
|
|
2017
|
+
}
|
|
2018
|
+
return lastUpdated >= expiryTime;
|
|
2019
|
+
}
|
|
2020
|
+
if (completionConditions.currencies) {
|
|
2021
|
+
for (const currencyId in completionConditions.currencies) {
|
|
2022
|
+
const currency = playerSnap.currencies?.[currencyId];
|
|
2023
|
+
if (!currency)
|
|
2024
|
+
continue;
|
|
2025
|
+
if (wasUpdatedAfterExpiry(currency))
|
|
2026
|
+
return false;
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
if (completionConditions.levels) {
|
|
2030
|
+
for (const skillId in completionConditions.levels) {
|
|
2031
|
+
const level = playerSnap.levels?.[skillId];
|
|
2032
|
+
if (!level)
|
|
2033
|
+
continue;
|
|
2034
|
+
if (wasUpdatedAfterExpiry(level))
|
|
2035
|
+
return false;
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
if (completionConditions.quests) {
|
|
2039
|
+
for (const questId in completionConditions.quests) {
|
|
2040
|
+
const quest = playerSnap.quests?.[questId];
|
|
2041
|
+
if (!quest)
|
|
2042
|
+
continue;
|
|
2043
|
+
if (wasUpdatedAfterExpiry(quest))
|
|
2044
|
+
return false;
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
if (completionConditions.memberships) {
|
|
2048
|
+
for (const membershipId in completionConditions.memberships) {
|
|
2049
|
+
const membership = playerSnap.memberships?.[membershipId];
|
|
2050
|
+
if (!membership)
|
|
2051
|
+
continue;
|
|
2052
|
+
if (wasUpdatedAfterExpiry(membership))
|
|
2053
|
+
return false;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
if (completionConditions.achievements) {
|
|
2057
|
+
for (const achievementId in completionConditions.achievements) {
|
|
2058
|
+
const achievement = playerSnap.achievements?.[achievementId];
|
|
2059
|
+
if (!achievement)
|
|
2060
|
+
continue;
|
|
2061
|
+
if (wasUpdatedAfterExpiry(achievement))
|
|
2062
|
+
return false;
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
if (completionConditions.stakedTokens) {
|
|
2066
|
+
for (const tokenId in completionConditions.stakedTokens) {
|
|
2067
|
+
const stakedToken = playerSnap.stakedTokens?.[tokenId];
|
|
2068
|
+
if (!stakedToken)
|
|
2069
|
+
continue;
|
|
2070
|
+
const lastStakeTime = new Date(stakedToken.lastStake ?? 0).getTime();
|
|
2071
|
+
const lastUnstakeTime = new Date(stakedToken.lastUnstake ?? 0).getTime();
|
|
2072
|
+
const lastUpdated = Math.max(lastStakeTime, lastUnstakeTime);
|
|
2073
|
+
if (lastUpdated >= expiryTime)
|
|
2074
|
+
return false;
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
if (completionConditions.minTrustScore !== undefined ||
|
|
2078
|
+
completionConditions.maxTrustScore !== undefined) {
|
|
2079
|
+
if (wasUpdatedAfterExpiry(playerSnap.trustLastUpdated))
|
|
2080
|
+
return false;
|
|
2081
|
+
}
|
|
2082
|
+
if (completionConditions.minDaysInGame !== undefined) {
|
|
2083
|
+
if (wasUpdatedAfterExpiry(playerSnap.daysInGameLastUpdated))
|
|
2084
|
+
return false;
|
|
2085
|
+
}
|
|
2086
|
+
if (completionConditions.login || completionConditions.loginStreak) {
|
|
2087
|
+
if (wasUpdatedAfterExpiry())
|
|
2088
|
+
return false;
|
|
2089
|
+
}
|
|
2090
|
+
if (completionConditions.social) {
|
|
2091
|
+
// Check if social content was attached/validated after expiry
|
|
2092
|
+
if (completionTrackers?.social?.lastChecked) {
|
|
2093
|
+
if (wasUpdatedAfterExpiry(completionTrackers.social.lastChecked))
|
|
2094
|
+
return false;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
// All conditions were met before expiry
|
|
2098
|
+
return true;
|
|
2099
|
+
};
|
|
1802
2100
|
/**
|
|
1803
2101
|
* Checks if a dynamic object meets a set of dynamic field conditions.
|
|
1804
2102
|
* @param dynamicObj - The object with any key and string or number value.
|
|
@@ -1877,8 +2175,9 @@
|
|
|
1877
2175
|
return result;
|
|
1878
2176
|
}
|
|
1879
2177
|
|
|
2178
|
+
const offerListenerEvents = ['claim_offer'];
|
|
1880
2179
|
const PlayerOfferStatuses = [
|
|
1881
|
-
'inQueue',
|
|
2180
|
+
// 'inQueue', // fuck this shit. just don't surface offers if their offer plate is full.
|
|
1882
2181
|
'surfaced',
|
|
1883
2182
|
'viewed',
|
|
1884
2183
|
'claimable',
|
|
@@ -1893,12 +2192,26 @@
|
|
|
1893
2192
|
'exp',
|
|
1894
2193
|
'trust_points',
|
|
1895
2194
|
'loyalty_currency', // loyalty currency that the player can exchange for rewards like on-chain via withdraw, etc.
|
|
2195
|
+
'discount', // handled by the external dev, using the rewardId to identify what it is for in their system
|
|
1896
2196
|
/** on-chain rewards require the builder to send funds to a custodial wallet that we use to send to player wallets*/
|
|
1897
2197
|
];
|
|
1898
2198
|
const rewardSchema = {
|
|
1899
2199
|
_id: false,
|
|
1900
2200
|
kind: { type: String, enum: rewardKinds },
|
|
1901
|
-
rewardId:
|
|
2201
|
+
rewardId: {
|
|
2202
|
+
type: String,
|
|
2203
|
+
validate: {
|
|
2204
|
+
validator: function (value) {
|
|
2205
|
+
// Require rewardId for item, coins, loyalty_currency, exp, and discount kinds
|
|
2206
|
+
const requiresRewardId = ['item', 'coins', 'loyalty_currency', 'exp', 'discount'].includes(this.kind);
|
|
2207
|
+
if (requiresRewardId) {
|
|
2208
|
+
return !!value;
|
|
2209
|
+
}
|
|
2210
|
+
return true;
|
|
2211
|
+
},
|
|
2212
|
+
message: 'rewardId is required for reward kinds: item, coins, loyalty_currency, exp, and discount',
|
|
2213
|
+
},
|
|
2214
|
+
},
|
|
1902
2215
|
skillId: String,
|
|
1903
2216
|
currencyId: String, // could be a loyalty currency
|
|
1904
2217
|
itemId: String,
|
|
@@ -1916,8 +2229,10 @@
|
|
|
1916
2229
|
exports.hasConditions = hasConditions;
|
|
1917
2230
|
exports.meetsBaseConditions = meetsBaseConditions;
|
|
1918
2231
|
exports.meetsCompletionConditions = meetsCompletionConditions;
|
|
2232
|
+
exports.meetsCompletionConditionsBeforeExpiry = meetsCompletionConditionsBeforeExpiry;
|
|
1919
2233
|
exports.meetsDynamicConditions = meetsDynamicConditions;
|
|
1920
2234
|
exports.meetsSurfacingConditions = meetsSurfacingConditions;
|
|
2235
|
+
exports.offerListenerEvents = offerListenerEvents;
|
|
1921
2236
|
exports.rewardKinds = rewardKinds;
|
|
1922
2237
|
exports.rewardSchema = rewardSchema;
|
|
1923
2238
|
|