@pixels-online/pixels-client-js-sdk 1.18.0 → 1.20.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/core/OfferStore.d.ts +20 -14
- package/dist/core/OfferwallClient.d.ts +6 -4
- package/dist/index.esm.js +257 -66
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +259 -65
- package/dist/index.js.map +1 -1
- package/dist/offerwall-sdk.umd.js +259 -65
- package/dist/offerwall-sdk.umd.js.map +1 -1
- package/dist/types/player.d.ts +98 -13
- package/dist/types/user.d.ts +62 -0
- package/dist/utils/blockchain_utils.d.ts +5 -0
- package/dist/utils/conditions.d.ts +25 -7
- package/dist/utils/dynamic.d.ts +2 -0
- package/dist/utils/template.d.ts +1 -0
- package/package.json +1 -1
|
@@ -634,25 +634,33 @@
|
|
|
634
634
|
}
|
|
635
635
|
|
|
636
636
|
class OfferStore {
|
|
637
|
-
constructor(config) {
|
|
637
|
+
constructor(config, client) {
|
|
638
|
+
this.client = client;
|
|
638
639
|
this.offers = new Map();
|
|
639
|
-
this.
|
|
640
|
+
this.players = new Map();
|
|
640
641
|
this.logger = createLogger(config, 'OfferStore');
|
|
641
642
|
}
|
|
642
|
-
getPlayer() {
|
|
643
|
-
|
|
643
|
+
getPlayer(targetId = this.client.getSelfId()) {
|
|
644
|
+
if (!targetId)
|
|
645
|
+
return null;
|
|
646
|
+
return this.players.get(targetId) || null;
|
|
644
647
|
}
|
|
645
648
|
setPlayer(player) {
|
|
646
|
-
this.player
|
|
649
|
+
this.players.set(player.gameData.playerId, player);
|
|
647
650
|
this.logger.log('Updated player:', player);
|
|
648
651
|
}
|
|
649
652
|
/**
|
|
650
653
|
* Set all offers (replaces existing)
|
|
651
654
|
*/
|
|
652
|
-
setOffers(offers) {
|
|
653
|
-
this.
|
|
655
|
+
setOffers(offers, target) {
|
|
656
|
+
const targetPlayer = target || this.getPlayer();
|
|
657
|
+
if (!targetPlayer) {
|
|
658
|
+
this.logger.warn('No target player to set offers for');
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
this.offers.set(targetPlayer.gameData.playerId, new Map());
|
|
654
662
|
offers.forEach((offer) => {
|
|
655
|
-
this.offers.set(offer.instanceId, offer);
|
|
663
|
+
this.offers.get(targetPlayer.gameData.playerId).set(offer.instanceId, offer);
|
|
656
664
|
});
|
|
657
665
|
this.logger.log(`Set ${offers.length} offers`);
|
|
658
666
|
}
|
|
@@ -660,16 +668,23 @@
|
|
|
660
668
|
* Add or update a single offer
|
|
661
669
|
*/
|
|
662
670
|
upsertOffer(offer) {
|
|
663
|
-
|
|
664
|
-
|
|
671
|
+
let playerOffers = this.offers.get(offer.playerId);
|
|
672
|
+
if (!playerOffers) {
|
|
673
|
+
playerOffers = new Map();
|
|
674
|
+
this.offers.set(offer.playerId, playerOffers);
|
|
675
|
+
}
|
|
676
|
+
const previousOffer = playerOffers.get(offer.instanceId);
|
|
677
|
+
playerOffers.set(offer.instanceId, offer);
|
|
665
678
|
this.logger.log(`${previousOffer ? 'Updated' : 'Added'} offer:`, offer.instanceId);
|
|
666
679
|
return previousOffer;
|
|
667
680
|
}
|
|
668
681
|
/**
|
|
669
682
|
* Remove an offer
|
|
670
683
|
*/
|
|
671
|
-
removeOffer(offerId) {
|
|
672
|
-
|
|
684
|
+
removeOffer(offerId, targetId = this.client.getSelfId()) {
|
|
685
|
+
if (!targetId)
|
|
686
|
+
return false;
|
|
687
|
+
const removed = this.offers.get(targetId)?.delete(offerId) || false;
|
|
673
688
|
if (removed) {
|
|
674
689
|
this.logger.log(`Removed offer:`, offerId);
|
|
675
690
|
}
|
|
@@ -678,26 +693,30 @@
|
|
|
678
693
|
/**
|
|
679
694
|
* Get a single offer
|
|
680
695
|
*/
|
|
681
|
-
getOffer(offerId) {
|
|
682
|
-
|
|
696
|
+
getOffer(offerId, targetId = this.client.getSelfId()) {
|
|
697
|
+
if (!targetId)
|
|
698
|
+
return undefined;
|
|
699
|
+
return this.offers.get(targetId)?.get(offerId);
|
|
683
700
|
}
|
|
684
701
|
/**
|
|
685
702
|
* Get all offers
|
|
686
703
|
*/
|
|
687
|
-
getAllOffers() {
|
|
688
|
-
|
|
704
|
+
getAllOffers(targetId = this.client.getSelfId()) {
|
|
705
|
+
if (!targetId)
|
|
706
|
+
return [];
|
|
707
|
+
return Array.from(this.offers.get(targetId)?.values() || []);
|
|
689
708
|
}
|
|
690
709
|
/**
|
|
691
710
|
* Get offers filtered by status
|
|
692
711
|
*/
|
|
693
|
-
getOffersByStatus(status) {
|
|
694
|
-
return this.getAllOffers().filter((offer) => offer.status === status);
|
|
712
|
+
getOffersByStatus(status, targetId = this.client.getSelfId()) {
|
|
713
|
+
return this.getAllOffers(targetId).filter((offer) => offer.status === status);
|
|
695
714
|
}
|
|
696
715
|
/**
|
|
697
716
|
* Get active offers (not expired, not claimed)
|
|
698
717
|
*/
|
|
699
|
-
getActiveOffers() {
|
|
700
|
-
return this.getAllOffers().filter((offer) => {
|
|
718
|
+
getActiveOffers(targetId = this.client.getSelfId()) {
|
|
719
|
+
return this.getAllOffers(targetId).filter((offer) => {
|
|
701
720
|
if (!offer.status)
|
|
702
721
|
return false; // Must have a status
|
|
703
722
|
return (offer.status === 'surfaced' ||
|
|
@@ -708,14 +727,14 @@
|
|
|
708
727
|
/**
|
|
709
728
|
* Get claimable offers
|
|
710
729
|
*/
|
|
711
|
-
getClaimableOffers() {
|
|
712
|
-
return this.getOffersByStatus('claimable');
|
|
730
|
+
getClaimableOffers(targetId = this.client.getSelfId()) {
|
|
731
|
+
return this.getOffersByStatus('claimable', targetId);
|
|
713
732
|
}
|
|
714
733
|
/**
|
|
715
734
|
* Check if an offer has expired
|
|
716
735
|
*/
|
|
717
|
-
isOfferExpired(offerId) {
|
|
718
|
-
const offer = this.getOffer(offerId);
|
|
736
|
+
isOfferExpired(offerId, targetId = this.client.getSelfId()) {
|
|
737
|
+
const offer = this.getOffer(offerId, targetId);
|
|
719
738
|
if (!offer)
|
|
720
739
|
return true;
|
|
721
740
|
// Check status
|
|
@@ -728,17 +747,29 @@
|
|
|
728
747
|
return false;
|
|
729
748
|
}
|
|
730
749
|
/**
|
|
731
|
-
* Clear all offers
|
|
750
|
+
* Clear all offers for a specific player
|
|
751
|
+
*/
|
|
752
|
+
clear(targetId = this.client.getSelfId()) {
|
|
753
|
+
if (!targetId)
|
|
754
|
+
return;
|
|
755
|
+
this.offers.set(targetId, new Map());
|
|
756
|
+
this.logger.log('Cleared all offers for player:', targetId);
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Clear all offers for all players
|
|
732
760
|
*/
|
|
733
|
-
|
|
761
|
+
clearAll() {
|
|
734
762
|
this.offers.clear();
|
|
735
|
-
this.logger.log('Cleared all offers');
|
|
763
|
+
this.logger.log('Cleared all offers for all players');
|
|
736
764
|
}
|
|
737
765
|
/**
|
|
738
|
-
* Get offer count
|
|
766
|
+
* Get offer count (for self player)
|
|
739
767
|
*/
|
|
740
768
|
get size() {
|
|
741
|
-
|
|
769
|
+
const selfId = this.client.getSelfId();
|
|
770
|
+
if (!selfId)
|
|
771
|
+
return 0;
|
|
772
|
+
return this.offers.get(selfId)?.size || 0;
|
|
742
773
|
}
|
|
743
774
|
}
|
|
744
775
|
|
|
@@ -841,7 +872,9 @@
|
|
|
841
872
|
return null;
|
|
842
873
|
}
|
|
843
874
|
setCurrencyAssetContents(currencies) {
|
|
844
|
-
|
|
875
|
+
Object.keys(currencies).forEach((key) => {
|
|
876
|
+
this.currencies[key] = currencies[key];
|
|
877
|
+
});
|
|
845
878
|
}
|
|
846
879
|
resolveReward(reward) {
|
|
847
880
|
if (reward.kind === 'loyalty_currency' && reward.rewardId) {
|
|
@@ -897,6 +930,7 @@
|
|
|
897
930
|
constructor(config) {
|
|
898
931
|
this.isInitializing = false;
|
|
899
932
|
this.pendingStackedToken = null;
|
|
933
|
+
this.selfId = null;
|
|
900
934
|
this.config = {
|
|
901
935
|
autoConnect: config.autoConnect ?? false,
|
|
902
936
|
reconnect: config.reconnect ?? true,
|
|
@@ -909,7 +943,7 @@
|
|
|
909
943
|
this.hooks = this.config.hooks || {};
|
|
910
944
|
this.logger = createLogger(this.config, 'OfferwallClient');
|
|
911
945
|
this.eventEmitter = new EventEmitter(this.config);
|
|
912
|
-
this.offerStore = new OfferStore(this.config);
|
|
946
|
+
this.offerStore = new OfferStore(this.config, this);
|
|
913
947
|
this.tokenManager = new TokenManager(this.config);
|
|
914
948
|
this.assetHelper = new AssetHelper(this.config);
|
|
915
949
|
this.sseConnection = new SSEConnection(this.config, this.eventEmitter, this.tokenManager);
|
|
@@ -950,6 +984,9 @@
|
|
|
950
984
|
get assets() {
|
|
951
985
|
return this.assetHelper;
|
|
952
986
|
}
|
|
987
|
+
getSelfId() {
|
|
988
|
+
return this.selfId;
|
|
989
|
+
}
|
|
953
990
|
/**
|
|
954
991
|
* Initialize the offerwall client and connect
|
|
955
992
|
*/
|
|
@@ -1012,8 +1049,9 @@
|
|
|
1012
1049
|
if (this.sseConnection) {
|
|
1013
1050
|
this.sseConnection.disconnect();
|
|
1014
1051
|
}
|
|
1015
|
-
this.offerStore.
|
|
1052
|
+
this.offerStore.clearAll();
|
|
1016
1053
|
this.tokenManager.clearToken();
|
|
1054
|
+
this.selfId = null;
|
|
1017
1055
|
if (this.hooks.afterDisconnect) {
|
|
1018
1056
|
await this.hooks.afterDisconnect();
|
|
1019
1057
|
}
|
|
@@ -1021,8 +1059,8 @@
|
|
|
1021
1059
|
/**
|
|
1022
1060
|
* Claim rewards for an offer
|
|
1023
1061
|
*/
|
|
1024
|
-
async claimReward(instanceId) {
|
|
1025
|
-
const offer = this.offerStore.getOffer(instanceId);
|
|
1062
|
+
async claimReward(instanceId, targetId = this.getSelfId()) {
|
|
1063
|
+
const offer = this.offerStore.getOffer(instanceId, targetId);
|
|
1026
1064
|
if (!offer) {
|
|
1027
1065
|
throw new Error(`Offer ${instanceId} not found`);
|
|
1028
1066
|
}
|
|
@@ -1037,7 +1075,7 @@
|
|
|
1037
1075
|
}
|
|
1038
1076
|
}
|
|
1039
1077
|
try {
|
|
1040
|
-
const response = await this.claimOfferAPI(instanceId);
|
|
1078
|
+
const response = await this.claimOfferAPI(instanceId, targetId);
|
|
1041
1079
|
const updatedOffer = { ...offer, status: 'claimed' };
|
|
1042
1080
|
this.offerStore.upsertOffer(updatedOffer);
|
|
1043
1081
|
this.eventEmitter.emit(exports.OfferEvent.OFFER_CLAIMED, {
|
|
@@ -1093,7 +1131,7 @@
|
|
|
1093
1131
|
});
|
|
1094
1132
|
*/
|
|
1095
1133
|
this.eventEmitter.on(exports.OfferEvent.OFFER_SURFACED, ({ offer }) => {
|
|
1096
|
-
this.offerStore.upsertOffer(offer);
|
|
1134
|
+
this.offerStore.upsertOffer(offer); // should always be selfId
|
|
1097
1135
|
this.logger.log(`Surfaced offer: ${offer.instanceId}`);
|
|
1098
1136
|
});
|
|
1099
1137
|
}
|
|
@@ -1123,34 +1161,40 @@
|
|
|
1123
1161
|
}
|
|
1124
1162
|
return response.json();
|
|
1125
1163
|
}
|
|
1126
|
-
async claimOfferAPI(instanceId) {
|
|
1164
|
+
async claimOfferAPI(instanceId, targetId = null) {
|
|
1127
1165
|
return this.postWithAuth('/v1/client/reward/claim', {
|
|
1128
1166
|
instanceId,
|
|
1129
1167
|
kind: 'offer',
|
|
1168
|
+
targetId: targetId || undefined,
|
|
1130
1169
|
});
|
|
1131
1170
|
}
|
|
1132
|
-
getPlayer() {
|
|
1133
|
-
return this.offerStore.getPlayer();
|
|
1171
|
+
getPlayer(targetId = this.getSelfId()) {
|
|
1172
|
+
return this.offerStore.getPlayer(targetId);
|
|
1134
1173
|
}
|
|
1135
|
-
getOffers() {
|
|
1136
|
-
return this.offerStore.getAllOffers();
|
|
1174
|
+
getOffers(targetId = this.getSelfId()) {
|
|
1175
|
+
return this.offerStore.getAllOffers(targetId);
|
|
1137
1176
|
}
|
|
1138
|
-
async refreshOffersAndPlayer() {
|
|
1177
|
+
async refreshOffersAndPlayer(targetId = null) {
|
|
1139
1178
|
try {
|
|
1140
|
-
const { offers, player } = await this.getOffersAndPlayer();
|
|
1141
|
-
|
|
1179
|
+
const { offers, player } = await this.getOffersAndPlayer(targetId);
|
|
1180
|
+
if (targetId == null) {
|
|
1181
|
+
this.selfId = player.gameData.playerId;
|
|
1182
|
+
}
|
|
1142
1183
|
this.offerStore.setPlayer(player);
|
|
1184
|
+
this.offerStore.setOffers(offers, player);
|
|
1143
1185
|
this.eventEmitter.emit(exports.OfferEvent.REFRESH, { offers, player: player });
|
|
1144
1186
|
this.logger.log('Refreshed offers and player snapshot');
|
|
1187
|
+
return offers;
|
|
1145
1188
|
}
|
|
1146
1189
|
catch (error) {
|
|
1147
1190
|
this.handleError(error, 'refreshOffersAndPlayer');
|
|
1148
1191
|
throw error;
|
|
1149
1192
|
}
|
|
1150
1193
|
}
|
|
1151
|
-
async getOffersAndPlayer() {
|
|
1194
|
+
async getOffersAndPlayer(targetId = null) {
|
|
1152
1195
|
const data = await this.postWithAuth('/v1/client/player/campaigns', {
|
|
1153
1196
|
viewingCampaigns: true,
|
|
1197
|
+
targetId: targetId || undefined,
|
|
1154
1198
|
});
|
|
1155
1199
|
if (!data.offers || !Array.isArray(data.offers)) {
|
|
1156
1200
|
throw new Error('No offers returned from offers endpoint');
|
|
@@ -1283,6 +1327,8 @@
|
|
|
1283
1327
|
}
|
|
1284
1328
|
}
|
|
1285
1329
|
|
|
1330
|
+
const DEFAULT_ENTITY_KIND = '_default';
|
|
1331
|
+
|
|
1286
1332
|
const keyPattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
1287
1333
|
/**
|
|
1288
1334
|
* This replaces {keyName} keys from the template with corresponding values from the dynamic object.
|
|
@@ -1320,11 +1366,25 @@
|
|
|
1320
1366
|
}));
|
|
1321
1367
|
}
|
|
1322
1368
|
|
|
1369
|
+
const dynamicTrackerToPrimitive = (dynaTrack) => {
|
|
1370
|
+
const primitive = {};
|
|
1371
|
+
for (const key in dynaTrack) {
|
|
1372
|
+
primitive[key] = dynaTrack[key].value || 0;
|
|
1373
|
+
}
|
|
1374
|
+
return primitive;
|
|
1375
|
+
};
|
|
1376
|
+
|
|
1377
|
+
const addressNetworkId = (contractAddress, network) => {
|
|
1378
|
+
return `${contractAddress.toLowerCase()}:${network.toUpperCase()}`;
|
|
1379
|
+
};
|
|
1380
|
+
|
|
1323
1381
|
const meetsBaseConditions = ({ conditions, playerSnap, addDetails,
|
|
1324
1382
|
/** this exists if calling meetsBaseConditions from meetsCompletionConditions. but surfacing
|
|
1325
1383
|
* check doesn't use this since we don't have a playerOffer at surfacing time
|
|
1326
1384
|
*/
|
|
1327
|
-
playerOffer,
|
|
1385
|
+
playerOffer,
|
|
1386
|
+
/** Additional data like fetched token balances that isn't part of playerSnap */
|
|
1387
|
+
additionalData, }) => {
|
|
1328
1388
|
const conditionData = [];
|
|
1329
1389
|
let isValid = true;
|
|
1330
1390
|
if (conditions?.minDaysInGame) {
|
|
@@ -1652,7 +1712,8 @@
|
|
|
1652
1712
|
// Validate link count conditions
|
|
1653
1713
|
if (conditions?.links && 'entityLinks' in playerSnap) {
|
|
1654
1714
|
for (const [linkType, constraint] of Object.entries(conditions.links)) {
|
|
1655
|
-
|
|
1715
|
+
// linkType should always exist. and be default is none was specified
|
|
1716
|
+
const linkCount = playerSnap.entityLinks?.filter((link) => (link.kind || DEFAULT_ENTITY_KIND) === linkType).length || 0;
|
|
1656
1717
|
if (constraint.min !== undefined) {
|
|
1657
1718
|
const isDisqualify = linkCount < constraint.min;
|
|
1658
1719
|
if (addDetails) {
|
|
@@ -1739,16 +1800,60 @@
|
|
|
1739
1800
|
return { isValid: false };
|
|
1740
1801
|
}
|
|
1741
1802
|
}
|
|
1803
|
+
// Evaluate token balance conditions
|
|
1804
|
+
for (const tokenCond of conditions?.tokenBalances || []) {
|
|
1805
|
+
const contracts = tokenCond.contracts || [];
|
|
1806
|
+
let totalBalance = 0;
|
|
1807
|
+
const fetchedBalances = aggregateTokenBalances(additionalData);
|
|
1808
|
+
for (const contract of contracts) {
|
|
1809
|
+
const balanceKey = addressNetworkId(contract.contractAddress, contract.network);
|
|
1810
|
+
totalBalance += fetchedBalances[balanceKey] || 0;
|
|
1811
|
+
}
|
|
1812
|
+
if (tokenCond.min !== undefined) {
|
|
1813
|
+
const isDisqualify = totalBalance < tokenCond.min;
|
|
1814
|
+
if (addDetails) {
|
|
1815
|
+
conditionData.push({
|
|
1816
|
+
isMet: !isDisqualify,
|
|
1817
|
+
kind: 'tokenBalances',
|
|
1818
|
+
trackerAmount: totalBalance,
|
|
1819
|
+
text: `Have at least ${tokenCond.min} ${tokenCond.name || 'tokens'}`,
|
|
1820
|
+
});
|
|
1821
|
+
if (isDisqualify)
|
|
1822
|
+
isValid = false;
|
|
1823
|
+
}
|
|
1824
|
+
else {
|
|
1825
|
+
if (isDisqualify)
|
|
1826
|
+
return { isValid: false };
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
if (tokenCond.max !== undefined) {
|
|
1830
|
+
const isDisqualify = totalBalance > tokenCond.max;
|
|
1831
|
+
if (addDetails) {
|
|
1832
|
+
conditionData.push({
|
|
1833
|
+
isMet: !isDisqualify,
|
|
1834
|
+
kind: 'tokenBalances',
|
|
1835
|
+
trackerAmount: totalBalance,
|
|
1836
|
+
text: `Have at most ${tokenCond.max} ${tokenCond.name || 'tokens'}`,
|
|
1837
|
+
});
|
|
1838
|
+
if (isDisqualify)
|
|
1839
|
+
isValid = false;
|
|
1840
|
+
}
|
|
1841
|
+
else {
|
|
1842
|
+
if (isDisqualify)
|
|
1843
|
+
return { isValid: false };
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1742
1847
|
return { isValid, conditionData: addDetails ? conditionData : undefined };
|
|
1743
1848
|
};
|
|
1744
|
-
const meetsSurfacingConditions = ({ surfacingConditions, playerSnap, context, playerOffers, }) => {
|
|
1849
|
+
const meetsSurfacingConditions = ({ surfacingConditions, playerSnap, context, playerOffers, additionalData, }) => {
|
|
1745
1850
|
if (surfacingConditions?.contexts?.length &&
|
|
1746
1851
|
!surfacingConditions.contexts?.includes(context || '')) {
|
|
1747
1852
|
// context is not in the list of surfacing contexts, so we don't want to surface this offer
|
|
1748
1853
|
return { isValid: false };
|
|
1749
1854
|
}
|
|
1750
1855
|
if (surfacingConditions?.targetEntityTypes?.length) {
|
|
1751
|
-
const playerTarget = playerSnap.entityKind ||
|
|
1856
|
+
const playerTarget = playerSnap.entityKind || DEFAULT_ENTITY_KIND;
|
|
1752
1857
|
// check if entity type is allowed
|
|
1753
1858
|
if (!surfacingConditions.targetEntityTypes.includes(playerTarget)) {
|
|
1754
1859
|
return { isValid: false };
|
|
@@ -1829,7 +1934,7 @@
|
|
|
1829
1934
|
}
|
|
1830
1935
|
}
|
|
1831
1936
|
}
|
|
1832
|
-
return meetsBaseConditions({ conditions, playerSnap });
|
|
1937
|
+
return meetsBaseConditions({ conditions, playerSnap, additionalData });
|
|
1833
1938
|
};
|
|
1834
1939
|
const hasConditions = (conditions) => {
|
|
1835
1940
|
if (!conditions)
|
|
@@ -1887,6 +1992,8 @@
|
|
|
1887
1992
|
return true;
|
|
1888
1993
|
if (surCond.networkRestrictions?.length)
|
|
1889
1994
|
return true;
|
|
1995
|
+
if (surCond.linkedEntityOffers?.offer_id)
|
|
1996
|
+
return true;
|
|
1890
1997
|
const compCond = conditions;
|
|
1891
1998
|
if (compCond.context)
|
|
1892
1999
|
return true;
|
|
@@ -1906,23 +2013,47 @@
|
|
|
1906
2013
|
return true;
|
|
1907
2014
|
if (compCond.dynamicTracker?.conditions?.length)
|
|
1908
2015
|
return true;
|
|
2016
|
+
if (conditions.tokenBalances?.length)
|
|
2017
|
+
return true;
|
|
2018
|
+
if (Object.keys(compCond.contractInteractions || {}).length > 0)
|
|
2019
|
+
return true;
|
|
1909
2020
|
return false;
|
|
1910
2021
|
};
|
|
1911
|
-
const
|
|
2022
|
+
const meetsLinkedEntityOffersCondition = ({ linkedEntityOffers, matchingLinks, linkedPOfferMap, }) => {
|
|
2023
|
+
if (!linkedPOfferMap)
|
|
2024
|
+
return { isValid: false };
|
|
2025
|
+
const linkedPlayerOffer_ids = [];
|
|
2026
|
+
for (const link of matchingLinks) {
|
|
2027
|
+
const key = `${link.playerId}:${linkedEntityOffers.offer_id}`;
|
|
2028
|
+
const po = linkedPOfferMap.get(key);
|
|
2029
|
+
if (po) {
|
|
2030
|
+
linkedPlayerOffer_ids.push(po._id.toString());
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
if (linkedPlayerOffer_ids.length > 0) {
|
|
2034
|
+
return { isValid: true, linkedPlayerOffer_ids };
|
|
2035
|
+
}
|
|
2036
|
+
return { isValid: false };
|
|
2037
|
+
};
|
|
2038
|
+
const offerMeetsCompletionConditions = (offer, snapshot, additionalData) => {
|
|
1912
2039
|
return meetsCompletionConditions({
|
|
1913
2040
|
completionConditions: offer.completionConditions || {},
|
|
1914
2041
|
completionTrackers: offer.completionTrackers,
|
|
1915
2042
|
playerSnap: snapshot,
|
|
1916
2043
|
playerOffer: offer,
|
|
1917
2044
|
addDetails: true,
|
|
2045
|
+
maxClaimCount: offer.maxClaimCount,
|
|
2046
|
+
additionalData,
|
|
1918
2047
|
});
|
|
1919
2048
|
};
|
|
1920
|
-
const meetsCompletionConditions = ({ completionConditions, completionTrackers, playerSnap, playerOffer, addDetails = false, maxClaimCount, }) => {
|
|
2049
|
+
const meetsCompletionConditions = ({ completionConditions, completionTrackers, playerSnap, playerOffer, addDetails = false, maxClaimCount, additionalData, }) => {
|
|
1921
2050
|
if (completionConditions) {
|
|
1922
2051
|
const conditions = completionConditions;
|
|
1923
2052
|
// For multi-claim offers, scale cumulative requirements by (claimedCount + 1)
|
|
1924
2053
|
const shouldScale = maxClaimCount === -1 || (maxClaimCount && maxClaimCount > 1);
|
|
1925
|
-
const claimMultiplier = shouldScale
|
|
2054
|
+
const claimMultiplier = shouldScale
|
|
2055
|
+
? (playerOffer.trackers?.claimedCount || 0) + 1
|
|
2056
|
+
: 1;
|
|
1926
2057
|
const conditionData = [];
|
|
1927
2058
|
let isValid = true;
|
|
1928
2059
|
let maxTotalClaimsFromScaling = Infinity;
|
|
@@ -2053,12 +2184,14 @@
|
|
|
2053
2184
|
}
|
|
2054
2185
|
}
|
|
2055
2186
|
if (conditions?.social) {
|
|
2056
|
-
const
|
|
2187
|
+
const tSocialAccumulate = completionTrackers?.social;
|
|
2188
|
+
const tSocialAttach = completionTrackers?.social;
|
|
2057
2189
|
const cSocial = completionConditions.social;
|
|
2058
2190
|
const mode = cSocial?.mode || 'attach';
|
|
2191
|
+
const tSocial = mode === 'accumulate' ? tSocialAccumulate : tSocialAttach;
|
|
2059
2192
|
const hasContent = Boolean(mode === 'accumulate'
|
|
2060
|
-
?
|
|
2061
|
-
:
|
|
2193
|
+
? tSocialAccumulate?.matchCount > 0
|
|
2194
|
+
: tSocialAttach?.videoId);
|
|
2062
2195
|
// Only scale social metrics in accumulate mode (attach mode is single content)
|
|
2063
2196
|
const socialMultiplier = mode === 'accumulate' ? claimMultiplier : 1;
|
|
2064
2197
|
const minLikes = (cSocial?.minLikes || 0) * socialMultiplier;
|
|
@@ -2093,7 +2226,7 @@
|
|
|
2093
2226
|
.join(' | ');
|
|
2094
2227
|
const requiredWords = cSocial?.requiredWords ?? [];
|
|
2095
2228
|
if (mode === 'accumulate') {
|
|
2096
|
-
const matchCount =
|
|
2229
|
+
const matchCount = tSocialAccumulate?.matchCount || 0;
|
|
2097
2230
|
conditionData.push({
|
|
2098
2231
|
isMet: hasContent,
|
|
2099
2232
|
kind: 'social',
|
|
@@ -2106,7 +2239,7 @@
|
|
|
2106
2239
|
});
|
|
2107
2240
|
}
|
|
2108
2241
|
else {
|
|
2109
|
-
const title =
|
|
2242
|
+
const title = tSocialAttach?.title;
|
|
2110
2243
|
conditionData.push({
|
|
2111
2244
|
isMet: hasContent,
|
|
2112
2245
|
kind: 'social',
|
|
@@ -2183,22 +2316,22 @@
|
|
|
2183
2316
|
if (conditions?.dynamicTracker?.conditions?.length) {
|
|
2184
2317
|
const resolvedConditions = replaceDynamicConditionKeys(conditions.dynamicTracker.conditions, playerOffer?.trackers || {});
|
|
2185
2318
|
// now we have the game-defined conditions with {{}} keys populated. feed these conditions into evaluator
|
|
2186
|
-
const dynamicResult = meetsDynamicConditions(completionTrackers?.dynamicTracker, {
|
|
2319
|
+
const dynamicResult = meetsDynamicConditions(dynamicTrackerToPrimitive(completionTrackers?.dynamicTracker || {}), {
|
|
2187
2320
|
...conditions.dynamicTracker,
|
|
2188
2321
|
conditions: resolvedConditions,
|
|
2189
2322
|
}, claimMultiplier);
|
|
2190
2323
|
if (shouldScale) {
|
|
2191
|
-
const dynamicMax = getMaxClaimsForDynamicGroup(completionTrackers?.dynamicTracker || {}, {
|
|
2324
|
+
const dynamicMax = getMaxClaimsForDynamicGroup(dynamicTrackerToPrimitive(completionTrackers?.dynamicTracker || {}), {
|
|
2192
2325
|
...conditions.dynamicTracker,
|
|
2193
2326
|
conditions: resolvedConditions,
|
|
2194
|
-
}, playerOffer
|
|
2327
|
+
}, playerOffer?.trackers?.claimedCount || 0);
|
|
2195
2328
|
updateMax(dynamicMax);
|
|
2196
2329
|
}
|
|
2197
2330
|
if (addDetails) {
|
|
2198
2331
|
conditionData.push({
|
|
2199
2332
|
isMet: dynamicResult,
|
|
2200
|
-
kind: '
|
|
2201
|
-
text: renderTemplate(conditions.dynamicTracker.template, completionTrackers?.dynamicTracker) || 'Dynamic conditions',
|
|
2333
|
+
kind: 'dynamicTracker',
|
|
2334
|
+
text: renderTemplate(conditions.dynamicTracker.template, dynamicTrackerToPrimitive(completionTrackers?.dynamicTracker || {})) || 'Dynamic conditions',
|
|
2202
2335
|
});
|
|
2203
2336
|
if (!dynamicResult)
|
|
2204
2337
|
isValid = false;
|
|
@@ -2208,19 +2341,64 @@
|
|
|
2208
2341
|
return { isValid: false, availableClaimsNow: 0 };
|
|
2209
2342
|
}
|
|
2210
2343
|
}
|
|
2344
|
+
// Evaluate contractInteractions completion trackers
|
|
2345
|
+
if (conditions?.contractInteractions) {
|
|
2346
|
+
for (const [conditionId, condition] of Object.entries(conditions.contractInteractions)) {
|
|
2347
|
+
const baseAmount = condition.amount || 0;
|
|
2348
|
+
const scaledAmount = baseAmount * claimMultiplier;
|
|
2349
|
+
const trackerValue = completionTrackers?.contractInteractions?.[conditionId] || 0;
|
|
2350
|
+
const isDisqualify = trackerValue < scaledAmount;
|
|
2351
|
+
if (shouldScale && baseAmount > 0) {
|
|
2352
|
+
updateMax(Math.floor(trackerValue / baseAmount));
|
|
2353
|
+
}
|
|
2354
|
+
if (addDetails) {
|
|
2355
|
+
let displayText;
|
|
2356
|
+
const eventType = condition.event;
|
|
2357
|
+
const name = condition.name || 'tokens';
|
|
2358
|
+
if (eventType === 'spend') {
|
|
2359
|
+
displayText = `Spend ${scaledAmount} ${name}`;
|
|
2360
|
+
}
|
|
2361
|
+
else if (eventType === 'earn') {
|
|
2362
|
+
displayText = `Earn ${scaledAmount} ${name}`;
|
|
2363
|
+
}
|
|
2364
|
+
else if (eventType === 'gain') {
|
|
2365
|
+
displayText = `Gain ${scaledAmount} ${name}`;
|
|
2366
|
+
}
|
|
2367
|
+
else if (eventType === 'lose') {
|
|
2368
|
+
displayText = `Lose ${scaledAmount} ${name}`;
|
|
2369
|
+
}
|
|
2370
|
+
else {
|
|
2371
|
+
displayText = `${name}: ${scaledAmount}`;
|
|
2372
|
+
}
|
|
2373
|
+
conditionData.push({
|
|
2374
|
+
isMet: !isDisqualify,
|
|
2375
|
+
kind: 'contractInteractions',
|
|
2376
|
+
trackerAmount: trackerValue,
|
|
2377
|
+
text: displayText,
|
|
2378
|
+
});
|
|
2379
|
+
if (isDisqualify)
|
|
2380
|
+
isValid = false;
|
|
2381
|
+
}
|
|
2382
|
+
else {
|
|
2383
|
+
if (isDisqualify)
|
|
2384
|
+
return { isValid: false, availableClaimsNow: 0 };
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2211
2388
|
const r = meetsBaseConditions({
|
|
2212
2389
|
conditions,
|
|
2213
2390
|
playerSnap,
|
|
2214
2391
|
addDetails: true,
|
|
2215
2392
|
playerOffer,
|
|
2393
|
+
additionalData,
|
|
2216
2394
|
});
|
|
2217
2395
|
isValid = isValid && r.isValid;
|
|
2218
2396
|
conditionData.push(...(r.conditionData || []));
|
|
2219
2397
|
if (maxClaimCount && maxClaimCount > 0) {
|
|
2220
2398
|
updateMax(maxClaimCount);
|
|
2221
2399
|
}
|
|
2222
|
-
const claimedCount = playerOffer
|
|
2223
|
-
|
|
2400
|
+
const claimedCount = playerOffer?.trackers?.claimedCount || 0;
|
|
2401
|
+
const availableClaimsNow = !isValid
|
|
2224
2402
|
? 0
|
|
2225
2403
|
: maxTotalClaimsFromScaling === Infinity
|
|
2226
2404
|
? -1
|
|
@@ -2571,6 +2749,19 @@
|
|
|
2571
2749
|
}
|
|
2572
2750
|
return { isValid: true };
|
|
2573
2751
|
}
|
|
2752
|
+
// returns contractAddress:network -> balance
|
|
2753
|
+
function aggregateTokenBalances(data) {
|
|
2754
|
+
const aggregatedBalances = {};
|
|
2755
|
+
for (const { balances } of data?.cryptoWallets || []) {
|
|
2756
|
+
for (const [key, balance] of Object.entries(balances)) {
|
|
2757
|
+
if (!aggregatedBalances[key]) {
|
|
2758
|
+
aggregatedBalances[key] = 0;
|
|
2759
|
+
}
|
|
2760
|
+
aggregatedBalances[key] += balance;
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
return aggregatedBalances;
|
|
2764
|
+
}
|
|
2574
2765
|
|
|
2575
2766
|
const offerListenerEvents = ['claim_offer'];
|
|
2576
2767
|
const PlayerOfferStatuses = [
|
|
@@ -2619,11 +2810,13 @@
|
|
|
2619
2810
|
};
|
|
2620
2811
|
|
|
2621
2812
|
exports.AssetHelper = AssetHelper;
|
|
2813
|
+
exports.DEFAULT_ENTITY_KIND = DEFAULT_ENTITY_KIND;
|
|
2622
2814
|
exports.EventEmitter = EventEmitter;
|
|
2623
2815
|
exports.OfferStore = OfferStore;
|
|
2624
2816
|
exports.OfferwallClient = OfferwallClient;
|
|
2625
2817
|
exports.PlayerOfferStatuses = PlayerOfferStatuses;
|
|
2626
2818
|
exports.SSEConnection = SSEConnection;
|
|
2819
|
+
exports.aggregateTokenBalances = aggregateTokenBalances;
|
|
2627
2820
|
exports.getMaxClaimsForDynamicCondition = getMaxClaimsForDynamicCondition;
|
|
2628
2821
|
exports.getMaxClaimsForDynamicGroup = getMaxClaimsForDynamicGroup;
|
|
2629
2822
|
exports.hasConditions = hasConditions;
|
|
@@ -2632,6 +2825,7 @@
|
|
|
2632
2825
|
exports.meetsCompletionConditions = meetsCompletionConditions;
|
|
2633
2826
|
exports.meetsCompletionConditionsBeforeExpiry = meetsCompletionConditionsBeforeExpiry;
|
|
2634
2827
|
exports.meetsDynamicConditions = meetsDynamicConditions;
|
|
2828
|
+
exports.meetsLinkedEntityOffersCondition = meetsLinkedEntityOffersCondition;
|
|
2635
2829
|
exports.meetsSurfacingConditions = meetsSurfacingConditions;
|
|
2636
2830
|
exports.offerListenerEvents = offerListenerEvents;
|
|
2637
2831
|
exports.offerMeetsCompletionConditions = offerMeetsCompletionConditions;
|