From b14d65218a408cb807313d7cad035477741b1af7 Mon Sep 17 00:00:00 2001 From: Clergue Valentin Date: Mon, 3 Jun 2024 21:11:40 +0200 Subject: [PATCH] Merge refacto/cleanFcm into develop (#18) * clean-up code * add examples --- RustPlusApi.sln | 28 +- .../Examples/Fcm/FcmListener/Program.cs | 38 --- .../Fcm/FcmRegister/FcmRegister.csproj | 14 - .../Examples/Fcm/FcmRegister/Program.cs | 10 - .../Fcm/RustPlusFcmListener/Program.cs | 75 +++++ .../RustPlusFcmListener.csproj | 15 + .../Fcm/RustPlusFcmListenerClient/Program.cs | 60 ++++ .../RustPlusFcmListenerClient.csproj} | 0 RustPlusApi/RustPlusApi.Fcm/Data/Constants.cs | 113 ------- .../RustPlusApi.Fcm/Data/Credentials.cs | 13 +- .../{AlarmEventArg.cs => AlarmEvent.cs} | 3 +- .../Data/Events/EntityEvent.cs | 9 + .../Data/Events/EntityEventArg.cs | 9 - .../Data/Events/MessageEventArg.cs | 2 +- .../Data/Events/ServerEvent.cs | 14 + .../Data/Events/ServerEventArg.cs | 9 - .../Data/Events/ServerFullEventArg.cs | 12 - .../RustPlusApi.Fcm/Data/FcmMessage.cs | 8 +- .../RustPlusApi.Fcm/Data/Notification.cs | 12 + RustPlusApi/RustPlusApi.Fcm/Data/Tags.cs | 26 ++ .../Extensions/BodyToEventModel.cs | 38 +++ .../Extensions/MessageDataToEventModel.cs | 17 + .../Extensions/StringToFcmMessageString.cs | 28 ++ RustPlusApi/RustPlusApi.Fcm/FcmListener.cs | 110 ------ RustPlusApi/RustPlusApi.Fcm/FcmRegister.cs | 23 -- .../ProtoBuf/AndroidCheckin.cs | 178 ---------- .../RustPlusApi.Fcm/ProtoBuf/Checkin.cs | 315 ------------------ .../ProtoBuf/android_checkin.proto | 96 ------ .../RustPlusApi.Fcm/ProtoBuf/checkin.proto | 155 --------- .../RustPlusApi.Fcm/RustPlusApi.Fcm.csproj | 2 +- .../RustPlusApi.Fcm/RustPlusFcmListener.cs | 94 ++++++ ...rBasic.cs => RustPlusFcmListenerClient.cs} | 225 +++++++------ RustPlusApi/RustPlusApi.Fcm/Tools/FcmTools.cs | 51 --- RustPlusApi/RustPlusApi.Fcm/Tools/GcmTools.cs | 137 -------- .../RustPlusApi.Fcm/Utils/RawMessageParser.cs | 98 ------ .../RustPlusApi.Fcm/Utils/ResponseHelper.cs | 29 ++ RustPlusApi/RustPlusApi.Fcm/Utils/Utils.cs | 52 +++ 37 files changed, 610 insertions(+), 1508 deletions(-) delete mode 100644 RustPlusApi/Examples/Fcm/FcmListener/Program.cs delete mode 100644 RustPlusApi/Examples/Fcm/FcmRegister/FcmRegister.csproj delete mode 100644 RustPlusApi/Examples/Fcm/FcmRegister/Program.cs create mode 100644 RustPlusApi/Examples/Fcm/RustPlusFcmListener/Program.cs create mode 100644 RustPlusApi/Examples/Fcm/RustPlusFcmListener/RustPlusFcmListener.csproj create mode 100644 RustPlusApi/Examples/Fcm/RustPlusFcmListenerClient/Program.cs rename RustPlusApi/Examples/Fcm/{FcmListener/FcmListener.csproj => RustPlusFcmListenerClient/RustPlusFcmListenerClient.csproj} (100%) delete mode 100644 RustPlusApi/RustPlusApi.Fcm/Data/Constants.cs rename RustPlusApi/RustPlusApi.Fcm/Data/Events/{AlarmEventArg.cs => AlarmEvent.cs} (68%) create mode 100644 RustPlusApi/RustPlusApi.Fcm/Data/Events/EntityEvent.cs delete mode 100644 RustPlusApi/RustPlusApi.Fcm/Data/Events/EntityEventArg.cs create mode 100644 RustPlusApi/RustPlusApi.Fcm/Data/Events/ServerEvent.cs delete mode 100644 RustPlusApi/RustPlusApi.Fcm/Data/Events/ServerEventArg.cs delete mode 100644 RustPlusApi/RustPlusApi.Fcm/Data/Events/ServerFullEventArg.cs create mode 100644 RustPlusApi/RustPlusApi.Fcm/Data/Notification.cs create mode 100644 RustPlusApi/RustPlusApi.Fcm/Data/Tags.cs create mode 100644 RustPlusApi/RustPlusApi.Fcm/Extensions/BodyToEventModel.cs create mode 100644 RustPlusApi/RustPlusApi.Fcm/Extensions/MessageDataToEventModel.cs create mode 100644 RustPlusApi/RustPlusApi.Fcm/Extensions/StringToFcmMessageString.cs delete mode 100644 RustPlusApi/RustPlusApi.Fcm/FcmListener.cs delete mode 100644 RustPlusApi/RustPlusApi.Fcm/FcmRegister.cs delete mode 100644 RustPlusApi/RustPlusApi.Fcm/ProtoBuf/AndroidCheckin.cs delete mode 100644 RustPlusApi/RustPlusApi.Fcm/ProtoBuf/Checkin.cs delete mode 100644 RustPlusApi/RustPlusApi.Fcm/ProtoBuf/android_checkin.proto delete mode 100644 RustPlusApi/RustPlusApi.Fcm/ProtoBuf/checkin.proto create mode 100644 RustPlusApi/RustPlusApi.Fcm/RustPlusFcmListener.cs rename RustPlusApi/RustPlusApi.Fcm/{FcmListenerBasic.cs => RustPlusFcmListenerClient.cs} (59%) delete mode 100644 RustPlusApi/RustPlusApi.Fcm/Tools/FcmTools.cs delete mode 100644 RustPlusApi/RustPlusApi.Fcm/Tools/GcmTools.cs delete mode 100644 RustPlusApi/RustPlusApi.Fcm/Utils/RawMessageParser.cs create mode 100644 RustPlusApi/RustPlusApi.Fcm/Utils/ResponseHelper.cs create mode 100644 RustPlusApi/RustPlusApi.Fcm/Utils/Utils.cs diff --git a/RustPlusApi.sln b/RustPlusApi.sln index 60b2043..6f908a7 100644 --- a/RustPlusApi.sln +++ b/RustPlusApi.sln @@ -23,10 +23,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RustPlusApi.Fcm", "RustPlus EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Fcm", "Fcm", "{A4EF8656-723B-4E2E-9457-57DCB24CB409}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FcmRegister", "RustPlusApi\Examples\Fcm\FcmRegister\FcmRegister.csproj", "{FF9758F9-2500-4CE1-AACE-8C67134046F4}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FcmListener", "RustPlusApi\Examples\Fcm\FcmListener\FcmListener.csproj", "{5B175E6B-4118-422D-BF08-F94B7337D229}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GetTeamChat", "RustPlusApi\Examples\GetTeamChat\GetTeamChat.csproj", "{1A186504-8461-4F19-BDC5-C3DB90A554D6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PromoteToLeader", "RustPlusApi\Examples\PromoteToLeader\PromoteToLeader.csproj", "{3DE93E0D-DAF5-48E7-A353-BD99ABB942DE}" @@ -99,6 +95,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StrobeSmartSwitch", "RustPl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SendTeamMessage", "RustPlusApi\Examples\SendTeamMessage\SendTeamMessage.csproj", "{83CC5666-ABBF-4057-BCDA-66A6F1881A7E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RustPlusFcmListenerClient", "RustPlusApi\Examples\Fcm\RustPlusFcmListenerClient\RustPlusFcmListenerClient.csproj", "{89321BD6-3D4E-479B-A795-B30A6898E7BF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RustPlusFcmListener", "RustPlusApi\Examples\Fcm\RustPlusFcmListener\RustPlusFcmListener.csproj", "{4D0DCF6E-ABA1-407F-B0C9-59306E7FC8CA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -137,14 +137,6 @@ Global {CFDFB335-A783-4F82-8094-3ED14E3EBC49}.Debug|Any CPU.Build.0 = Debug|Any CPU {CFDFB335-A783-4F82-8094-3ED14E3EBC49}.Release|Any CPU.ActiveCfg = Release|Any CPU {CFDFB335-A783-4F82-8094-3ED14E3EBC49}.Release|Any CPU.Build.0 = Release|Any CPU - {FF9758F9-2500-4CE1-AACE-8C67134046F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FF9758F9-2500-4CE1-AACE-8C67134046F4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FF9758F9-2500-4CE1-AACE-8C67134046F4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FF9758F9-2500-4CE1-AACE-8C67134046F4}.Release|Any CPU.Build.0 = Release|Any CPU - {5B175E6B-4118-422D-BF08-F94B7337D229}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B175E6B-4118-422D-BF08-F94B7337D229}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B175E6B-4118-422D-BF08-F94B7337D229}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B175E6B-4118-422D-BF08-F94B7337D229}.Release|Any CPU.Build.0 = Release|Any CPU {1A186504-8461-4F19-BDC5-C3DB90A554D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1A186504-8461-4F19-BDC5-C3DB90A554D6}.Debug|Any CPU.Build.0 = Debug|Any CPU {1A186504-8461-4F19-BDC5-C3DB90A554D6}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -285,6 +277,14 @@ Global {83CC5666-ABBF-4057-BCDA-66A6F1881A7E}.Debug|Any CPU.Build.0 = Debug|Any CPU {83CC5666-ABBF-4057-BCDA-66A6F1881A7E}.Release|Any CPU.ActiveCfg = Release|Any CPU {83CC5666-ABBF-4057-BCDA-66A6F1881A7E}.Release|Any CPU.Build.0 = Release|Any CPU + {89321BD6-3D4E-479B-A795-B30A6898E7BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {89321BD6-3D4E-479B-A795-B30A6898E7BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89321BD6-3D4E-479B-A795-B30A6898E7BF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {89321BD6-3D4E-479B-A795-B30A6898E7BF}.Release|Any CPU.Build.0 = Release|Any CPU + {4D0DCF6E-ABA1-407F-B0C9-59306E7FC8CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D0DCF6E-ABA1-407F-B0C9-59306E7FC8CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D0DCF6E-ABA1-407F-B0C9-59306E7FC8CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D0DCF6E-ABA1-407F-B0C9-59306E7FC8CA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -297,8 +297,6 @@ Global {E9F90342-34DE-4166-9769-483A3FA1F144} = {BC948ADE-1674-4955-B27C-F0E96100978E} {DD34FAE8-30AA-4787-A6D1-77C330A0928F} = {BC948ADE-1674-4955-B27C-F0E96100978E} {A4EF8656-723B-4E2E-9457-57DCB24CB409} = {BC948ADE-1674-4955-B27C-F0E96100978E} - {FF9758F9-2500-4CE1-AACE-8C67134046F4} = {A4EF8656-723B-4E2E-9457-57DCB24CB409} - {5B175E6B-4118-422D-BF08-F94B7337D229} = {A4EF8656-723B-4E2E-9457-57DCB24CB409} {1A186504-8461-4F19-BDC5-C3DB90A554D6} = {BC948ADE-1674-4955-B27C-F0E96100978E} {3DE93E0D-DAF5-48E7-A353-BD99ABB942DE} = {BC948ADE-1674-4955-B27C-F0E96100978E} {D39626D0-079C-40C6-A260-09852E11C70A} = {BC948ADE-1674-4955-B27C-F0E96100978E} @@ -335,6 +333,8 @@ Global {9B61744E-51F4-4EBC-B42C-3EABE7CC7487} = {BC948ADE-1674-4955-B27C-F0E96100978E} {EE0FE493-5230-485F-8D45-B5887FA50D86} = {BC948ADE-1674-4955-B27C-F0E96100978E} {83CC5666-ABBF-4057-BCDA-66A6F1881A7E} = {BC948ADE-1674-4955-B27C-F0E96100978E} + {89321BD6-3D4E-479B-A795-B30A6898E7BF} = {A4EF8656-723B-4E2E-9457-57DCB24CB409} + {4D0DCF6E-ABA1-407F-B0C9-59306E7FC8CA} = {A4EF8656-723B-4E2E-9457-57DCB24CB409} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A4B4251F-ADA4-418D-95B5-27BA99A307A3} diff --git a/RustPlusApi/Examples/Fcm/FcmListener/Program.cs b/RustPlusApi/Examples/Fcm/FcmListener/Program.cs deleted file mode 100644 index da03dcb..0000000 --- a/RustPlusApi/Examples/Fcm/FcmListener/Program.cs +++ /dev/null @@ -1,38 +0,0 @@ -using RustPlusApi.Fcm; -using RustPlusApi.Fcm.Data; - -var credentials = new Credentials -{ - Keys = new Keys - { - PrivateKey = "", - PublicKey = "", - AuthSecret = "", - }, - Fcm = new FcmCredentials - { - Token = "", - PushSet = "", - }, - Gcm = new GcmCredentials - { - Token = "", - AndroidId = 0, - SecurityToken = 0, - AppId = "", - } -}; - -var listener = new FcmListener(credentials); - -listener.NotificationReceived += (_, message) => -{ - Console.WriteLine($"[MESSAGE]: {DateTime.Now}:\n{message}"); -}; - -listener.ErrorOccurred += (_, error) => -{ - Console.WriteLine($"[ERROR]: {error}"); -}; - -await listener.ConnectAsync(); \ No newline at end of file diff --git a/RustPlusApi/Examples/Fcm/FcmRegister/FcmRegister.csproj b/RustPlusApi/Examples/Fcm/FcmRegister/FcmRegister.csproj deleted file mode 100644 index 65c5f8b..0000000 --- a/RustPlusApi/Examples/Fcm/FcmRegister/FcmRegister.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - Exe - net8.0 - enable - enable - - - - - - - diff --git a/RustPlusApi/Examples/Fcm/FcmRegister/Program.cs b/RustPlusApi/Examples/Fcm/FcmRegister/Program.cs deleted file mode 100644 index 8266013..0000000 --- a/RustPlusApi/Examples/Fcm/FcmRegister/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using RustPlusApi.Fcm; -using Newtonsoft.Json; - -// NOT WORKING, NEEDS TO BE FIXED -return; - -/* var senderId = "976529667804"; -var credentials = await FcmRegister.RegisterAsync(senderId); - -Console.WriteLine(JsonConvert.SerializeObject(credentials, Formatting.Indented));*/ \ No newline at end of file diff --git a/RustPlusApi/Examples/Fcm/RustPlusFcmListener/Program.cs b/RustPlusApi/Examples/Fcm/RustPlusFcmListener/Program.cs new file mode 100644 index 0000000..a1d9bcc --- /dev/null +++ b/RustPlusApi/Examples/Fcm/RustPlusFcmListener/Program.cs @@ -0,0 +1,75 @@ +using Newtonsoft.Json; + +using RustPlusApi.Fcm; +using RustPlusApi.Fcm.Data; + +using static __Constants.ExamplesConst; + +var credentials = new Credentials +{ + Keys = new Keys + { + PrivateKey = "", + PublicKey = "", + AuthSecret = "", + }, + Gcm = new Gcm + { + AndroidId = 0, + SecurityToken = 0, + } +}; + +var listener = new RustPlusFcmListener(credentials); + +listener.Connecting += (_, _) => +{ + Console.WriteLine($"[CONNECTING]: {DateTime.Now}"); +}; + +listener.Connected += (_, _) => +{ + Console.WriteLine($"[CONNECTED]: {DateTime.Now}"); +}; + +listener.SocketClosed += (_, _) => +{ + Console.WriteLine($"[SOCKET CLOSED]: {DateTime.Now}"); +}; + +listener.ErrorOccurred += (_, error) => +{ + Console.WriteLine($"[ERROR]: {error}"); +}; + +listener.Disconnecting += (_, _) => +{ + Console.WriteLine($"[DISCONNECTING]: {DateTime.Now}"); +}; + +listener.Disconnected += (_, _) => +{ + Console.WriteLine($"[DISCONNECTED]: {DateTime.Now}"); +}; + +/* Specials events */ + +listener.OnServerPairing += (_, pairing) => +{ + Console.WriteLine($"[SERVER PAIRING]:\n{JsonConvert.SerializeObject(pairing, JsonSettings)}"); +}; + +listener.OnEntityParing += (_, pairing) => +{ + Console.WriteLine($"[ENTITY PAIRING]:\n{JsonConvert.SerializeObject(pairing, JsonSettings)}"); +}; + +listener.OnAlarmTriggered += (_, alarm) => +{ + Console.WriteLine($"[ALARM TRIGGERED]:\n{JsonConvert.SerializeObject(alarm, JsonSettings)}"); +}; + +await listener.ConnectAsync(); + +Console.ReadLine(); +listener.Disconnect(); \ No newline at end of file diff --git a/RustPlusApi/Examples/Fcm/RustPlusFcmListener/RustPlusFcmListener.csproj b/RustPlusApi/Examples/Fcm/RustPlusFcmListener/RustPlusFcmListener.csproj new file mode 100644 index 0000000..7275aac --- /dev/null +++ b/RustPlusApi/Examples/Fcm/RustPlusFcmListener/RustPlusFcmListener.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + diff --git a/RustPlusApi/Examples/Fcm/RustPlusFcmListenerClient/Program.cs b/RustPlusApi/Examples/Fcm/RustPlusFcmListenerClient/Program.cs new file mode 100644 index 0000000..a5415cf --- /dev/null +++ b/RustPlusApi/Examples/Fcm/RustPlusFcmListenerClient/Program.cs @@ -0,0 +1,60 @@ +using RustPlusApi.Fcm; +using RustPlusApi.Fcm.Data; +using RustPlusApi.Fcm.Extensions; + +var credentials = new Credentials +{ + Keys = new Keys + { + PrivateKey = "", + PublicKey = "", + AuthSecret = "", + }, + Gcm = new Gcm + { + AndroidId = 0, + SecurityToken = 0, + } +}; + +var listener = new RustPlusFcmListenerClient(credentials); + +listener.Connecting += (_, _) => +{ + Console.WriteLine($"[CONNECTING]: {DateTime.Now}"); +}; + +listener.Connected += (_, _) => +{ + Console.WriteLine($"[CONNECTED]: {DateTime.Now}"); +}; + +listener.NotificationReceived += (_, message) => +{ + Console.WriteLine($"[NOTIFICATION]: {DateTime.Now}:\n{message.ToFcmMessageString()}"); +}; + +listener.SocketClosed += (_, _) => +{ + Console.WriteLine($"[SOCKET CLOSED]: {DateTime.Now}"); +}; + +listener.ErrorOccurred += (_, error) => +{ + Console.WriteLine($"[ERROR]: {error}"); +}; + +listener.Disconnecting += (_, _) => +{ + Console.WriteLine($"[DISCONNECTING]: {DateTime.Now}"); +}; + +listener.Disconnected += (_, _) => +{ + Console.WriteLine($"[DISCONNECTED]: {DateTime.Now}"); +}; + +await listener.ConnectAsync(); + +Console.ReadLine(); +listener.Disconnect(); \ No newline at end of file diff --git a/RustPlusApi/Examples/Fcm/FcmListener/FcmListener.csproj b/RustPlusApi/Examples/Fcm/RustPlusFcmListenerClient/RustPlusFcmListenerClient.csproj similarity index 100% rename from RustPlusApi/Examples/Fcm/FcmListener/FcmListener.csproj rename to RustPlusApi/Examples/Fcm/RustPlusFcmListenerClient/RustPlusFcmListenerClient.csproj diff --git a/RustPlusApi/RustPlusApi.Fcm/Data/Constants.cs b/RustPlusApi/RustPlusApi.Fcm/Data/Constants.cs deleted file mode 100644 index 288543c..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/Data/Constants.cs +++ /dev/null @@ -1,113 +0,0 @@ -namespace RustPlusApi.Fcm.Data -{ - public static class Constants - { - public enum ProcessingState - { - McsVersionTagAndSize = 0, - McsTagAndSize = 1, - McsSize = 2, - McsProtoBytes = 3 - } - - public const int ReadTimeoutSecs = 60 * 60; - public const int MinResetIntervalSecs = 5 * 60; - public const int MaxSilentIntervalSecs = 60 * 60; - - public const int KVersionPacketLen = 1; - public const int KTagPacketLen = 1; - public const int KSizePacketLenMin = 1; - public const int KSizePacketLenMax = 5; - - public const int KMcsVersion = 41; - - public enum McsProtoTag - { - KHeartbeatPingTag = 0, - KHeartbeatAckTag = 1, - KLoginRequestTag = 2, - KLoginResponseTag = 3, - KCloseTag = 4, - KMessageStanzaTag = 5, - KPresenceStanzaTag = 6, - KIqStanzaTag = 7, - KDataMessageStanzaTag = 8, - KBatchPresenceStanzaTag = 9, - KStreamErrorStanzaTag = 10, - KHttpRequestTag = 11, - KHttpResponseTag = 12, - KBindAccountRequestTag = 13, - KBindAccountResponseTag = 14, - KTalkMetadataTag = 15, - KNumProtoTypes = 16 - } - - public static readonly byte[] ServerKey = [ - 0x04, - 0x33, - 0x94, - 0xf7, - 0xdf, - 0xa1, - 0xeb, - 0xb1, - 0xdc, - 0x03, - 0xa2, - 0x5e, - 0x15, - 0x71, - 0xdb, - 0x48, - 0xd3, - 0x2e, - 0xed, - 0xed, - 0xb2, - 0x34, - 0xdb, - 0xb7, - 0x47, - 0x3a, - 0x0c, - 0x8f, - 0xc4, - 0xcc, - 0xe1, - 0x6f, - 0x3c, - 0x8c, - 0x84, - 0xdf, - 0xab, - 0xb6, - 0x66, - 0x3e, - 0xf2, - 0x0c, - 0xd4, - 0x8b, - 0xfe, - 0xe3, - 0xf9, - 0x76, - 0x2f, - 0x14, - 0x1c, - 0x63, - 0x08, - 0x6a, - 0x6f, - 0x2d, - 0xb1, - 0x1a, - 0x95, - 0xb0, - 0xce, - 0x37, - 0xc0, - 0x9c, - 0x6e - ]; - } -} diff --git a/RustPlusApi/RustPlusApi.Fcm/Data/Credentials.cs b/RustPlusApi/RustPlusApi.Fcm/Data/Credentials.cs index e73a3e2..15d60cc 100644 --- a/RustPlusApi/RustPlusApi.Fcm/Data/Credentials.cs +++ b/RustPlusApi/RustPlusApi.Fcm/Data/Credentials.cs @@ -3,8 +3,7 @@ public sealed class Credentials { public Keys Keys { get; set; } = null!; - public FcmCredentials Fcm { get; set; } = null!; - public GcmCredentials Gcm { get; set; } = null!; + public Gcm Gcm { get; set; } = null!; } public sealed class Keys @@ -14,17 +13,9 @@ public sealed class Keys public string AuthSecret { get; set; } = null!; } - public sealed class FcmCredentials + public sealed class Gcm { - public string Token { get; set; } = null!; - public string PushSet { get; set; } = null!; - } - - public sealed class GcmCredentials - { - public string Token { get; set; } = null!; public ulong AndroidId { get; set; } public ulong SecurityToken { get; set; } - public string AppId { get; set; } = null!; } } diff --git a/RustPlusApi/RustPlusApi.Fcm/Data/Events/AlarmEventArg.cs b/RustPlusApi/RustPlusApi.Fcm/Data/Events/AlarmEvent.cs similarity index 68% rename from RustPlusApi/RustPlusApi.Fcm/Data/Events/AlarmEventArg.cs rename to RustPlusApi/RustPlusApi.Fcm/Data/Events/AlarmEvent.cs index 445ffe7..6502f03 100644 --- a/RustPlusApi/RustPlusApi.Fcm/Data/Events/AlarmEventArg.cs +++ b/RustPlusApi/RustPlusApi.Fcm/Data/Events/AlarmEvent.cs @@ -1,8 +1,7 @@ namespace RustPlusApi.Fcm.Data.Events { - public class AlarmEventArg + public class AlarmEvent { - public Guid ServerId { get; set; } public string Title { get; set; } = null!; public string Message { get; set; } = null!; } diff --git a/RustPlusApi/RustPlusApi.Fcm/Data/Events/EntityEvent.cs b/RustPlusApi/RustPlusApi.Fcm/Data/Events/EntityEvent.cs new file mode 100644 index 0000000..8d9f8ba --- /dev/null +++ b/RustPlusApi/RustPlusApi.Fcm/Data/Events/EntityEvent.cs @@ -0,0 +1,9 @@ +namespace RustPlusApi.Fcm.Data.Events +{ + public class EntityEvent + { + public int? EntityType { get; set; } + public int? EntityId { get; set; } + public string? EntityName { get; set; } + } +} diff --git a/RustPlusApi/RustPlusApi.Fcm/Data/Events/EntityEventArg.cs b/RustPlusApi/RustPlusApi.Fcm/Data/Events/EntityEventArg.cs deleted file mode 100644 index 509000b..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/Data/Events/EntityEventArg.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace RustPlusApi.Fcm.Data.Events -{ - public class EntityEventArg - { - public int EntityType { get; set; } - public int EntityId { get; set; } - public string EntityName { get; set; } = null!; - } -} diff --git a/RustPlusApi/RustPlusApi.Fcm/Data/Events/MessageEventArg.cs b/RustPlusApi/RustPlusApi.Fcm/Data/Events/MessageEventArg.cs index 34a64d7..4dfc6f8 100644 --- a/RustPlusApi/RustPlusApi.Fcm/Data/Events/MessageEventArg.cs +++ b/RustPlusApi/RustPlusApi.Fcm/Data/Events/MessageEventArg.cs @@ -1,4 +1,4 @@ -using static RustPlusApi.Fcm.Data.Constants; +using static RustPlusApi.Fcm.Data.Tags; namespace RustPlusApi.Fcm.Data.Events { diff --git a/RustPlusApi/RustPlusApi.Fcm/Data/Events/ServerEvent.cs b/RustPlusApi/RustPlusApi.Fcm/Data/Events/ServerEvent.cs new file mode 100644 index 0000000..6c8a0da --- /dev/null +++ b/RustPlusApi/RustPlusApi.Fcm/Data/Events/ServerEvent.cs @@ -0,0 +1,14 @@ +namespace RustPlusApi.Fcm.Data.Events +{ + public class ServerEvent + { + public Guid Id { get; set; } + public string Name { get; set; } = null!; + public string Ip { get; set; } = null!; + public int Port { get; set; } + public string? Desc { get; set; } + public string? Logo { get; set; } + public string? Img { get; set; } + public string? Url { get; set; } + } +} diff --git a/RustPlusApi/RustPlusApi.Fcm/Data/Events/ServerEventArg.cs b/RustPlusApi/RustPlusApi.Fcm/Data/Events/ServerEventArg.cs deleted file mode 100644 index 8e53c3d..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/Data/Events/ServerEventArg.cs +++ /dev/null @@ -1,9 +0,0 @@ - -namespace RustPlusApi.Fcm.Data.Events -{ - public class ServerEventArg - { - public Guid Id { get; set; } - public string Name { get; set; } = null!; - } -} diff --git a/RustPlusApi/RustPlusApi.Fcm/Data/Events/ServerFullEventArg.cs b/RustPlusApi/RustPlusApi.Fcm/Data/Events/ServerFullEventArg.cs deleted file mode 100644 index f6a9cf5..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/Data/Events/ServerFullEventArg.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace RustPlusApi.Fcm.Data.Events -{ - public class ServerFullEventArg : ServerEventArg - { - public string Ip { get; set; } = null!; - public int Port { get; set; } - public string Desc { get; set; } = null!; - public int Logo { get; set; } - public int Img { get; set; } - public string Url { get; set; } = null!; - } -} diff --git a/RustPlusApi/RustPlusApi.Fcm/Data/FcmMessage.cs b/RustPlusApi/RustPlusApi.Fcm/Data/FcmMessage.cs index aaa2cf0..870c2d1 100644 --- a/RustPlusApi/RustPlusApi.Fcm/Data/FcmMessage.cs +++ b/RustPlusApi/RustPlusApi.Fcm/Data/FcmMessage.cs @@ -30,10 +30,10 @@ public class Body public string Ip { get; set; } = null!; public int Port { get; set; } public string Name { get; set; } = null!; - public string Desc { get; set; } = null!; - public int Logo { get; set; } - public int Img { get; set; } - public string Url { get; set; } = null!; + public string? Desc { get; set; } + public string? Logo { get; set; } + public string? Img { get; set; } + public string? Url { get; set; } public ulong PlayerId { get; set; } public string PlayerToken { get; set; } = null!; public string Type { get; set; } = null!; diff --git a/RustPlusApi/RustPlusApi.Fcm/Data/Notification.cs b/RustPlusApi/RustPlusApi.Fcm/Data/Notification.cs new file mode 100644 index 0000000..7c57702 --- /dev/null +++ b/RustPlusApi/RustPlusApi.Fcm/Data/Notification.cs @@ -0,0 +1,12 @@ +using RustPlusApi.Fcm.Data.Events; + +namespace RustPlusApi.Fcm.Data +{ + public class Notification + { + public ulong PlayerId { get; set; } + public int PlayerToken { get; set; } + public Guid ServerId { get; set; } + public T? Data { get; set; } + } +} diff --git a/RustPlusApi/RustPlusApi.Fcm/Data/Tags.cs b/RustPlusApi/RustPlusApi.Fcm/Data/Tags.cs new file mode 100644 index 0000000..e32eb58 --- /dev/null +++ b/RustPlusApi/RustPlusApi.Fcm/Data/Tags.cs @@ -0,0 +1,26 @@ +namespace RustPlusApi.Fcm.Data +{ + public static class Tags + { + public enum McsProtoTag + { + KHeartbeatPingTag = 0, + KHeartbeatAckTag = 1, + KLoginRequestTag = 2, + KLoginResponseTag = 3, + KCloseTag = 4, + KMessageStanzaTag = 5, + KPresenceStanzaTag = 6, + KIqStanzaTag = 7, + KDataMessageStanzaTag = 8, + KBatchPresenceStanzaTag = 9, + KStreamErrorStanzaTag = 10, + KHttpRequestTag = 11, + KHttpResponseTag = 12, + KBindAccountRequestTag = 13, + KBindAccountResponseTag = 14, + KTalkMetadataTag = 15, + KNumProtoTypes = 16 + } + } +} diff --git a/RustPlusApi/RustPlusApi.Fcm/Extensions/BodyToEventModel.cs b/RustPlusApi/RustPlusApi.Fcm/Extensions/BodyToEventModel.cs new file mode 100644 index 0000000..5af1bbe --- /dev/null +++ b/RustPlusApi/RustPlusApi.Fcm/Extensions/BodyToEventModel.cs @@ -0,0 +1,38 @@ +using RustPlusApi.Fcm.Data; +using RustPlusApi.Fcm.Data.Events; + +namespace RustPlusApi.Fcm.Extensions +{ + public static class BodyToEventModel + { + public static int? ToEntityId(this Body body) + { + return body.EntityId; + } + + public static EntityEvent ToEntityEvent(this Body body) + { + return new EntityEvent + { + EntityType = body.EntityType, + EntityId = body.EntityId, + EntityName = body.EntityName + }; + } + + public static ServerEvent ToServerEvent(this Body body) + { + return new ServerEvent + { + Id = body.Id, + Name = body.EntityName, + Ip = body.Ip, + Port = body.Port, + Desc = body.Desc, + Logo = body.Logo, + Img = body.Img, + Url = body.Url + }; + } + } +} diff --git a/RustPlusApi/RustPlusApi.Fcm/Extensions/MessageDataToEventModel.cs b/RustPlusApi/RustPlusApi.Fcm/Extensions/MessageDataToEventModel.cs new file mode 100644 index 0000000..c939216 --- /dev/null +++ b/RustPlusApi/RustPlusApi.Fcm/Extensions/MessageDataToEventModel.cs @@ -0,0 +1,17 @@ +using RustPlusApi.Fcm.Data; +using RustPlusApi.Fcm.Data.Events; + +namespace RustPlusApi.Fcm.Extensions +{ + public static class MessageDataToEventModel + { + public static AlarmEvent ToAlarmEvent(this MessageData data) + { + return new AlarmEvent + { + Title = data.Title, + Message = data.Message + }; + } + } +} diff --git a/RustPlusApi/RustPlusApi.Fcm/Extensions/StringToFcmMessageString.cs b/RustPlusApi/RustPlusApi.Fcm/Extensions/StringToFcmMessageString.cs new file mode 100644 index 0000000..10cc5bf --- /dev/null +++ b/RustPlusApi/RustPlusApi.Fcm/Extensions/StringToFcmMessageString.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace RustPlusApi.Fcm.Extensions +{ + public static class StringToFcmMessageString + { + public static string ToFcmMessageString(this string message) + { + try + { + var jsonObject = JObject.Parse(message); + + var bodyString = jsonObject["data"]!["body"]!.ToString(); + var bodyObject = JObject.Parse(bodyString); + + var formattedBody = bodyObject.ToString(Formatting.Indented); + jsonObject["data"]!["body"] = JToken.Parse(formattedBody); + + return jsonObject.ToString(Formatting.Indented); + } + catch + { + return message; + } + } + } +} diff --git a/RustPlusApi/RustPlusApi.Fcm/FcmListener.cs b/RustPlusApi/RustPlusApi.Fcm/FcmListener.cs deleted file mode 100644 index ccea4e9..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/FcmListener.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System.Diagnostics; - -using RustPlusApi.Fcm.Data; -using RustPlusApi.Fcm.Data.Events; - -namespace RustPlusApi.Fcm -{ - public class FcmListener(Credentials credentials, ICollection? persistentIds = null) : FcmListenerBasic(credentials, persistentIds) - { - public event EventHandler? OnParing; - - public event EventHandler<(ServerEventArg, EntityEventArg)>? OnEntityParing; - public event EventHandler? OnServerPairing; - - public event EventHandler<(ServerEventArg, int)>? OnSmartSwitchParing; - public event EventHandler<(ServerEventArg, int)>? OnSmartAlarmParing; - public event EventHandler<(ServerEventArg, int)>? OnStorageMonitorParing; - - public event EventHandler? OnAlarmTriggered; - - protected override void ParseNotification(FcmMessage? message) - { - if (message == null) return; - - switch (message.Data.ChannelId) - { - case "pairing": - OnParing?.Invoke(this, message.Data); - ParsePairing(message.Data.Body); - break; - case "alarm": - var alarm = new AlarmEventArg() - { - ServerId = message.Data.Body.Id, - Title = message.Data.Title, - Message = message.Data.Message - }; - OnAlarmTriggered?.Invoke(this, alarm); - break; - default: - Debug.WriteLine($"Unknown channel: {message.Data.ChannelId}"); - break; - } - } - - private void ParsePairing(Body body) - { - switch (body.Type) - { - case "entity": - var server = new ServerEventArg() - { - Id = body.Id, - Name = body.Name - }; - var entity = new EntityEventArg() - { - EntityType = body.EntityType ?? 0, - EntityId = body.EntityId ?? 0, - EntityName = body.EntityName - }; - OnEntityParing?.Invoke(this, (server, entity)); - ParsePairingEntity(body); - break; - case "server": - var serverFull = new ServerFullEventArg() - { - Id = body.Id, - Name = body.Name, - Ip = body.Ip, - Port = body.Port, - Desc = body.Desc, - Logo = body.Logo, - Img = body.Img, - Url = body.Url - }; - OnServerPairing?.Invoke(this, serverFull); - break; - default: - Debug.WriteLine($"Unknown pairing type: {body.Type}"); - break; - } - } - - private void ParsePairingEntity(Body body) - { - var server = new ServerEventArg() - { - Id = body.Id, - Name = body.Name - }; - - switch (body.EntityType) - { - case 1: - OnSmartSwitchParing?.Invoke(this, (server, body.EntityId ?? 0)); - break; - case 2: - OnSmartAlarmParing?.Invoke(this, (server, body.EntityId ?? 0)); - break; - case 3: - OnStorageMonitorParing?.Invoke(this, (server, body.EntityId ?? 0)); - break; - default: - Debug.WriteLine($"Unknown entity type: {body.EntityType}"); - break; - } - } - } -} diff --git a/RustPlusApi/RustPlusApi.Fcm/FcmRegister.cs b/RustPlusApi/RustPlusApi.Fcm/FcmRegister.cs deleted file mode 100644 index 7209aed..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/FcmRegister.cs +++ /dev/null @@ -1,23 +0,0 @@ -using RustPlusApi.Fcm.Data; -using RustPlusApi.Fcm.Tools; - -namespace RustPlusApi.Fcm -{ - /*public class FcmRegister - { - public static async Task RegisterAsync(string senderId) - { - var appId = $"wp:receiver.push.com#{Guid.NewGuid()}"; - - var gcmSubscription = await GcmTools.RegisterAsync(appId); - var fcmRegistration = await FcmTools.RegisterFcmAsync(senderId, gcmSubscription.Token); - - return new Credentials - { - Keys = fcmRegistration.Item1, - Gcm = gcmSubscription, - Fcm = fcmRegistration.Item2, - }; - } - }*/ -} diff --git a/RustPlusApi/RustPlusApi.Fcm/ProtoBuf/AndroidCheckin.cs b/RustPlusApi/RustPlusApi.Fcm/ProtoBuf/AndroidCheckin.cs deleted file mode 100644 index b858339..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/ProtoBuf/AndroidCheckin.cs +++ /dev/null @@ -1,178 +0,0 @@ -// -// This file was generated by a tool; you should avoid making direct changes. -// Consider using 'partial classes' to extend these types -// Input: my.proto -// - -#region Designer generated code -#pragma warning disable CS0612, CS0618, CS1591, CS3021, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192 -namespace AndroidCheckinProto -{ - - [global::ProtoBuf.ProtoContract()] - public partial class ChromeBuildProto : global::ProtoBuf.IExtensible - { - private global::ProtoBuf.IExtension __pbn__extensionData; - global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) - => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing); - - [global::ProtoBuf.ProtoMember(1)] - [global::System.ComponentModel.DefaultValue(Platform.PlatformWin)] - public Platform platform - { - get => __pbn__platform ?? Platform.PlatformWin; - set => __pbn__platform = value; - } - public bool ShouldSerializeplatform() => __pbn__platform != null; - public void Resetplatform() => __pbn__platform = null; - private Platform? __pbn__platform; - - [global::ProtoBuf.ProtoMember(2, Name = @"chrome_version")] - [global::System.ComponentModel.DefaultValue("")] - public string ChromeVersion - { - get => __pbn__ChromeVersion ?? ""; - set => __pbn__ChromeVersion = value; - } - public bool ShouldSerializeChromeVersion() => __pbn__ChromeVersion != null; - public void ResetChromeVersion() => __pbn__ChromeVersion = null; - private string __pbn__ChromeVersion; - - [global::ProtoBuf.ProtoMember(3)] - [global::System.ComponentModel.DefaultValue(Channel.ChannelStable)] - public Channel channel - { - get => __pbn__channel ?? Channel.ChannelStable; - set => __pbn__channel = value; - } - public bool ShouldSerializechannel() => __pbn__channel != null; - public void Resetchannel() => __pbn__channel = null; - private Channel? __pbn__channel; - - [global::ProtoBuf.ProtoContract()] - public enum Platform - { - [global::ProtoBuf.ProtoEnum(Name = @"PLATFORM_WIN")] - PlatformWin = 1, - [global::ProtoBuf.ProtoEnum(Name = @"PLATFORM_MAC")] - PlatformMac = 2, - [global::ProtoBuf.ProtoEnum(Name = @"PLATFORM_LINUX")] - PlatformLinux = 3, - [global::ProtoBuf.ProtoEnum(Name = @"PLATFORM_CROS")] - PlatformCros = 4, - [global::ProtoBuf.ProtoEnum(Name = @"PLATFORM_IOS")] - PlatformIos = 5, - [global::ProtoBuf.ProtoEnum(Name = @"PLATFORM_ANDROID")] - PlatformAndroid = 6, - } - - [global::ProtoBuf.ProtoContract()] - public enum Channel - { - [global::ProtoBuf.ProtoEnum(Name = @"CHANNEL_STABLE")] - ChannelStable = 1, - [global::ProtoBuf.ProtoEnum(Name = @"CHANNEL_BETA")] - ChannelBeta = 2, - [global::ProtoBuf.ProtoEnum(Name = @"CHANNEL_DEV")] - ChannelDev = 3, - [global::ProtoBuf.ProtoEnum(Name = @"CHANNEL_CANARY")] - ChannelCanary = 4, - [global::ProtoBuf.ProtoEnum(Name = @"CHANNEL_UNKNOWN")] - ChannelUnknown = 5, - } - - } - - [global::ProtoBuf.ProtoContract()] - public partial class AndroidCheckinProto : global::ProtoBuf.IExtensible - { - private global::ProtoBuf.IExtension __pbn__extensionData; - global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) - => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing); - - [global::ProtoBuf.ProtoMember(2, Name = @"last_checkin_msec")] - public long LastCheckinMsec - { - get => __pbn__LastCheckinMsec.GetValueOrDefault(); - set => __pbn__LastCheckinMsec = value; - } - public bool ShouldSerializeLastCheckinMsec() => __pbn__LastCheckinMsec != null; - public void ResetLastCheckinMsec() => __pbn__LastCheckinMsec = null; - private long? __pbn__LastCheckinMsec; - - [global::ProtoBuf.ProtoMember(6, Name = @"cell_operator")] - [global::System.ComponentModel.DefaultValue("")] - public string CellOperator - { - get => __pbn__CellOperator ?? ""; - set => __pbn__CellOperator = value; - } - public bool ShouldSerializeCellOperator() => __pbn__CellOperator != null; - public void ResetCellOperator() => __pbn__CellOperator = null; - private string __pbn__CellOperator; - - [global::ProtoBuf.ProtoMember(7, Name = @"sim_operator")] - [global::System.ComponentModel.DefaultValue("")] - public string SimOperator - { - get => __pbn__SimOperator ?? ""; - set => __pbn__SimOperator = value; - } - public bool ShouldSerializeSimOperator() => __pbn__SimOperator != null; - public void ResetSimOperator() => __pbn__SimOperator = null; - private string __pbn__SimOperator; - - [global::ProtoBuf.ProtoMember(8, Name = @"roaming")] - [global::System.ComponentModel.DefaultValue("")] - public string Roaming - { - get => __pbn__Roaming ?? ""; - set => __pbn__Roaming = value; - } - public bool ShouldSerializeRoaming() => __pbn__Roaming != null; - public void ResetRoaming() => __pbn__Roaming = null; - private string __pbn__Roaming; - - [global::ProtoBuf.ProtoMember(9, Name = @"user_number")] - public int UserNumber - { - get => __pbn__UserNumber.GetValueOrDefault(); - set => __pbn__UserNumber = value; - } - public bool ShouldSerializeUserNumber() => __pbn__UserNumber != null; - public void ResetUserNumber() => __pbn__UserNumber = null; - private int? __pbn__UserNumber; - - [global::ProtoBuf.ProtoMember(12, Name = @"type")] - [global::System.ComponentModel.DefaultValue(DeviceType.DeviceAndroidOs)] - public DeviceType Type - { - get => __pbn__Type ?? DeviceType.DeviceAndroidOs; - set => __pbn__Type = value; - } - public bool ShouldSerializeType() => __pbn__Type != null; - public void ResetType() => __pbn__Type = null; - private DeviceType? __pbn__Type; - - [global::ProtoBuf.ProtoMember(13, Name = @"chrome_build")] - public ChromeBuildProto ChromeBuild { get; set; } - - } - - [global::ProtoBuf.ProtoContract()] - public enum DeviceType - { - [global::ProtoBuf.ProtoEnum(Name = @"DEVICE_ANDROID_OS")] - DeviceAndroidOs = 1, - [global::ProtoBuf.ProtoEnum(Name = @"DEVICE_IOS_OS")] - DeviceIosOs = 2, - [global::ProtoBuf.ProtoEnum(Name = @"DEVICE_CHROME_BROWSER")] - DeviceChromeBrowser = 3, - [global::ProtoBuf.ProtoEnum(Name = @"DEVICE_CHROME_OS")] - DeviceChromeOs = 4, - } - -} - -#pragma warning restore CS0612, CS0618, CS1591, CS3021, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192 -#endregion diff --git a/RustPlusApi/RustPlusApi.Fcm/ProtoBuf/Checkin.cs b/RustPlusApi/RustPlusApi.Fcm/ProtoBuf/Checkin.cs deleted file mode 100644 index 2388427..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/ProtoBuf/Checkin.cs +++ /dev/null @@ -1,315 +0,0 @@ -// This file needs to be changed -// -// This file was generated by a tool; you should avoid making direct changes. -// Consider using 'partial classes' to extend these types -// Input: my.proto -// - -#region Designer generated code -#pragma warning disable CS0612, CS0618, CS1591, CS3021, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192 -namespace CheckinProto -{ - - [global::ProtoBuf.ProtoContract()] - public partial class GservicesSetting : global::ProtoBuf.IExtensible - { - private global::ProtoBuf.IExtension __pbn__extensionData; - global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) - => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing); - - [global::ProtoBuf.ProtoMember(1, Name = @"name", IsRequired = true)] - public byte[] Name { get; set; } - - [global::ProtoBuf.ProtoMember(2, Name = @"value", IsRequired = true)] - public byte[] Value { get; set; } - - } - - [global::ProtoBuf.ProtoContract()] - public partial class AndroidCheckinRequest : global::ProtoBuf.IExtensible - { - private global::ProtoBuf.IExtension __pbn__extensionData; - global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) - => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing); - - [global::ProtoBuf.ProtoMember(1, Name = @"imei")] - [global::System.ComponentModel.DefaultValue("")] - public string Imei - { - get => __pbn__Imei ?? ""; - set => __pbn__Imei = value; - } - public bool ShouldSerializeImei() => __pbn__Imei != null; - public void ResetImei() => __pbn__Imei = null; - private string __pbn__Imei; - - [global::ProtoBuf.ProtoMember(10, Name = @"meid")] - [global::System.ComponentModel.DefaultValue("")] - public string Meid - { - get => __pbn__Meid ?? ""; - set => __pbn__Meid = value; - } - public bool ShouldSerializeMeid() => __pbn__Meid != null; - public void ResetMeid() => __pbn__Meid = null; - private string __pbn__Meid; - - [global::ProtoBuf.ProtoMember(9, Name = @"mac_addr")] - public global::System.Collections.Generic.List MacAddrs { get; } = new global::System.Collections.Generic.List(); - - [global::ProtoBuf.ProtoMember(19, Name = @"mac_addr_type")] - public global::System.Collections.Generic.List MacAddrTypes { get; } = new global::System.Collections.Generic.List(); - - [global::ProtoBuf.ProtoMember(16, Name = @"serial_number")] - [global::System.ComponentModel.DefaultValue("")] - public string SerialNumber - { - get => __pbn__SerialNumber ?? ""; - set => __pbn__SerialNumber = value; - } - public bool ShouldSerializeSerialNumber() => __pbn__SerialNumber != null; - public void ResetSerialNumber() => __pbn__SerialNumber = null; - private string __pbn__SerialNumber; - - [global::ProtoBuf.ProtoMember(17, Name = @"esn")] - [global::System.ComponentModel.DefaultValue("")] - public string Esn - { - get => __pbn__Esn ?? ""; - set => __pbn__Esn = value; - } - public bool ShouldSerializeEsn() => __pbn__Esn != null; - public void ResetEsn() => __pbn__Esn = null; - private string __pbn__Esn; - - [global::ProtoBuf.ProtoMember(2, Name = @"id")] - public long Id - { - get => __pbn__Id.GetValueOrDefault(); - set => __pbn__Id = value; - } - public bool ShouldSerializeId() => __pbn__Id != null; - public void ResetId() => __pbn__Id = null; - private long? __pbn__Id; - - [global::ProtoBuf.ProtoMember(7, Name = @"logging_id")] - public long LoggingId - { - get => __pbn__LoggingId.GetValueOrDefault(); - set => __pbn__LoggingId = value; - } - public bool ShouldSerializeLoggingId() => __pbn__LoggingId != null; - public void ResetLoggingId() => __pbn__LoggingId = null; - private long? __pbn__LoggingId; - - [global::ProtoBuf.ProtoMember(3, Name = @"digest")] - [global::System.ComponentModel.DefaultValue("")] - public string Digest - { - get => __pbn__Digest ?? ""; - set => __pbn__Digest = value; - } - public bool ShouldSerializeDigest() => __pbn__Digest != null; - public void ResetDigest() => __pbn__Digest = null; - private string __pbn__Digest; - - [global::ProtoBuf.ProtoMember(6, Name = @"locale")] - [global::System.ComponentModel.DefaultValue("")] - public string Locale - { - get => __pbn__Locale ?? ""; - set => __pbn__Locale = value; - } - public bool ShouldSerializeLocale() => __pbn__Locale != null; - public void ResetLocale() => __pbn__Locale = null; - private string __pbn__Locale; - - [global::ProtoBuf.ProtoMember(4, Name = @"checkin", IsRequired = true)] - public AndroidCheckinProto.AndroidCheckinProto Checkin { get; set; } - - [global::ProtoBuf.ProtoMember(5, Name = @"desired_build")] - [global::System.ComponentModel.DefaultValue("")] - public string DesiredBuild - { - get => __pbn__DesiredBuild ?? ""; - set => __pbn__DesiredBuild = value; - } - public bool ShouldSerializeDesiredBuild() => __pbn__DesiredBuild != null; - public void ResetDesiredBuild() => __pbn__DesiredBuild = null; - private string __pbn__DesiredBuild; - - [global::ProtoBuf.ProtoMember(8, Name = @"market_checkin")] - [global::System.ComponentModel.DefaultValue("")] - public string MarketCheckin - { - get => __pbn__MarketCheckin ?? ""; - set => __pbn__MarketCheckin = value; - } - public bool ShouldSerializeMarketCheckin() => __pbn__MarketCheckin != null; - public void ResetMarketCheckin() => __pbn__MarketCheckin = null; - private string __pbn__MarketCheckin; - - [global::ProtoBuf.ProtoMember(11, Name = @"account_cookie")] - public global::System.Collections.Generic.List AccountCookies { get; } = new global::System.Collections.Generic.List(); - - [global::ProtoBuf.ProtoMember(12, Name = @"time_zone")] - [global::System.ComponentModel.DefaultValue("")] - public string TimeZone - { - get => __pbn__TimeZone ?? ""; - set => __pbn__TimeZone = value; - } - public bool ShouldSerializeTimeZone() => __pbn__TimeZone != null; - public void ResetTimeZone() => __pbn__TimeZone = null; - private string __pbn__TimeZone; - - [global::ProtoBuf.ProtoMember(13, Name = @"security_token", DataFormat = global::ProtoBuf.DataFormat.FixedSize)] - public ulong SecurityToken - { - get => __pbn__SecurityToken.GetValueOrDefault(); - set => __pbn__SecurityToken = value; - } - public bool ShouldSerializeSecurityToken() => __pbn__SecurityToken != null; - public void ResetSecurityToken() => __pbn__SecurityToken = null; - private ulong? __pbn__SecurityToken; - - [global::ProtoBuf.ProtoMember(14, Name = @"version")] - public int Version - { - get => __pbn__Version.GetValueOrDefault(); - set => __pbn__Version = value; - } - public bool ShouldSerializeVersion() => __pbn__Version != null; - public void ResetVersion() => __pbn__Version = null; - private int? __pbn__Version; - - [global::ProtoBuf.ProtoMember(15, Name = @"ota_cert")] - public global::System.Collections.Generic.List OtaCerts { get; } = new global::System.Collections.Generic.List(); - - [global::ProtoBuf.ProtoMember(20, Name = @"fragment")] - public int Fragment - { - get => __pbn__Fragment.GetValueOrDefault(); - set => __pbn__Fragment = value; - } - public bool ShouldSerializeFragment() => __pbn__Fragment != null; - public void ResetFragment() => __pbn__Fragment = null; - private int? __pbn__Fragment; - - [global::ProtoBuf.ProtoMember(21, Name = @"user_name")] - [global::System.ComponentModel.DefaultValue("")] - public string UserName - { - get => __pbn__UserName ?? ""; - set => __pbn__UserName = value; - } - public bool ShouldSerializeUserName() => __pbn__UserName != null; - public void ResetUserName() => __pbn__UserName = null; - private string __pbn__UserName; - - [global::ProtoBuf.ProtoMember(22, Name = @"user_serial_number")] - public int UserSerialNumber - { - get => __pbn__UserSerialNumber.GetValueOrDefault(); - set => __pbn__UserSerialNumber = value; - } - public bool ShouldSerializeUserSerialNumber() => __pbn__UserSerialNumber != null; - public void ResetUserSerialNumber() => __pbn__UserSerialNumber = null; - private int? __pbn__UserSerialNumber; - - } - - [global::ProtoBuf.ProtoContract()] - public partial class AndroidCheckinResponse : global::ProtoBuf.IExtensible - { - private global::ProtoBuf.IExtension __pbn__extensionData; - global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) - => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing); - - [global::ProtoBuf.ProtoMember(1, Name = @"stats_ok", IsRequired = true)] - public bool StatsOk { get; set; } - - [global::ProtoBuf.ProtoMember(3, Name = @"time_msec")] - public long TimeMsec - { - get => __pbn__TimeMsec.GetValueOrDefault(); - set => __pbn__TimeMsec = value; - } - public bool ShouldSerializeTimeMsec() => __pbn__TimeMsec != null; - public void ResetTimeMsec() => __pbn__TimeMsec = null; - private long? __pbn__TimeMsec; - - [global::ProtoBuf.ProtoMember(4, Name = @"digest")] - [global::System.ComponentModel.DefaultValue("")] - public string Digest - { - get => __pbn__Digest ?? ""; - set => __pbn__Digest = value; - } - public bool ShouldSerializeDigest() => __pbn__Digest != null; - public void ResetDigest() => __pbn__Digest = null; - private string __pbn__Digest; - - [global::ProtoBuf.ProtoMember(9, Name = @"settings_diff")] - public bool SettingsDiff - { - get => __pbn__SettingsDiff.GetValueOrDefault(); - set => __pbn__SettingsDiff = value; - } - public bool ShouldSerializeSettingsDiff() => __pbn__SettingsDiff != null; - public void ResetSettingsDiff() => __pbn__SettingsDiff = null; - private bool? __pbn__SettingsDiff; - - [global::ProtoBuf.ProtoMember(10, Name = @"delete_setting")] - public global::System.Collections.Generic.List DeleteSettings { get; } = new global::System.Collections.Generic.List(); - - [global::ProtoBuf.ProtoMember(5, Name = @"setting")] - public global::System.Collections.Generic.List Settings { get; } = new global::System.Collections.Generic.List(); - - [global::ProtoBuf.ProtoMember(6, Name = @"market_ok")] - public bool MarketOk - { - get => __pbn__MarketOk.GetValueOrDefault(); - set => __pbn__MarketOk = value; - } - public bool ShouldSerializeMarketOk() => __pbn__MarketOk != null; - public void ResetMarketOk() => __pbn__MarketOk = null; - private bool? __pbn__MarketOk; - - [global::ProtoBuf.ProtoMember(7, Name = @"android_id", DataFormat = global::ProtoBuf.DataFormat.FixedSize)] - public ulong AndroidId - { - get => __pbn__AndroidId.GetValueOrDefault(); - set => __pbn__AndroidId = value; - } - public bool ShouldSerializeAndroidId() => __pbn__AndroidId != null; - public void ResetAndroidId() => __pbn__AndroidId = null; - private ulong? __pbn__AndroidId; - - [global::ProtoBuf.ProtoMember(8, Name = @"security_token", DataFormat = global::ProtoBuf.DataFormat.FixedSize)] - public ulong SecurityToken - { - get => __pbn__SecurityToken.GetValueOrDefault(); - set => __pbn__SecurityToken = value; - } - public bool ShouldSerializeSecurityToken() => __pbn__SecurityToken != null; - public void ResetSecurityToken() => __pbn__SecurityToken = null; - private ulong? __pbn__SecurityToken; - - [global::ProtoBuf.ProtoMember(11, Name = @"version_info")] - [global::System.ComponentModel.DefaultValue("")] - public string VersionInfo - { - get => __pbn__VersionInfo ?? ""; - set => __pbn__VersionInfo = value; - } - public bool ShouldSerializeVersionInfo() => __pbn__VersionInfo != null; - public void ResetVersionInfo() => __pbn__VersionInfo = null; - private string __pbn__VersionInfo; - - } - -} - -#pragma warning restore CS0612, CS0618, CS1591, CS3021, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192 -#endregion diff --git a/RustPlusApi/RustPlusApi.Fcm/ProtoBuf/android_checkin.proto b/RustPlusApi/RustPlusApi.Fcm/ProtoBuf/android_checkin.proto deleted file mode 100644 index 07bacd7..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/ProtoBuf/android_checkin.proto +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// Logging information for Android "checkin" events (automatic, periodic -// requests made by Android devices to the server). - -syntax = "proto2"; - -option optimize_for = LITE_RUNTIME; -package checkin_proto; - -// Build characteristics unique to the Chrome browser, and Chrome OS -message ChromeBuildProto { - enum Platform { - PLATFORM_WIN = 1; - PLATFORM_MAC = 2; - PLATFORM_LINUX = 3; - PLATFORM_CROS = 4; - PLATFORM_IOS = 5; - // Just a placeholder. Likely don't need it due to the presence of the - // Android GCM on phone/tablet devices. - PLATFORM_ANDROID = 6; - } - - enum Channel { - CHANNEL_STABLE = 1; - CHANNEL_BETA = 2; - CHANNEL_DEV = 3; - CHANNEL_CANARY = 4; - CHANNEL_UNKNOWN = 5; // for tip of tree or custom builds - } - - // The platform of the device. - optional Platform platform = 1; - - // The Chrome instance's version. - optional string chrome_version = 2; - - // The Channel (build type) of Chrome. - optional Channel channel = 3; -} - -// Information sent by the device in a "checkin" request. -message AndroidCheckinProto { - // Miliseconds since the Unix epoch of the device's last successful checkin. - optional int64 last_checkin_msec = 2; - - // The current MCC+MNC of the mobile device's current cell. - optional string cell_operator = 6; - - // The MCC+MNC of the SIM card (different from operator if the - // device is roaming, for instance). - optional string sim_operator = 7; - - // The device's current roaming state (reported starting in eclair builds). - // Currently one of "{,not}mobile-{,not}roaming", if it is present at all. - optional string roaming = 8; - - // For devices supporting multiple user profiles (which may be - // supported starting in jellybean), the ordinal number of the - // profile that is checking in. This is 0 for the primary profile - // (which can't be changed without wiping the device), and 1,2,3,... - // for additional profiles (which can be added and deleted freely). - optional int32 user_number = 9; - - // Class of device. Indicates the type of build proto - // (IosBuildProto/ChromeBuildProto/AndroidBuildProto) - // That is included in this proto - optional DeviceType type = 12 [default = DEVICE_ANDROID_OS]; - - // For devices running MCS on Chrome, build-specific characteristics - // of the browser. There are no hardware aspects (except for ChromeOS). - // This will only be populated for Chrome builds/ChromeOS devices - optional checkin_proto.ChromeBuildProto chrome_build = 13; - - // Note: Some of the Android specific optional fields were skipped to limit - // the protobuf definition. - // Next 14 -} - -// enum values correspond to the type of device. -// Used in the AndroidCheckinProto and Device proto. -enum DeviceType { - // Android Device - DEVICE_ANDROID_OS = 1; - - // Apple IOS device - DEVICE_IOS_OS = 2; - - // Chrome browser - Not Chrome OS. No hardware records. - DEVICE_CHROME_BROWSER = 3; - - // Chrome OS - DEVICE_CHROME_OS = 4; -} diff --git a/RustPlusApi/RustPlusApi.Fcm/ProtoBuf/checkin.proto b/RustPlusApi/RustPlusApi.Fcm/ProtoBuf/checkin.proto deleted file mode 100644 index b741653..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/ProtoBuf/checkin.proto +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// Request and reply to the "checkin server" devices poll every few hours. - -syntax = "proto2"; - -option optimize_for = LITE_RUNTIME; - -package checkin_proto; - -import "android_checkin.proto"; - -// A concrete name/value pair sent to the device's Gservices database. -message GservicesSetting { - required bytes name = 1; - required bytes value = 2; -} - -// Devices send this every few hours to tell us how they're doing. -message AndroidCheckinRequest { - // IMEI (used by GSM phones) is sent and stored as 15 decimal - // digits; the 15th is a check digit. - optional string imei = 1; // IMEI, reported but not logged. - - // MEID (used by CDMA phones) is sent and stored as 14 hexadecimal - // digits (no check digit). - optional string meid = 10; // MEID, reported but not logged. - - // MAC address (used by non-phone devices). 12 hexadecimal digits; - // no separators (eg "0016E6513AC2", not "00:16:E6:51:3A:C2"). - repeated string mac_addr = 9; // MAC address, reported but not logged. - - // An array parallel to mac_addr, describing the type of interface. - // Currently accepted values: "wifi", "ethernet", "bluetooth". If - // not present, "wifi" is assumed. - repeated string mac_addr_type = 19; - - // Serial number (a manufacturer-defined unique hardware - // identifier). Alphanumeric, case-insensitive. - optional string serial_number = 16; - - // Older CDMA networks use an ESN (8 hex digits) instead of an MEID. - optional string esn = 17; // ESN, reported but not logged - - optional int64 id = 2; // Android device ID, not logged - optional int64 logging_id = 7; // Pseudonymous logging ID for Sawmill - optional string digest = 3; // Digest of device provisioning, not logged. - optional string locale = 6; // Current locale in standard (xx_XX) format - required AndroidCheckinProto checkin = 4; - - // DEPRECATED, see AndroidCheckinProto.requested_group - optional string desired_build = 5; - - // Blob of data from the Market app to be passed to Market API server - optional string market_checkin = 8; - - // SID cookies of any google accounts stored on the phone. Not logged. - repeated string account_cookie = 11; - - // Time zone. Not currently logged. - optional string time_zone = 12; - - // Security token used to validate the checkin request. - // Required for android IDs issued to Froyo+ devices, not for legacy IDs. - optional fixed64 security_token = 13; - - // Version of checkin protocol. - // - // There are currently two versions: - // - // - version field missing: android IDs are assigned based on - // hardware identifiers. unsecured in the sense that you can - // "unregister" someone's phone by sending a registration request - // with their IMEI/MEID/MAC. - // - // - version=2: android IDs are assigned randomly. The device is - // sent a security token that must be included in all future - // checkins for that android id. - // - // - version=3: same as version 2, but the 'fragment' field is - // provided, and the device understands incremental updates to the - // gservices table (ie, only returning the keys whose values have - // changed.) - // - // (version=1 was skipped to avoid confusion with the "missing" - // version field that is effectively version 1.) - optional int32 version = 14; - - // OTA certs accepted by device (base-64 SHA-1 of cert files). Not - // logged. - repeated string ota_cert = 15; - - // Honeycomb and newer devices send configuration data with their checkin. - // optional DeviceConfigurationProto device_configuration = 18; - - // A single CheckinTask on the device may lead to multiple checkin - // requests if there is too much log data to upload in a single - // request. For version 3 and up, this field will be filled in with - // the number of the request, starting with 0. - optional int32 fragment = 20; - - // For devices supporting multiple users, the name of the current - // profile (they all check in independently, just as if they were - // multiple physical devices). This may not be set, even if the - // device is using multiuser. (checkin.user_number should be set to - // the ordinal of the user.) - optional string user_name = 21; - - // For devices supporting multiple user profiles, the serial number - // for the user checking in. Not logged. May not be set, even if - // the device supportes multiuser. checkin.user_number is the - // ordinal of the user (0, 1, 2, ...), which may be reused if users - // are deleted and re-created. user_serial_number is never reused - // (unless the device is wiped). - optional int32 user_serial_number = 22; - - // NEXT TAG: 23 -} - -// The response to the device. -message AndroidCheckinResponse { - required bool stats_ok = 1; // Whether statistics were recorded properly. - optional int64 time_msec = 3; // Time of day from server (Java epoch). - // repeated AndroidIntentProto intent = 2; - - // Provisioning is sent if the request included an obsolete digest. - // - // For version <= 2, 'digest' contains the digest that should be - // sent back to the server on the next checkin, and 'setting' - // contains the entire gservices table (which replaces the entire - // current table on the device). - // - // for version >= 3, 'digest' will be absent. If 'settings_diff' - // is false, then 'setting' contains the entire table, as in version - // 2. If 'settings_diff' is true, then 'delete_setting' contains - // the keys to delete, and 'setting' contains only keys to be added - // or for which the value has changed. All other keys in the - // current table should be left untouched. If 'settings_diff' is - // absent, don't touch the existing gservices table. - // - optional string digest = 4; - optional bool settings_diff = 9; - repeated string delete_setting = 10; - repeated GservicesSetting setting = 5; - - optional bool market_ok = 6; // If Market got the market_checkin data OK. - - optional fixed64 android_id = 7; // From the request, or newly assigned - optional fixed64 security_token = 8; // The associated security token - - optional string version_info = 11; - // NEXT TAG: 12 -} diff --git a/RustPlusApi/RustPlusApi.Fcm/RustPlusApi.Fcm.csproj b/RustPlusApi/RustPlusApi.Fcm/RustPlusApi.Fcm.csproj index 9fa298f..8f151be 100644 --- a/RustPlusApi/RustPlusApi.Fcm/RustPlusApi.Fcm.csproj +++ b/RustPlusApi/RustPlusApi.Fcm/RustPlusApi.Fcm.csproj @@ -5,7 +5,7 @@ enable enable RustPlusApi.Fcm - 1.0.2 + 1.0.0 HandyS11 HandyS11 A Rust+ API websocket made in C#. diff --git a/RustPlusApi/RustPlusApi.Fcm/RustPlusFcmListener.cs b/RustPlusApi/RustPlusApi.Fcm/RustPlusFcmListener.cs new file mode 100644 index 0000000..83464e0 --- /dev/null +++ b/RustPlusApi/RustPlusApi.Fcm/RustPlusFcmListener.cs @@ -0,0 +1,94 @@ +using System.Diagnostics; + +using Newtonsoft.Json; + +using RustPlusApi.Fcm.Data; +using RustPlusApi.Fcm.Data.Events; +using RustPlusApi.Fcm.Extensions; + +using static RustPlusApi.Fcm.Utils.ResponseHelper; + +namespace RustPlusApi.Fcm +{ + /// + /// Represents a RustPlus FCM listener. + /// + /// The credentials used for authentication. + /// The collection of persistent IDs. + public class RustPlusFcmListener(Credentials credentials, ICollection? persistentIds = null) + : RustPlusFcmListenerClient(credentials, persistentIds) + { + public event EventHandler? OnParing; + + public event EventHandler>? OnEntityParing; + public event EventHandler>? OnServerPairing; + + public event EventHandler>? OnSmartSwitchParing; + public event EventHandler>? OnSmartAlarmParing; + public event EventHandler>? OnStorageMonitorParing; + + public event EventHandler? OnAlarmTriggered; + + protected override void ParseNotification(string? message) + { + if (message is null) return; + + var msg = JsonConvert.DeserializeObject(message); + if (msg is null) return; + + switch (msg.Data.ChannelId) + { + case "pairing": + OnParing?.Invoke(this, msg.Data); + ParsePairing(msg.Data.Body); + break; + case "alarm": + OnAlarmTriggered?.Invoke(this, msg.Data.ToAlarmEvent()); + break; + default: + Debug.WriteLine($"Unknown channel: {msg.Data.ChannelId}"); + break; + } + } + + private void ParsePairing(Body body) + { + switch (body.Type) + { + case "entity": + var entity = BuildGenericOutput(body, body.ToEntityEvent()); + OnEntityParing?.Invoke(this, entity); + ParsePairingEntity(body); + break; + case "server": + var server = BuildGenericOutput(body, body.ToServerEvent()); + OnServerPairing?.Invoke(this, server); + break; + default: + Debug.WriteLine($"Unknown pairing type: {body.Type}"); + break; + } + } + + private void ParsePairingEntity(Body body) + { + var response = BuildGenericOutput(body, body.ToEntityId()); + + switch (body.EntityType) + { + case 1: + OnSmartSwitchParing?.Invoke(this, response); + break; + case 2: + OnSmartAlarmParing?.Invoke(this, response); + break; + case 3: + OnStorageMonitorParing?.Invoke(this, response); + break; + default: + Debug.WriteLine($"Unknown entity type: {body.EntityType}"); + break; + } + } + } +} diff --git a/RustPlusApi/RustPlusApi.Fcm/FcmListenerBasic.cs b/RustPlusApi/RustPlusApi.Fcm/RustPlusFcmListenerClient.cs similarity index 59% rename from RustPlusApi/RustPlusApi.Fcm/FcmListenerBasic.cs rename to RustPlusApi/RustPlusApi.Fcm/RustPlusFcmListenerClient.cs index 9f28df7..211f49e 100644 --- a/RustPlusApi/RustPlusApi.Fcm/FcmListenerBasic.cs +++ b/RustPlusApi/RustPlusApi.Fcm/RustPlusFcmListenerClient.cs @@ -5,46 +5,57 @@ using McsProto; -using Newtonsoft.Json; - using ProtoBuf; using RustPlusApi.Fcm.Data; +using RustPlusApi.Fcm.Data.Events; using RustPlusApi.Fcm.Utils; -using static RustPlusApi.Fcm.Data.Constants; +using static RustPlusApi.Fcm.Data.Tags; +using static RustPlusApi.Fcm.Utils.Utils; using static System.GC; -using RustPlusApi.Fcm.Data.Events; namespace RustPlusApi.Fcm { - public class FcmListenerBasic(Credentials credentials, ICollection? persistentIds = null) : IDisposable + /// + /// Represents a RustPlus FCM listener. + /// + /// The credentials used for authentication. + /// The collection of persistent IDs. + public class RustPlusFcmListenerClient(Credentials credentials, ICollection? persistentIds = null) : IDisposable { private const string Host = "mtalk.google.com"; private const int Port = 5228; + private const int KMcsVersion = 41; + private TcpClient? _tcpClient; private SslStream? _sslStream; - private DateTime _lastReset; - private DateTime _timeLastMessageReceived; - private Timer? _checkinTimer; + + private readonly CancellationTokenSource _cancellationTokenSource = new(); + private CancellationToken _cancellationToken => _cancellationTokenSource.Token; public event EventHandler? Connecting; public event EventHandler? Connected; + public event EventHandler? NotificationReceived; + + public event EventHandler? Disconnecting; public event EventHandler? Disconnected; + + public event EventHandler? SocketClosed; public event EventHandler? ErrorOccurred; public async Task ConnectAsync() { + Connecting?.Invoke(this, EventArgs.Empty); + _tcpClient = new TcpClient(); await _tcpClient.ConnectAsync(Host, Port); _sslStream = new SslStream(_tcpClient.GetStream(), false); await _sslStream.AuthenticateAsClientAsync(Host); - Connecting?.Invoke(this, EventArgs.Empty); - try { var loginRequest = new LoginRequest @@ -68,73 +79,108 @@ public async Task ConnectAsync() SendPacket(loginRequest); - _lastReset = DateTime.Now; - _timeLastMessageReceived = DateTime.Now; - Connected?.Invoke(this, EventArgs.Empty); - StatusCheck(); - ReceiveMessages(); + _ = Task.Run(ReceiveMessages, _cancellationToken); } catch (Exception ex) { + Debug.WriteLine($"Exception occured on ConnectAsync: {ex}"); ErrorOccurred?.Invoke(this, ex); - Dispose(); } } - public void Dispose() + /// + /// Disconnects the FCM listener client asynchronously. + /// + /// A task representing the asynchronous operation. + public void Disconnect() { - _sslStream?.Dispose(); - _tcpClient?.Dispose(); + Disconnecting?.Invoke(this, EventArgs.Empty); - Disconnected?.Invoke(this, EventArgs.Empty); + _cancellationTokenSource.Cancel(); + + _sslStream?.Close(); + _tcpClient?.Close(); - SuppressFinalize(this); + Disconnected?.Invoke(this, EventArgs.Empty); } + /// + /// Disposes the FCM listener client. + /// + public void Dispose() => SuppressFinalize(this); + + /// + /// Receives messages from the FCM listener. + /// private void ReceiveMessages() { - var parser = new RawMessageParser(); - parser.MessageReceived += (_, e) => OnMessage(e); - - // First receival (LoginResponse) - byte[] header = Read(2); + // Read the header + var header = Read(2); int version = header[0]; int tag = header[1]; if (version < KMcsVersion && version != 38) throw new InvalidOperationException($"Protocol version {version} unsupported"); - int size = ReadVarint32(); - Debug.WriteLine($"Got message size: {size} bytes"); - - byte[] payload = Read(size); - Debug.WriteLine($"Successfully read: {payload.Length} bytes"); - - Type type = RawMessageParser.BuildProtobufFromTag(((McsProtoTag)tag)); - Debug.WriteLine($"RECEIVED PROTO OF TYPE {type.Name}"); + var size = ReadVarint32(); + var payload = Read(size); + var type = BuildProtobufFromTag((McsProtoTag)tag); if (type != typeof(LoginResponse)) throw new Exception($"Got wrong login response. Expected {typeof(LoginResponse).Name}, got {type.Name}"); - parser.OnGotLoginResponse(); - parser.OnData(payload, type); + OnGotMessageBytes(payload, type); - // Start receival of the rest of messages - Debug.WriteLine("Starting receiver loop."); - while (true) + while (!_cancellationToken.IsCancellationRequested) { + // Read the tag and size tag = _sslStream!.ReadByte(); size = ReadVarint32(); payload = Read(size); - type = RawMessageParser.BuildProtobufFromTag((McsProtoTag)tag); - Debug.WriteLine($"RECEIVED PROTO OF TYPE {type.Name}"); + type = BuildProtobufFromTag((McsProtoTag)tag); + + OnGotMessageBytes(payload, type); + } + } + + /// + /// Handles the received message bytes. + /// + /// The message bytes. + /// The type of the message. + private void OnGotMessageBytes(byte[] data, Type type) + { + try + { + var messageTag = GetTagFromProtobufType(type); + + if (data.Length == 0) + { + OnMessage(new MessageEventArgs { Tag = messageTag, Object = Activator.CreateInstance(type) }); + return; + } + + var buffer = data.Take(data.Length).ToArray(); + data = data.Skip(data.Length).ToArray(); - parser.OnData(payload, type); + using var stream = new MemoryStream(buffer); + var message = Serializer.NonGeneric.Deserialize(type, stream); + + OnMessage(new MessageEventArgs { Tag = messageTag, Object = message }); + } + catch (Exception ex) + { + ErrorOccurred?.Invoke(this, ex); } } + /// + /// Reads the specified number of bytes from the SSL stream. + /// + /// The number of bytes to read. + /// An array of bytes read from the stream. private byte[] Read(int size) { byte[] buffer = new byte[size]; @@ -146,6 +192,10 @@ private byte[] Read(int size) return buffer; } + /// + /// Reads a variable-length 32-bit integer from the SSL stream. + /// + /// The 32-bit integer read from the stream. private int ReadVarint32() { int result = 0; @@ -160,22 +210,13 @@ private int ReadVarint32() return result; } - private static byte[] EncodeVarint32(int value) - { - List result = []; - while (value != 0) - { - byte b = (byte)(value & 0x7F); - value >>= 7; - if (value != 0) b |= 0x80; - result.Add(b); - } - return [.. result]; - } - + /// + /// Sends a packet over the SSL stream. + /// + /// The packet to send. private void SendPacket(object packet) { - var tagEnum = RawMessageParser.GetTagFromProtobufType(packet.GetType()); + var tagEnum = GetTagFromProtobufType(packet.GetType()); var header = new byte[] { KMcsVersion, (byte)(int)tagEnum }; using var ms = new MemoryStream(); @@ -185,6 +226,10 @@ private void SendPacket(object packet) _sslStream!.Write([.. header, .. EncodeVarint32(payload.Length), .. payload]); } + /// + /// Handles a ping message by sending a ping response. + /// + /// The ping message to handle. private void HandlePing(HeartbeatPing? ping) { if (ping == null) return; @@ -200,49 +245,12 @@ private void HandlePing(HeartbeatPing? ping) SendPacket(pingResponse); } - private void Reset(bool noWait = false) - { - if (!noWait) - { - var timeSinceLastReset = DateTime.Now - _lastReset; - - if (timeSinceLastReset < TimeSpan.FromSeconds(MinResetIntervalSecs)) - { - Debug.WriteLine($"{timeSinceLastReset.TotalSeconds}s since last reset attempt."); - - var waitTime = TimeSpan.FromSeconds(MinResetIntervalSecs) - timeSinceLastReset; - - Debug.WriteLine($"Waiting {waitTime.TotalSeconds}seconds"); - Thread.Sleep(waitTime); - } - } - _lastReset = DateTime.Now; - - Debug.WriteLine("Resetting listener."); - Dispose(); - - ConnectAsync().GetAwaiter().GetResult(); - } - - private void StatusCheck(object? state = null) - { - TimeSpan timeSinceLastMessage = DateTime.UtcNow - _timeLastMessageReceived; - if (timeSinceLastMessage > TimeSpan.FromSeconds(MaxSilentIntervalSecs)) - { - Debug.WriteLine($"No communications received in {timeSinceLastMessage.TotalSeconds}s. Resetting connection."); - Reset(true); - } - else - { - int expectedTimeout = 1 + MaxSilentIntervalSecs - (int)timeSinceLastMessage.TotalSeconds; - _checkinTimer = new Timer(StatusCheck, null, expectedTimeout * 1000, Timeout.Infinite); - } - } - + /// + /// Handles a message received event. + /// + /// The message event arguments. private void OnMessage(MessageEventArgs e) { - _timeLastMessageReceived = DateTime.Now; - // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault switch (e.Tag) { @@ -256,7 +264,8 @@ private void OnMessage(MessageEventArgs e) HandlePing(e.Object as HeartbeatPing); break; case McsProtoTag.KCloseTag: - Reset(true); + SocketClosed?.Invoke(this, EventArgs.Empty); + Disconnect(); break; case McsProtoTag.KIqStanzaTag: break; // I'm not sure about what this message does, and it arrives partially empty, so I will just leave it like this for now @@ -265,6 +274,10 @@ private void OnMessage(MessageEventArgs e) } } + /// + /// Handles a data message received event. + /// + /// The data message stanza. private void OnDataMessage(DataMessageStanza? dataMessage) { if (dataMessage?.PersistentId != null @@ -292,16 +305,14 @@ private void OnDataMessage(DataMessageStanza? dataMessage) persistentIds?.Add(dataMessage!.PersistentId); } - var fcmMessage = JsonConvert.DeserializeObject(message); - - fcmMessage!.Data.Body.Desc = ""; - - var betterMessage = JsonConvert.SerializeObject(fcmMessage, Formatting.Indented); - - ParseNotification(fcmMessage); - NotificationReceived?.Invoke(this, betterMessage); + ParseNotification(message); + NotificationReceived?.Invoke(this, message); } - protected virtual void ParseNotification(FcmMessage? message) { } + /// + /// Parses the notification message. + /// + /// The notification message to parse. + protected virtual void ParseNotification(string message) { } } } diff --git a/RustPlusApi/RustPlusApi.Fcm/Tools/FcmTools.cs b/RustPlusApi/RustPlusApi.Fcm/Tools/FcmTools.cs deleted file mode 100644 index cbee472..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/Tools/FcmTools.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Security.Cryptography; -using System.Text.Json; - -using RustPlusApi.Fcm.Data; - -namespace RustPlusApi.Fcm.Tools -{ - internal static class FcmTools - { - private const string FcmSubscribeUrl = "https://fcm.googleapis.com/fcm/connect/subscribe"; - private const string FcmEndpoint = "https://fcm.googleapis.com/fcm/send"; - - private static readonly HttpClient HttpClient = new(); - - internal static async Task> RegisterFcmAsync(string senderId, string token) - { - var keys = CreateKeys(); - var response = await HttpClient.PostAsync(FcmSubscribeUrl, new FormUrlEncodedContent(new Dictionary - { - { "authorized_entity", senderId }, - { "endpoint", $"{FcmEndpoint}/{token}" }, - { "encryption_key", keys.PublicKey }, - { "encryption_auth", keys.AuthSecret }, - })); - - var responseText = await response.Content.ReadAsStringAsync(); - var fcmResponse = JsonSerializer.Deserialize(responseText); - - return new Tuple(keys, fcmResponse!); - } - - private static Keys CreateKeys() - { - using var dh = ECDiffieHellman.Create(); - var privateKey = Convert.ToBase64String(dh.ExportECPrivateKey()); - var publicKey = Convert.ToBase64String(dh.PublicKey.ExportSubjectPublicKeyInfo()); - - var authSecretBytes = new byte[16]; - using var rng = RandomNumberGenerator.Create(); - rng.GetBytes(authSecretBytes); - var authSecret = Convert.ToBase64String(authSecretBytes); - - return new Keys - { - PrivateKey = privateKey, - PublicKey = publicKey, - AuthSecret = authSecret - }; - } - } -} diff --git a/RustPlusApi/RustPlusApi.Fcm/Tools/GcmTools.cs b/RustPlusApi/RustPlusApi.Fcm/Tools/GcmTools.cs deleted file mode 100644 index 048d5cd..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/Tools/GcmTools.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System.Diagnostics; -using System.Net.Http.Headers; - -using AndroidCheckinProto; - -using CheckinProto; - -using ProtoBuf; - -using RustPlusApi.Fcm.Data; - -namespace RustPlusApi.Fcm.Tools -{ - internal static class GcmTools - { - private static readonly HttpClient HttpClient = new(); - - private const string CheckInUrl = "https://android.clients.google.com/checkin"; - private const string RegisterUrl = "https://android.clients.google.com/c2dm/register3"; - - private static readonly string ServerKey = Convert.ToBase64String(Constants.ServerKey); - - public static async Task RegisterAsync(string appId) - { - var options = await CheckInAsync(); - var credentials = await DoRegisterAsync(options, appId); - return credentials; - } - - internal static async Task CheckInAsync(ulong? androidId = null, ulong? securityToken = null) - { - try - { - var id = (androidId != null) ? (long)androidId : (long?)null; - var requestBody = GetCheckInRequest(id, securityToken); - - var request = new HttpRequestMessage(HttpMethod.Post, CheckInUrl); - - using var ms = new MemoryStream(); - Serializer.Serialize(ms, requestBody); - - var content = new ByteArrayContent(ms.ToArray()); - content.Headers.ContentType = new MediaTypeHeaderValue("application/x-protobuf"); - - request.Content = content; - - var response = await HttpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); - - var data = await response.Content.ReadAsByteArrayAsync(); - - using var stream = new MemoryStream(data); - var message = Serializer.Deserialize(stream); - - return message; - } - catch (Exception ex) - { - throw new ApplicationException("Error during check-in request.", ex); - } - } - - - private static AndroidCheckinRequest GetCheckInRequest(long? androidId = null, ulong? securityToken = null) - { - return new AndroidCheckinRequest - { - UserSerialNumber = 0, - Checkin = new AndroidCheckinProto.AndroidCheckinProto - { - Type = DeviceType.DeviceChromeBrowser, - ChromeBuild = new ChromeBuildProto - { - platform = ChromeBuildProto.Platform.PlatformMac, - ChromeVersion = "63.0.3234.0", - channel = ChromeBuildProto.Channel.ChannelStable - } - }, - Version = 3, - Id = androidId ?? default, - SecurityToken = securityToken ?? default - }; - } - - private static async Task DoRegisterAsync(AndroidCheckinResponse option, string appId) - { - var body = new Dictionary - { - { "app", "org.chromium.linux" }, - { "X-subtype", appId }, - { "device", option.AndroidId.ToString() }, - { "sender", ServerKey } - }; - - var response = await PostRegisterAsync(option.AndroidId, option.SecurityToken, body); - var token = response.Split('=')[1]; - - return new GcmCredentials - { - Token = token, - AndroidId = option.AndroidId, - SecurityToken = option.SecurityToken, - AppId = appId - }; - } - - private static async Task PostRegisterAsync(ulong androidId, ulong securityToken, Dictionary body, int retry = 0) - { - while (true) - { - var request = new HttpRequestMessage(HttpMethod.Post, RegisterUrl) - { - Headers = - { - { "Authorization", $"AidLogin {androidId}:{securityToken}" }, - { "Content-Type", "application/x-www-form-urlencoded" } - }, - Content = new FormUrlEncodedContent(body) - }; - - var response = await HttpClient.SendAsync(request); - var responseText = await response.Content.ReadAsStringAsync(); - - if (!responseText.Contains("Error")) return responseText; - Debug.WriteLine($"Register request has failed with {responseText}"); - if (retry >= 5) - { - throw new Exception("GCM register has failed"); - } - - Debug.WriteLine($"Retry... {retry + 1}"); - await Task.Delay(1000); - retry++; - } - } - } -} diff --git a/RustPlusApi/RustPlusApi.Fcm/Utils/RawMessageParser.cs b/RustPlusApi/RustPlusApi.Fcm/Utils/RawMessageParser.cs deleted file mode 100644 index cd7b541..0000000 --- a/RustPlusApi/RustPlusApi.Fcm/Utils/RawMessageParser.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Diagnostics; - -using McsProto; - -using ProtoBuf; - -using RustPlusApi.Fcm.Data.Events; - -using static RustPlusApi.Fcm.Data.Constants; - -namespace RustPlusApi.Fcm.Utils -{ - internal class RawMessageParser() - { - internal event EventHandler? ErrorOccurred; - internal event EventHandler? MessageReceived; - - private byte[] _data = []; - private bool _handshakeComplete; - - internal void OnData(byte[] buffer, Type type) - { - _data = buffer; - OnGotMessageBytes(type); - } - - internal void OnGotLoginResponse() - { - _handshakeComplete = true; - } - - private void OnGotMessageBytes(Type type) - { - try - { - var messageTag = GetTagFromProtobufType(type); - - if (_data.Length == 0) - { - MessageReceived?.Invoke(this, new MessageEventArgs { Tag = messageTag, Object = Activator.CreateInstance(type) }); - return; - } - - var buffer = _data.Take(_data.Length).ToArray(); - _data = _data.Skip(_data.Length).ToArray(); - - using var stream = new MemoryStream(buffer); - var message = Serializer.NonGeneric.Deserialize(type, stream); - - MessageReceived?.Invoke(this, new MessageEventArgs { Tag = messageTag, Object = message }); - - if (messageTag == McsProtoTag.KLoginResponseTag) - { - if (_handshakeComplete) Debug.WriteLine("Unexpected login response"); - else - { - _handshakeComplete = true; - Debug.WriteLine("GCM Handshake complete."); - } - } - } - catch (Exception ex) - { - ErrorOccurred?.Invoke(this, ex); - } - } - - internal static McsProtoTag GetTagFromProtobufType(Type type) - { - if (type == typeof(HeartbeatPing)) return McsProtoTag.KHeartbeatPingTag; - else if (type == typeof(HeartbeatAck)) return McsProtoTag.KHeartbeatAckTag; - else if (type == typeof(LoginRequest)) return McsProtoTag.KLoginRequestTag; - else if (type == typeof(LoginResponse)) return McsProtoTag.KLoginResponseTag; - else if (type == typeof(Close)) return McsProtoTag.KCloseTag; - else if (type == typeof(IqStanza)) return McsProtoTag.KIqStanzaTag; - else if (type == typeof(DataMessageStanza)) return McsProtoTag.KDataMessageStanzaTag; - else if (type == typeof(StreamErrorStanza)) return McsProtoTag.KStreamErrorStanzaTag; - else throw new ArgumentOutOfRangeException(nameof(type), type, null); - } - - internal static Type BuildProtobufFromTag(McsProtoTag tag) - { - // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault - return tag switch - { - McsProtoTag.KHeartbeatPingTag => typeof(HeartbeatPing), - McsProtoTag.KHeartbeatAckTag => typeof(HeartbeatAck), - McsProtoTag.KLoginRequestTag => typeof(LoginRequest), - McsProtoTag.KLoginResponseTag => typeof(LoginResponse), - McsProtoTag.KCloseTag => typeof(Close), - McsProtoTag.KIqStanzaTag => typeof(IqStanza), - McsProtoTag.KDataMessageStanzaTag => typeof(DataMessageStanza), - McsProtoTag.KStreamErrorStanzaTag => typeof(StreamErrorStanza), - _ => throw new ArgumentOutOfRangeException(nameof(tag), tag, null) - }; - } - } -} diff --git a/RustPlusApi/RustPlusApi.Fcm/Utils/ResponseHelper.cs b/RustPlusApi/RustPlusApi.Fcm/Utils/ResponseHelper.cs new file mode 100644 index 0000000..6313f6e --- /dev/null +++ b/RustPlusApi/RustPlusApi.Fcm/Utils/ResponseHelper.cs @@ -0,0 +1,29 @@ +using RustPlusApi.Fcm.Data; + +namespace RustPlusApi.Fcm.Utils +{ + public static class ResponseHelper + { + public static Notification BuildGenericOutput(ulong playerId, int playerToken, Guid serverId, T data) + { + return new Notification + { + PlayerId = playerId, + PlayerToken = playerToken, + ServerId = serverId, + Data = data + }; + } + + public static Notification BuildGenericOutput(Body body, T data) + { + return new Notification + { + PlayerId = body.PlayerId, + PlayerToken = int.Parse(body.PlayerToken), + ServerId = body.Id, + Data = data + }; + } + } +} diff --git a/RustPlusApi/RustPlusApi.Fcm/Utils/Utils.cs b/RustPlusApi/RustPlusApi.Fcm/Utils/Utils.cs new file mode 100644 index 0000000..cb280b4 --- /dev/null +++ b/RustPlusApi/RustPlusApi.Fcm/Utils/Utils.cs @@ -0,0 +1,52 @@ +using McsProto; + +using static RustPlusApi.Fcm.Data.Tags; + +namespace RustPlusApi.Fcm.Utils +{ + public static class Utils + { + public static byte[] EncodeVarint32(int value) + { + List result = []; + while (value != 0) + { + byte b = (byte)(value & 0x7F); + value >>= 7; + if (value != 0) b |= 0x80; + result.Add(b); + } + return [.. result]; + } + + public static McsProtoTag GetTagFromProtobufType(Type type) + { + if (type == typeof(HeartbeatPing)) return McsProtoTag.KHeartbeatPingTag; + else if (type == typeof(HeartbeatAck)) return McsProtoTag.KHeartbeatAckTag; + else if (type == typeof(LoginRequest)) return McsProtoTag.KLoginRequestTag; + else if (type == typeof(LoginResponse)) return McsProtoTag.KLoginResponseTag; + else if (type == typeof(Close)) return McsProtoTag.KCloseTag; + else if (type == typeof(IqStanza)) return McsProtoTag.KIqStanzaTag; + else if (type == typeof(DataMessageStanza)) return McsProtoTag.KDataMessageStanzaTag; + else if (type == typeof(StreamErrorStanza)) return McsProtoTag.KStreamErrorStanzaTag; + else throw new ArgumentOutOfRangeException(nameof(type), type, null); + } + + public static Type BuildProtobufFromTag(McsProtoTag tag) + { + // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault + return tag switch + { + McsProtoTag.KHeartbeatPingTag => typeof(HeartbeatPing), + McsProtoTag.KHeartbeatAckTag => typeof(HeartbeatAck), + McsProtoTag.KLoginRequestTag => typeof(LoginRequest), + McsProtoTag.KLoginResponseTag => typeof(LoginResponse), + McsProtoTag.KCloseTag => typeof(Close), + McsProtoTag.KIqStanzaTag => typeof(IqStanza), + McsProtoTag.KDataMessageStanzaTag => typeof(DataMessageStanza), + McsProtoTag.KStreamErrorStanzaTag => typeof(StreamErrorStanza), + _ => throw new ArgumentOutOfRangeException(nameof(tag), tag, null) + }; + } + } +}