@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
package/dist/index.js
CHANGED
|
@@ -630,25 +630,33 @@ class SSEConnection {
|
|
|
630
630
|
}
|
|
631
631
|
|
|
632
632
|
class OfferStore {
|
|
633
|
-
constructor(config) {
|
|
633
|
+
constructor(config, client) {
|
|
634
|
+
this.client = client;
|
|
634
635
|
this.offers = new Map();
|
|
635
|
-
this.
|
|
636
|
+
this.players = new Map();
|
|
636
637
|
this.logger = createLogger(config, 'OfferStore');
|
|
637
638
|
}
|
|
638
|
-
getPlayer() {
|
|
639
|
-
|
|
639
|
+
getPlayer(targetId = this.client.getSelfId()) {
|
|
640
|
+
if (!targetId)
|
|
641
|
+
return null;
|
|
642
|
+
return this.players.get(targetId) || null;
|
|
640
643
|
}
|
|
641
644
|
setPlayer(player) {
|
|
642
|
-
this.player
|
|
645
|
+
this.players.set(player.gameData.playerId, player);
|
|
643
646
|
this.logger.log('Updated player:', player);
|
|
644
647
|
}
|
|
645
648
|
/**
|
|
646
649
|
* Set all offers (replaces existing)
|
|
647
650
|
*/
|
|
648
|
-
setOffers(offers) {
|
|
649
|
-
this.
|
|
651
|
+
setOffers(offers, target) {
|
|
652
|
+
const targetPlayer = target || this.getPlayer();
|
|
653
|
+
if (!targetPlayer) {
|
|
654
|
+
this.logger.warn('No target player to set offers for');
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
this.offers.set(targetPlayer.gameData.playerId, new Map());
|
|
650
658
|
offers.forEach((offer) => {
|
|
651
|
-
this.offers.set(offer.instanceId, offer);
|
|
659
|
+
this.offers.get(targetPlayer.gameData.playerId).set(offer.instanceId, offer);
|
|
652
660
|
});
|
|
653
661
|
this.logger.log(`Set ${offers.length} offers`);
|
|
654
662
|
}
|
|
@@ -656,16 +664,23 @@ class OfferStore {
|
|
|
656
664
|
* Add or update a single offer
|
|
657
665
|
*/
|
|
658
666
|
upsertOffer(offer) {
|
|
659
|
-
|
|
660
|
-
|
|
667
|
+
let playerOffers = this.offers.get(offer.playerId);
|
|
668
|
+
if (!playerOffers) {
|
|
669
|
+
playerOffers = new Map();
|
|
670
|
+
this.offers.set(offer.playerId, playerOffers);
|
|
671
|
+
}
|
|
672
|
+
const previousOffer = playerOffers.get(offer.instanceId);
|
|
673
|
+
playerOffers.set(offer.instanceId, offer);
|
|
661
674
|
this.logger.log(`${previousOffer ? 'Updated' : 'Added'} offer:`, offer.instanceId);
|
|
662
675
|
return previousOffer;
|
|
663
676
|
}
|
|
664
677
|
/**
|
|
665
678
|
* Remove an offer
|
|
666
679
|
*/
|
|
667
|
-
removeOffer(offerId) {
|
|
668
|
-
|
|
680
|
+
removeOffer(offerId, targetId = this.client.getSelfId()) {
|
|
681
|
+
if (!targetId)
|
|
682
|
+
return false;
|
|
683
|
+
const removed = this.offers.get(targetId)?.delete(offerId) || false;
|
|
669
684
|
if (removed) {
|
|
670
685
|
this.logger.log(`Removed offer:`, offerId);
|
|
671
686
|
}
|
|
@@ -674,26 +689,30 @@ class OfferStore {
|
|
|
674
689
|
/**
|
|
675
690
|
* Get a single offer
|
|
676
691
|
*/
|
|
677
|
-
getOffer(offerId) {
|
|
678
|
-
|
|
692
|
+
getOffer(offerId, targetId = this.client.getSelfId()) {
|
|
693
|
+
if (!targetId)
|
|
694
|
+
return undefined;
|
|
695
|
+
return this.offers.get(targetId)?.get(offerId);
|
|
679
696
|
}
|
|
680
697
|
/**
|
|
681
698
|
* Get all offers
|
|
682
699
|
*/
|
|
683
|
-
getAllOffers() {
|
|
684
|
-
|
|
700
|
+
getAllOffers(targetId = this.client.getSelfId()) {
|
|
701
|
+
if (!targetId)
|
|
702
|
+
return [];
|
|
703
|
+
return Array.from(this.offers.get(targetId)?.values() || []);
|
|
685
704
|
}
|
|
686
705
|
/**
|
|
687
706
|
* Get offers filtered by status
|
|
688
707
|
*/
|
|
689
|
-
getOffersByStatus(status) {
|
|
690
|
-
return this.getAllOffers().filter((offer) => offer.status === status);
|
|
708
|
+
getOffersByStatus(status, targetId = this.client.getSelfId()) {
|
|
709
|
+
return this.getAllOffers(targetId).filter((offer) => offer.status === status);
|
|
691
710
|
}
|
|
692
711
|
/**
|
|
693
712
|
* Get active offers (not expired, not claimed)
|
|
694
713
|
*/
|
|
695
|
-
getActiveOffers() {
|
|
696
|
-
return this.getAllOffers().filter((offer) => {
|
|
714
|
+
getActiveOffers(targetId = this.client.getSelfId()) {
|
|
715
|
+
return this.getAllOffers(targetId).filter((offer) => {
|
|
697
716
|
if (!offer.status)
|
|
698
717
|
return false; // Must have a status
|
|
699
718
|
return (offer.status === 'surfaced' ||
|
|
@@ -704,14 +723,14 @@ class OfferStore {
|
|
|
704
723
|
/**
|
|
705
724
|
* Get claimable offers
|
|
706
725
|
*/
|
|
707
|
-
getClaimableOffers() {
|
|
708
|
-
return this.getOffersByStatus('claimable');
|
|
726
|
+
getClaimableOffers(targetId = this.client.getSelfId()) {
|
|
727
|
+
return this.getOffersByStatus('claimable', targetId);
|
|
709
728
|
}
|
|
710
729
|
/**
|
|
711
730
|
* Check if an offer has expired
|
|
712
731
|
*/
|
|
713
|
-
isOfferExpired(offerId) {
|
|
714
|
-
const offer = this.getOffer(offerId);
|
|
732
|
+
isOfferExpired(offerId, targetId = this.client.getSelfId()) {
|
|
733
|
+
const offer = this.getOffer(offerId, targetId);
|
|
715
734
|
if (!offer)
|
|
716
735
|
return true;
|
|
717
736
|
// Check status
|
|
@@ -724,17 +743,29 @@ class OfferStore {
|
|
|
724
743
|
return false;
|
|
725
744
|
}
|
|
726
745
|
/**
|
|
727
|
-
* Clear all offers
|
|
746
|
+
* Clear all offers for a specific player
|
|
747
|
+
*/
|
|
748
|
+
clear(targetId = this.client.getSelfId()) {
|
|
749
|
+
if (!targetId)
|
|
750
|
+
return;
|
|
751
|
+
this.offers.set(targetId, new Map());
|
|
752
|
+
this.logger.log('Cleared all offers for player:', targetId);
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Clear all offers for all players
|
|
728
756
|
*/
|
|
729
|
-
|
|
757
|
+
clearAll() {
|
|
730
758
|
this.offers.clear();
|
|
731
|
-
this.logger.log('Cleared all offers');
|
|
759
|
+
this.logger.log('Cleared all offers for all players');
|
|
732
760
|
}
|
|
733
761
|
/**
|
|
734
|
-
* Get offer count
|
|
762
|
+
* Get offer count (for self player)
|
|
735
763
|
*/
|
|
736
764
|
get size() {
|
|
737
|
-
|
|
765
|
+
const selfId = this.client.getSelfId();
|
|
766
|
+
if (!selfId)
|
|
767
|
+
return 0;
|
|
768
|
+
return this.offers.get(selfId)?.size || 0;
|
|
738
769
|
}
|
|
739
770
|
}
|
|
740
771
|
|
|
@@ -837,7 +868,9 @@ class AssetHelper {
|
|
|
837
868
|
return null;
|
|
838
869
|
}
|
|
839
870
|
setCurrencyAssetContents(currencies) {
|
|
840
|
-
|
|
871
|
+
Object.keys(currencies).forEach((key) => {
|
|
872
|
+
this.currencies[key] = currencies[key];
|
|
873
|
+
});
|
|
841
874
|
}
|
|
842
875
|
resolveReward(reward) {
|
|
843
876
|
if (reward.kind === 'loyalty_currency' && reward.rewardId) {
|
|
@@ -893,6 +926,7 @@ class OfferwallClient {
|
|
|
893
926
|
constructor(config) {
|
|
894
927
|
this.isInitializing = false;
|
|
895
928
|
this.pendingStackedToken = null;
|
|
929
|
+
this.selfId = null;
|
|
896
930
|
this.config = {
|
|
897
931
|
autoConnect: config.autoConnect ?? false,
|
|
898
932
|
reconnect: config.reconnect ?? true,
|
|
@@ -905,7 +939,7 @@ class OfferwallClient {
|
|
|
905
939
|
this.hooks = this.config.hooks || {};
|
|
906
940
|
this.logger = createLogger(this.config, 'OfferwallClient');
|
|
907
941
|
this.eventEmitter = new EventEmitter(this.config);
|
|
908
|
-
this.offerStore = new OfferStore(this.config);
|
|
942
|
+
this.offerStore = new OfferStore(this.config, this);
|
|
909
943
|
this.tokenManager = new TokenManager(this.config);
|
|
910
944
|
this.assetHelper = new AssetHelper(this.config);
|
|
911
945
|
this.sseConnection = new SSEConnection(this.config, this.eventEmitter, this.tokenManager);
|
|
@@ -946,6 +980,9 @@ class OfferwallClient {
|
|
|
946
980
|
get assets() {
|
|
947
981
|
return this.assetHelper;
|
|
948
982
|
}
|
|
983
|
+
getSelfId() {
|
|
984
|
+
return this.selfId;
|
|
985
|
+
}
|
|
949
986
|
/**
|
|
950
987
|
* Initialize the offerwall client and connect
|
|
951
988
|
*/
|
|
@@ -1008,8 +1045,9 @@ class OfferwallClient {
|
|
|
1008
1045
|
if (this.sseConnection) {
|
|
1009
1046
|
this.sseConnection.disconnect();
|
|
1010
1047
|
}
|
|
1011
|
-
this.offerStore.
|
|
1048
|
+
this.offerStore.clearAll();
|
|
1012
1049
|
this.tokenManager.clearToken();
|
|
1050
|
+
this.selfId = null;
|
|
1013
1051
|
if (this.hooks.afterDisconnect) {
|
|
1014
1052
|
await this.hooks.afterDisconnect();
|
|
1015
1053
|
}
|
|
@@ -1017,8 +1055,8 @@ class OfferwallClient {
|
|
|
1017
1055
|
/**
|
|
1018
1056
|
* Claim rewards for an offer
|
|
1019
1057
|
*/
|
|
1020
|
-
async claimReward(instanceId) {
|
|
1021
|
-
const offer = this.offerStore.getOffer(instanceId);
|
|
1058
|
+
async claimReward(instanceId, targetId = this.getSelfId()) {
|
|
1059
|
+
const offer = this.offerStore.getOffer(instanceId, targetId);
|
|
1022
1060
|
if (!offer) {
|
|
1023
1061
|
throw new Error(`Offer ${instanceId} not found`);
|
|
1024
1062
|
}
|
|
@@ -1033,7 +1071,7 @@ class OfferwallClient {
|
|
|
1033
1071
|
}
|
|
1034
1072
|
}
|
|
1035
1073
|
try {
|
|
1036
|
-
const response = await this.claimOfferAPI(instanceId);
|
|
1074
|
+
const response = await this.claimOfferAPI(instanceId, targetId);
|
|
1037
1075
|
const updatedOffer = { ...offer, status: 'claimed' };
|
|
1038
1076
|
this.offerStore.upsertOffer(updatedOffer);
|
|
1039
1077
|
this.eventEmitter.emit(exports.OfferEvent.OFFER_CLAIMED, {
|
|
@@ -1089,7 +1127,7 @@ class OfferwallClient {
|
|
|
1089
1127
|
});
|
|
1090
1128
|
*/
|
|
1091
1129
|
this.eventEmitter.on(exports.OfferEvent.OFFER_SURFACED, ({ offer }) => {
|
|
1092
|
-
this.offerStore.upsertOffer(offer);
|
|
1130
|
+
this.offerStore.upsertOffer(offer); // should always be selfId
|
|
1093
1131
|
this.logger.log(`Surfaced offer: ${offer.instanceId}`);
|
|
1094
1132
|
});
|
|
1095
1133
|
}
|
|
@@ -1119,34 +1157,40 @@ class OfferwallClient {
|
|
|
1119
1157
|
}
|
|
1120
1158
|
return response.json();
|
|
1121
1159
|
}
|
|
1122
|
-
async claimOfferAPI(instanceId) {
|
|
1160
|
+
async claimOfferAPI(instanceId, targetId = null) {
|
|
1123
1161
|
return this.postWithAuth('/v1/client/reward/claim', {
|
|
1124
1162
|
instanceId,
|
|
1125
1163
|
kind: 'offer',
|
|
1164
|
+
targetId: targetId || undefined,
|
|
1126
1165
|
});
|
|
1127
1166
|
}
|
|
1128
|
-
getPlayer() {
|
|
1129
|
-
return this.offerStore.getPlayer();
|
|
1167
|
+
getPlayer(targetId = this.getSelfId()) {
|
|
1168
|
+
return this.offerStore.getPlayer(targetId);
|
|
1130
1169
|
}
|
|
1131
|
-
getOffers() {
|
|
1132
|
-
return this.offerStore.getAllOffers();
|
|
1170
|
+
getOffers(targetId = this.getSelfId()) {
|
|
1171
|
+
return this.offerStore.getAllOffers(targetId);
|
|
1133
1172
|
}
|
|
1134
|
-
async refreshOffersAndPlayer() {
|
|
1173
|
+
async refreshOffersAndPlayer(targetId = null) {
|
|
1135
1174
|
try {
|
|
1136
|
-
const { offers, player } = await this.getOffersAndPlayer();
|
|
1137
|
-
|
|
1175
|
+
const { offers, player } = await this.getOffersAndPlayer(targetId);
|
|
1176
|
+
if (targetId == null) {
|
|
1177
|
+
this.selfId = player.gameData.playerId;
|
|
1178
|
+
}
|
|
1138
1179
|
this.offerStore.setPlayer(player);
|
|
1180
|
+
this.offerStore.setOffers(offers, player);
|
|
1139
1181
|
this.eventEmitter.emit(exports.OfferEvent.REFRESH, { offers, player: player });
|
|
1140
1182
|
this.logger.log('Refreshed offers and player snapshot');
|
|
1183
|
+
return offers;
|
|
1141
1184
|
}
|
|
1142
1185
|
catch (error) {
|
|
1143
1186
|
this.handleError(error, 'refreshOffersAndPlayer');
|
|
1144
1187
|
throw error;
|
|
1145
1188
|
}
|
|
1146
1189
|
}
|
|
1147
|
-
async getOffersAndPlayer() {
|
|
1190
|
+
async getOffersAndPlayer(targetId = null) {
|
|
1148
1191
|
const data = await this.postWithAuth('/v1/client/player/campaigns', {
|
|
1149
1192
|
viewingCampaigns: true,
|
|
1193
|
+
targetId: targetId || undefined,
|
|
1150
1194
|
});
|
|
1151
1195
|
if (!data.offers || !Array.isArray(data.offers)) {
|
|
1152
1196
|
throw new Error('No offers returned from offers endpoint');
|
|
@@ -1279,6 +1323,8 @@ class OfferwallClient {
|
|
|
1279
1323
|
}
|
|
1280
1324
|
}
|
|
1281
1325
|
|
|
1326
|
+
const DEFAULT_ENTITY_KIND = '_default';
|
|
1327
|
+
|
|
1282
1328
|
const keyPattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
1283
1329
|
/**
|
|
1284
1330
|
* This replaces {keyName} keys from the template with corresponding values from the dynamic object.
|
|
@@ -1316,11 +1362,25 @@ function replaceDynamicConditionKeys(conditions, trackers) {
|
|
|
1316
1362
|
}));
|
|
1317
1363
|
}
|
|
1318
1364
|
|
|
1365
|
+
const dynamicTrackerToPrimitive = (dynaTrack) => {
|
|
1366
|
+
const primitive = {};
|
|
1367
|
+
for (const key in dynaTrack) {
|
|
1368
|
+
primitive[key] = dynaTrack[key].value || 0;
|
|
1369
|
+
}
|
|
1370
|
+
return primitive;
|
|
1371
|
+
};
|
|
1372
|
+
|
|
1373
|
+
const addressNetworkId = (contractAddress, network) => {
|
|
1374
|
+
return `${contractAddress.toLowerCase()}:${network.toUpperCase()}`;
|
|
1375
|
+
};
|
|
1376
|
+
|
|
1319
1377
|
const meetsBaseConditions = ({ conditions, playerSnap, addDetails,
|
|
1320
1378
|
/** this exists if calling meetsBaseConditions from meetsCompletionConditions. but surfacing
|
|
1321
1379
|
* check doesn't use this since we don't have a playerOffer at surfacing time
|
|
1322
1380
|
*/
|
|
1323
|
-
playerOffer,
|
|
1381
|
+
playerOffer,
|
|
1382
|
+
/** Additional data like fetched token balances that isn't part of playerSnap */
|
|
1383
|
+
additionalData, }) => {
|
|
1324
1384
|
const conditionData = [];
|
|
1325
1385
|
let isValid = true;
|
|
1326
1386
|
if (conditions?.minDaysInGame) {
|
|
@@ -1648,7 +1708,8 @@ playerOffer, }) => {
|
|
|
1648
1708
|
// Validate link count conditions
|
|
1649
1709
|
if (conditions?.links && 'entityLinks' in playerSnap) {
|
|
1650
1710
|
for (const [linkType, constraint] of Object.entries(conditions.links)) {
|
|
1651
|
-
|
|
1711
|
+
// linkType should always exist. and be default is none was specified
|
|
1712
|
+
const linkCount = playerSnap.entityLinks?.filter((link) => (link.kind || DEFAULT_ENTITY_KIND) === linkType).length || 0;
|
|
1652
1713
|
if (constraint.min !== undefined) {
|
|
1653
1714
|
const isDisqualify = linkCount < constraint.min;
|
|
1654
1715
|
if (addDetails) {
|
|
@@ -1735,16 +1796,60 @@ playerOffer, }) => {
|
|
|
1735
1796
|
return { isValid: false };
|
|
1736
1797
|
}
|
|
1737
1798
|
}
|
|
1799
|
+
// Evaluate token balance conditions
|
|
1800
|
+
for (const tokenCond of conditions?.tokenBalances || []) {
|
|
1801
|
+
const contracts = tokenCond.contracts || [];
|
|
1802
|
+
let totalBalance = 0;
|
|
1803
|
+
const fetchedBalances = aggregateTokenBalances(additionalData);
|
|
1804
|
+
for (const contract of contracts) {
|
|
1805
|
+
const balanceKey = addressNetworkId(contract.contractAddress, contract.network);
|
|
1806
|
+
totalBalance += fetchedBalances[balanceKey] || 0;
|
|
1807
|
+
}
|
|
1808
|
+
if (tokenCond.min !== undefined) {
|
|
1809
|
+
const isDisqualify = totalBalance < tokenCond.min;
|
|
1810
|
+
if (addDetails) {
|
|
1811
|
+
conditionData.push({
|
|
1812
|
+
isMet: !isDisqualify,
|
|
1813
|
+
kind: 'tokenBalances',
|
|
1814
|
+
trackerAmount: totalBalance,
|
|
1815
|
+
text: `Have at least ${tokenCond.min} ${tokenCond.name || 'tokens'}`,
|
|
1816
|
+
});
|
|
1817
|
+
if (isDisqualify)
|
|
1818
|
+
isValid = false;
|
|
1819
|
+
}
|
|
1820
|
+
else {
|
|
1821
|
+
if (isDisqualify)
|
|
1822
|
+
return { isValid: false };
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
if (tokenCond.max !== undefined) {
|
|
1826
|
+
const isDisqualify = totalBalance > tokenCond.max;
|
|
1827
|
+
if (addDetails) {
|
|
1828
|
+
conditionData.push({
|
|
1829
|
+
isMet: !isDisqualify,
|
|
1830
|
+
kind: 'tokenBalances',
|
|
1831
|
+
trackerAmount: totalBalance,
|
|
1832
|
+
text: `Have at most ${tokenCond.max} ${tokenCond.name || 'tokens'}`,
|
|
1833
|
+
});
|
|
1834
|
+
if (isDisqualify)
|
|
1835
|
+
isValid = false;
|
|
1836
|
+
}
|
|
1837
|
+
else {
|
|
1838
|
+
if (isDisqualify)
|
|
1839
|
+
return { isValid: false };
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1738
1843
|
return { isValid, conditionData: addDetails ? conditionData : undefined };
|
|
1739
1844
|
};
|
|
1740
|
-
const meetsSurfacingConditions = ({ surfacingConditions, playerSnap, context, playerOffers, }) => {
|
|
1845
|
+
const meetsSurfacingConditions = ({ surfacingConditions, playerSnap, context, playerOffers, additionalData, }) => {
|
|
1741
1846
|
if (surfacingConditions?.contexts?.length &&
|
|
1742
1847
|
!surfacingConditions.contexts?.includes(context || '')) {
|
|
1743
1848
|
// context is not in the list of surfacing contexts, so we don't want to surface this offer
|
|
1744
1849
|
return { isValid: false };
|
|
1745
1850
|
}
|
|
1746
1851
|
if (surfacingConditions?.targetEntityTypes?.length) {
|
|
1747
|
-
const playerTarget = playerSnap.entityKind ||
|
|
1852
|
+
const playerTarget = playerSnap.entityKind || DEFAULT_ENTITY_KIND;
|
|
1748
1853
|
// check if entity type is allowed
|
|
1749
1854
|
if (!surfacingConditions.targetEntityTypes.includes(playerTarget)) {
|
|
1750
1855
|
return { isValid: false };
|
|
@@ -1825,7 +1930,7 @@ const meetsSurfacingConditions = ({ surfacingConditions, playerSnap, context, pl
|
|
|
1825
1930
|
}
|
|
1826
1931
|
}
|
|
1827
1932
|
}
|
|
1828
|
-
return meetsBaseConditions({ conditions, playerSnap });
|
|
1933
|
+
return meetsBaseConditions({ conditions, playerSnap, additionalData });
|
|
1829
1934
|
};
|
|
1830
1935
|
const hasConditions = (conditions) => {
|
|
1831
1936
|
if (!conditions)
|
|
@@ -1883,6 +1988,8 @@ const hasConditions = (conditions) => {
|
|
|
1883
1988
|
return true;
|
|
1884
1989
|
if (surCond.networkRestrictions?.length)
|
|
1885
1990
|
return true;
|
|
1991
|
+
if (surCond.linkedEntityOffers?.offer_id)
|
|
1992
|
+
return true;
|
|
1886
1993
|
const compCond = conditions;
|
|
1887
1994
|
if (compCond.context)
|
|
1888
1995
|
return true;
|
|
@@ -1902,23 +2009,47 @@ const hasConditions = (conditions) => {
|
|
|
1902
2009
|
return true;
|
|
1903
2010
|
if (compCond.dynamicTracker?.conditions?.length)
|
|
1904
2011
|
return true;
|
|
2012
|
+
if (conditions.tokenBalances?.length)
|
|
2013
|
+
return true;
|
|
2014
|
+
if (Object.keys(compCond.contractInteractions || {}).length > 0)
|
|
2015
|
+
return true;
|
|
1905
2016
|
return false;
|
|
1906
2017
|
};
|
|
1907
|
-
const
|
|
2018
|
+
const meetsLinkedEntityOffersCondition = ({ linkedEntityOffers, matchingLinks, linkedPOfferMap, }) => {
|
|
2019
|
+
if (!linkedPOfferMap)
|
|
2020
|
+
return { isValid: false };
|
|
2021
|
+
const linkedPlayerOffer_ids = [];
|
|
2022
|
+
for (const link of matchingLinks) {
|
|
2023
|
+
const key = `${link.playerId}:${linkedEntityOffers.offer_id}`;
|
|
2024
|
+
const po = linkedPOfferMap.get(key);
|
|
2025
|
+
if (po) {
|
|
2026
|
+
linkedPlayerOffer_ids.push(po._id.toString());
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
if (linkedPlayerOffer_ids.length > 0) {
|
|
2030
|
+
return { isValid: true, linkedPlayerOffer_ids };
|
|
2031
|
+
}
|
|
2032
|
+
return { isValid: false };
|
|
2033
|
+
};
|
|
2034
|
+
const offerMeetsCompletionConditions = (offer, snapshot, additionalData) => {
|
|
1908
2035
|
return meetsCompletionConditions({
|
|
1909
2036
|
completionConditions: offer.completionConditions || {},
|
|
1910
2037
|
completionTrackers: offer.completionTrackers,
|
|
1911
2038
|
playerSnap: snapshot,
|
|
1912
2039
|
playerOffer: offer,
|
|
1913
2040
|
addDetails: true,
|
|
2041
|
+
maxClaimCount: offer.maxClaimCount,
|
|
2042
|
+
additionalData,
|
|
1914
2043
|
});
|
|
1915
2044
|
};
|
|
1916
|
-
const meetsCompletionConditions = ({ completionConditions, completionTrackers, playerSnap, playerOffer, addDetails = false, maxClaimCount, }) => {
|
|
2045
|
+
const meetsCompletionConditions = ({ completionConditions, completionTrackers, playerSnap, playerOffer, addDetails = false, maxClaimCount, additionalData, }) => {
|
|
1917
2046
|
if (completionConditions) {
|
|
1918
2047
|
const conditions = completionConditions;
|
|
1919
2048
|
// For multi-claim offers, scale cumulative requirements by (claimedCount + 1)
|
|
1920
2049
|
const shouldScale = maxClaimCount === -1 || (maxClaimCount && maxClaimCount > 1);
|
|
1921
|
-
const claimMultiplier = shouldScale
|
|
2050
|
+
const claimMultiplier = shouldScale
|
|
2051
|
+
? (playerOffer.trackers?.claimedCount || 0) + 1
|
|
2052
|
+
: 1;
|
|
1922
2053
|
const conditionData = [];
|
|
1923
2054
|
let isValid = true;
|
|
1924
2055
|
let maxTotalClaimsFromScaling = Infinity;
|
|
@@ -2049,12 +2180,14 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
2049
2180
|
}
|
|
2050
2181
|
}
|
|
2051
2182
|
if (conditions?.social) {
|
|
2052
|
-
const
|
|
2183
|
+
const tSocialAccumulate = completionTrackers?.social;
|
|
2184
|
+
const tSocialAttach = completionTrackers?.social;
|
|
2053
2185
|
const cSocial = completionConditions.social;
|
|
2054
2186
|
const mode = cSocial?.mode || 'attach';
|
|
2187
|
+
const tSocial = mode === 'accumulate' ? tSocialAccumulate : tSocialAttach;
|
|
2055
2188
|
const hasContent = Boolean(mode === 'accumulate'
|
|
2056
|
-
?
|
|
2057
|
-
:
|
|
2189
|
+
? tSocialAccumulate?.matchCount > 0
|
|
2190
|
+
: tSocialAttach?.videoId);
|
|
2058
2191
|
// Only scale social metrics in accumulate mode (attach mode is single content)
|
|
2059
2192
|
const socialMultiplier = mode === 'accumulate' ? claimMultiplier : 1;
|
|
2060
2193
|
const minLikes = (cSocial?.minLikes || 0) * socialMultiplier;
|
|
@@ -2089,7 +2222,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
2089
2222
|
.join(' | ');
|
|
2090
2223
|
const requiredWords = cSocial?.requiredWords ?? [];
|
|
2091
2224
|
if (mode === 'accumulate') {
|
|
2092
|
-
const matchCount =
|
|
2225
|
+
const matchCount = tSocialAccumulate?.matchCount || 0;
|
|
2093
2226
|
conditionData.push({
|
|
2094
2227
|
isMet: hasContent,
|
|
2095
2228
|
kind: 'social',
|
|
@@ -2102,7 +2235,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
2102
2235
|
});
|
|
2103
2236
|
}
|
|
2104
2237
|
else {
|
|
2105
|
-
const title =
|
|
2238
|
+
const title = tSocialAttach?.title;
|
|
2106
2239
|
conditionData.push({
|
|
2107
2240
|
isMet: hasContent,
|
|
2108
2241
|
kind: 'social',
|
|
@@ -2179,22 +2312,22 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
2179
2312
|
if (conditions?.dynamicTracker?.conditions?.length) {
|
|
2180
2313
|
const resolvedConditions = replaceDynamicConditionKeys(conditions.dynamicTracker.conditions, playerOffer?.trackers || {});
|
|
2181
2314
|
// now we have the game-defined conditions with {{}} keys populated. feed these conditions into evaluator
|
|
2182
|
-
const dynamicResult = meetsDynamicConditions(completionTrackers?.dynamicTracker, {
|
|
2315
|
+
const dynamicResult = meetsDynamicConditions(dynamicTrackerToPrimitive(completionTrackers?.dynamicTracker || {}), {
|
|
2183
2316
|
...conditions.dynamicTracker,
|
|
2184
2317
|
conditions: resolvedConditions,
|
|
2185
2318
|
}, claimMultiplier);
|
|
2186
2319
|
if (shouldScale) {
|
|
2187
|
-
const dynamicMax = getMaxClaimsForDynamicGroup(completionTrackers?.dynamicTracker || {}, {
|
|
2320
|
+
const dynamicMax = getMaxClaimsForDynamicGroup(dynamicTrackerToPrimitive(completionTrackers?.dynamicTracker || {}), {
|
|
2188
2321
|
...conditions.dynamicTracker,
|
|
2189
2322
|
conditions: resolvedConditions,
|
|
2190
|
-
}, playerOffer
|
|
2323
|
+
}, playerOffer?.trackers?.claimedCount || 0);
|
|
2191
2324
|
updateMax(dynamicMax);
|
|
2192
2325
|
}
|
|
2193
2326
|
if (addDetails) {
|
|
2194
2327
|
conditionData.push({
|
|
2195
2328
|
isMet: dynamicResult,
|
|
2196
|
-
kind: '
|
|
2197
|
-
text: renderTemplate(conditions.dynamicTracker.template, completionTrackers?.dynamicTracker) || 'Dynamic conditions',
|
|
2329
|
+
kind: 'dynamicTracker',
|
|
2330
|
+
text: renderTemplate(conditions.dynamicTracker.template, dynamicTrackerToPrimitive(completionTrackers?.dynamicTracker || {})) || 'Dynamic conditions',
|
|
2198
2331
|
});
|
|
2199
2332
|
if (!dynamicResult)
|
|
2200
2333
|
isValid = false;
|
|
@@ -2204,19 +2337,64 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
2204
2337
|
return { isValid: false, availableClaimsNow: 0 };
|
|
2205
2338
|
}
|
|
2206
2339
|
}
|
|
2340
|
+
// Evaluate contractInteractions completion trackers
|
|
2341
|
+
if (conditions?.contractInteractions) {
|
|
2342
|
+
for (const [conditionId, condition] of Object.entries(conditions.contractInteractions)) {
|
|
2343
|
+
const baseAmount = condition.amount || 0;
|
|
2344
|
+
const scaledAmount = baseAmount * claimMultiplier;
|
|
2345
|
+
const trackerValue = completionTrackers?.contractInteractions?.[conditionId] || 0;
|
|
2346
|
+
const isDisqualify = trackerValue < scaledAmount;
|
|
2347
|
+
if (shouldScale && baseAmount > 0) {
|
|
2348
|
+
updateMax(Math.floor(trackerValue / baseAmount));
|
|
2349
|
+
}
|
|
2350
|
+
if (addDetails) {
|
|
2351
|
+
let displayText;
|
|
2352
|
+
const eventType = condition.event;
|
|
2353
|
+
const name = condition.name || 'tokens';
|
|
2354
|
+
if (eventType === 'spend') {
|
|
2355
|
+
displayText = `Spend ${scaledAmount} ${name}`;
|
|
2356
|
+
}
|
|
2357
|
+
else if (eventType === 'earn') {
|
|
2358
|
+
displayText = `Earn ${scaledAmount} ${name}`;
|
|
2359
|
+
}
|
|
2360
|
+
else if (eventType === 'gain') {
|
|
2361
|
+
displayText = `Gain ${scaledAmount} ${name}`;
|
|
2362
|
+
}
|
|
2363
|
+
else if (eventType === 'lose') {
|
|
2364
|
+
displayText = `Lose ${scaledAmount} ${name}`;
|
|
2365
|
+
}
|
|
2366
|
+
else {
|
|
2367
|
+
displayText = `${name}: ${scaledAmount}`;
|
|
2368
|
+
}
|
|
2369
|
+
conditionData.push({
|
|
2370
|
+
isMet: !isDisqualify,
|
|
2371
|
+
kind: 'contractInteractions',
|
|
2372
|
+
trackerAmount: trackerValue,
|
|
2373
|
+
text: displayText,
|
|
2374
|
+
});
|
|
2375
|
+
if (isDisqualify)
|
|
2376
|
+
isValid = false;
|
|
2377
|
+
}
|
|
2378
|
+
else {
|
|
2379
|
+
if (isDisqualify)
|
|
2380
|
+
return { isValid: false, availableClaimsNow: 0 };
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2207
2384
|
const r = meetsBaseConditions({
|
|
2208
2385
|
conditions,
|
|
2209
2386
|
playerSnap,
|
|
2210
2387
|
addDetails: true,
|
|
2211
2388
|
playerOffer,
|
|
2389
|
+
additionalData,
|
|
2212
2390
|
});
|
|
2213
2391
|
isValid = isValid && r.isValid;
|
|
2214
2392
|
conditionData.push(...(r.conditionData || []));
|
|
2215
2393
|
if (maxClaimCount && maxClaimCount > 0) {
|
|
2216
2394
|
updateMax(maxClaimCount);
|
|
2217
2395
|
}
|
|
2218
|
-
const claimedCount = playerOffer
|
|
2219
|
-
|
|
2396
|
+
const claimedCount = playerOffer?.trackers?.claimedCount || 0;
|
|
2397
|
+
const availableClaimsNow = !isValid
|
|
2220
2398
|
? 0
|
|
2221
2399
|
: maxTotalClaimsFromScaling === Infinity
|
|
2222
2400
|
? -1
|
|
@@ -2567,6 +2745,19 @@ function meetsClaimableConditions({ claimableConditions, playerOfferTrackers, cl
|
|
|
2567
2745
|
}
|
|
2568
2746
|
return { isValid: true };
|
|
2569
2747
|
}
|
|
2748
|
+
// returns contractAddress:network -> balance
|
|
2749
|
+
function aggregateTokenBalances(data) {
|
|
2750
|
+
const aggregatedBalances = {};
|
|
2751
|
+
for (const { balances } of data?.cryptoWallets || []) {
|
|
2752
|
+
for (const [key, balance] of Object.entries(balances)) {
|
|
2753
|
+
if (!aggregatedBalances[key]) {
|
|
2754
|
+
aggregatedBalances[key] = 0;
|
|
2755
|
+
}
|
|
2756
|
+
aggregatedBalances[key] += balance;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
return aggregatedBalances;
|
|
2760
|
+
}
|
|
2570
2761
|
|
|
2571
2762
|
const offerListenerEvents = ['claim_offer'];
|
|
2572
2763
|
const PlayerOfferStatuses = [
|
|
@@ -2615,11 +2806,13 @@ const rewardSchema = {
|
|
|
2615
2806
|
};
|
|
2616
2807
|
|
|
2617
2808
|
exports.AssetHelper = AssetHelper;
|
|
2809
|
+
exports.DEFAULT_ENTITY_KIND = DEFAULT_ENTITY_KIND;
|
|
2618
2810
|
exports.EventEmitter = EventEmitter;
|
|
2619
2811
|
exports.OfferStore = OfferStore;
|
|
2620
2812
|
exports.OfferwallClient = OfferwallClient;
|
|
2621
2813
|
exports.PlayerOfferStatuses = PlayerOfferStatuses;
|
|
2622
2814
|
exports.SSEConnection = SSEConnection;
|
|
2815
|
+
exports.aggregateTokenBalances = aggregateTokenBalances;
|
|
2623
2816
|
exports.getMaxClaimsForDynamicCondition = getMaxClaimsForDynamicCondition;
|
|
2624
2817
|
exports.getMaxClaimsForDynamicGroup = getMaxClaimsForDynamicGroup;
|
|
2625
2818
|
exports.hasConditions = hasConditions;
|
|
@@ -2628,6 +2821,7 @@ exports.meetsClaimableConditions = meetsClaimableConditions;
|
|
|
2628
2821
|
exports.meetsCompletionConditions = meetsCompletionConditions;
|
|
2629
2822
|
exports.meetsCompletionConditionsBeforeExpiry = meetsCompletionConditionsBeforeExpiry;
|
|
2630
2823
|
exports.meetsDynamicConditions = meetsDynamicConditions;
|
|
2824
|
+
exports.meetsLinkedEntityOffersCondition = meetsLinkedEntityOffersCondition;
|
|
2631
2825
|
exports.meetsSurfacingConditions = meetsSurfacingConditions;
|
|
2632
2826
|
exports.offerListenerEvents = offerListenerEvents;
|
|
2633
2827
|
exports.offerMeetsCompletionConditions = offerMeetsCompletionConditions;
|