diff --git a/migrations/12.sql b/migrations/12.sql new file mode 100644 index 0000000..bfdb4c7 --- /dev/null +++ b/migrations/12.sql @@ -0,0 +1,4 @@ +ALTER TABLE `pokestop` + ADD COLUMN `lure_first_seen_timestamp` int(11) unsigned NOT NULL DEFAULT 0, + MODIFY COLUMN `lure_expire_timestamp` int(11) unsigned DEFAULT 0; +UPDATE `pokestop` SET `lure_expire_timestamp` = 0 WHERE `lure_expire_timestamp` IS NULL OR `lure_expire_timestamp` < UNIX_TIMESTAMP(); diff --git a/src/configs/default.json b/src/configs/default.json index 3c559f4..deed067 100644 --- a/src/configs/default.json +++ b/src/configs/default.json @@ -40,6 +40,10 @@ ], "v2": true }, + "lure": { + "v2": false, + "v2Webhook": false + }, "pvp": { "rankCacheAge": 86400000, "levelCaps": [ diff --git a/src/models/incident.js b/src/models/incident.js index 8841d67..d24a8a4 100644 --- a/src/models/incident.js +++ b/src/models/incident.js @@ -51,7 +51,6 @@ class Incident extends Model { longitude: pokestop.lon, pokestop_name: pokestop.name || oldPokestop && oldPokestop.name || "Unknown", url: pokestop.url || oldPokestop && oldPokestop.url, - lure_expiration: pokestop.lureExpireTimestamp || 0, last_modified: pokestop.lastModifiedTimestamp || 0, enabled: pokestop.enabled || true, lure_id: pokestop.lureId || 0, diff --git a/src/models/pokestop.js b/src/models/pokestop.js index 72a3ad2..f83ebc7 100644 --- a/src/models/pokestop.js +++ b/src/models/pokestop.js @@ -13,7 +13,7 @@ const config = require('../services/config.js'); * Pokestop model class. */ class Pokestop extends Model { - static LureTime = config.dataparser.lureTime; + static LureTime = config.dataparser.lureTime * 60; static fromFortFields = [ 'lat', @@ -24,6 +24,7 @@ class Pokestop extends Model { 'cellId', 'updated', 'deleted', + 'lureFirstSeenTimestamp', 'lureExpireTimestamp', 'lureId', 'incidentExpireTimestamp', @@ -47,17 +48,12 @@ class Pokestop extends Model { deleted: false, arScanEligible: fort.is_ar_scan_eligible, }; - if (!fort.active_fort_modifier || fort.active_fort_modifier.length <= 0 || - fort.active_fort_modifier.every((mod) => - mod < POGOProtos.Rpc.Item.ITEM_TROY_DISK || mod >= POGOProtos.Rpc.Item.ITEM_TROY_DISK + 99)) { - record.lureExpireTimestamp = 0; - } else if (record.lureExpireTimestamp < ts || record.lureId !== fort.active_fort_modifier[0]) { - record.lureExpireTimestamp = Math.floor(record.lastModifiedTimestamp + Pokestop.LureTime); - if (record.lureExpireTimestamp < ts) { // event extended lure duration - record.lureExpireTimestamp = Math.floor(ts + Pokestop.LureTime); - } + if (fort.active_fort_modifier && fort.active_fort_modifier.length > 0) { + if (fort.active_fort_modifier.length > 1) console.warn('[Lure] More than one lure?', record.id); record.lureId = fort.active_fort_modifier[0]; - } + record.lureFirstSeenTimestamp = record.lastModifiedTimestamp; + record.lureExpireTimestamp = null; + } else record.lureExpireTimestamp = 0; let incidents = []; if (config.dataparser.incident.v1 || config.dataparser.incident.v2) { incidents = fort.pokestop_displays; @@ -248,6 +244,22 @@ class Pokestop extends Model { }); } + static fromFortDetailsColumnsAdditional = [ + 'lureId', + 'lureExpireTimestamp', + ]; + static fromFortDetails(fortDetails, record) { + if (fortDetails.modifier && fortDetails.modifier.length > 0) { + if (fortDetails.modifier.length > 1) console.warn('[Lure] More than one lure?', fortDetails.fort_id); + const modifier = fortDetails.modifier[0]; + record.lureId = modifier.modifier_type; + record.lureFirstSeenTimestamp = Date.now() / 1000; + record.lureExpireTimestamp = modifier.expiration_time_ms / 1000; + // modifier.deploying_player_codename + } + return record; + } + static getAll(minLat, maxLat, minLon, maxLon) { return Pokestop.findAll({ where: { @@ -291,8 +303,23 @@ class Pokestop extends Model { WebhookController.instance.addPokestopEvent(this.toJson('pokestop', oldPokestop)); oldPokestop = {}; } - if ((oldPokestop.lureExpireTimestamp || 0) < (this.lureExpireTimestamp || 0)) { - WebhookController.instance.addLureEvent(this.toJson('lure', oldPokestop)); + if (config.dataparser.lure.v2) { + if (this.lureExpireTimestamp !== 0) { + if (oldPokestop.lureId === this.lureId && (oldPokestop.lureExpireTimestamp === null || + oldPokestop.lureExpireTimestamp >= this.updated)) { + this.lureFirstSeenTimestamp = oldPokestop.lureFirstSeenTimestamp; + this.lureExpireTimestamp = oldPokestop.lureExpireTimestamp; + } else { + WebhookController.instance.addLureEvent(this.toJson('lure', oldPokestop)); + } + } + } else if (this.lureExpireTimestamp !== 0) { + if (oldPokestop.lureId === this.lureId && oldPokestop.lureExpireTimestamp !== 0) { + this.lureFirstSeenTimestamp = oldPokestop.lureFirstSeenTimestamp; + this.lureExpireTimestamp = this.estimateLureExpireLegacy(oldPokestop.lureFirstSeenTimestamp); + } else { + WebhookController.instance.addLureEvent(this.toJson('lure', oldPokestop)); + } } if (config.dataparser.incident.v2) { if (incidents) await Promise.all(incidents.map(incident => incident.triggerWebhook(this, oldPokestop))); @@ -303,6 +330,13 @@ class Pokestop extends Model { return false; } + estimateLureExpireLegacy(firstSeen) { + let result = Math.floor(firstSeen + Pokestop.LureTime); + // event extended lure duration + if (result < this.updated) result += Pokestop.LureTime * Math.ceil((this.updated - result) / Pokestop.LureTime); + return result; + } + /** * Get Pokestop object */ @@ -412,23 +446,32 @@ class Pokestop extends Model { updated: this.updated || 1 } }; - default: // Pokestop - return { - type: "pokestop", - message: { - pokestop_id: this.id, - latitude: this.lat, - longitude: this.lon, - pokestop_name: this.name || old && old.name || "Unknown", - url: this.url || old && old.url, - lure_expiration: this.lureExpireTimestamp || 0, - last_modified: this.lastModifiedTimestamp || 0, - enabled: this.enabled || true, - lure_id: this.lureId || 0, - ar_scan_eligible: this.arScanEligible, - updated: this.updated || 1 - } + default: { // Pokestop/Lure + const message = { + pokestop_id: this.id, + latitude: this.lat, + longitude: this.lon, + pokestop_name: this.name || old && old.name || "Unknown", + url: this.url || old && old.url, + last_modified: this.lastModifiedTimestamp || 0, + enabled: this.enabled || true, + lure_id: this.lureId || 0, + ar_scan_eligible: this.arScanEligible, + updated: this.updated || 1 }; + if (config.dataparser.lure.v2) { + if (config.dataparser.lure.v2Webhook) { + message.lure_first_seen = this.lureFirstSeenTimestamp; + message.lure_expiration = this.lureExpireTimestamp; + } else { + message.lure_expiration = this.lureExpireTimestamp === null + ? Pokestop.estimateLureExpireLegacy(this.lureFirstSeenTimestamp) : this.lureExpireTimestamp; + } + } else { + message.lure_expiration = this.lureExpireTimestamp || 0; + } + return { type: "pokestop", message }; + } } } } @@ -454,9 +497,14 @@ Pokestop.init({ type: DataTypes.STRING(200), defaultValue: null, }, + lureFirstSeenTimestamp: { + type: DataTypes.INTEGER(11).UNSIGNED, + defaultValue: 0, + allowNull: false, + }, lureExpireTimestamp: { type: DataTypes.INTEGER(11).UNSIGNED, - defaultValue: null, + defaultValue: 0, }, lastModifiedTimestamp: { type: DataTypes.INTEGER(11).UNSIGNED, diff --git a/src/services/consumer.js b/src/services/consumer.js index 9003fce..1d2b1b1 100644 --- a/src/services/consumer.js +++ b/src/services/consumer.js @@ -128,6 +128,7 @@ class Consumer { 'url', 'updated', ]; + static fortStopColumns = Consumer.fortColumns.concat(Pokestop.fromFortDetailsColumnsAdditional); async updateFortDetails(fortDetails) { // Update Forts @@ -152,7 +153,7 @@ class Consumer { updatedGyms.push(record); break; case POGOProtos.Rpc.FortType.CHECKPOINT: - updatedPokestops.push(record); + updatedPokestops.push(Pokestop.fromFortDetails(record)); break; } } @@ -171,7 +172,7 @@ class Consumer { if (updatedPokestops.length > 0) { try { const result = await Pokestop.bulkCreate(updatedPokestops.sort(stringCompare('id')), { - updateOnDuplicate: Consumer.fortColumns, + updateOnDuplicate: Consumer.fortStopColumns, }); //console.log('[FortDetails] Result:', result.length); } catch (err) {