diff --git a/config/config.go b/config/config.go index 6184b73b..5cfa9081 100644 --- a/config/config.go +++ b/config/config.go @@ -95,6 +95,7 @@ type scanRule struct { ProcessCells *bool `koanf:"cells"` ProcessPokestops *bool `koanf:"pokestops"` ProcessGyms *bool `koanf:"gyms"` + ProcessLobbies *bool `koanf:"lobbies"` } var Config configDefinition diff --git a/decoder/gym.go b/decoder/gym.go index b9b872f0..ebe38e4b 100644 --- a/decoder/gym.go +++ b/decoder/gym.go @@ -56,6 +56,8 @@ type Gym struct { PowerUpPoints null.Int `db:"power_up_points"` PowerUpEndTimestamp null.Int `db:"power_up_end_timestamp"` Description null.String `db:"description"` + LobbyPlayerCount int32 + LobbyJoinEndMs int64 //`id` varchar(35) NOT NULL, //`lat` double(18,14) NOT NULL, //`lon` double(18,14) NOT NULL, @@ -379,11 +381,75 @@ func createGymFortWebhooks(oldGym *Gym, gym *Gym) { } } +func makeRaidWebhook(gym *Gym) (payload map[string]interface{}) { + payload = map[string]interface{}{ + "gym_id": gym.Id, + "gym_name": func() string { + if !gym.Name.Valid { + return "Unknown" + } else { + return gym.Name.String + } + }(), + "gym_url": gym.Url.ValueOrZero(), + "latitude": gym.Lat, + "longitude": gym.Lon, + "team_id": gym.TeamId.ValueOrZero(), + "spawn": gym.RaidSpawnTimestamp.ValueOrZero(), + "start": gym.RaidBattleTimestamp.ValueOrZero(), + "end": gym.RaidEndTimestamp.ValueOrZero(), + "level": gym.RaidLevel.ValueOrZero(), + "pokemon_id": gym.RaidPokemonId.ValueOrZero(), + "cp": gym.RaidPokemonCp.ValueOrZero(), + "gender": gym.RaidPokemonGender.ValueOrZero(), + "form": gym.RaidPokemonForm.ValueOrZero(), + "alignment": gym.RaidPokemonAlignment.ValueOrZero(), + "costume": gym.RaidPokemonCostume.ValueOrZero(), + "evolution": gym.RaidPokemonEvolution.ValueOrZero(), + "move_1": gym.RaidPokemonMove1.ValueOrZero(), + "move_2": gym.RaidPokemonMove2.ValueOrZero(), + "ex_raid_eligible": gym.ExRaidEligible.ValueOrZero(), + "is_exclusive": gym.RaidIsExclusive.ValueOrZero(), + "sponsor_id": gym.SponsorId.ValueOrZero(), + "partner_id": gym.PartnerId.ValueOrZero(), + "power_up_points": gym.PowerUpPoints.ValueOrZero(), + "power_up_level": gym.PowerUpLevel.ValueOrZero(), + "power_up_end_timestamp": gym.PowerUpEndTimestamp.ValueOrZero(), + "ar_scan_eligible": gym.ArScanEligible.ValueOrZero(), + "lobby_player_count": gym.LobbyPlayerCount, + "lobby_join_end_ms": gym.LobbyJoinEndMs, + } + return +} + +func CreateRaidLobbyPlayerCountWebhooks(ctx context.Context, db db.DbDetails, lobby *pogo.RaidLobbyPlayerCountProto) bool { + gymMutex, _ := gymStripedMutex.GetLock(lobby.GymId) + gymMutex.Lock() + defer gymMutex.Unlock() + + gym, _ := getGymRecord(ctx, db, lobby.GymId) + if gym == nil { // skip reporting for unseen gyms + return false + } + if gym.LobbyPlayerCount == lobby.PlayerCount && + (lobby.PlayerCount == 0 || gym.LobbyJoinEndMs == lobby.LobbyJoinUntilMs) { + // skip unchanged lobbies or empty lobby updates + return false + } + gym.LobbyPlayerCount = lobby.PlayerCount + gym.LobbyJoinEndMs = lobby.LobbyJoinUntilMs + gymCache.Set(gym.Id, *gym, ttlcache.DefaultTTL) + payload := makeRaidWebhook(gym) + areas := MatchStatsGeofence(gym.Lat, gym.Lon) + webhooks.AddMessage(webhooks.Raid, payload, areas) + return true +} + func createGymWebhooks(oldGym *Gym, gym *Gym) { areas := MatchStatsGeofence(gym.Lat, gym.Lon) if oldGym == nil || (oldGym.AvailableSlots != gym.AvailableSlots || oldGym.TeamId != gym.TeamId || oldGym.InBattle != gym.InBattle) { - gymDetails := GymDetailsWebhook{ + webhooks.AddMessage(webhooks.GymDetails, GymDetailsWebhook{ Id: gym.Id, Name: gym.Name.ValueOrZero(), Url: gym.Url.ValueOrZero(), @@ -400,9 +466,7 @@ func createGymWebhooks(oldGym *Gym, gym *Gym) { }(), ExRaidEligible: gym.ExRaidEligible.ValueOrZero(), InBattle: func() bool { return gym.InBattle.ValueOrZero() != 0 }(), - } - - webhooks.AddMessage(webhooks.GymDetails, gymDetails, areas) + }, areas) } if gym.RaidSpawnTimestamp.ValueOrZero() > 0 && @@ -415,43 +479,7 @@ func createGymWebhooks(oldGym *Gym, gym *Gym) { if (raidBattleTime > now && gym.RaidLevel.ValueOrZero() > 0) || (raidEndTime > now && gym.RaidPokemonId.ValueOrZero() > 0) { - raidHook := map[string]interface{}{ - "gym_id": gym.Id, - "gym_name": func() string { - if !gym.Name.Valid { - return "Unknown" - } else { - return gym.Name.String - } - }(), - "gym_url": gym.Url.ValueOrZero(), - "latitude": gym.Lat, - "longitude": gym.Lon, - "team_id": gym.TeamId.ValueOrZero(), - "spawn": gym.RaidSpawnTimestamp.ValueOrZero(), - "start": gym.RaidBattleTimestamp.ValueOrZero(), - "end": gym.RaidEndTimestamp.ValueOrZero(), - "level": gym.RaidLevel.ValueOrZero(), - "pokemon_id": gym.RaidPokemonId.ValueOrZero(), - "cp": gym.RaidPokemonCp.ValueOrZero(), - "gender": gym.RaidPokemonGender.ValueOrZero(), - "form": gym.RaidPokemonForm.ValueOrZero(), - "alignment": gym.RaidPokemonAlignment.ValueOrZero(), - "costume": gym.RaidPokemonCostume.ValueOrZero(), - "evolution": gym.RaidPokemonEvolution.ValueOrZero(), - "move_1": gym.RaidPokemonMove1.ValueOrZero(), - "move_2": gym.RaidPokemonMove2.ValueOrZero(), - "ex_raid_eligible": gym.ExRaidEligible.ValueOrZero(), - "is_exclusive": gym.RaidIsExclusive.ValueOrZero(), - "sponsor_id": gym.SponsorId.ValueOrZero(), - "partner_id": gym.PartnerId.ValueOrZero(), - "power_up_points": gym.PowerUpPoints.ValueOrZero(), - "power_up_level": gym.PowerUpLevel.ValueOrZero(), - "power_up_end_timestamp": gym.PowerUpEndTimestamp.ValueOrZero(), - "ar_scan_eligible": gym.ArScanEligible.ValueOrZero(), - } - - webhooks.AddMessage(webhooks.Raid, raidHook, areas) + webhooks.AddMessage(webhooks.Raid, makeRaidWebhook(gym), areas) } } diff --git a/decoder/scanarea.go b/decoder/scanarea.go index 6091fcdf..be46da12 100644 --- a/decoder/scanarea.go +++ b/decoder/scanarea.go @@ -14,6 +14,7 @@ type ScanParameters struct { ProcessPokestops bool ProcessGyms bool ProcessCells bool + ProcessLobbies bool } func FindScanConfiguration(scanContext string, lat, lon float64) ScanParameters { @@ -59,6 +60,7 @@ func FindScanConfiguration(scanContext string, lat, lon float64) ScanParameters ProcessWeather: defaultTrue(rule.ProcessWeather), ProcessPokestops: defaultTrue(rule.ProcessPokestops), ProcessGyms: defaultTrue(rule.ProcessGyms), + ProcessLobbies: defaultTrue(rule.ProcessLobbies), } } @@ -70,5 +72,6 @@ func FindScanConfiguration(scanContext string, lat, lon float64) ScanParameters ProcessWeather: true, ProcessGyms: true, ProcessPokestops: true, + ProcessLobbies: true, } } diff --git a/main.go b/main.go index 232f100d..64975a3a 100644 --- a/main.go +++ b/main.go @@ -289,6 +289,11 @@ func decode(ctx context.Context, method int, protoData *ProtoData) { case pogo.Method_METHOD_GET_MAP_FORTS: result = decodeGetMapForts(ctx, protoData.Data) processed = true + case pogo.Method_METHOD_GET_RAID_LOBBY_COUNTER: + if getScanParameters(protoData).ProcessLobbies { + result = decodeGetRaidLobbyCounter(ctx, protoData.Data) + } + processed = true default: log.Debugf("Did not process hook type %s", pogo.Method(method)) } @@ -458,6 +463,30 @@ func decodeGetMapForts(ctx context.Context, sDec []byte) string { return "No forts updated" } +func decodeGetRaidLobbyCounter(ctx context.Context, sDec []byte) string { + decodedRaidLobbyCounter := &pogo.GetRaidLobbyCounterOutProto{} + if err := proto.Unmarshal(sDec, decodedRaidLobbyCounter); err != nil { + log.Errorf("Failed to parse %s", err) + return fmt.Sprintf("Failed to parse %s", err) + } + + if decodedRaidLobbyCounter.Result != pogo.GetRaidLobbyCounterOutProto_SUCCESS { + return fmt.Sprintf(`GetRaidLobbyCounterOutProto: Ignored non-success value %d:%s`, + decodedRaidLobbyCounter.Result, + decodedRaidLobbyCounter.Result.String()) + } + + processedLobbies := 0 + for _, lobby := range decodedRaidLobbyCounter.RaidLobbyPlayerCount { + if decoder.CreateRaidLobbyPlayerCountWebhooks(ctx, dbDetails, lobby) { + processedLobbies += 1 + } + } + + return fmt.Sprintf("Updated %d/%d lobbies", + processedLobbies, len(decodedRaidLobbyCounter.RaidLobbyPlayerCount)) +} + func decodeGetGymInfo(ctx context.Context, sDec []byte) string { decodedGymInfo := &pogo.GymGetInfoOutProto{} if err := proto.Unmarshal(sDec, decodedGymInfo); err != nil { diff --git a/protos.md b/protos.md index 9f1d63a8..cd8292ae 100644 --- a/protos.md +++ b/protos.md @@ -43,6 +43,10 @@ requires the proto request to be present in the raw. - Provides confirmation of a real or decoy Giovanni +`Method_METHOD_GET_RAID_LOBBY_COUNTER` + +- Bulk lobby query for gyms + # Social actions - The master `ClientAction_CLIENT_ACTION_PROXY_SOCIAL_ACTION` proto will be