From b5805cbe032440164beff5eef0bdabaa8a44608b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AE=D1=80=D0=B8=D0=B9=20=D0=9F=D0=B5=D1=80=D1=88=D0=B8?= =?UTF-8?q?=D0=BD?= Date: Tue, 29 Mar 2022 05:05:21 +0500 Subject: [PATCH] meetups seems to be working now --- protobuf/events.proto | 72 +++++++---- protobuf/events_pb2.py | 81 +++++++----- variants.txt | 1 + zwift_offline.py | 271 +++++++++++++++++++++++++++-------------- 4 files changed, 277 insertions(+), 148 deletions(-) diff --git a/protobuf/events.proto b/protobuf/events.proto index dba7412..c6077c8 100644 --- a/protobuf/events.proto +++ b/protobuf/events.proto @@ -2,39 +2,44 @@ syntax = "proto2"; import "profile.proto"; //enums PlayerType and Sport message InvitedProfileProto { - required uint64 ipp_f1 = 1; - optional string ipp_f2 = 2; - optional string ipp_f3 = 3; - optional string ipp_f4 = 4; - optional bool ipp_f5 = 5; - optional bool ipp_f6 = 6; + required uint64 player_id = 1; + optional string firstName = 2; + optional string lastName = 3; + optional string imageSrc = 4; + optional bool enrolledZwiftAcademy = 5; + optional bool male = 6; optional PlayerType player_type = 7; } +enum EventInviteStatus { + PENDING = 0; + ACCEPTED = 1; + REJECTED = 2; +} message EventInviteProto { - required InvitedProfileProto profile = 1; - required uint64 invite_f2 = 2; + optional InvitedProfileProto profile = 1; + required EventInviteStatus status = 2; } message PrivateEventFeedProto { - required uint64 pef_f1 = 1; //read in PrivateEventsManagerV2::RegisterPrivateEvent + required uint64 event_id = 1; //read in PrivateEventsManagerV2::RegisterPrivateEvent optional Sport sport = 2; optional uint64 eventSubgroupStart = 3; //IsPrivateEventExpired: return ZNETWORK_GetNetworkSyncedTimeGMT() > a1->m_eventSubgroupStart / 1000uLL + 1800 optional uint64 route_id = 4; - optional uint32 pef_f5 = 5; - optional float pef_f6 = 6; - optional uint64 pef_f7 = 7; //stored as int32 - optional uint32 pef_f8 = 8; - optional uint32 pef_f9 = 9; - optional uint32 pef_f10 = 10; - optional string pef_f11 = 11; - optional uint64 pef_f12 = 12; - optional string pef_f13 = 13; - optional string pef_f14 = 14; - optional uint64 pef_f15 = 15; + optional uint32 durationInSeconds = 5; + optional float distanceInMeters = 6; + optional uint64 answeredCount = 7; //stored as int32 + optional uint32 invitedTotalCount = 8; + optional uint32 acceptedFolloweeCount = 9; + optional uint32 acceptedTotalCount = 10; + optional string organizerImageUrl = 11; + optional uint64 organizerProfileId = 12; + optional string organizerFirstName = 13; + optional string organizerLastName = 14; + optional uint64 updateDate = 15; optional uint64 subgroupId = 16; - optional uint32 pef_f17 = 17; - optional bool pef_f18 = 18; + optional uint32 laps = 17; + optional bool rubberbanding = 18; //keep together "rulesId": "1024" } message PrivateEventFeedListProto { @@ -42,13 +47,13 @@ message PrivateEventFeedListProto { } message PlayerJoinedEvent { - required uint64 ev_sg_id = 1; + required uint64 rel_id = 1; //subgroup id optional uint64 player_id = 2; optional uint64 pje_f3 = 3; } -message PlayerLeftEvent { //didn't decompiled yet, just a guess - required uint64 ev_sg_id = 1; +message PlayerLeftEvent { + required uint64 rel_id = 1; //event id optional uint64 player_id = 2; optional uint64 ple_f3 = 3; } @@ -205,3 +210,20 @@ message Event { //real name: EventProtobuf; where is shortName, shortDescription message Events { //real name: EventsProtobuf repeated Event events = 1; } + +message PrivateEventProto { + required uint64 id = 1; + optional Sport sport = 2; + optional uint64 eventStart = 3; + optional uint64 routeId = 4; + optional uint64 startLocation = 5; + optional uint32 durationInSeconds = 6; + optional float distanceInMeters = 7; + optional string description = 8; + optional uint64 workoutHash = 9; //or laps + optional uint64 organizerId = 10; + repeated EventInviteProto eventInvites = 11; + optional bool showResults = 12; + optional uint32 laps = 13; //or workoutHash + optional bool rubberbanding = 14; +} \ No newline at end of file diff --git a/protobuf/events_pb2.py b/protobuf/events_pb2.py index 4719986..b292d93 100644 --- a/protobuf/events_pb2.py +++ b/protobuf/events_pb2.py @@ -16,8 +16,10 @@ _sym_db = _symbol_database.Default() import profile_pb2 as profile__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x65vents.proto\x1a\rprofile.proto\"\x97\x01\n\x13InvitedProfileProto\x12\x0e\n\x06ipp_f1\x18\x01 \x02(\x04\x12\x0e\n\x06ipp_f2\x18\x02 \x01(\t\x12\x0e\n\x06ipp_f3\x18\x03 \x01(\t\x12\x0e\n\x06ipp_f4\x18\x04 \x01(\t\x12\x0e\n\x06ipp_f5\x18\x05 \x01(\x08\x12\x0e\n\x06ipp_f6\x18\x06 \x01(\x08\x12 \n\x0bplayer_type\x18\x07 \x01(\x0e\x32\x0b.PlayerType\"L\n\x10\x45ventInviteProto\x12%\n\x07profile\x18\x01 \x02(\x0b\x32\x14.InvitedProfileProto\x12\x11\n\tinvite_f2\x18\x02 \x02(\x04\"\xd8\x02\n\x15PrivateEventFeedProto\x12\x0e\n\x06pef_f1\x18\x01 \x02(\x04\x12\x15\n\x05sport\x18\x02 \x01(\x0e\x32\x06.Sport\x12\x1a\n\x12\x65ventSubgroupStart\x18\x03 \x01(\x04\x12\x10\n\x08route_id\x18\x04 \x01(\x04\x12\x0e\n\x06pef_f5\x18\x05 \x01(\r\x12\x0e\n\x06pef_f6\x18\x06 \x01(\x02\x12\x0e\n\x06pef_f7\x18\x07 \x01(\x04\x12\x0e\n\x06pef_f8\x18\x08 \x01(\r\x12\x0e\n\x06pef_f9\x18\t \x01(\r\x12\x0f\n\x07pef_f10\x18\n \x01(\r\x12\x0f\n\x07pef_f11\x18\x0b \x01(\t\x12\x0f\n\x07pef_f12\x18\x0c \x01(\x04\x12\x0f\n\x07pef_f13\x18\r \x01(\t\x12\x0f\n\x07pef_f14\x18\x0e \x01(\t\x12\x0f\n\x07pef_f15\x18\x0f \x01(\x04\x12\x12\n\nsubgroupId\x18\x10 \x01(\x04\x12\x0f\n\x07pef_f17\x18\x11 \x01(\r\x12\x0f\n\x07pef_f18\x18\x12 \x01(\x08\"@\n\x19PrivateEventFeedListProto\x12#\n\x03pef\x18\x01 \x03(\x0b\x32\x16.PrivateEventFeedProto\"H\n\x11PlayerJoinedEvent\x12\x10\n\x08\x65v_sg_id\x18\x01 \x02(\x04\x12\x11\n\tplayer_id\x18\x02 \x01(\x04\x12\x0e\n\x06pje_f3\x18\x03 \x01(\x04\"F\n\x0fPlayerLeftEvent\x12\x10\n\x08\x65v_sg_id\x18\x01 \x02(\x04\x12\x11\n\tplayer_id\x18\x02 \x01(\x04\x12\x0e\n\x06ple_f3\x18\x03 \x01(\x04\"\xd3\x07\n\x15\x45ventSubgroupProtobuf\x12\n\n\x02id\x18\x01 \x02(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0e\n\x06\x65vs_f4\x18\x04 \x01(\r\x12\x0e\n\x06\x65vs_f5\x18\x05 \x01(\r\x12\x0e\n\x06\x65vs_f6\x18\x06 \x01(\r\x12\r\n\x05scode\x18\x07 \x01(\t\x12\x10\n\x08rules_id\x18\x08 \x01(\x04\x12\x19\n\x11registrationStart\x18\t \x01(\x04\x12\x1b\n\x13registrationStartWT\x18\n \x01(\x04\x12\x17\n\x0fregistrationEnd\x18\x0b \x01(\x04\x12\x19\n\x11registrationEndWT\x18\x0c \x01(\x04\x12\x13\n\x0blineUpStart\x18\r \x01(\x04\x12\x15\n\rlineUpStartWT\x18\x0e \x01(\x04\x12\x11\n\tlineUpEnd\x18\x0f \x01(\x04\x12\x13\n\x0blineUpEndWT\x18\x10 \x01(\x04\x12\x1a\n\x12\x65ventSubgroupStart\x18\x11 \x01(\x04\x12\x1c\n\x14\x65ventSubgroupStartWT\x18\x12 \x01(\x04\x12\x0f\n\x07\x65vs_f19\x18\x13 \x01(\x04\x12\x0f\n\x07\x65vs_f20\x18\x14 \x01(\x04\x12\x0f\n\x07\x65vs_f21\x18\x15 \x01(\x08\x12\x10\n\x08route_id\x18\x16 \x02(\x04\x12\x16\n\x0einvitedLeaders\x18\x17 \x03(\x04\x12\x18\n\x10\x64istanceInMeters\x18\x18 \x01(\x02\x12\x0c\n\x04laps\x18\x19 \x01(\r\x12\x15\n\rstartLocation\x18\x1d \x01(\x04\x12\r\n\x05label\x18\x1e \x01(\r\x12\x10\n\x08paceType\x18\x1f \x01(\r\x12\x15\n\rfromPaceValue\x18 \x01(\x02\x12\x13\n\x0btoPaceValue\x18! \x01(\x02\x12\x19\n\x11\x64urationInSeconds\x18\" \x01(\r\x12\x0f\n\x07\x65vs_f35\x18# \x01(\r\x12\x12\n\njerseyHash\x18$ \x01(\x04\x12\x0f\n\x07\x65vs_f37\x18% \x01(\x08\x12\x0f\n\x07\x65vs_f38\x18& \x01(\r\x12\x14\n\x0c\x61uxiliaryUrl\x18\' \x01(\t\x12\x10\n\x08\x62ikeHash\x18( \x01(\x04\x12\x17\n\x0finvitedSweepers\x18) \x03(\x04\x12\x0f\n\x07\x65vs_f42\x18* \x01(\x04\x12\x11\n\tcustomUrl\x18+ \x01(\t\x12\x0f\n\x07\x65vs_f44\x18, \x01(\x08\x12\x0c\n\x04tags\x18- \x01(\t\x12\x19\n\x11lateJoinInMinutes\x18. \x01(\r\x12\x11\n\tcourse_id\x18/ \x01(\x04\x12\x0f\n\x07\x65vs_f48\x18\x30 \x01(\x04\x12\x10\n\x08routeUrl\x18\x31 \x01(\t\x12\x0f\n\x07\x65vs_f50\x18\x32 \x03(\x05\x12\x0f\n\x07\x65vs_f51\x18\x33 \x01(\x08\"g\n\x15MicroserviceEventData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1a\n\x12\x65xternalResourceId\x18\x02 \x01(\x0c\x12$\n\nvisibility\x18\x03 \x01(\x0e\x32\x10.EventVisibility\"D\n\x13\x45ventSeriesProtobuf\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\"\x7f\n\x15\x45ventTimeTrialOptions\x12\x1c\n\x14timeGapBetweenRowsMs\x18\x01 \x01(\r\x12\x0f\n\x07maxRows\x18\x02 \x01(\r\x12\x17\n\x0fmaxRidersPerRow\x18\x03 \x01(\r\x12\x0e\n\x06\x65vt_f4\x18\x04 \x01(\r\x12\x0e\n\x06\x65vt_f5\x18\x05 \x01(\x04\"\xcf\x06\n\x05\x45vent\x12\n\n\x02id\x18\x01 \x02(\x04\x12\x14\n\x0cserver_realm\x18\x02 \x01(\x04\x12\x0c\n\x04name\x18\x03 \x02(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\x12\n\neventStart\x18\x05 \x01(\x04\x12\x0c\n\x04\x65_f6\x18\x06 \x01(\t\x12\x18\n\x10\x64istanceInMeters\x18\x07 \x01(\x02\x12\x0c\n\x04laps\x18\x08 \x01(\r\x12\x0c\n\x04\x65_f9\x18\t \x01(\r\x12(\n\x08\x63\x61tegory\x18\n \x03(\x0b\x32\x16.EventSubgroupProtobuf\x12\r\n\x05\x65_f11\x18\x0b \x01(\t\x12\x10\n\x08imageUrl\x18\x0c \x01(\t\x12\x19\n\x11\x64urationInSeconds\x18\r \x01(\r\x12\x10\n\x08route_id\x18\x0e \x01(\x04\x12\x10\n\x08rules_id\x18\x0f \x01(\x04\x12\r\n\x05\x65_f16\x18\x10 \x01(\r\x12\x0f\n\x07visible\x18\x11 \x01(\x08\x12\x12\n\njerseyHash\x18\x12 \x01(\x04\x12\r\n\x05\x65_f19\x18\x13 \x01(\t\x12\r\n\x05\x65_f20\x18\x14 \x01(\t\x12\r\n\x05\x65_f21\x18\x15 \x01(\t\x12\x15\n\x05sport\x18\x16 \x01(\x0e\x32\x06.Sport\x12\r\n\x05\x65_f23\x18\x17 \x01(\x04\x12\x1d\n\teventType\x18\x18 \x01(\x0e\x32\n.EventType\x12\r\n\x05\x65_f25\x18\x19 \x01(\x04\x12\r\n\x05\x65_f26\x18\x1a \x01(\t\x12\r\n\x05\x65_f27\x18\x1b \x01(\x04\x12\x1e\n\x16overrideMapPreferences\x18\x1c \x01(\x08\x12\"\n\x1ainvisibleToNonParticipants\x18\x1d \x01(\x08\x12&\n\x08\x65vSeries\x18\x1e \x01(\x0b\x32\x14.EventSeriesProtobuf\x12\x0c\n\x04tags\x18\x1f \x01(\t\x12\r\n\x05\x65_f32\x18 \x01(\x04\x12\x0e\n\x06\x65_wtrl\x18! \x01(\x08\x12\x19\n\x11lateJoinInMinutes\x18\" \x01(\r\x12\x11\n\tcourse_id\x18# \x01(\x04\x12#\n\x03tto\x18$ \x01(\x0b\x32\x16.EventTimeTrialOptions\x12\r\n\x05\x65_f37\x18% \x01(\t\x12\r\n\x05\x65_f38\x18& \x01(\t\x12\r\n\x05\x65_f39\x18\' \x01(\r\x12$\n\x04msed\x18( \x01(\x0b\x32\x16.MicroserviceEventData\x12\r\n\x05\x65_f41\x18) \x03(\r\" \n\x06\x45vents\x12\x16\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x06.Event*V\n\x0f\x45ventVisibility\x12\x0b\n\x07\x45V_NULL\x10\x00\x12\x10\n\x0c\x45V_PUB_SHARE\x10\x01\x12\x12\n\x0e\x45V_BY_RESOURCE\x10\x02\x12\x10\n\x0c\x45V_SHAREABLE\x10\x03*\xa5\x01\n\x0b\x45ventTypeV2\x12\x16\n\x12\x45VENT_TYPE_UNKNOWN\x10\x00\x12\x15\n\x11\x45VENT_TYPE_EFONDO\x10\x01\x12\x13\n\x0f\x45VENT_TYPE_RACE\x10\x02\x12\x19\n\x15\x45VENT_TYPE_GROUP_RIDE\x10\x03\x12\x1c\n\x18\x45VENT_TYPE_GROUP_WORKOUT\x10\x04\x12\x19\n\x15\x45VENT_TYPE_TIME_TRIAL\x10\x05*d\n\tEventType\x12\x0e\n\nET_UNKNOWN\x10\x00\x12\n\n\x06\x45\x46ONDO\x10\x01\x12\x08\n\x04RACE\x10\x02\x12\x0e\n\nGROUP_RIDE\x10\x03\x12\x11\n\rGROUP_WORKOUT\x10\x04\x12\x0e\n\nTIME_TRIAL\x10\x05*o\n\x0c\x45ventCulling\x12\x15\n\x11\x43ULLING_UNDEFINED\x10\x00\x12\x15\n\x11\x43ULLING_EVERYBODY\x10\x01\x12\x16\n\x12\x43ULLING_EVENT_ONLY\x10\x02\x12\x19\n\x15\x43ULLING_SUBGROUP_ONLY\x10\x03') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x65vents.proto\x1a\rprofile.proto\"\xad\x01\n\x13InvitedProfileProto\x12\x11\n\tplayer_id\x18\x01 \x02(\x04\x12\x11\n\tfirstName\x18\x02 \x01(\t\x12\x10\n\x08lastName\x18\x03 \x01(\t\x12\x10\n\x08imageSrc\x18\x04 \x01(\t\x12\x1c\n\x14\x65nrolledZwiftAcademy\x18\x05 \x01(\x08\x12\x0c\n\x04male\x18\x06 \x01(\x08\x12 \n\x0bplayer_type\x18\x07 \x01(\x0e\x32\x0b.PlayerType\"]\n\x10\x45ventInviteProto\x12%\n\x07profile\x18\x01 \x01(\x0b\x32\x14.InvitedProfileProto\x12\"\n\x06status\x18\x02 \x02(\x0e\x32\x12.EventInviteStatus\"\xcb\x03\n\x15PrivateEventFeedProto\x12\x10\n\x08\x65vent_id\x18\x01 \x02(\x04\x12\x15\n\x05sport\x18\x02 \x01(\x0e\x32\x06.Sport\x12\x1a\n\x12\x65ventSubgroupStart\x18\x03 \x01(\x04\x12\x10\n\x08route_id\x18\x04 \x01(\x04\x12\x19\n\x11\x64urationInSeconds\x18\x05 \x01(\r\x12\x18\n\x10\x64istanceInMeters\x18\x06 \x01(\x02\x12\x15\n\ransweredCount\x18\x07 \x01(\x04\x12\x19\n\x11invitedTotalCount\x18\x08 \x01(\r\x12\x1d\n\x15\x61\x63\x63\x65ptedFolloweeCount\x18\t \x01(\r\x12\x1a\n\x12\x61\x63\x63\x65ptedTotalCount\x18\n \x01(\r\x12\x19\n\x11organizerImageUrl\x18\x0b \x01(\t\x12\x1a\n\x12organizerProfileId\x18\x0c \x01(\x04\x12\x1a\n\x12organizerFirstName\x18\r \x01(\t\x12\x19\n\x11organizerLastName\x18\x0e \x01(\t\x12\x12\n\nupdateDate\x18\x0f \x01(\x04\x12\x12\n\nsubgroupId\x18\x10 \x01(\x04\x12\x0c\n\x04laps\x18\x11 \x01(\r\x12\x15\n\rrubberbanding\x18\x12 \x01(\x08\"@\n\x19PrivateEventFeedListProto\x12#\n\x03pef\x18\x01 \x03(\x0b\x32\x16.PrivateEventFeedProto\"F\n\x11PlayerJoinedEvent\x12\x0e\n\x06rel_id\x18\x01 \x02(\x04\x12\x11\n\tplayer_id\x18\x02 \x01(\x04\x12\x0e\n\x06pje_f3\x18\x03 \x01(\x04\"D\n\x0fPlayerLeftEvent\x12\x0e\n\x06rel_id\x18\x01 \x02(\x04\x12\x11\n\tplayer_id\x18\x02 \x01(\x04\x12\x0e\n\x06ple_f3\x18\x03 \x01(\x04\"\xd3\x07\n\x15\x45ventSubgroupProtobuf\x12\n\n\x02id\x18\x01 \x02(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0e\n\x06\x65vs_f4\x18\x04 \x01(\r\x12\x0e\n\x06\x65vs_f5\x18\x05 \x01(\r\x12\x0e\n\x06\x65vs_f6\x18\x06 \x01(\r\x12\r\n\x05scode\x18\x07 \x01(\t\x12\x10\n\x08rules_id\x18\x08 \x01(\x04\x12\x19\n\x11registrationStart\x18\t \x01(\x04\x12\x1b\n\x13registrationStartWT\x18\n \x01(\x04\x12\x17\n\x0fregistrationEnd\x18\x0b \x01(\x04\x12\x19\n\x11registrationEndWT\x18\x0c \x01(\x04\x12\x13\n\x0blineUpStart\x18\r \x01(\x04\x12\x15\n\rlineUpStartWT\x18\x0e \x01(\x04\x12\x11\n\tlineUpEnd\x18\x0f \x01(\x04\x12\x13\n\x0blineUpEndWT\x18\x10 \x01(\x04\x12\x1a\n\x12\x65ventSubgroupStart\x18\x11 \x01(\x04\x12\x1c\n\x14\x65ventSubgroupStartWT\x18\x12 \x01(\x04\x12\x0f\n\x07\x65vs_f19\x18\x13 \x01(\x04\x12\x0f\n\x07\x65vs_f20\x18\x14 \x01(\x04\x12\x0f\n\x07\x65vs_f21\x18\x15 \x01(\x08\x12\x10\n\x08route_id\x18\x16 \x02(\x04\x12\x16\n\x0einvitedLeaders\x18\x17 \x03(\x04\x12\x18\n\x10\x64istanceInMeters\x18\x18 \x01(\x02\x12\x0c\n\x04laps\x18\x19 \x01(\r\x12\x15\n\rstartLocation\x18\x1d \x01(\x04\x12\r\n\x05label\x18\x1e \x01(\r\x12\x10\n\x08paceType\x18\x1f \x01(\r\x12\x15\n\rfromPaceValue\x18 \x01(\x02\x12\x13\n\x0btoPaceValue\x18! \x01(\x02\x12\x19\n\x11\x64urationInSeconds\x18\" \x01(\r\x12\x0f\n\x07\x65vs_f35\x18# \x01(\r\x12\x12\n\njerseyHash\x18$ \x01(\x04\x12\x0f\n\x07\x65vs_f37\x18% \x01(\x08\x12\x0f\n\x07\x65vs_f38\x18& \x01(\r\x12\x14\n\x0c\x61uxiliaryUrl\x18\' \x01(\t\x12\x10\n\x08\x62ikeHash\x18( \x01(\x04\x12\x17\n\x0finvitedSweepers\x18) \x03(\x04\x12\x0f\n\x07\x65vs_f42\x18* \x01(\x04\x12\x11\n\tcustomUrl\x18+ \x01(\t\x12\x0f\n\x07\x65vs_f44\x18, \x01(\x08\x12\x0c\n\x04tags\x18- \x01(\t\x12\x19\n\x11lateJoinInMinutes\x18. \x01(\r\x12\x11\n\tcourse_id\x18/ \x01(\x04\x12\x0f\n\x07\x65vs_f48\x18\x30 \x01(\x04\x12\x10\n\x08routeUrl\x18\x31 \x01(\t\x12\x0f\n\x07\x65vs_f50\x18\x32 \x03(\x05\x12\x0f\n\x07\x65vs_f51\x18\x33 \x01(\x08\"g\n\x15MicroserviceEventData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1a\n\x12\x65xternalResourceId\x18\x02 \x01(\x0c\x12$\n\nvisibility\x18\x03 \x01(\x0e\x32\x10.EventVisibility\"D\n\x13\x45ventSeriesProtobuf\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\"\x7f\n\x15\x45ventTimeTrialOptions\x12\x1c\n\x14timeGapBetweenRowsMs\x18\x01 \x01(\r\x12\x0f\n\x07maxRows\x18\x02 \x01(\r\x12\x17\n\x0fmaxRidersPerRow\x18\x03 \x01(\r\x12\x0e\n\x06\x65vt_f4\x18\x04 \x01(\r\x12\x0e\n\x06\x65vt_f5\x18\x05 \x01(\x04\"\xcf\x06\n\x05\x45vent\x12\n\n\x02id\x18\x01 \x02(\x04\x12\x14\n\x0cserver_realm\x18\x02 \x01(\x04\x12\x0c\n\x04name\x18\x03 \x02(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\x12\n\neventStart\x18\x05 \x01(\x04\x12\x0c\n\x04\x65_f6\x18\x06 \x01(\t\x12\x18\n\x10\x64istanceInMeters\x18\x07 \x01(\x02\x12\x0c\n\x04laps\x18\x08 \x01(\r\x12\x0c\n\x04\x65_f9\x18\t \x01(\r\x12(\n\x08\x63\x61tegory\x18\n \x03(\x0b\x32\x16.EventSubgroupProtobuf\x12\r\n\x05\x65_f11\x18\x0b \x01(\t\x12\x10\n\x08imageUrl\x18\x0c \x01(\t\x12\x19\n\x11\x64urationInSeconds\x18\r \x01(\r\x12\x10\n\x08route_id\x18\x0e \x01(\x04\x12\x10\n\x08rules_id\x18\x0f \x01(\x04\x12\r\n\x05\x65_f16\x18\x10 \x01(\r\x12\x0f\n\x07visible\x18\x11 \x01(\x08\x12\x12\n\njerseyHash\x18\x12 \x01(\x04\x12\r\n\x05\x65_f19\x18\x13 \x01(\t\x12\r\n\x05\x65_f20\x18\x14 \x01(\t\x12\r\n\x05\x65_f21\x18\x15 \x01(\t\x12\x15\n\x05sport\x18\x16 \x01(\x0e\x32\x06.Sport\x12\r\n\x05\x65_f23\x18\x17 \x01(\x04\x12\x1d\n\teventType\x18\x18 \x01(\x0e\x32\n.EventType\x12\r\n\x05\x65_f25\x18\x19 \x01(\x04\x12\r\n\x05\x65_f26\x18\x1a \x01(\t\x12\r\n\x05\x65_f27\x18\x1b \x01(\x04\x12\x1e\n\x16overrideMapPreferences\x18\x1c \x01(\x08\x12\"\n\x1ainvisibleToNonParticipants\x18\x1d \x01(\x08\x12&\n\x08\x65vSeries\x18\x1e \x01(\x0b\x32\x14.EventSeriesProtobuf\x12\x0c\n\x04tags\x18\x1f \x01(\t\x12\r\n\x05\x65_f32\x18 \x01(\x04\x12\x0e\n\x06\x65_wtrl\x18! \x01(\x08\x12\x19\n\x11lateJoinInMinutes\x18\" \x01(\r\x12\x11\n\tcourse_id\x18# \x01(\x04\x12#\n\x03tto\x18$ \x01(\x0b\x32\x16.EventTimeTrialOptions\x12\r\n\x05\x65_f37\x18% \x01(\t\x12\r\n\x05\x65_f38\x18& \x01(\t\x12\r\n\x05\x65_f39\x18\' \x01(\r\x12$\n\x04msed\x18( \x01(\x0b\x32\x16.MicroserviceEventData\x12\r\n\x05\x65_f41\x18) \x03(\r\" \n\x06\x45vents\x12\x16\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x06.Event\"\xc9\x02\n\x11PrivateEventProto\x12\n\n\x02id\x18\x01 \x02(\x04\x12\x15\n\x05sport\x18\x02 \x01(\x0e\x32\x06.Sport\x12\x12\n\neventStart\x18\x03 \x01(\x04\x12\x0f\n\x07routeId\x18\x04 \x01(\x04\x12\x15\n\rstartLocation\x18\x05 \x01(\x04\x12\x19\n\x11\x64urationInSeconds\x18\x06 \x01(\r\x12\x18\n\x10\x64istanceInMeters\x18\x07 \x01(\x02\x12\x13\n\x0b\x64\x65scription\x18\x08 \x01(\t\x12\x13\n\x0bworkoutHash\x18\t \x01(\x04\x12\x13\n\x0borganizerId\x18\n \x01(\x04\x12\'\n\x0c\x65ventInvites\x18\x0b \x03(\x0b\x32\x11.EventInviteProto\x12\x13\n\x0bshowResults\x18\x0c \x01(\x08\x12\x0c\n\x04laps\x18\r \x01(\r\x12\x15\n\rrubberbanding\x18\x0e \x01(\x08*<\n\x11\x45ventInviteStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x41\x43\x43\x45PTED\x10\x01\x12\x0c\n\x08REJECTED\x10\x02*V\n\x0f\x45ventVisibility\x12\x0b\n\x07\x45V_NULL\x10\x00\x12\x10\n\x0c\x45V_PUB_SHARE\x10\x01\x12\x12\n\x0e\x45V_BY_RESOURCE\x10\x02\x12\x10\n\x0c\x45V_SHAREABLE\x10\x03*\xa5\x01\n\x0b\x45ventTypeV2\x12\x16\n\x12\x45VENT_TYPE_UNKNOWN\x10\x00\x12\x15\n\x11\x45VENT_TYPE_EFONDO\x10\x01\x12\x13\n\x0f\x45VENT_TYPE_RACE\x10\x02\x12\x19\n\x15\x45VENT_TYPE_GROUP_RIDE\x10\x03\x12\x1c\n\x18\x45VENT_TYPE_GROUP_WORKOUT\x10\x04\x12\x19\n\x15\x45VENT_TYPE_TIME_TRIAL\x10\x05*d\n\tEventType\x12\x0e\n\nET_UNKNOWN\x10\x00\x12\n\n\x06\x45\x46ONDO\x10\x01\x12\x08\n\x04RACE\x10\x02\x12\x0e\n\nGROUP_RIDE\x10\x03\x12\x11\n\rGROUP_WORKOUT\x10\x04\x12\x0e\n\nTIME_TRIAL\x10\x05*o\n\x0c\x45ventCulling\x12\x15\n\x11\x43ULLING_UNDEFINED\x10\x00\x12\x15\n\x11\x43ULLING_EVERYBODY\x10\x01\x12\x16\n\x12\x43ULLING_EVENT_ONLY\x10\x02\x12\x19\n\x15\x43ULLING_SUBGROUP_ONLY\x10\x03') +_EVENTINVITESTATUS = DESCRIPTOR.enum_types_by_name['EventInviteStatus'] +EventInviteStatus = enum_type_wrapper.EnumTypeWrapper(_EVENTINVITESTATUS) _EVENTVISIBILITY = DESCRIPTOR.enum_types_by_name['EventVisibility'] EventVisibility = enum_type_wrapper.EnumTypeWrapper(_EVENTVISIBILITY) _EVENTTYPEV2 = DESCRIPTOR.enum_types_by_name['EventTypeV2'] @@ -26,6 +28,9 @@ _EVENTTYPE = DESCRIPTOR.enum_types_by_name['EventType'] EventType = enum_type_wrapper.EnumTypeWrapper(_EVENTTYPE) _EVENTCULLING = DESCRIPTOR.enum_types_by_name['EventCulling'] EventCulling = enum_type_wrapper.EnumTypeWrapper(_EVENTCULLING) +PENDING = 0 +ACCEPTED = 1 +REJECTED = 2 EV_NULL = 0 EV_PUB_SHARE = 1 EV_BY_RESOURCE = 2 @@ -60,6 +65,7 @@ _EVENTSERIESPROTOBUF = DESCRIPTOR.message_types_by_name['EventSeriesProtobuf'] _EVENTTIMETRIALOPTIONS = DESCRIPTOR.message_types_by_name['EventTimeTrialOptions'] _EVENT = DESCRIPTOR.message_types_by_name['Event'] _EVENTS = DESCRIPTOR.message_types_by_name['Events'] +_PRIVATEEVENTPROTO = DESCRIPTOR.message_types_by_name['PrivateEventProto'] InvitedProfileProto = _reflection.GeneratedProtocolMessageType('InvitedProfileProto', (_message.Message,), { 'DESCRIPTOR' : _INVITEDPROFILEPROTO, '__module__' : 'events_pb2' @@ -144,39 +150,50 @@ Events = _reflection.GeneratedProtocolMessageType('Events', (_message.Message,), }) _sym_db.RegisterMessage(Events) +PrivateEventProto = _reflection.GeneratedProtocolMessageType('PrivateEventProto', (_message.Message,), { + 'DESCRIPTOR' : _PRIVATEEVENTPROTO, + '__module__' : 'events_pb2' + # @@protoc_insertion_point(class_scope:PrivateEventProto) + }) +_sym_db.RegisterMessage(PrivateEventProto) + if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - _EVENTVISIBILITY._serialized_start=2992 - _EVENTVISIBILITY._serialized_end=3078 - _EVENTTYPEV2._serialized_start=3081 - _EVENTTYPEV2._serialized_end=3246 - _EVENTTYPE._serialized_start=3248 - _EVENTTYPE._serialized_end=3348 - _EVENTCULLING._serialized_start=3350 - _EVENTCULLING._serialized_end=3461 + _EVENTINVITESTATUS._serialized_start=3474 + _EVENTINVITESTATUS._serialized_end=3534 + _EVENTVISIBILITY._serialized_start=3536 + _EVENTVISIBILITY._serialized_end=3622 + _EVENTTYPEV2._serialized_start=3625 + _EVENTTYPEV2._serialized_end=3790 + _EVENTTYPE._serialized_start=3792 + _EVENTTYPE._serialized_end=3892 + _EVENTCULLING._serialized_start=3894 + _EVENTCULLING._serialized_end=4005 _INVITEDPROFILEPROTO._serialized_start=32 - _INVITEDPROFILEPROTO._serialized_end=183 - _EVENTINVITEPROTO._serialized_start=185 - _EVENTINVITEPROTO._serialized_end=261 - _PRIVATEEVENTFEEDPROTO._serialized_start=264 - _PRIVATEEVENTFEEDPROTO._serialized_end=608 - _PRIVATEEVENTFEEDLISTPROTO._serialized_start=610 - _PRIVATEEVENTFEEDLISTPROTO._serialized_end=674 - _PLAYERJOINEDEVENT._serialized_start=676 - _PLAYERJOINEDEVENT._serialized_end=748 - _PLAYERLEFTEVENT._serialized_start=750 - _PLAYERLEFTEVENT._serialized_end=820 - _EVENTSUBGROUPPROTOBUF._serialized_start=823 - _EVENTSUBGROUPPROTOBUF._serialized_end=1802 - _MICROSERVICEEVENTDATA._serialized_start=1804 - _MICROSERVICEEVENTDATA._serialized_end=1907 - _EVENTSERIESPROTOBUF._serialized_start=1909 - _EVENTSERIESPROTOBUF._serialized_end=1977 - _EVENTTIMETRIALOPTIONS._serialized_start=1979 - _EVENTTIMETRIALOPTIONS._serialized_end=2106 - _EVENT._serialized_start=2109 - _EVENT._serialized_end=2956 - _EVENTS._serialized_start=2958 - _EVENTS._serialized_end=2990 + _INVITEDPROFILEPROTO._serialized_end=205 + _EVENTINVITEPROTO._serialized_start=207 + _EVENTINVITEPROTO._serialized_end=300 + _PRIVATEEVENTFEEDPROTO._serialized_start=303 + _PRIVATEEVENTFEEDPROTO._serialized_end=762 + _PRIVATEEVENTFEEDLISTPROTO._serialized_start=764 + _PRIVATEEVENTFEEDLISTPROTO._serialized_end=828 + _PLAYERJOINEDEVENT._serialized_start=830 + _PLAYERJOINEDEVENT._serialized_end=900 + _PLAYERLEFTEVENT._serialized_start=902 + _PLAYERLEFTEVENT._serialized_end=970 + _EVENTSUBGROUPPROTOBUF._serialized_start=973 + _EVENTSUBGROUPPROTOBUF._serialized_end=1952 + _MICROSERVICEEVENTDATA._serialized_start=1954 + _MICROSERVICEEVENTDATA._serialized_end=2057 + _EVENTSERIESPROTOBUF._serialized_start=2059 + _EVENTSERIESPROTOBUF._serialized_end=2127 + _EVENTTIMETRIALOPTIONS._serialized_start=2129 + _EVENTTIMETRIALOPTIONS._serialized_end=2256 + _EVENT._serialized_start=2259 + _EVENT._serialized_end=3106 + _EVENTS._serialized_start=3108 + _EVENTS._serialized_end=3140 + _PRIVATEEVENTPROTO._serialized_start=3143 + _PRIVATEEVENTPROTO._serialized_end=3472 # @@protoc_insertion_point(module_scope) diff --git a/variants.txt b/variants.txt index 7b1b8e2..bf112d5 100644 --- a/variants.txt +++ b/variants.txt @@ -249,6 +249,7 @@ }, { "name": "game_1_20_home_screen", + "value": true, "values": {} }, { diff --git a/zwift_offline.py b/zwift_offline.py index 91a0847..e7f1161 100644 --- a/zwift_offline.py +++ b/zwift_offline.py @@ -207,10 +207,23 @@ class AnonUser(User, AnonymousUserMixin, db.Model): return True class PartialProfile: + player_id = 0 first_name = '' last_name = '' country_code = 0 route = 0 + player_type = 'NORMAL' + male = True + imageSrc = '' + def to_json(self): + return {"countryCode": self.country_code, + "enrolledZwiftAcademy": False, #don't need + "firstName": self.first_name, + "id": self.player_id, + "imageSrc": self.imageSrc, + "lastName": self.last_name, + "male": self.male, + "playerType": self.player_type } class Online: total = 0 @@ -312,9 +325,13 @@ def get_partial_profile(player_id): profile = profile_pb2.PlayerProfile() profile.ParseFromString(fd.read()) partial_profile = PartialProfile() + partial_profile.player_id = player_id + partial_profile.imageSrc = "https://us-or-rly101.zwift.com/download/%s/avatarLarge.jpg" % player_id partial_profile.first_name = profile.first_name partial_profile.last_name = profile.last_name partial_profile.country_code = profile.country_code + partial_profile.player_type = profile_pb2.PlayerType.Name(jsf(profile, 'player_type', 1)) + partial_profile.male = profile.is_male for f in profile.public_attributes: #0x69520F20=1766985504 - crc32 of "PACE PARTNER - ROUTE" #TODO: -1021012238: figure out @@ -653,6 +670,11 @@ def user_home(username): return render_template("user_home.html", username=current_user.username, enable_ghosts=bool(current_user.enable_ghosts), online=get_online(), is_admin=current_user.is_admin, restarting=restarting, restarting_in_minutes=restarting_in_minutes, server_ip=os.path.exists(SERVER_IP_FILE)) +def enqueue_player_update(player_id, wa_bytes): + if not player_id in player_update_queue: + player_update_queue[player_id] = list() + player_update_queue[player_id].append(wa_bytes) + def send_message_to_all_online(message, sender='Server'): player_update = udp_node_msgs_pb2.WorldAttribute() player_update.server_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID @@ -672,11 +694,9 @@ def send_message_to_all_online(message, sender='Server'): chat_message.countryCode = 0 player_update.payload = chat_message.SerializeToString() - - for recieving_player_id in online.keys(): - if not recieving_player_id in player_update_queue: - player_update_queue[recieving_player_id] = list() - player_update_queue[recieving_player_id].append(player_update.SerializeToString()) + player_update_s = player_update.SerializeToString() + for receiving_player_id in online.keys(): + enqueue_player_update(receiving_player_id, player_update_s) def send_restarting_message(): @@ -922,7 +942,7 @@ def activity_moving_time(activity): def activity_protobuf_to_json(activity): profile = get_partial_profile(activity.player_id) return {"id":activity.id,"profile":{"id":str(activity.player_id),"firstName":profile.first_name,"lastName":profile.last_name, \ - "imageSrc":"https://us-or-rly101.zwift.com/download/%s/avatarLarge.jpg" % activity.player_id,"approvalRequired":None}, \ + "imageSrc":profile.imageSrc,"approvalRequired":None}, \ "worldId":activity.f3,"name":activity.name,"sport":str_sport(activity.f29),"startDate":activity.start_date, \ "endDate":activity.end_date,"distanceInMeters":activity.distance, \ "totalElevation":activity.total_elevation,"calories":activity.calories,"primaryImageUrl":"", \ @@ -1141,11 +1161,29 @@ def api_events_search(): else: return events.SerializeToString(), 200 -@app.route('/api/events/subgroups/signup/', methods=['POST']) -@app.route('/api/events/signup/', methods=['DELETE']) +def create_event_wat(rel_id, wa_type, pe, dest_ids): + player_update = udp_node_msgs_pb2.WorldAttribute() + player_update.server_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID + player_update.wa_type = wa_type + player_update.world_time_born = world_time() + player_update.world_time_expire = world_time() + 60000 + player_update.wa_f12 = 1 + player_update.timestamp = int(get_utc_time()*1000000) + player_update.rel_id = current_user.player_id + + pe.rel_id = rel_id + pe.player_id = current_user.player_id + #optional uint64 pje_f3/ple_f3 = 3; + player_update.payload = pe.SerializeToString() + + for receiving_player_id in dest_ids: + enqueue_player_update(receiving_player_id, player_update_s) + +@app.route('/api/events/subgroups/signup/', methods=['POST']) +@app.route('/api/events/signup/', methods=['DELETE']) @jwt_to_session_cookie @login_required -def api_events_subgroups_signup_id(event_id): +def api_events_subgroups_signup_id(rel_id): if request.method == 'POST': wa_type = udp_node_msgs_pb2.WA_TYPE.WAT_JOIN_E pe = events_pb2.PlayerJoinedEvent() @@ -1155,42 +1193,24 @@ def api_events_subgroups_signup_id(event_id): pe = events_pb2.PlayerLeftEvent() ret = False #empty request.data - player_update = udp_node_msgs_pb2.WorldAttribute() - player_update.server_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID - player_update.wa_type = wa_type - player_update.world_time_born = world_time() - player_update.world_time_expire = world_time() + 60000 - player_update.wa_f12 = 1 - player_update.timestamp = int(get_utc_time()*1000000) - - pe.ev_sg_id = event_id - pe.player_id = current_user.player_id - #optional uint64 pje_f3/ple_f3 = 3; - player_update.payload = pe.SerializeToString() - - for recieving_player_id in online.keys(): - if not recieving_player_id in player_update_queue: - player_update_queue[recieving_player_id] = list() - player_update_queue[recieving_player_id].append(player_update.SerializeToString()) - + create_event_wat(rel_id, wa_type, pe, online.keys()) return jsonify({"signedUp":ret}) - -@app.route('/api/events/subgroups/register/', methods=['POST']) -def api_events_subgroups_register_id(event_id): +@app.route('/api/events/subgroups/register/', methods=['POST']) +def api_events_subgroups_register_id(ev_sg_id): return '{"registered":true}', 200 -@app.route('/api/events/subgroups/entrants/', methods=['GET']) -def api_events_subgroups_entrants_id(event_id): +@app.route('/api/events/subgroups/entrants/', methods=['GET']) +def api_events_subgroups_entrants_id(ev_sg_id): return '[]', 200 -@app.route('/api/events/subgroups/invited_ride_sweepers/', methods=['GET']) -def api_events_subgroups_invited_ride_sweepers_id(event_id): +@app.route('/api/events/subgroups/invited_ride_sweepers/', methods=['GET']) +def api_events_subgroups_invited_ride_sweepers_id(ev_sg_id): return '[]', 200 -@app.route('/api/events/subgroups/invited_ride_leaders/', methods=['GET']) -def api_events_subgroups_invited_ride_leaders_id(event_id): +@app.route('/api/events/subgroups/invited_ride_leaders/', methods=['GET']) +def api_events_subgroups_invited_ride_leaders_id(ev_sg_id): return '[]', 200 @app.route('/relay/race/event_starting_line/', methods=['POST']) @@ -1856,10 +1876,10 @@ def api_profiles_activities_id(player_id, activity_id): zwift_upload(player_id, activity) return response, 200 -@app.route('/api/profiles//activities/0/rideon', methods=['POST']) #activity_id Seem to always be 0, even when giving ride on to ppl with 30km+ +@app.route('/api/profiles//activities/0/rideon', methods=['POST']) #activity_id Seem to always be 0, even when giving ride on to ppl with 30km+ @jwt_to_session_cookie @login_required -def api_profiles_activities_rideon(recieving_player_id): +def api_profiles_activities_rideon(receiving_player_id): sending_player_id = request.json['profileId'] profile = get_partial_profile(sending_player_id) if not profile == None: @@ -1872,22 +1892,24 @@ def api_profiles_activities_rideon(recieving_player_id): ride_on = udp_node_msgs_pb2.RideOn() ride_on.player_id = int(sending_player_id) - ride_on.to_player_id = int(recieving_player_id) + ride_on.to_player_id = int(receiving_player_id) ride_on.firstName = profile.first_name ride_on.lastName = profile.last_name ride_on.countryCode = profile.country_code player_update.payload = ride_on.SerializeToString() - if not recieving_player_id in player_update_queue: - player_update_queue[recieving_player_id] = list() - player_update_queue[recieving_player_id].append(player_update.SerializeToString()) + enqueue_player_update(receiving_player_id, player_update.SerializeToString()) - receiver = get_partial_profile(recieving_player_id) + receiver = get_partial_profile(receiving_player_id) message = 'Ride on ' + receiver.first_name + ' ' + receiver.last_name + '!' discord.send_message(message, sending_player_id) return '{}', 200 +def stime_to_timestamp(stime): + utc_offset = datetime.datetime.fromtimestamp(0) - datetime.datetime.utcfromtimestamp(0) + return int((datetime.datetime.strptime(stime, '%Y-%m-%dT%H:%M:%SZ') + utc_offset).timestamp()) + glb_notifications = {} #player_id -> dictionary, todo: move to database glb_cur_notif_id = 1 def create_zca_notification(player_id, private_event, organizer): @@ -1895,9 +1917,8 @@ def create_zca_notification(player_id, private_event, organizer): if not player_id in glb_notifications.keys(): glb_notifications[player_id] = {} d = glb_notifications[player_id] - utc_offset = datetime.datetime.fromtimestamp(0) - datetime.datetime.utcfromtimestamp(0) argString0 = json.dumps({"eventId":private_event['id'],"eventStartDate": \ - int((datetime.datetime.strptime(private_event['eventStart'], '%Y-%m-%dT%H:%M:%SZ') + utc_offset).timestamp()), \ + stime_to_timestamp(private_event['eventStart']), \ "otherInviteeCount":len(private_event['invitedProfileIds'])}) n = { "activity": None, "argLong0": 0, "argLong1": 0, "argString0": argString0, "createdOn": str_timestamp(unix_time_millis(datetime.datetime.now())), @@ -1964,13 +1985,13 @@ def edit_private_event(player_id, meetup_id, decision): @app.route('/api/private_event//accept', methods=['PUT']) @jwt_to_session_cookie @login_required -def api_private_event_accept(meetup_id): #todo: check if world attribute sent +def api_private_event_accept(meetup_id): return edit_private_event(current_user.player_id, meetup_id, 'ACCEPTED') @app.route('/api/private_event//reject', methods=['PUT']) @jwt_to_session_cookie @login_required -def api_private_event_reject(meetup_id): #todo: check if world attribute sent +def api_private_event_reject(meetup_id): return edit_private_event(current_user.player_id, meetup_id, 'REJECTED') @app.route('/api/private_event/', methods=['PUT']) @@ -1992,6 +2013,11 @@ def api_private_event_edit(meetup_id): n['lastModified'] = org_json_pe['updateDate'] return jsonify({"id":meetup_id}) +def enqueue_wa_event_invites(player_id, wa): + for wat in (udp_node_msgs_pb2.WA_TYPE.WAT_EVENT, udp_node_msgs_pb2.WA_TYPE.WAT_INV_W): + wa.wa_type = wat + enqueue_player_update(player_id, wa.SerializeToString()) + @app.route('/api/private_event', methods=['POST']) @jwt_to_session_cookie @login_required @@ -2006,57 +2032,64 @@ def api_private_event_new(): #{"culling":true,"description":"mesg","distanceInMe json_pe['eventSubgroupId'] = pe_id + 2 json_pe['name'] = "Route #%s" % json_pe['routeId'] #todo: more readable json_pe['acceptedTotalCount'] = len(json_pe['invitedProfileIds']) #todo: real count - json_pe['acceptedFolloweeCount'] = len(json_pe['invitedProfileIds']) #todo: real count - json_pe['invitedTotalCount'] = len(json_pe['invitedProfileIds']) + json_pe['acceptedFolloweeCount'] = len(json_pe['invitedProfileIds']) + 1 #todo: real count + json_pe['invitedTotalCount'] = len(json_pe['invitedProfileIds']) + 1 partial_profile = get_partial_profile(current_user.player_id) json_pe['organizerProfileId'] = current_user.player_id + json_pe['organizerId'] = current_user.player_id + json_pe['startLocation'] = 1 #todo_pe + json_pe['allowsLateJoin'] = True #todo_pe json_pe['organizerFirstName'] = partial_profile.first_name json_pe['organizerLastName'] = partial_profile.last_name json_pe['updateDate'] = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") json_pe['organizerImageUrl'] = "https://us-or-rly101.zwift.com/download/%s/avatarLarge.jpg" % current_user.player_id - eventInvites = [{"invitedProfile": { - "countryCode": partial_profile.country_code, - "enrolledZwiftAcademy": False, #todo - "firstName": partial_profile.first_name, - "id": current_user.player_id, - "imageSrc": json_pe['organizerImageUrl'], - "lastName": partial_profile.last_name, - "male": True, #todo - "playerType": "NORMAL", #todo - }, - "status": "ACCEPTED"}] + eventInvites = [{"invitedProfile": partial_profile.to_json(), "status": "ACCEPTED"}] create_event_wat(ev_sg_id, udp_node_msgs_pb2.WA_TYPE.WAT_JOIN_E, events_pb2.PlayerJoinedEvent(), online.keys()) - pe = events_pb2.EventInviteProto() + pe = events_pb2.Event() player_update = udp_node_msgs_pb2.WorldAttribute() player_update.server_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID - player_update.wa_type = udp_node_msgs_pb2.WA_TYPE.WAT_INV_W player_update.world_time_born = world_time() player_update.world_time_expire = world_time() + 60000 player_update.wa_f12 = 1 player_update.timestamp = int(get_utc_time()*1000000) - pe.invite_f2 = 1 + pe.id = pe_id + pe.server_realm = udp_node_msgs_pb2.ZofflineConstants.RealmID + pe.name = json_pe['name'] + if 'description' in json_pe: + pe.description = json_pe['description'] + pe.eventStart = stime_to_timestamp(json_pe['eventStart'])*1000 + pe.distanceInMeters = json_pe['distanceInMeters'] + pe.laps = json_pe['laps'] + if 'imageUrl' in json_pe: + pe.imageUrl = json_pe['imageUrl'] + pe.durationInSeconds = json_pe['durationInSeconds'] + pe.route_id = json_pe['routeId'] + #{"rubberbanding":true,"showResults":false,"workoutHash":0} todo_pe + pe.visible = True + pe.jerseyHash = 0 + pe.sport = sport_from_str(json_pe['sport']) + #pe.uint64 e_f23 = 23; =0 + pe.eventType = events_pb2.EventType.EFONDO + if 'culling' in json_pe: + if json_pe['culling']: + pe.eventType = events_pb2.EventType.RACE + #pe.uint64 e_f25 = 25; =0 + pe.e_f27 = 2 #<=4, ENUM? saw = 2 + #pe.bool overrideMapPreferences = 28; =0 + #pe.bool invisibleToNonParticipants = 29; =0 todo_pe + pe.lateJoinInMinutes = 30 #todo_pe + #pe.course_id = 1 #todo_pe =f(json_pe['routeId']) ??? player_update.payload = pe.SerializeToString() - player_update_s = player_update.SerializeToString() + enqueue_wa_event_invites(current_user.player_id, player_update) for peer_id in json_pe['invitedProfileIds']: create_zca_notification(peer_id, json_pe, eventInvites[0]["invitedProfile"]) - if not peer_id in player_update_queue: - player_update_queue[peer_id] = list() - player_update_queue[peer_id].append(player_update_s) + player_update.rel_id = peer_id + enqueue_wa_event_invites(peer_id, player_update) p_partial_profile = get_partial_profile(peer_id) - eventInvites.append({"invitedProfile": { - "countryCode": p_partial_profile.country_code, - "enrolledZwiftAcademy": False, #todo - "firstName": p_partial_profile.first_name, - "id": peer_id, - "imageSrc": "https://us-or-rly101.zwift.com/download/%s/avatarLarge.jpg" % peer_id, - "lastName": p_partial_profile.last_name, - "male": True, #todo - "playerType": "NORMAL", #todo - }, - "status": "PENDING"}) + eventInvites.append({"invitedProfile": p_partial_profile.to_json(), "status": "PENDING"}) json_pe['eventInvites'] = eventInvites glb_private_events[pe_id] = json_pe @@ -2076,7 +2109,33 @@ def clone_and_append_social(player_id, private_event): ret['inviteStatus'] = status return ret -@app.route('/api/private_event/feed', methods=['GET', 'POST']) +def jsonPrivateEventFeedToProtobuf(jfeed): + ret = events_pb2.PrivateEventFeedListProto() + #print(jfeed) + for jpef in jfeed: + pef = ret.pef.add() + pef.event_id = jpef['id'] + pef.sport = sport_from_str(jpef['sport']) + pef.eventSubgroupStart = stime_to_timestamp(jpef['eventStart'])*1000 + pef.route_id = jpef['routeId'] + pef.durationInSeconds = jpef['durationInSeconds'] + pef.distanceInMeters = jpef['distanceInMeters'] + pef.answeredCount = 1 #todo + pef.invitedTotalCount = jpef['invitedTotalCount'] + pef.acceptedFolloweeCount = jpef['acceptedFolloweeCount'] + pef.acceptedTotalCount = jpef['acceptedTotalCount'] + pef.organizerImageUrl = jpef['organizerImageUrl'] + pef.organizerProfileId = jpef['organizerProfileId'] + pef.organizerFirstName = jpef['organizerFirstName'] + pef.organizerLastName = jpef['organizerLastName'] + pef.updateDate = stime_to_timestamp(jpef['updateDate'])*1000 + pef.subgroupId = jpef['eventSubgroupId'] + pef.laps = jpef['laps'] + pef.rubberbanding = jpef['rubberbanding'] + #print(ret) + return ret + +@app.route('/api/private_event/feed', methods=['GET']) @jwt_to_session_cookie @login_required def api_private_event_feed(): @@ -2084,7 +2143,39 @@ def api_private_event_feed(): for pe in glb_private_events.values(): ret.append(clone_and_append_social(current_user.player_id, pe)) #print(ret) - return jsonify(ret) + if(request.headers['Accept'] == 'application/json'): + return jsonify(ret) + return jsonPrivateEventFeedToProtobuf(ret).SerializeToString(), 200 + +def jsonPrivateEventToProtobuf(je): + ret = events_pb2.PrivateEventProto() + ret.id = je['id'] + ret.sport = sport_from_str(je['sport']) + ret.eventStart = stime_to_timestamp(je['eventStart'])*1000 + ret.routeId = je['routeId'] + ret.startLocation = je['startLocation'] + ret.durationInSeconds = je['durationInSeconds'] + ret.distanceInMeters = je['distanceInMeters'] + if 'description' in je: + ret.description = je['description'] + ret.workoutHash = je['workoutHash'] + ret.organizerId = je['organizerProfileId'] + for jinv in je['eventInvites']: + jp = jinv['invitedProfile'] + inv = ret.eventInvites.add() + inv.profile.player_id = jp['id'] + inv.profile.firstName = jp['firstName'] + inv.profile.lastName = jp['lastName'] + inv.profile.imageSrc = jp['imageSrc'] + inv.profile.enrolledZwiftAcademy = jp['enrolledZwiftAcademy'] + inv.profile.male = jp['male'] + inv.profile.player_type = profile_pb2.PlayerType.Value(jp['playerType']) + inv.status = events_pb2.EventInviteStatus.Value(jinv['status']) + ret.showResults = je['showResults'] + ret.laps = je['laps'] + ret.rubberbanding = je['rubberbanding'] + #print(je) + return ret @app.route('/api/private_event/', methods=['GET']) @jwt_to_session_cookie @@ -2092,7 +2183,9 @@ def api_private_event_feed(): def api_private_event_id(event_id): ret = clone_and_append_social(current_user.player_id, glb_private_events[event_id]) #print(ret) - return jsonify(ret) + if(request.headers['Accept'] == 'application/json'): + return jsonify(ret) + return jsonPrivateEventToProtobuf(ret).SerializeToString(), 200 @app.route('/api/private_event/entitlement', methods=['GET']) def api_private_event_entitlement(): @@ -2361,10 +2454,10 @@ def relay_worlds_generic(server_realm=None): player_update.world_time_expire = world_time() + 60000 player_update.wa_f12 = 1 player_update.timestamp = int(get_utc_time()*1000000) - for recieving_player_id in online.keys(): + for receiving_player_id in online.keys(): should_receive = False if player_update.wa_type == udp_node_msgs_pb2.WA_TYPE.WAT_SPA or player_update.wa_type == udp_node_msgs_pb2.WA_TYPE.WAT_SR: - recieving_player = online[recieving_player_id] + receiving_player = online[receiving_player_id] #Chat message if player_update.wa_type == udp_node_msgs_pb2.WA_TYPE.WAT_SPA: chat_message = tcp_node_msgs_pb2.SocialPlayerAction() @@ -2373,7 +2466,7 @@ def relay_worlds_generic(server_realm=None): if sending_player_id in online: sending_player = online[sending_player_id] #Check that players are on same course and close to each other - if is_nearby(sending_player, recieving_player): + if is_nearby(sending_player, receiving_player): should_receive = True #Segment complete else: @@ -2383,15 +2476,13 @@ def relay_worlds_generic(server_realm=None): if sending_player_id in online: sending_player = online[sending_player_id] #Check that players are on same course - if get_course(sending_player) == get_course(recieving_player) or recieving_player.watchingRiderId == sending_player_id: + if get_course(sending_player) == get_course(receiving_player) or receiving_player.watchingRiderId == sending_player_id: should_receive = True #Other PlayerUpdate, send to all else: should_receive = True if should_receive: - if not recieving_player_id in player_update_queue: - player_update_queue[recieving_player_id] = list() - player_update_queue[recieving_player_id].append(player_update.SerializeToString()) + enqueue_player_update(receiving_player_id, player_update.SerializeToString()) if player_update.wa_type == udp_node_msgs_pb2.WA_TYPE.WAT_SPA: chat_message = tcp_node_msgs_pb2.SocialPlayerAction() chat_message.ParseFromString(player_update.payload) @@ -2601,9 +2692,7 @@ def relay_worlds_attributes(): else: should_receive = True if should_receive: - if not receiving_player_id in player_update_queue: - player_update_queue[receiving_player_id] = list() - player_update_queue[receiving_player_id].append(player_update.SerializeToString()) + enqueue_player_update(receiving_player_id, player_update.SerializeToString()) # If it's a chat message, send to Discord if player_update.wa_type == udp_node_msgs_pb2.WA_TYPE.WAT_SPA: chat_message = tcp_node_msgs_pb2.SocialPlayerAction()