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