@pixels-online/pixels-client-js-sdk 1.13.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 +378 -59
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +379 -58
- package/dist/index.js.map +1 -1
- package/dist/offerwall-sdk.umd.js +379 -58
- package/dist/offerwall-sdk.umd.js.map +1 -1
- package/dist/types/offer.d.ts +102 -19
- package/dist/types/player.d.ts +126 -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
package/dist/index.js
CHANGED
|
@@ -601,22 +601,28 @@ class SSEConnection {
|
|
|
601
601
|
}, backoffDelay);
|
|
602
602
|
}
|
|
603
603
|
disconnect() {
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
604
|
+
try {
|
|
605
|
+
if (this.reconnectTimeout) {
|
|
606
|
+
clearTimeout(this.reconnectTimeout);
|
|
607
|
+
this.reconnectTimeout = null;
|
|
608
|
+
}
|
|
609
|
+
if (this.eventSource) {
|
|
610
|
+
this.logger.log('Closing SSE connection');
|
|
611
|
+
this.eventSource.close();
|
|
612
|
+
this.eventSource = null;
|
|
613
|
+
this.setConnectionState(exports.ConnectionState.DISCONNECTED);
|
|
614
|
+
this.eventEmitter.emit(exports.OfferEvent.DISCONNECTED, {
|
|
615
|
+
reason: 'manual',
|
|
616
|
+
timestamp: new Date(),
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
this.isConnecting = false;
|
|
620
|
+
this.reconnectAttempts = 0;
|
|
607
621
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
this.
|
|
611
|
-
this.eventSource = null;
|
|
612
|
-
this.setConnectionState(exports.ConnectionState.DISCONNECTED);
|
|
613
|
-
this.eventEmitter.emit(exports.OfferEvent.DISCONNECTED, {
|
|
614
|
-
reason: 'manual',
|
|
615
|
-
timestamp: new Date(),
|
|
616
|
-
});
|
|
622
|
+
catch (error) {
|
|
623
|
+
// should be safe to ignore, likely mid-stream error during disconnect
|
|
624
|
+
this.logger.log('Error during disconnect:', error);
|
|
617
625
|
}
|
|
618
|
-
this.isConnecting = false;
|
|
619
|
-
this.reconnectAttempts = 0;
|
|
620
626
|
}
|
|
621
627
|
isConnected() {
|
|
622
628
|
return this.eventSource?.getReadyState() === ReadyState.OPEN;
|
|
@@ -1165,6 +1171,19 @@ class OfferwallClient {
|
|
|
1165
1171
|
}
|
|
1166
1172
|
}
|
|
1167
1173
|
|
|
1174
|
+
const keyPattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
1175
|
+
// renders template by replacing {keyName} with values from dynamic
|
|
1176
|
+
function renderTemplate(template, dynamic) {
|
|
1177
|
+
if (!template)
|
|
1178
|
+
return '';
|
|
1179
|
+
return template.replace(keyPattern, (match, key) => {
|
|
1180
|
+
if (dynamic && dynamic[key] !== undefined) {
|
|
1181
|
+
return String(dynamic[key]);
|
|
1182
|
+
}
|
|
1183
|
+
return '{?}'; // indicate missing key
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1168
1187
|
const meetsBaseConditions = ({ conditions, playerSnap, addDetails, }) => {
|
|
1169
1188
|
const conditionData = [];
|
|
1170
1189
|
let isValid = true;
|
|
@@ -1490,6 +1509,92 @@ const meetsBaseConditions = ({ conditions, playerSnap, addDetails, }) => {
|
|
|
1490
1509
|
}
|
|
1491
1510
|
}
|
|
1492
1511
|
}
|
|
1512
|
+
// Validate link count conditions
|
|
1513
|
+
if (conditions?.links) {
|
|
1514
|
+
for (const [linkType, constraint] of Object.entries(conditions.links)) {
|
|
1515
|
+
const linkCount = playerSnap.entityLinks?.filter((link) => link.kind === linkType).length || 0;
|
|
1516
|
+
if (constraint.min !== undefined) {
|
|
1517
|
+
const isDisqualify = linkCount < constraint.min;
|
|
1518
|
+
if (addDetails) {
|
|
1519
|
+
conditionData.push({
|
|
1520
|
+
isMet: !isDisqualify,
|
|
1521
|
+
kind: 'links',
|
|
1522
|
+
trackerAmount: linkCount,
|
|
1523
|
+
text: `At least ${constraint.min} ${linkType} link(s)`,
|
|
1524
|
+
});
|
|
1525
|
+
if (isDisqualify)
|
|
1526
|
+
isValid = false;
|
|
1527
|
+
}
|
|
1528
|
+
else {
|
|
1529
|
+
if (isDisqualify)
|
|
1530
|
+
return { isValid: false };
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
if (constraint.max !== undefined) {
|
|
1534
|
+
const isDisqualify = linkCount > constraint.max;
|
|
1535
|
+
if (addDetails) {
|
|
1536
|
+
conditionData.push({
|
|
1537
|
+
isMet: !isDisqualify,
|
|
1538
|
+
kind: 'links',
|
|
1539
|
+
trackerAmount: linkCount,
|
|
1540
|
+
text: `At most ${constraint.max} ${linkType} link(s)`,
|
|
1541
|
+
});
|
|
1542
|
+
if (isDisqualify)
|
|
1543
|
+
isValid = false;
|
|
1544
|
+
}
|
|
1545
|
+
else {
|
|
1546
|
+
if (isDisqualify)
|
|
1547
|
+
return { isValid: false };
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
// Evaluate dynamic conditions
|
|
1553
|
+
if (conditions?.dynamic?.conditions?.length) {
|
|
1554
|
+
const dynamicResult = meetsDynamicConditions(playerSnap.dynamic || {}, conditions.dynamic);
|
|
1555
|
+
if (addDetails) {
|
|
1556
|
+
conditionData.push({
|
|
1557
|
+
isMet: dynamicResult,
|
|
1558
|
+
kind: 'dynamic',
|
|
1559
|
+
text: renderTemplate(conditions.dynamic.template, playerSnap.dynamic) ||
|
|
1560
|
+
'Dynamic conditions',
|
|
1561
|
+
});
|
|
1562
|
+
if (!dynamicResult)
|
|
1563
|
+
isValid = false;
|
|
1564
|
+
}
|
|
1565
|
+
else {
|
|
1566
|
+
if (!dynamicResult)
|
|
1567
|
+
return { isValid: false };
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
if (conditions?.identifiers?.platforms?.length) {
|
|
1571
|
+
const playerPlatforms = new Set(playerSnap.identifiers?.map((i) => i.platform.toLowerCase()) || []);
|
|
1572
|
+
const isAndBehaviour = conditions.identifiers.behaviour === 'AND';
|
|
1573
|
+
const platformsToCheck = conditions.identifiers.platforms;
|
|
1574
|
+
let isMet;
|
|
1575
|
+
let displayText;
|
|
1576
|
+
if (isAndBehaviour) {
|
|
1577
|
+
isMet = platformsToCheck.every((platform) => playerPlatforms.has(platform.toLowerCase()));
|
|
1578
|
+
displayText = `Link all: ${platformsToCheck.join(', ')}`;
|
|
1579
|
+
}
|
|
1580
|
+
else {
|
|
1581
|
+
isMet = platformsToCheck.some((platform) => playerPlatforms.has(platform.toLowerCase()));
|
|
1582
|
+
displayText = `Link any: ${platformsToCheck.join(', ')}`;
|
|
1583
|
+
}
|
|
1584
|
+
if (addDetails) {
|
|
1585
|
+
conditionData.push({
|
|
1586
|
+
isMet,
|
|
1587
|
+
kind: 'identifiers',
|
|
1588
|
+
text: displayText,
|
|
1589
|
+
});
|
|
1590
|
+
if (!isMet)
|
|
1591
|
+
isValid = false;
|
|
1592
|
+
}
|
|
1593
|
+
else {
|
|
1594
|
+
if (!isMet)
|
|
1595
|
+
return { isValid: false };
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1493
1598
|
return { isValid, conditionData: addDetails ? conditionData : undefined };
|
|
1494
1599
|
};
|
|
1495
1600
|
const meetsSurfacingConditions = ({ surfacingConditions, playerSnap, context, playerOffers, }) => {
|
|
@@ -1498,6 +1603,13 @@ const meetsSurfacingConditions = ({ surfacingConditions, playerSnap, context, pl
|
|
|
1498
1603
|
// context is not in the list of surfacing contexts, so we don't want to surface this offer
|
|
1499
1604
|
return { isValid: false };
|
|
1500
1605
|
}
|
|
1606
|
+
if (surfacingConditions?.targetEntityTypes?.length) {
|
|
1607
|
+
const playerTarget = playerSnap.target || 'default';
|
|
1608
|
+
// check if entity type is allowed
|
|
1609
|
+
if (!surfacingConditions.targetEntityTypes.includes(playerTarget)) {
|
|
1610
|
+
return { isValid: false };
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1501
1613
|
const conditions = surfacingConditions;
|
|
1502
1614
|
if (conditions?.andTags?.length) {
|
|
1503
1615
|
// check if player has all of the tags
|
|
@@ -1536,12 +1648,6 @@ const meetsSurfacingConditions = ({ surfacingConditions, playerSnap, context, pl
|
|
|
1536
1648
|
(!playerSnap.dateSignedUp || playerSnap.dateSignedUp > conditions.maxDateSignedUp)) {
|
|
1537
1649
|
return { isValid: false };
|
|
1538
1650
|
}
|
|
1539
|
-
// Check dynamic conditions if present
|
|
1540
|
-
if (conditions.dynamic?.conditions?.length) {
|
|
1541
|
-
if (!meetsDynamicConditions(playerSnap.dynamic || {}, conditions.dynamic)) {
|
|
1542
|
-
return { isValid: false };
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
1651
|
const completedOfferIds = new Set();
|
|
1546
1652
|
for (const pOffer of playerOffers?.values() || []) {
|
|
1547
1653
|
if (pOffer.status === 'claimed' || pOffer.status === 'claimable') {
|
|
@@ -1577,6 +1683,10 @@ const hasConditions = (conditions) => {
|
|
|
1577
1683
|
return true;
|
|
1578
1684
|
if (conditions.minDaysInGame)
|
|
1579
1685
|
return true;
|
|
1686
|
+
if (conditions.dynamic?.conditions?.length)
|
|
1687
|
+
return true;
|
|
1688
|
+
if (conditions.identifiers?.platforms?.length)
|
|
1689
|
+
return true;
|
|
1580
1690
|
const surCond = conditions;
|
|
1581
1691
|
if (surCond.contexts?.length)
|
|
1582
1692
|
return true;
|
|
@@ -1596,6 +1706,12 @@ const hasConditions = (conditions) => {
|
|
|
1596
1706
|
return true;
|
|
1597
1707
|
if (surCond.completedOffers?.length)
|
|
1598
1708
|
return true;
|
|
1709
|
+
if (surCond.programmatic)
|
|
1710
|
+
return true;
|
|
1711
|
+
if (surCond.targetEntityTypes?.length)
|
|
1712
|
+
return true;
|
|
1713
|
+
if (surCond.links && Object.keys(surCond.links).length > 0)
|
|
1714
|
+
return true;
|
|
1599
1715
|
const compCond = conditions;
|
|
1600
1716
|
if (compCond.context)
|
|
1601
1717
|
return true;
|
|
@@ -1611,6 +1727,8 @@ const hasConditions = (conditions) => {
|
|
|
1611
1727
|
return true;
|
|
1612
1728
|
if (compCond.loginStreak)
|
|
1613
1729
|
return true;
|
|
1730
|
+
if (compCond.linkedCompletions)
|
|
1731
|
+
return true;
|
|
1614
1732
|
return false;
|
|
1615
1733
|
};
|
|
1616
1734
|
const meetsCompletionConditions = ({ completionConditions, completionTrackers, playerSnap, addDetails = false, }) => {
|
|
@@ -1724,51 +1842,107 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
1724
1842
|
}
|
|
1725
1843
|
}
|
|
1726
1844
|
if (conditions?.social) {
|
|
1727
|
-
const hasAttachedContent = !!completionTrackers?.social?.videoId;
|
|
1728
1845
|
const tSocial = completionTrackers?.social;
|
|
1729
1846
|
const cSocial = completionConditions.social;
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1847
|
+
const mode = cSocial?.mode || 'attach';
|
|
1848
|
+
const hasContent = Boolean(mode === 'accumulate'
|
|
1849
|
+
? tSocial?.mode === 'accumulate'
|
|
1850
|
+
: tSocial && tSocial.mode !== 'accumulate' && !!tSocial.videoId);
|
|
1851
|
+
const minLikes = cSocial?.minLikes || 0;
|
|
1852
|
+
const minViews = cSocial?.minViews || 0;
|
|
1853
|
+
const minComments = cSocial?.minComments || 0;
|
|
1854
|
+
const likes = tSocial?.likes || 0;
|
|
1855
|
+
const views = tSocial?.views || 0;
|
|
1856
|
+
const comments = tSocial?.comments || 0;
|
|
1857
|
+
let isDisqualify = !hasContent;
|
|
1858
|
+
if (likes < minLikes || views < minViews || comments < minComments) {
|
|
1738
1859
|
isDisqualify = true;
|
|
1739
1860
|
}
|
|
1740
1861
|
if (addDetails) {
|
|
1741
|
-
// Build detailed text about requirements
|
|
1742
1862
|
const platformMap = {
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1863
|
+
tiktok: 'TikTok',
|
|
1864
|
+
instagram: 'Instagram',
|
|
1865
|
+
youtube: 'YouTube',
|
|
1746
1866
|
};
|
|
1747
|
-
const platformText = conditions.social.platforms
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1867
|
+
const platformText = conditions.social.platforms
|
|
1868
|
+
.map((platform) => platformMap[platform])
|
|
1869
|
+
.join(' | ');
|
|
1870
|
+
const requiredWords = cSocial?.requiredWords ?? [];
|
|
1871
|
+
if (mode === 'accumulate') {
|
|
1872
|
+
const matchCount = (tSocial?.mode === 'accumulate' && tSocial.matchCount) || 0;
|
|
1873
|
+
conditionData.push({
|
|
1874
|
+
isMet: hasContent,
|
|
1875
|
+
kind: 'social',
|
|
1876
|
+
trackerAmount: matchCount,
|
|
1877
|
+
text: hasContent
|
|
1878
|
+
? `Found ${matchCount} matching ${platformText} post${matchCount !== 1 ? 's' : ''}`
|
|
1879
|
+
: requiredWords.length > 0
|
|
1880
|
+
? `Post ${platformText} content with ${requiredWords.map((w) => `"${w}"`).join(', ')}`
|
|
1881
|
+
: `Post ${platformText} content`,
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
else {
|
|
1885
|
+
const title = (tSocial && tSocial.mode !== 'accumulate' && tSocial.title) || undefined;
|
|
1886
|
+
conditionData.push({
|
|
1887
|
+
isMet: hasContent,
|
|
1888
|
+
kind: 'social',
|
|
1889
|
+
trackerAmount: 0,
|
|
1890
|
+
text: !hasContent
|
|
1891
|
+
? requiredWords.length > 0
|
|
1892
|
+
? `Attach a ${platformText} post with ${requiredWords.map((w) => `"${w}"`).join(', ')} in the title`
|
|
1893
|
+
: `Attach a ${platformText} post`
|
|
1894
|
+
: `Attached: ${title}`,
|
|
1895
|
+
});
|
|
1761
1896
|
}
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
:
|
|
1897
|
+
if (minLikes > 0) {
|
|
1898
|
+
conditionData.push({
|
|
1899
|
+
isMet: hasContent && likes >= minLikes,
|
|
1900
|
+
kind: 'social',
|
|
1901
|
+
trackerAmount: likes,
|
|
1902
|
+
text: mode === 'accumulate'
|
|
1903
|
+
? `Combined ${minLikes} Likes`
|
|
1904
|
+
: `Reach ${minLikes} Likes`,
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
if (minViews > 0) {
|
|
1908
|
+
conditionData.push({
|
|
1909
|
+
isMet: hasContent && views >= minViews,
|
|
1910
|
+
kind: 'social',
|
|
1911
|
+
trackerAmount: views,
|
|
1912
|
+
text: mode === 'accumulate'
|
|
1913
|
+
? `Combined ${minViews} Views`
|
|
1914
|
+
: `Reach ${minViews} Views`,
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1917
|
+
if (minComments > 0) {
|
|
1918
|
+
conditionData.push({
|
|
1919
|
+
isMet: hasContent && comments >= minComments,
|
|
1920
|
+
kind: 'social',
|
|
1921
|
+
trackerAmount: comments,
|
|
1922
|
+
text: mode === 'accumulate'
|
|
1923
|
+
? `Combined ${minComments} Comments`
|
|
1924
|
+
: `Reach ${minComments} Comments`,
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
if (isDisqualify)
|
|
1928
|
+
isValid = false;
|
|
1929
|
+
}
|
|
1930
|
+
else {
|
|
1931
|
+
if (isDisqualify)
|
|
1932
|
+
return { isValid: false };
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
// Linked completions - wait for N linked entities to complete
|
|
1936
|
+
if (conditions?.linkedCompletions?.min) {
|
|
1937
|
+
const currentCount = completionTrackers?.linkedCompletions || 0;
|
|
1938
|
+
const requiredCount = conditions.linkedCompletions.min;
|
|
1939
|
+
const isDisqualify = currentCount < requiredCount;
|
|
1940
|
+
if (addDetails) {
|
|
1767
1941
|
conditionData.push({
|
|
1768
|
-
isMet:
|
|
1769
|
-
kind: '
|
|
1770
|
-
trackerAmount,
|
|
1771
|
-
text:
|
|
1942
|
+
isMet: !isDisqualify,
|
|
1943
|
+
kind: 'linkedCompletions',
|
|
1944
|
+
trackerAmount: currentCount,
|
|
1945
|
+
text: `Wait for ${requiredCount} linked ${requiredCount === 1 ? 'entity' : 'entities'} to complete`,
|
|
1772
1946
|
});
|
|
1773
1947
|
if (isDisqualify)
|
|
1774
1948
|
isValid = false;
|
|
@@ -1789,6 +1963,136 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
1789
1963
|
}
|
|
1790
1964
|
return { isValid: true, conditionData: [] };
|
|
1791
1965
|
};
|
|
1966
|
+
/**
|
|
1967
|
+
* Checks if completion conditions were met before a specific expiry time.
|
|
1968
|
+
* Returns true if all relevant condition fields were updated before expiryTime.
|
|
1969
|
+
*
|
|
1970
|
+
* @param completionConditions - The completion conditions to check
|
|
1971
|
+
* @param completionTrackers - The completion trackers (for buyItem, spendCurrency, etc.)
|
|
1972
|
+
* @param playerSnap - The player snapshot with field timestamps
|
|
1973
|
+
* @param expiryTime - The expiry timestamp in milliseconds
|
|
1974
|
+
* @returns true if all conditions were met before expiry, false otherwise
|
|
1975
|
+
*/
|
|
1976
|
+
const meetsCompletionConditionsBeforeExpiry = ({ completionConditions, completionTrackers, playerSnap, expiryTime, }) => {
|
|
1977
|
+
if (!completionConditions)
|
|
1978
|
+
return false;
|
|
1979
|
+
// Check if there are actually any conditions to evaluate
|
|
1980
|
+
if (!hasConditions(completionConditions))
|
|
1981
|
+
return false;
|
|
1982
|
+
// First check if conditions are actually met
|
|
1983
|
+
const conditionsMet = meetsCompletionConditions({
|
|
1984
|
+
completionConditions,
|
|
1985
|
+
completionTrackers,
|
|
1986
|
+
playerSnap,
|
|
1987
|
+
});
|
|
1988
|
+
if (!conditionsMet.isValid)
|
|
1989
|
+
return false;
|
|
1990
|
+
const lastSnapshotUpdate = new Date(playerSnap.snapshotLastUpdated).getTime();
|
|
1991
|
+
/**
|
|
1992
|
+
* Checks if a field was updated after the expiry time.
|
|
1993
|
+
* Returns true if updated AFTER or AT expiry (violates grace period).
|
|
1994
|
+
* Returns false if updated BEFORE expiry (allows grace period).
|
|
1995
|
+
*/
|
|
1996
|
+
function wasUpdatedAfterExpiry(data) {
|
|
1997
|
+
let lastUpdated;
|
|
1998
|
+
if (typeof data === 'object' && data !== null && !(data instanceof Date)) {
|
|
1999
|
+
// Object with optional lastUpdated field
|
|
2000
|
+
lastUpdated = data.lastUpdated
|
|
2001
|
+
? new Date(data.lastUpdated).getTime()
|
|
2002
|
+
: lastSnapshotUpdate;
|
|
2003
|
+
}
|
|
2004
|
+
else if (data instanceof Date) {
|
|
2005
|
+
lastUpdated = data.getTime();
|
|
2006
|
+
}
|
|
2007
|
+
else if (typeof data === 'string' || typeof data === 'number') {
|
|
2008
|
+
lastUpdated = new Date(data).getTime();
|
|
2009
|
+
}
|
|
2010
|
+
else {
|
|
2011
|
+
// No data provided, use snapshot timestamp
|
|
2012
|
+
lastUpdated = lastSnapshotUpdate;
|
|
2013
|
+
}
|
|
2014
|
+
return lastUpdated >= expiryTime;
|
|
2015
|
+
}
|
|
2016
|
+
if (completionConditions.currencies) {
|
|
2017
|
+
for (const currencyId in completionConditions.currencies) {
|
|
2018
|
+
const currency = playerSnap.currencies?.[currencyId];
|
|
2019
|
+
if (!currency)
|
|
2020
|
+
continue;
|
|
2021
|
+
if (wasUpdatedAfterExpiry(currency))
|
|
2022
|
+
return false;
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
if (completionConditions.levels) {
|
|
2026
|
+
for (const skillId in completionConditions.levels) {
|
|
2027
|
+
const level = playerSnap.levels?.[skillId];
|
|
2028
|
+
if (!level)
|
|
2029
|
+
continue;
|
|
2030
|
+
if (wasUpdatedAfterExpiry(level))
|
|
2031
|
+
return false;
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
if (completionConditions.quests) {
|
|
2035
|
+
for (const questId in completionConditions.quests) {
|
|
2036
|
+
const quest = playerSnap.quests?.[questId];
|
|
2037
|
+
if (!quest)
|
|
2038
|
+
continue;
|
|
2039
|
+
if (wasUpdatedAfterExpiry(quest))
|
|
2040
|
+
return false;
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
if (completionConditions.memberships) {
|
|
2044
|
+
for (const membershipId in completionConditions.memberships) {
|
|
2045
|
+
const membership = playerSnap.memberships?.[membershipId];
|
|
2046
|
+
if (!membership)
|
|
2047
|
+
continue;
|
|
2048
|
+
if (wasUpdatedAfterExpiry(membership))
|
|
2049
|
+
return false;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
if (completionConditions.achievements) {
|
|
2053
|
+
for (const achievementId in completionConditions.achievements) {
|
|
2054
|
+
const achievement = playerSnap.achievements?.[achievementId];
|
|
2055
|
+
if (!achievement)
|
|
2056
|
+
continue;
|
|
2057
|
+
if (wasUpdatedAfterExpiry(achievement))
|
|
2058
|
+
return false;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
if (completionConditions.stakedTokens) {
|
|
2062
|
+
for (const tokenId in completionConditions.stakedTokens) {
|
|
2063
|
+
const stakedToken = playerSnap.stakedTokens?.[tokenId];
|
|
2064
|
+
if (!stakedToken)
|
|
2065
|
+
continue;
|
|
2066
|
+
const lastStakeTime = new Date(stakedToken.lastStake ?? 0).getTime();
|
|
2067
|
+
const lastUnstakeTime = new Date(stakedToken.lastUnstake ?? 0).getTime();
|
|
2068
|
+
const lastUpdated = Math.max(lastStakeTime, lastUnstakeTime);
|
|
2069
|
+
if (lastUpdated >= expiryTime)
|
|
2070
|
+
return false;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
if (completionConditions.minTrustScore !== undefined ||
|
|
2074
|
+
completionConditions.maxTrustScore !== undefined) {
|
|
2075
|
+
if (wasUpdatedAfterExpiry(playerSnap.trustLastUpdated))
|
|
2076
|
+
return false;
|
|
2077
|
+
}
|
|
2078
|
+
if (completionConditions.minDaysInGame !== undefined) {
|
|
2079
|
+
if (wasUpdatedAfterExpiry(playerSnap.daysInGameLastUpdated))
|
|
2080
|
+
return false;
|
|
2081
|
+
}
|
|
2082
|
+
if (completionConditions.login || completionConditions.loginStreak) {
|
|
2083
|
+
if (wasUpdatedAfterExpiry())
|
|
2084
|
+
return false;
|
|
2085
|
+
}
|
|
2086
|
+
if (completionConditions.social) {
|
|
2087
|
+
// Check if social content was attached/validated after expiry
|
|
2088
|
+
if (completionTrackers?.social?.lastChecked) {
|
|
2089
|
+
if (wasUpdatedAfterExpiry(completionTrackers.social.lastChecked))
|
|
2090
|
+
return false;
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
// All conditions were met before expiry
|
|
2094
|
+
return true;
|
|
2095
|
+
};
|
|
1792
2096
|
/**
|
|
1793
2097
|
* Checks if a dynamic object meets a set of dynamic field conditions.
|
|
1794
2098
|
* @param dynamicObj - The object with any key and string or number value.
|
|
@@ -1867,8 +2171,9 @@ function meetsDynamicConditions(dynamicObj, dynamicGroup) {
|
|
|
1867
2171
|
return result;
|
|
1868
2172
|
}
|
|
1869
2173
|
|
|
2174
|
+
const offerListenerEvents = ['claim_offer'];
|
|
1870
2175
|
const PlayerOfferStatuses = [
|
|
1871
|
-
'inQueue',
|
|
2176
|
+
// 'inQueue', // fuck this shit. just don't surface offers if their offer plate is full.
|
|
1872
2177
|
'surfaced',
|
|
1873
2178
|
'viewed',
|
|
1874
2179
|
'claimable',
|
|
@@ -1883,12 +2188,26 @@ const rewardKinds = [
|
|
|
1883
2188
|
'exp',
|
|
1884
2189
|
'trust_points',
|
|
1885
2190
|
'loyalty_currency', // loyalty currency that the player can exchange for rewards like on-chain via withdraw, etc.
|
|
2191
|
+
'discount', // handled by the external dev, using the rewardId to identify what it is for in their system
|
|
1886
2192
|
/** on-chain rewards require the builder to send funds to a custodial wallet that we use to send to player wallets*/
|
|
1887
2193
|
];
|
|
1888
2194
|
const rewardSchema = {
|
|
1889
2195
|
_id: false,
|
|
1890
2196
|
kind: { type: String, enum: rewardKinds },
|
|
1891
|
-
rewardId:
|
|
2197
|
+
rewardId: {
|
|
2198
|
+
type: String,
|
|
2199
|
+
validate: {
|
|
2200
|
+
validator: function (value) {
|
|
2201
|
+
// Require rewardId for item, coins, loyalty_currency, exp, and discount kinds
|
|
2202
|
+
const requiresRewardId = ['item', 'coins', 'loyalty_currency', 'exp', 'discount'].includes(this.kind);
|
|
2203
|
+
if (requiresRewardId) {
|
|
2204
|
+
return !!value;
|
|
2205
|
+
}
|
|
2206
|
+
return true;
|
|
2207
|
+
},
|
|
2208
|
+
message: 'rewardId is required for reward kinds: item, coins, loyalty_currency, exp, and discount',
|
|
2209
|
+
},
|
|
2210
|
+
},
|
|
1892
2211
|
skillId: String,
|
|
1893
2212
|
currencyId: String, // could be a loyalty currency
|
|
1894
2213
|
itemId: String,
|
|
@@ -1906,8 +2225,10 @@ exports.SSEConnection = SSEConnection;
|
|
|
1906
2225
|
exports.hasConditions = hasConditions;
|
|
1907
2226
|
exports.meetsBaseConditions = meetsBaseConditions;
|
|
1908
2227
|
exports.meetsCompletionConditions = meetsCompletionConditions;
|
|
2228
|
+
exports.meetsCompletionConditionsBeforeExpiry = meetsCompletionConditionsBeforeExpiry;
|
|
1909
2229
|
exports.meetsDynamicConditions = meetsDynamicConditions;
|
|
1910
2230
|
exports.meetsSurfacingConditions = meetsSurfacingConditions;
|
|
2231
|
+
exports.offerListenerEvents = offerListenerEvents;
|
|
1911
2232
|
exports.rewardKinds = rewardKinds;
|
|
1912
2233
|
exports.rewardSchema = rewardSchema;
|
|
1913
2234
|
//# sourceMappingURL=index.js.map
|