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)
+ };
+ }
+ }
+}