diff --git a/Fluxer.Net/Data/Apps/Application.cs b/Fluxer.Net/Data/Apps/Application.cs
index 3864a50..3f6dc9d 100644
--- a/Fluxer.Net/Data/Apps/Application.cs
+++ b/Fluxer.Net/Data/Apps/Application.cs
@@ -23,7 +23,7 @@ public static Application Create(FluxerBaseClient client, ApplicationJson json)
return data;
}
- internal void Update(FluxerBaseClient client, ApplicationJson json)
+ internal virtual void Update(FluxerBaseClient client, ApplicationJson json)
{
base.Update(client, json);
RedirectUrls = json.RedirectUrls;
diff --git a/Fluxer.Net/Data/Channels/Channel.cs b/Fluxer.Net/Data/Channels/Channel.cs
index c387079..f74e6c7 100644
--- a/Fluxer.Net/Data/Channels/Channel.cs
+++ b/Fluxer.Net/Data/Channels/Channel.cs
@@ -106,7 +106,7 @@ public static Channel Create(FluxerBaseClient client, ChannelJson json)
break;
case ChannelType.DmPersonalNotes:
{
- data = new SavedMessagesChannel(client);
+ data = new SavedNotesChannel(client);
data.IsTextable = true;
}
break;
@@ -128,7 +128,10 @@ public static Channel Create(FluxerBaseClient client, ChannelJson json)
break;
default:
{
- data = new Channel(client);
+ if (data.GuildId.HasValue)
+ data = new GuildChannel(client);
+ else
+ data = new Channel(client);
data.IsTextable = true;
}
break;
@@ -137,7 +140,7 @@ public static Channel Create(FluxerBaseClient client, ChannelJson json)
return data;
}
- internal void Update(FluxerBaseClient client, ChannelJson json)
+ internal virtual void Update(FluxerBaseClient client, ChannelJson json)
{
Id = json.Id;
GuildId = json.GuildId;
diff --git a/Fluxer.Net/Data/Channels/GuildChannel.cs b/Fluxer.Net/Data/Channels/GuildChannel.cs
index baac528..16d3a8d 100644
--- a/Fluxer.Net/Data/Channels/GuildChannel.cs
+++ b/Fluxer.Net/Data/Channels/GuildChannel.cs
@@ -6,16 +6,4 @@ internal GuildChannel(FluxerBaseClient client) : base(client)
{
}
-
- public static GuildChannel Create(FluxerBaseClient client, ChannelJson json)
- {
- var data = new GuildChannel(client);
- data.Update(client, json);
- return data;
- }
-
- internal void Update(FluxerBaseClient client, ChannelJson json)
- {
- base.Update(client, json);
- }
}
diff --git a/Fluxer.Net/Data/Channels/IPermissionOverwrite.cs b/Fluxer.Net/Data/Channels/IPermissionOverwrite.cs
index a74274c..a5c0459 100644
--- a/Fluxer.Net/Data/Channels/IPermissionOverwrite.cs
+++ b/Fluxer.Net/Data/Channels/IPermissionOverwrite.cs
@@ -15,10 +15,10 @@ public interface IPermissionOverwrite
///
/// The bitwise value of allowed permissions.
///
- ulong Allow { get; }
+ ChannelPermissions Allow { get; }
///
/// The bitwise value of denied permissions.
///
- ulong Deny { get; }
+ ChannelPermissions Deny { get; }
}
diff --git a/Fluxer.Net/Data/Channels/NotesChannel.cs b/Fluxer.Net/Data/Channels/NotesChannel.cs
deleted file mode 100644
index 9d3c14c..0000000
--- a/Fluxer.Net/Data/Channels/NotesChannel.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Fluxer.Net;
-
-public class SavedMessagesChannel : Channel, ITextable
-{
- internal SavedMessagesChannel(FluxerBaseClient client) : base(client)
- {
-
- }
-}
diff --git a/Fluxer.Net/Data/Channels/PermissionOverwrite.cs b/Fluxer.Net/Data/Channels/PermissionOverwrite.cs
index b2f6660..13ece8b 100644
--- a/Fluxer.Net/Data/Channels/PermissionOverwrite.cs
+++ b/Fluxer.Net/Data/Channels/PermissionOverwrite.cs
@@ -10,10 +10,10 @@ public class PermissionOverwrite : Entity, IPermissionOverwrite
public int Type { get; internal set; }
///
- public ulong Allow { get; internal set; }
+ public ChannelPermissions Allow { get; internal set; }
///
- public ulong Deny { get; internal set; }
+ public ChannelPermissions Deny { get; internal set; }
internal PermissionOverwrite(FluxerBaseClient client) : base(client)
{
diff --git a/Fluxer.Net/Data/Channels/PermissionOverwriteJson.cs b/Fluxer.Net/Data/Channels/PermissionOverwriteJson.cs
index 0e14b79..648907f 100644
--- a/Fluxer.Net/Data/Channels/PermissionOverwriteJson.cs
+++ b/Fluxer.Net/Data/Channels/PermissionOverwriteJson.cs
@@ -18,9 +18,9 @@ public class PermissionOverwriteJson : IPermissionOverwrite
///
[JsonProperty("allow")]
- public ulong Allow { get; set; }
+ public ChannelPermissions Allow { get; set; }
///
[JsonProperty("deny")]
- public ulong Deny { get; set; }
+ public ChannelPermissions Deny { get; set; }
}
diff --git a/Fluxer.Net/Data/Channels/SavedNotesChannel.cs b/Fluxer.Net/Data/Channels/SavedNotesChannel.cs
new file mode 100644
index 0000000..1833ccd
--- /dev/null
+++ b/Fluxer.Net/Data/Channels/SavedNotesChannel.cs
@@ -0,0 +1,9 @@
+namespace Fluxer.Net;
+
+public class SavedNotesChannel : Channel, ITextable
+{
+ internal SavedNotesChannel(FluxerBaseClient client) : base(client)
+ {
+
+ }
+}
diff --git a/Fluxer.Net/Data/Channels/SocketCategoryChannel.cs b/Fluxer.Net/Data/Channels/SocketCategoryChannel.cs
new file mode 100644
index 0000000..8fa63fe
--- /dev/null
+++ b/Fluxer.Net/Data/Channels/SocketCategoryChannel.cs
@@ -0,0 +1,16 @@
+namespace Fluxer.Net;
+
+public class SocketCategoryChannel : CategoryChannel
+{
+ internal SocketCategoryChannel(FluxerBaseClient client) : base(client)
+ {
+
+ }
+
+ public static SocketCategoryChannel Create(FluxerBaseClient client, ChannelJson json)
+ {
+ var data = new SocketCategoryChannel(client);
+ data.Update(client, json);
+ return data;
+ }
+}
diff --git a/Fluxer.Net/Data/Channels/SocketChannel.cs b/Fluxer.Net/Data/Channels/SocketChannel.cs
deleted file mode 100644
index 5fa6384..0000000
--- a/Fluxer.Net/Data/Channels/SocketChannel.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-namespace Fluxer.Net;
-
-public class SocketChannel : Channel
-{
- ///
- /// Permissions for the channel.
- ///
- public ChannelPermissions Permissions { get; internal set; }
-
- internal SocketChannel(FluxerBaseClient client) : base(client)
- {
-
- }
-
- public static SocketChannel Create(FluxerBaseClient client, ChannelJson json, ulong guildId)
- {
- SocketChannel data = new SocketChannel(client);
- data.GuildId = guildId;
- data.Update(client, json);
- return data;
- }
-
- internal void Update(FluxerBaseClient client, ChannelJson json)
- {
- base.Update(client, json);
- PermissionOverwriteJson? overwrite = json.PermissionOverwrites.FirstOrDefault(x => x.Id == Id);
- if (overwrite != null)
- Permissions = new ChannelPermissions((Permissions)overwrite.Allow);
- else
- Permissions = new ChannelPermissions(0);
- }
-}
\ No newline at end of file
diff --git a/Fluxer.Net/Data/Channels/SocketDMChannel.cs b/Fluxer.Net/Data/Channels/SocketDMChannel.cs
new file mode 100644
index 0000000..c0c12c5
--- /dev/null
+++ b/Fluxer.Net/Data/Channels/SocketDMChannel.cs
@@ -0,0 +1,9 @@
+namespace Fluxer.Net;
+
+public class SocketDMChannel : DMChannel
+{
+ internal SocketDMChannel(FluxerBaseClient client) : base(client)
+ {
+
+ }
+}
diff --git a/Fluxer.Net/Data/Channels/SocketGroupChannel.cs b/Fluxer.Net/Data/Channels/SocketGroupChannel.cs
new file mode 100644
index 0000000..807b453
--- /dev/null
+++ b/Fluxer.Net/Data/Channels/SocketGroupChannel.cs
@@ -0,0 +1,16 @@
+namespace Fluxer.Net;
+
+public class SocketGroupChannel : GroupChannel
+{
+ internal SocketGroupChannel(FluxerBaseClient client) : base(client)
+ {
+
+ }
+
+ public static SocketGroupChannel Create(FluxerBaseClient client, ChannelJson json)
+ {
+ var data = new SocketGroupChannel(client);
+ data.Update(client, json);
+ return data;
+ }
+}
diff --git a/Fluxer.Net/Data/Channels/SocketLinkChannel.cs b/Fluxer.Net/Data/Channels/SocketLinkChannel.cs
new file mode 100644
index 0000000..6d59fef
--- /dev/null
+++ b/Fluxer.Net/Data/Channels/SocketLinkChannel.cs
@@ -0,0 +1,16 @@
+namespace Fluxer.Net;
+
+public class SocketLinkChannel : LinkChannel
+{
+ internal SocketLinkChannel(FluxerBaseClient client) : base(client)
+ {
+
+ }
+
+ public static SocketLinkChannel Create(FluxerBaseClient client, ChannelJson json)
+ {
+ var data = new SocketLinkChannel(client);
+ data.Update(client, json);
+ return data;
+ }
+}
diff --git a/Fluxer.Net/Data/Channels/SocketSavedNotesChannel.cs b/Fluxer.Net/Data/Channels/SocketSavedNotesChannel.cs
new file mode 100644
index 0000000..98d5442
--- /dev/null
+++ b/Fluxer.Net/Data/Channels/SocketSavedNotesChannel.cs
@@ -0,0 +1,16 @@
+namespace Fluxer.Net;
+
+public class SocketSavedNotesChannel : SavedNotesChannel
+{
+ internal SocketSavedNotesChannel(FluxerBaseClient client) : base(client)
+ {
+
+ }
+
+ public static SocketSavedNotesChannel Create(FluxerBaseClient client, ChannelJson json)
+ {
+ var data = new SocketSavedNotesChannel(client);
+ data.Update(client, json);
+ return data;
+ }
+}
diff --git a/Fluxer.Net/Data/Channels/SocketTextChannel.cs b/Fluxer.Net/Data/Channels/SocketTextChannel.cs
new file mode 100644
index 0000000..2c44f76
--- /dev/null
+++ b/Fluxer.Net/Data/Channels/SocketTextChannel.cs
@@ -0,0 +1,9 @@
+namespace Fluxer.Net;
+
+public class SocketTextChannel : TextChannel
+{
+ internal SocketTextChannel(FluxerBaseClient client) : base(client)
+ {
+
+ }
+}
diff --git a/Fluxer.Net/Data/Channels/SocketUnknownChannel.cs b/Fluxer.Net/Data/Channels/SocketUnknownChannel.cs
new file mode 100644
index 0000000..01dd8ca
--- /dev/null
+++ b/Fluxer.Net/Data/Channels/SocketUnknownChannel.cs
@@ -0,0 +1,84 @@
+namespace Fluxer.Net;
+
+public class SocketUnknownChannel : Channel
+{
+ ///
+ /// Permissions for the channel.
+ ///
+ public ChannelPermissions Permissions { get; internal set; }
+
+ internal SocketUnknownChannel(FluxerBaseClient client) : base(client)
+ {
+
+ }
+
+ public static Channel Create(FluxerBaseClient client, ChannelJson json, ulong guildId)
+ {
+ Channel data = null;
+
+ switch (json.Type)
+ {
+ case ChannelType.GuildText:
+ {
+ data = new SocketTextChannel(client);
+ data.IsTextable = true;
+ }
+ break;
+ case ChannelType.GuildVoice:
+ {
+ data = new SocketVoiceChannel(client);
+ }
+ break;
+ case ChannelType.Dm:
+ {
+ data = new SocketDMChannel(client);
+ data.IsTextable = true;
+ }
+ break;
+ case ChannelType.DmPersonalNotes:
+ {
+ data = new SocketSavedNotesChannel(client);
+ data.IsTextable = true;
+ }
+ break;
+ case ChannelType.GroupDm:
+ {
+ data = new SocketGroupChannel(client);
+ data.IsTextable = true;
+ }
+ break;
+ case ChannelType.GuildCategory:
+ {
+ data = new SocketCategoryChannel(client);
+ }
+ break;
+ case ChannelType.GuildLink:
+ {
+ data = new SocketLinkChannel(client);
+ }
+ break;
+ default:
+ {
+ if (data.GuildId.HasValue)
+ data = new SocketUnknownGuildChannel(client);
+ else
+ data = new SocketUnknownChannel(client);
+ data.IsTextable = true;
+ }
+ break;
+ }
+ data.GuildId = guildId;
+ data.Update(client, json);
+ return data;
+ }
+
+ internal override void Update(FluxerBaseClient client, ChannelJson json)
+ {
+ base.Update(client, json);
+ PermissionOverwriteJson? overwrite = json.PermissionOverwrites.FirstOrDefault(x => x.Id == Id);
+ if (overwrite != null)
+ Permissions = overwrite.Allow;
+ else
+ Permissions = new ChannelPermissions(0);
+ }
+}
\ No newline at end of file
diff --git a/Fluxer.Net/Data/Channels/SocketUnknownGuildChannel.cs b/Fluxer.Net/Data/Channels/SocketUnknownGuildChannel.cs
new file mode 100644
index 0000000..5627bf7
--- /dev/null
+++ b/Fluxer.Net/Data/Channels/SocketUnknownGuildChannel.cs
@@ -0,0 +1,9 @@
+namespace Fluxer.Net;
+
+public class SocketUnknownGuildChannel : GuildChannel
+{
+ internal SocketUnknownGuildChannel(FluxerBaseClient client) : base(client)
+ {
+
+ }
+}
diff --git a/Fluxer.Net/Data/Channels/SocketVoiceChannel.cs b/Fluxer.Net/Data/Channels/SocketVoiceChannel.cs
new file mode 100644
index 0000000..aa14312
--- /dev/null
+++ b/Fluxer.Net/Data/Channels/SocketVoiceChannel.cs
@@ -0,0 +1,9 @@
+namespace Fluxer.Net;
+
+public class SocketVoiceChannel : VoiceChannel
+{
+ internal SocketVoiceChannel(FluxerBaseClient client) : base(client)
+ {
+
+ }
+}
diff --git a/Fluxer.Net/Data/Channels/VoiceChannel.cs b/Fluxer.Net/Data/Channels/VoiceChannel.cs
index 8bde753..8f318ab 100644
--- a/Fluxer.Net/Data/Channels/VoiceChannel.cs
+++ b/Fluxer.Net/Data/Channels/VoiceChannel.cs
@@ -1,6 +1,6 @@
namespace Fluxer.Net;
-internal class VoiceChannel : GuildChannel
+public class VoiceChannel : GuildChannel
{
internal VoiceChannel(FluxerBaseClient client) : base(client)
{
diff --git a/Fluxer.Net/Data/Guilds/Guild.cs b/Fluxer.Net/Data/Guilds/Guild.cs
index 03735c5..cd15907 100644
--- a/Fluxer.Net/Data/Guilds/Guild.cs
+++ b/Fluxer.Net/Data/Guilds/Guild.cs
@@ -57,7 +57,7 @@ public static Guild Create(FluxerBaseClient client, GuildJson json)
return data;
}
- internal void Update(FluxerBaseClient client, GuildJson json)
+ internal virtual void Update(FluxerBaseClient client, GuildJson json)
{
base.Update(client, json);
OwnerId = json.OwnerId;
diff --git a/Fluxer.Net/Data/Guilds/Members/GuildMember.cs b/Fluxer.Net/Data/Guilds/Members/GuildMember.cs
index ed6aecf..52e4f62 100644
--- a/Fluxer.Net/Data/Guilds/Members/GuildMember.cs
+++ b/Fluxer.Net/Data/Guilds/Members/GuildMember.cs
@@ -86,7 +86,7 @@ public static GuildMember Create(FluxerBaseClient client, GuildMemberJson json)
return data;
}
- internal void Update(FluxerBaseClient client, GuildMemberJson json)
+ internal virtual void Update(FluxerBaseClient client, GuildMemberJson json)
{
GuildId = json.GuildId;
User = User.Create(client, json.User);
diff --git a/Fluxer.Net/Data/Guilds/Members/IRole.cs b/Fluxer.Net/Data/Guilds/Members/IRole.cs
index 5a41c17..4b0664e 100644
--- a/Fluxer.Net/Data/Guilds/Members/IRole.cs
+++ b/Fluxer.Net/Data/Guilds/Members/IRole.cs
@@ -20,7 +20,7 @@ public interface IRole
///
/// The role's permission bitfield. Sent as a quoted string by the gateway (e.g. "8933636165184").
///
- Permissions Permissions { get; }
+ GuildPermissions Permissions { get; }
///
/// The position of the role in the role hierarchy.
diff --git a/Fluxer.Net/Data/Guilds/Members/Role.cs b/Fluxer.Net/Data/Guilds/Members/Role.cs
index e501626..7b8c3a9 100644
--- a/Fluxer.Net/Data/Guilds/Members/Role.cs
+++ b/Fluxer.Net/Data/Guilds/Members/Role.cs
@@ -16,7 +16,7 @@ public class Role : Entity, IRole
public string Name { get; internal set; }
///
- public Permissions Permissions { get; internal set; }
+ public GuildPermissions Permissions { get; internal set; }
///
public int Position { get; internal set; }
@@ -46,7 +46,7 @@ public static Role Create(FluxerBaseClient client, RoleJson json, ulong guildId)
return data;
}
- internal void Update(FluxerBaseClient client, RoleJson json)
+ internal virtual void Update(FluxerBaseClient client, RoleJson json)
{
Id = json.Id;
Name = json.Name;
diff --git a/Fluxer.Net/Data/Guilds/Members/RoleJson.cs b/Fluxer.Net/Data/Guilds/Members/RoleJson.cs
index df65c6f..a2ea468 100644
--- a/Fluxer.Net/Data/Guilds/Members/RoleJson.cs
+++ b/Fluxer.Net/Data/Guilds/Members/RoleJson.cs
@@ -19,7 +19,7 @@ public class RoleJson : IRole
///
[JsonProperty("permissions")]
- public Permissions Permissions { get; set; }
+ public GuildPermissions Permissions { get; set; }
///
[JsonProperty("position")]
diff --git a/Fluxer.Net/Data/Guilds/Members/SocketGuildMember.cs b/Fluxer.Net/Data/Guilds/Members/SocketGuildMember.cs
index 5934942..9291664 100644
--- a/Fluxer.Net/Data/Guilds/Members/SocketGuildMember.cs
+++ b/Fluxer.Net/Data/Guilds/Members/SocketGuildMember.cs
@@ -4,6 +4,20 @@ public class SocketGuildMember : GuildMember
{
public SocketGuild Guild { get; internal set; }
+ public IEnumerable Roles
+ => RoleIds.Select(id => Guild.Roles[id]).Where(x => x != null);
+
+ public bool HasPermission(Permissions permission)
+ {
+ foreach (var r in Roles)
+ {
+ if (r.Permissions.RawValue.HasFlag(permission))
+ return true;
+ }
+
+ return false;
+ }
+
internal SocketGuildMember(FluxerBaseClient client) : base(client)
{
@@ -16,7 +30,7 @@ public static SocketGuildMember Create(FluxerBaseClient client, GuildMemberJson
return data;
}
- internal void Update(FluxerBaseClient client, GuildMemberJson json)
+ internal override void Update(FluxerBaseClient client, GuildMemberJson json)
{
base.Update(client, json);
}
diff --git a/Fluxer.Net/Data/Guilds/Members/SocketRole.cs b/Fluxer.Net/Data/Guilds/Members/SocketRole.cs
index 9ace1c0..6b974f9 100644
--- a/Fluxer.Net/Data/Guilds/Members/SocketRole.cs
+++ b/Fluxer.Net/Data/Guilds/Members/SocketRole.cs
@@ -2,20 +2,34 @@
public class SocketRole : Role
{
+ public SocketGuild Guild { get; internal set; }
+
+ public bool HasPermission(Permissions permission)
+ {
+ if (Permissions.RawValue.HasFlag(permission))
+ return true;
+
+ if (Guild.Permissions.RawValue.HasFlag(permission))
+ return true;
+
+ return false;
+ }
+
internal SocketRole(FluxerBaseClient client) : base(client)
{
}
- public static SocketRole Create(FluxerBaseClient client, RoleJson json, ulong guildId)
+ public static SocketRole Create(FluxerBaseClient client, RoleJson json, SocketGuild guild)
{
SocketRole data = new SocketRole(client);
- data.GuildId = guildId;
+ data.GuildId = guild.Id;
+ data.Guild = guild;
data.Update(client, json);
return data;
}
- internal void Update(FluxerBaseClient client, RoleJson json)
+ internal override void Update(FluxerBaseClient client, RoleJson json)
{
base.Update(client, json);
}
diff --git a/Fluxer.Net/Data/Guilds/SocketGuild.cs b/Fluxer.Net/Data/Guilds/SocketGuild.cs
index 9ff63d2..6af7ffe 100644
--- a/Fluxer.Net/Data/Guilds/SocketGuild.cs
+++ b/Fluxer.Net/Data/Guilds/SocketGuild.cs
@@ -1,15 +1,42 @@
-namespace Fluxer.Net;
+using System.Collections.Concurrent;
+
+namespace Fluxer.Net;
///
/// Cached guild from the gateway.
///
public class SocketGuild : Guild
{
+ internal TaskCompletionSource? _downloaderPromise;
+
///
/// Cached current logged in member for the guild.
///
public SocketGuildMember CurrentMember { get; internal set; }
+ public SocketRole EveryoneRole => Roles.GetValueOrDefault(Id);
+
+ public bool HasAllMembers { get; internal set; }
+
+ public ConcurrentDictionary Members { get; internal set; } = new ConcurrentDictionary();
+ public ConcurrentDictionary Channels { get; internal set; } = new ConcurrentDictionary();
+ public ConcurrentDictionary Roles { get; internal set; } = new ConcurrentDictionary();
+
+ public SocketGuildMember? GetMember(ulong userId)
+ {
+ return Members.GetValueOrDefault(userId);
+ }
+
+ public SocketRole? GetRole(ulong roleId)
+ {
+ return Roles.GetValueOrDefault(roleId);
+ }
+
+ public Channel? GetChannel(ulong channelId)
+ {
+ return Channels.GetValueOrDefault(channelId);
+ }
+
///
/// Permissions for the guild.
///
@@ -24,18 +51,27 @@ public static SocketGuild Create(FluxerBaseClient client, GuildJson json, Socket
{
SocketGuild data = new SocketGuild(client);
data.CurrentMember = member;
+ data.Members.TryAdd(member.UserId, member);
data.CurrentMember.Guild = data;
data.Update(client, json);
return data;
}
- internal void Update(FluxerBaseClient client, GuildJson json)
+ internal override void Update(FluxerBaseClient client, GuildJson json)
{
base.Update(client, json);
}
internal void UpdatePermissions(SocketRole role)
{
- Permissions = new GuildPermissions(role.Permissions);
+ Permissions = role.Permissions;
+ }
+
+ internal void AddOrUpdate(FluxerClient client, GuildMemberJson member)
+ {
+ var mem = SocketGuildMember.Create(client, member);
+ mem.Guild = this;
+ if (!Members.TryAdd(member.UserId, mem))
+ Members[member.UserId].Update(client, member);
}
}
\ No newline at end of file
diff --git a/Fluxer.Net/Extensions/ChannelPermissionsConverter.cs b/Fluxer.Net/Extensions/ChannelPermissionsConverter.cs
new file mode 100644
index 0000000..dfb0431
--- /dev/null
+++ b/Fluxer.Net/Extensions/ChannelPermissionsConverter.cs
@@ -0,0 +1,24 @@
+using Newtonsoft.Json;
+
+namespace Fluxer.Net.Extensions;
+
+public class ChannelPermissionsConverter : JsonConverter
+{
+ public override ChannelPermissions ReadJson(JsonReader reader, Type objectType, ChannelPermissions GuildPermissions, bool hasExistingValue, JsonSerializer serializer)
+ {
+ if (reader.Value == null)
+ return new ChannelPermissions(0);
+
+ if (reader.TokenType == JsonToken.String)
+ return new ChannelPermissions((Permissions)ulong.Parse((string)reader.Value));
+
+ if (reader.TokenType == JsonToken.Integer)
+ return new ChannelPermissions((Permissions)Convert.ToUInt64(reader.Value));
+
+ throw new JsonSerializationException(
+ $"Unexpected token type '{reader.TokenType}' when deserializing GuildPermissions at path '{reader.Path}'.");
+ }
+
+ public override void WriteJson(JsonWriter writer, ChannelPermissions value, JsonSerializer serializer)
+ => writer.WriteValue((ulong)value.RawValue);
+}
\ No newline at end of file
diff --git a/Fluxer.Net/Extensions/GuildPermissionsConverter.cs b/Fluxer.Net/Extensions/GuildPermissionsConverter.cs
new file mode 100644
index 0000000..c8f4d76
--- /dev/null
+++ b/Fluxer.Net/Extensions/GuildPermissionsConverter.cs
@@ -0,0 +1,24 @@
+using Newtonsoft.Json;
+
+namespace Fluxer.Net.Extensions;
+
+public class GuildPermissionsConverter : JsonConverter
+{
+ public override GuildPermissions ReadJson(JsonReader reader, Type objectType, GuildPermissions GuildPermissions, bool hasExistingValue, JsonSerializer serializer)
+ {
+ if (reader.Value == null)
+ return new GuildPermissions(0);
+
+ if (reader.TokenType == JsonToken.String)
+ return new GuildPermissions((Permissions)ulong.Parse((string)reader.Value));
+
+ if (reader.TokenType == JsonToken.Integer)
+ return new GuildPermissions((Permissions)Convert.ToUInt64(reader.Value));
+
+ throw new JsonSerializationException(
+ $"Unexpected token type '{reader.TokenType}' when deserializing GuildPermissions at path '{reader.Path}'.");
+ }
+
+ public override void WriteJson(JsonWriter writer, GuildPermissions value, JsonSerializer serializer)
+ => writer.WriteValue((ulong)value.RawValue);
+}
diff --git a/Fluxer.Net/FluxerClient.cs b/Fluxer.Net/FluxerClient.cs
index 68d340f..044c90e 100644
--- a/Fluxer.Net/FluxerClient.cs
+++ b/Fluxer.Net/FluxerClient.cs
@@ -66,12 +66,6 @@ public FluxerClient(string token, FluxerConfig? config = null)
internal static JsonSerializer _serializer { get; set; } = CreateGatewaySerializer();
- // Used by both api and gateway
- internal static JsonSerializerSettings _serializerSettings { get; set; } = new JsonSerializerSettings()
- {
- NullValueHandling = NullValueHandling.Ignore
- };
-
internal static JsonSerializer CreateGatewaySerializer()
{
var serializer = new JsonSerializer
@@ -79,9 +73,26 @@ internal static JsonSerializer CreateGatewaySerializer()
NullValueHandling = NullValueHandling.Ignore
};
serializer.Converters.Add(new StringUInt64Converter());
+ serializer.Converters.Add(new GuildPermissionsConverter());
+ serializer.Converters.Add(new ChannelPermissionsConverter());
return serializer;
}
+ // Used by both api and gateway
+ internal static JsonSerializerSettings _serializerSettings { get; set; } = CreateRestSerializer();
+
+ internal static JsonSerializerSettings CreateRestSerializer()
+ {
+ var serializer = new JsonSerializerSettings
+ {
+ NullValueHandling = NullValueHandling.Ignore
+ };
+ serializer.Converters.Add(new GuildPermissionsConverter());
+ serializer.Converters.Add(new ChannelPermissionsConverter());
+ return serializer;
+ }
+
+
///
/// Validates that the token has a recognized prefix for the Fluxer API.
///
diff --git a/Fluxer.Net/FluxerNet.xml b/Fluxer.Net/FluxerNet.xml
index 10bfafd..c6aedfe 100644
--- a/Fluxer.Net/FluxerNet.xml
+++ b/Fluxer.Net/FluxerNet.xml
@@ -1314,7 +1314,7 @@
-
+
Permissions for the channel.
@@ -7255,6 +7255,11 @@
Useful for managing event load in bots that are in many guilds.
+
+
+ The guild ID.
+
+
WebSocket gateway client for real-time events from the Fluxer platform.
diff --git a/Fluxer.Net/Gateway/Data/Guilds/GuildMembersChunkGatewayData.cs b/Fluxer.Net/Gateway/Data/Guilds/GuildMembersChunkGatewayData.cs
new file mode 100644
index 0000000..3831adc
--- /dev/null
+++ b/Fluxer.Net/Gateway/Data/Guilds/GuildMembersChunkGatewayData.cs
@@ -0,0 +1,18 @@
+using Newtonsoft.Json;
+
+namespace Fluxer.Net.Gateway.Data.Guilds;
+
+public class GuildMembersChunkGatewayData
+{
+ [JsonProperty("chunk_count")]
+ public int ChunkCount { get; set; }
+
+ [JsonProperty("chunk_index")]
+ public int ChunkIndex { get; set; }
+
+ [JsonProperty("guild_id")]
+ public ulong GuildId { get; set; }
+
+ [JsonProperty("members")]
+ public GuildMemberJson[] Members { get; set; }
+}
diff --git a/Fluxer.Net/Gateway/GatewayClient.cs b/Fluxer.Net/Gateway/GatewayClient.cs
index 072bdad..fb150df 100644
--- a/Fluxer.Net/Gateway/GatewayClient.cs
+++ b/Fluxer.Net/Gateway/GatewayClient.cs
@@ -13,6 +13,7 @@
using Newtonsoft.Json.Linq;
using Serilog.Core;
using System.Collections.Concurrent;
+using System.Data;
using System.Diagnostics;
using System.Reflection;
using System.Text.RegularExpressions;
@@ -102,10 +103,32 @@ internal GatewayClient(FluxerClient client)
#region Cache
public CurrentUser? CurrentUser { get; internal set; }
- public ConcurrentDictionary Guilds = new ConcurrentDictionary();
- public ConcurrentDictionary Channels = new ConcurrentDictionary();
- public ConcurrentDictionary Roles = new ConcurrentDictionary();
- public ConcurrentDictionary CurrentMembers = new ConcurrentDictionary();
+ public ConcurrentDictionary Guilds { get; internal set; } = new ConcurrentDictionary();
+ public ConcurrentDictionary Channels { get; internal set; } = new ConcurrentDictionary();
+ public ConcurrentDictionary Roles { get; internal set; } = new ConcurrentDictionary();
+ public ConcurrentDictionary CurrentMembers { get; internal set; } = new ConcurrentDictionary();
+ public RtcRegion[] RtcRegions;
+
+ public SocketGuildMember? GetCurrentMember(ulong userId)
+ {
+ return CurrentMembers.GetValueOrDefault(userId);
+ }
+
+ public SocketGuild? GetGuild(ulong guildId)
+ {
+ return Guilds.GetValueOrDefault(guildId);
+ }
+
+ public SocketRole? GetRole(ulong roleId)
+ {
+ return Roles.GetValueOrDefault(roleId);
+ }
+
+ public Channel? GetChannel(ulong channelId)
+ {
+ return Channels.GetValueOrDefault(channelId);
+ }
+
#endregion
#region Gateway
@@ -228,6 +251,29 @@ public async Task ConnectAsync()
}
}
+ public async Task DownloadMembersAsync(SocketGuild guild)
+ {
+ if (guild._downloaderPromise != null)
+ return await guild._downloaderPromise.Task;
+
+ guild._downloaderPromise = new TaskCompletionSource();
+
+ CancellationToken token = CancellationToken.None;
+ GatewayPacket login = new GatewayPacket
+ {
+ OpCode = FluxerOpCode.RequestGuildMembers,
+ Data = JToken.FromObject(new RequestMembersPacket
+ {
+ GuildId = guild.Id.ToString()
+ })
+ };
+ SendGatewayPacket(login);
+
+ await Task.WhenAny(guild._downloaderPromise.Task, Task.Delay(new TimeSpan(0, 0, 30)));
+
+ return guild._downloaderPromise.Task.Result;
+ }
+
///
/// Sends a gateway packet to the Fluxer WebSocket server.
///
@@ -462,23 +508,36 @@ private void HandleDispatch(GatewayPacket p)
if (data != null)
{
CurrentUser = CurrentUser.Create(_client, data.User);
+
+ _sessionId = data.SessionId;
+ _isConnecting = false; // Connection successfully established
+ _reconnectAttemptCount = 0; // Reset backoff counter
+
GuildIds = data.Guilds.Select(x => x.Id).ToHashSet();
Channels.Clear();
Roles.Clear();
CurrentMembers.Clear();
Guilds.Clear();
+ RtcRegions = data.RtcRegions.Select(x => RtcRegion.Create(_client, x)).ToArray();
if (data.Guilds != null)
{
- foreach (var g in data.Guilds)
+ foreach (GuildGatewayData g in data.Guilds)
{
CurrentMembers.TryAdd(g.Id, SocketGuildMember.Create(_client, g.Members.First(x => x.UserId == CurrentUser.Id)));
- Guilds.TryAdd(g.Id, SocketGuild.Create(_client, g.Properties, CurrentMembers[g.Id]));
+ SocketGuild guild = SocketGuild.Create(_client, g.Properties, CurrentMembers[g.Id]);
+ foreach (Gateway.Data.Guilds.GuildMemberGatewayData m in g.Members)
+ {
+ if (m.UserId != CurrentUser.Id)
+ {
+ SocketGuildMember member = SocketGuildMember.Create(_client, m);
+ member.Guild = guild;
+ guild.Members.TryAdd(m.UserId, member);
+ }
+ }
+ Guilds.TryAdd(g.Id, guild);
}
}
- _sessionId = data.SessionId;
- _isConnecting = false; // Connection successfully established
- _reconnectAttemptCount = 0; // Reset backoff counter
_logger.Information("Connection established successfully with session ID: {SessionId}", _sessionId);
Ready?.Invoke(data);
}
@@ -592,7 +651,15 @@ private void HandleDispatch(GatewayPacket p)
ChannelGatewayData? data = p.Data.ToObject(FluxerClient._serializer);
if (data != null)
{
- Channels.TryAdd(data.Id, SocketChannel.Create(_client, data, data.GuildId.Value));
+ Channel channel = SocketUnknownChannel.Create(_client, data, data.GuildId.Value);
+ if (!Channels.TryAdd(channel.Id, channel))
+ {
+ channel = Channels[channel.Id];
+ channel.Update(_client, data);
+ }
+ if (channel.GuildId.HasValue && Guilds.TryGetValue(channel.GuildId.Value, out var guild))
+ guild.Channels.TryAdd(channel.Id, channel);
+
ChannelCreate?.Invoke(data);
}
else
@@ -604,8 +671,9 @@ private void HandleDispatch(GatewayPacket p)
ChannelGatewayData? data = p.Data.ToObject(FluxerClient._serializer);
if (data != null)
{
- if (Channels.TryGetValue(data.Id, out SocketChannel channel))
+ if (Channels.TryGetValue(data.Id, out Channel channel))
channel.Update(_client, data);
+
ChannelUpdate?.Invoke(data);
}
else
@@ -855,20 +923,50 @@ private void HandleDispatch(GatewayPacket p)
GuildIds.Add(data.Id);
if (!data.Unavailable.GetValueOrDefault())
{
- SocketGuildMember member = SocketGuildMember.Create(_client, data.Members.First(x => x.UserId == CurrentUser.Id));
+ Gateway.Data.Guilds.GuildMemberGatewayData json = data.Members.First(x => x.UserId == CurrentUser.Id);
+ SocketGuildMember member = SocketGuildMember.Create(_client, json);
+
+ // Add or update current member
+ if (!CurrentMembers.TryAdd(data.Id, member))
+ {
+ member = CurrentMembers[data.Id];
+ member.Update(_client, json);
+ }
+
SocketGuild guild = SocketGuild.Create(_client, data.Properties, member);
- if (Guilds.TryAdd(data.Id, guild))
- CurrentMembers.TryAdd(data.Id, member);
- foreach (ChannelGatewayData c in data.Channels)
+ // Ad or update guild
+ if (!Guilds.TryAdd(data.Id, guild))
{
- Channels.TryAdd(c.Id, SocketChannel.Create(_client, c, data.Id));
+ guild = Guilds[data.Id];
+ guild.Update(_client, data.Properties);
}
+
+ // Add or update roles
foreach (RoleJson r in data.Roles)
{
- Roles.TryAdd(r.Id, SocketRole.Create(_client, r, data.Id));
+ var role = SocketRole.Create(_client, r, guild);
+ if (!Roles.TryAdd(r.Id, role))
+ {
+ role = Roles[r.Id];
+ role.Update(_client, r);
+ }
+
+ guild.Roles.TryAdd(r.Id, role);
}
guild.UpdatePermissions(Roles[data.Id]);
+
+ // Add or update channels
+ foreach (ChannelGatewayData c in data.Channels)
+ {
+ var channel = SocketUnknownChannel.Create(_client, c, data.Id);
+ if (!Channels.TryAdd(c.Id, channel))
+ {
+ channel = Channels[c.Id];
+ channel.Update(_client, c);
+ }
+ guild.Channels.TryAdd(c.Id, channel);
+ }
}
GuildCreate?.Invoke(data);
@@ -884,6 +982,7 @@ private void HandleDispatch(GatewayPacket p)
{
if (Guilds.TryGetValue(data.Id, out SocketGuild guild))
guild.Update(_client, data.Properties);
+
GuildUpdate?.Invoke(data);
}
else
@@ -904,15 +1003,13 @@ private void HandleDispatch(GatewayPacket p)
{
Guilds.TryRemove(data.Id, out _);
CurrentMembers.TryRemove(data.Id, out _);
- foreach (SocketChannel c in Channels.Values)
+ foreach (SocketUnknownChannel c in Channels.Values.Where(x => x.GuildId == data.Id))
{
- if (c.GuildId == data.Id)
- Channels.TryRemove(c.Id, out _);
+ Channels.TryRemove(c.Id, out _);
}
- foreach (SocketRole r in Roles.Values)
+ foreach (SocketRole r in Roles.Values.Where(x => x.GuildId == data.Id))
{
- if (r.GuildId == data.Id)
- Roles.TryRemove(r.Id, out _);
+ Roles.TryRemove(r.Id, out _);
}
GuildIds.Remove(data.Id);
GuildDelete?.Invoke(data);
@@ -926,7 +1023,12 @@ private void HandleDispatch(GatewayPacket p)
{
Gateway.Data.Guilds.GuildMemberGatewayData? data = p.Data.ToObject(FluxerClient._serializer);
if (data != null)
+ {
+ if (Guilds.TryGetValue(data.GuildId, out SocketGuild guild))
+ guild.AddOrUpdate(_client, data);
+
GuildMemberAdd?.Invoke(data);
+ }
else
_logger.Warning("GUILD_MEMBER_ADD event received but data could not be cast to GuildMemberGatewayData");
}
@@ -936,7 +1038,7 @@ private void HandleDispatch(GatewayPacket p)
Gateway.Data.Guilds.GuildMemberGatewayData? data = p.Data.ToObject(FluxerClient._serializer);
if (data != null)
{
- if (CurrentMembers.TryGetValue(data.GuildId, out SocketGuildMember member) && member.UserId == data.UserId)
+ if (Guilds.TryGetValue(data.GuildId, out SocketGuild guild) && guild.Members.TryGetValue(data.UserId, out var member))
member.Update(_client, data);
GuildMemberUpdate?.Invoke(data);
@@ -949,17 +1051,55 @@ private void HandleDispatch(GatewayPacket p)
{
EntityRemovedGatewayData? data = p.Data.ToObject(FluxerClient._serializer);
if (data != null)
+ {
+ if (Guilds.TryGetValue(data.GuildId.Value, out SocketGuild guild))
+ guild.Members.TryRemove(data.Id.Value, out _);
+
GuildMemberRemove?.Invoke(data);
+ }
else
_logger.Warning("GUILD_MEMBER_REMOVE event received but data could not be cast to EntityRemovedGatewayData");
}
return;
+ case "GUILD_MEMBERS_CHUNK":
+ {
+ GuildMembersChunkGatewayData? data = p.Data.ToObject(FluxerClient._serializer);
+ if (data != null)
+ {
+ if (Guilds.TryGetValue(data.GuildId, out var guild))
+ {
+ foreach (var m in data.Members)
+ {
+ guild.AddOrUpdate(_client, m);
+ }
+ if ((data.ChunkIndex + 1) == data.ChunkCount)
+ {
+ guild.HasAllMembers = true;
+ if (guild._downloaderPromise != null)
+ guild._downloaderPromise.SetResult(true);
+ }
+ }
+ }
+ else
+ _logger.Warning("GUILD_MEMBERS_CHUNK event received but data could not be cast to GuildMembersChunkGatewayData");
+ }
+ return;
case "GUILD_ROLE_CREATE":
{
GuildRoleGatewayData? data = p.Data.ToObject(FluxerClient._serializer);
if (data != null)
{
- Roles.TryAdd(data.Role.Id, SocketRole.Create(_client, data.Role, data.GuildId));
+ if (Guilds.TryGetValue(data.GuildId, out var guild))
+ {
+ SocketRole role = SocketRole.Create(_client, data.Role, guild);
+ if (!Roles.TryAdd(data.Role.Id, role))
+ {
+ role = Roles[data.Role.Id];
+ role.Update(_client, data.Role);
+ }
+ }
+
+
GuildRoleCreate?.Invoke(data);
}
else
@@ -972,11 +1112,11 @@ private void HandleDispatch(GatewayPacket p)
if (data != null)
{
if (Roles.TryGetValue(data.Role.Id, out SocketRole role))
- {
role.Update(_client, data.Role);
- if (Guilds.TryGetValue(role.Id, out SocketGuild guild))
- guild.UpdatePermissions(role);
- }
+
+ if (data.Role.Id == data.GuildId && Guilds.TryGetValue(data.GuildId, out SocketGuild guild))
+ guild.UpdatePermissions(role);
+
GuildRoleUpdate?.Invoke(data);
}
else
@@ -989,6 +1129,9 @@ private void HandleDispatch(GatewayPacket p)
if (data != null)
{
Roles.TryRemove(data.RoleId, out _);
+ if (Guilds.TryGetValue(data.GuildId, out SocketGuild guild))
+ guild.Roles.TryRemove(data.RoleId, out _);
+
GuildRoleDelete?.Invoke(data);
}
else
@@ -999,7 +1142,17 @@ private void HandleDispatch(GatewayPacket p)
{
GuildRoleUpdateBulkGatewayData? data = p.Data.ToObject(FluxerClient._serializer);
if (data != null)
+ {
+ foreach (var r in data.Roles)
+ {
+ if (Roles.TryGetValue(r.Id, out SocketRole role))
+ role.Update(_client, r);
+
+ if (r.Id == data.GuildId && Guilds.TryGetValue(data.GuildId, out SocketGuild guild))
+ guild.UpdatePermissions(role);
+ }
GuildRoleUpdateBulk?.Invoke(data);
+ }
else
_logger.Warning("GUILD_ROLE_UPDATE_BULK event received but data could not be cast to GuildRoleUpdateBulkGatewayData");
}
diff --git a/Fluxer.Net/Gateway/Packets/RequestMembersPacket.cs b/Fluxer.Net/Gateway/Packets/RequestMembersPacket.cs
new file mode 100644
index 0000000..945d6ed
--- /dev/null
+++ b/Fluxer.Net/Gateway/Packets/RequestMembersPacket.cs
@@ -0,0 +1,21 @@
+using Newtonsoft.Json;
+
+namespace Fluxer.Net.Gateway.Packets;
+
+public class RequestMembersPacket
+{
+ ///
+ /// The guild ID.
+ ///
+ [JsonProperty("guild_id")]
+ public string GuildId { get; set; }
+
+ [JsonProperty("query")]
+ public string Query { get; set; } = "";
+
+ [JsonProperty("presences")]
+ public bool Presences { get; set; } = false;
+
+ [JsonProperty("limit")]
+ public int Limit { get; set; } = 0;
+}