Compare commits

...
4 Commits
Author SHA1 Message Date
laci0503 6b3c273008 Some fixes 2024-07-29 17:22:55 +02:00
laci0503 12791a0c3e Lots of model updates 2024-07-29 17:19:55 +02:00
laci0503 265647ca7c WIP 2024-07-28 19:40:54 +02:00
laci0503 da80a3133d Guild Channels 2024-07-26 13:53:40 +02:00
25 changed files with 328 additions and 99 deletions
+2 -7
View File
@@ -458,11 +458,9 @@ public class JsonTests
GuildId: 5678, GuildId: 5678,
Type: 2, Type: 2,
Position: 3, Position: 3,
Topic: "A very interesting topic",
Nsfw: true, Nsfw: true,
Bitrate: 420, Bitrate: 420,
ParentId: 5555, ParentId: 5555
LastMessageId: 6969
} }
}); });
@@ -504,15 +502,12 @@ public class JsonTests
Data: Data:
{ {
Id: 922243411795390570, Id: 922243411795390570,
Name: "voice csennel",
GuildId: 5678, GuildId: 5678,
Type: 2, Type: 2,
Position: 3, Position: 3,
Topic: "A very interesting topic",
Nsfw: true, Nsfw: true,
Bitrate: 420, Bitrate: 420,
ParentId: 5555, ParentId: 5555
LastMessageId: 6969
} }
}); });
} }
+1 -1
View File
@@ -2,7 +2,7 @@ using System.Text.Json.Serialization;
namespace Discord.API; namespace Discord.API;
public class ChannelData public class ChannelData : IId
{ {
public required ulong Id { get; init; } public required ulong Id { get; init; }
public required int Type { get; init; } public required int Type { get; init; }
+1 -1
View File
@@ -6,7 +6,7 @@ public class GuildCreateData : GuildData{
public required uint MemberCount { get; init; } public required uint MemberCount { get; init; }
public required VoiceStateData[] VoiceStates { get; init; } public required VoiceStateData[] VoiceStates { get; init; }
public required GuildMemberDataWithUser[] Members { get; init; } public required GuildMemberDataWithUser[] Members { get; init; }
public required ChannelData[] Channels { get; init; } public required GuildChannelData[] Channels { get; init; }
public override void OnDeserialized() public override void OnDeserialized()
{ {
@@ -1,7 +1,11 @@
namespace Discord.API; using System.Text.Json.Serialization;
public class GuildMemberDataWithUser : GuildMemberData namespace Discord.API;
public class GuildMemberDataWithUser : GuildMemberData, IId
{ {
[JsonIgnore]
public ulong Id => User.Id;
public required UserData User { get; init; } public required UserData User { get; init; }
public override void OnDeserialized() public override void OnDeserialized()
+6
View File
@@ -0,0 +1,6 @@
namespace Discord.API;
public interface IId
{
public ulong Id {get;}
}
+1 -1
View File
@@ -2,7 +2,7 @@ using System.Text.Json.Serialization;
namespace Discord.API; namespace Discord.API;
public class RoleData : IJsonOnDeserialized public class RoleData : IJsonOnDeserialized, IId
{ {
public required ulong Id { get; init; } public required ulong Id { get; init; }
public required string Name { get; init; } public required string Name { get; init; }
+1 -1
View File
@@ -2,7 +2,7 @@ using System.Text.Json.Serialization;
namespace Discord.API; namespace Discord.API;
public sealed class UserData : IJsonOnDeserialized public sealed class UserData : IJsonOnDeserialized, IId
{ {
public required ulong Id { get; init; } public required ulong Id { get; init; }
public required string Username { get; init; } public required string Username { get; init; }
@@ -10,5 +10,5 @@ internal class ChannelCreatePacket : DispatchPacket
[JsonRequired] [JsonRequired]
[JsonPropertyName("d")] [JsonPropertyName("d")]
public required ChannelData Data { get; init; } public required GuildChannelData Data { get; init; }
} }
@@ -10,5 +10,5 @@ internal class ChannelUpdatePacket : DispatchPacket
[JsonRequired] [JsonRequired]
[JsonPropertyName("d")] [JsonPropertyName("d")]
public required ChannelData Data { get; init; } public required GuildChannelData Data { get; init; }
} }
+20
View File
@@ -0,0 +1,20 @@
namespace Discord.Model;
public abstract class BaseType<T>
{
public virtual Snowflake Id {get; }
public event EventHandler? Deleted;
public event EventHandler? Updated;
protected BaseType(Snowflake id){
this.Id=id;
}
protected BaseType(){
}
internal virtual void Delete(){
Deleted?.Invoke(this, EventArgs.Empty);
}
internal abstract void Update(T data);
}
-38
View File
@@ -1,38 +0,0 @@
using Discord.API;
namespace Discord.Model;
public class Channel
{
public enum ChannelType {
GuildText = 0,
DM = 1,
GuildVoice = 2,
GroupDM = 3,
GuildCategory = 4,
GuildAnnouncement = 5,
AnnouncementThread = 10,
PublicThread = 11,
PrivateThread = 12,
GuildStageVoice = 13,
GuildDirectory = 14,
GuildForum = 15,
GuildMedia = 16
}
public Snowflake Id { get; }
public ChannelType Type { get; protected set; }
public Channel(ChannelData data){
Id = data.Id;
Update(data, false);
}
protected void Update(ChannelData data, bool call_base_update){
Type = (ChannelType)data.Type;
}
public virtual void Update(ChannelData data){
Update(data, true);
}
}
+18
View File
@@ -0,0 +1,18 @@
namespace Discord.Model;
public enum ChannelType
{
GuildText = 0,
DM = 1,
GuildVoice = 2,
GroupDM = 3,
GuildCategory = 4,
GuildAnnouncement = 5,
AnnouncementThread = 10,
PublicThread = 11,
PrivateThread = 12,
GuildStageVoice = 13,
GuildDirectory = 14,
GuildForum = 15,
GuildMedia = 16
}
+74
View File
@@ -0,0 +1,74 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using Discord.API;
using Serilog;
namespace Discord.Model;
public abstract class DiscordCollection<D, T> : IReadOnlyDictionary<Snowflake, T> where T : BaseType<D> where D: IId
{
internal DiscordCollection(int capacity){
_dict = new(capacity);
}
internal void Update(D[] data){
// Update existing, add new items
foreach(var item in data){
if(_dict.TryGetValue(item.Id, out T? obj)){
obj.Update(item);
}else{
_dict.Add(item.Id, CreateInstance(item));
}
}
// Delete items no longer present
foreach(Snowflake id in _dict.Select(item => item.Value.Id)){
if(!data.Any(data => data.Id == id)){
T obj = _dict[id];
_dict.Remove(id);
obj.Delete();
}
}
}
internal void Remove(Snowflake id){
if(_dict.TryGetValue(id, out T? item)){
_dict.Remove(id);
item.Delete();
}else{
Log.Warning("Trying to delete item not in the dictionary. Id: {id}, Type: {type}", id, typeof(T));
}
}
internal T UpdateSingle(D data){
if(_dict.TryGetValue(data.Id, out T? obj)){
obj.Update(data);
}else{
obj = CreateInstance(data);
_dict.Add(data.Id, obj);
}
return obj;
}
protected abstract T CreateInstance(D data);
private Dictionary<Snowflake, T> _dict;
public T this[Snowflake key] => _dict[key];
public IEnumerable<Snowflake> Keys => _dict.Keys;
public IEnumerable<T> Values => _dict.Values;
public int Count => _dict.Count;
public bool ContainsKey(Snowflake key) => _dict.ContainsKey(key);
public IEnumerator<KeyValuePair<Snowflake, T>> GetEnumerator() => _dict.GetEnumerator();
public bool TryGetValue(Snowflake key, [MaybeNullWhen(false)] out T value)
=> _dict.TryGetValue(key, out value);
IEnumerator IEnumerable.GetEnumerator() => _dict.GetEnumerator();
}
+47
View File
@@ -0,0 +1,47 @@
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using Discord.API;
namespace Discord.Model;
public class Guild : BaseType<GuildData>
{
private UserCollection Users {get; }
public string Name { get; private set; }
public ulong? AfkChannelId { get; private set; }
public int AfkTimeout { get; private set; }
public RoleCollection Roles {get; }
public ulong? SystemChannelId { get; private set; }
public uint SystemChannelFlags { get; private set; }
public string? Description { get; private set; }
public int NsfwLevel { get; private set; }
public GuildChannelCollection Channels {get; }
public GuildMemberCollection Members {get; }
internal Guild(GuildCreateData data, UserCollection users) : base(data.Id){
Roles = new(data.Roles.Length);
Users = users;
Channels = new(data.Channels.Length);
Channels.Update(data.Channels);
Members = new GuildMemberCollection(data.Members.Length, users);
Members.Update(data.Members);
foreach(VoiceStateData vt_data in data.VoiceStates){
Members[vt_data.UserId].UpdateVoiceState(vt_data);
}
Update(data);
}
[MemberNotNull(nameof(Name))]
internal override void Update(GuildData data){
Name = data.Name;
AfkChannelId = data.AfkChannelId;
AfkTimeout = data.AfkTimeout;
Roles.Update(data.Roles);
SystemChannelId = data.SystemChannelId;
SystemChannelFlags = data.SystemChannelFlags;
Description = data.Description;
NsfwLevel = data.NsfwLevel;
}
}
+35 -26
View File
@@ -1,47 +1,56 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Reflection.Metadata.Ecma335;
using Discord.API; using Discord.API;
using Serilog; using Serilog;
namespace Discord.Model; namespace Discord.Model;
public class GuildChannel : Channel public class GuildChannel : BaseType<GuildChannelData>
{ {
public string Name {get; protected set;} public string Name {get; private set;}
public Snowflake? ParentId {get; protected set;} public Snowflake? ParentId {get; private set;}
public ChannelType Type {get; private set;}
internal GuildChannel(GuildChannelData data) : base(data){ //TODO: Optimise this shit
Update(data, false); protected virtual ChannelType[] ValidTypes { get; } = [
ChannelType.GuildText,
ChannelType.GuildVoice,
ChannelType.GuildCategory,
ChannelType.GuildAnnouncement,
ChannelType.GuildStageVoice,
ChannelType.GuildForum,
ChannelType.GuildMedia
];
internal GuildChannel(GuildChannelData data) : base(data.Id) {
Update(data);
} }
[MemberNotNull(nameof(Name))] [MemberNotNull(nameof(Name))]
protected void Update(GuildChannelData data, bool call_base_update) internal override void Update(GuildChannelData data)
{ {
if((ChannelType)data.Type is not ChannelType.GuildText or if(!ValidTypes.Contains((ChannelType)data.Type))
ChannelType.GuildVoice or
ChannelType.GuildCategory or
ChannelType.GuildAnnouncement or
ChannelType.AnnouncementThread or
ChannelType.PublicThread or
ChannelType.PrivateThread or
ChannelType.GuildStageVoice or
ChannelType.GuildForum or
ChannelType.GuildMedia)
{ {
Log.Warning("GuildChannel has type {type} that is not compatible", (ChannelType)data.Type); Log.Warning("GuildChannel has type {type} that is not compatible", (ChannelType)data.Type);
} }
Name = data.Name; Name = data.Name;
ParentId = data.ParentId; ParentId = data.ParentId;
Type = (ChannelType) data.Type;
if(call_base_update) base.Update(data);
} }
public override void Update(ChannelData data) public static GuildChannel Create(GuildChannelData data)
{ => (ChannelType)data.Type switch {
if(data is GuildChannelData guild_data){ ChannelType.GuildText
Update(guild_data, true); or ChannelType.GuildAnnouncement
}else{ or ChannelType.GuildForum
throw new ArgumentException("data must be GuildChannelData", nameof(data)); => new GuildTextChannel(data),
} ChannelType.GuildVoice
} or ChannelType.GuildMedia
or ChannelType.GuildStageVoice
=> new GuildVoiceChannel(data),
ChannelType.GuildCategory
=> new GuildChannel(data),
_ => throw new ArgumentException("Invalid channel type", nameof(data))
};
} }
@@ -0,0 +1,13 @@
using Discord.API;
namespace Discord.Model;
public class GuildChannelCollection : DiscordCollection<GuildChannelData, GuildChannel>
{
public GuildChannelCollection(int capacity) : base(capacity)
{
}
protected override GuildChannel CreateInstance(GuildChannelData data)
=> GuildChannel.Create(data);
}
+15 -5
View File
@@ -3,27 +3,37 @@ using Discord.API;
namespace Discord.Model; namespace Discord.Model;
public class GuildMember public class GuildMember : BaseType<GuildMemberDataWithUser>
{ {
public Snowflake Id => User.Id; public override Snowflake Id => User.Id;
public User User {get; } public User User {get; }
public string? Nick { get; private set; } public string? Nick { get; private set; }
public Snowflake[] Roles { get; private set; } public Snowflake[] Roles { get; private set; }
public DateTime JoinedAt { get; private set; } public DateTime JoinedAt { get; private set; }
public bool Deaf { get; private set; } public bool Deaf { get; private set; }
public bool Mute { get; private set; } public bool Mute { get; private set; }
public VoiceState? VoiceState {get; private set; }
internal GuildMember(GuildMemberData data, User user){ internal GuildMember(GuildMemberDataWithUser data, User user){
User = user; User = user;
Update(data); Update(data); // This double-updates in some cases, but it's mostly fine //TODO
} }
[MemberNotNull(nameof(Roles))] [MemberNotNull(nameof(Roles))]
public void Update(GuildMemberData data){ internal override void Update(GuildMemberDataWithUser data){
User.Update(data.User);
Nick = data.Nick; Nick = data.Nick;
Roles = data.Roles.Select(num => (Snowflake)num).ToArray(); Roles = data.Roles.Select(num => (Snowflake)num).ToArray();
JoinedAt = data.JoinedAt; JoinedAt = data.JoinedAt;
Deaf = data.Deaf; Deaf = data.Deaf;
Mute = data.Mute; Mute = data.Mute;
} }
internal void UpdateVoiceState(VoiceStateData data){
if(VoiceState is null){
VoiceState = new VoiceState(data);
}else{
VoiceState.Update(data);
}
}
} }
@@ -0,0 +1,15 @@
using Discord.API;
namespace Discord.Model;
public class GuildMemberCollection : DiscordCollection<GuildMemberDataWithUser, GuildMember>
{
private UserCollection Users {get; }
public GuildMemberCollection(int capacity, UserCollection users) : base(capacity)
{
this.Users = users;
}
protected override GuildMember CreateInstance(GuildMemberDataWithUser data)
=> new GuildMember(data, Users.UpdateSingle(data.User));
}
+6 -2
View File
@@ -1,12 +1,16 @@
using Discord.API; using Discord.API;
using Serilog;
namespace Discord.Model; namespace Discord.Model;
public class GuildTextChannel : GuildChannel public class GuildTextChannel : GuildChannel
{ {
protected override ChannelType[] ValidTypes { get; } = [
ChannelType.GuildText,
ChannelType.GuildAnnouncement,
ChannelType.GuildForum
];
internal GuildTextChannel(GuildChannelData data) : base(data){ internal GuildTextChannel(GuildChannelData data) : base(data){
} }
} }
+29
View File
@@ -0,0 +1,29 @@
using Discord.API;
using Serilog;
namespace Discord.Model;
public class GuildVoiceChannel : GuildChannel
{
public int Bitrate {get; protected set;}
protected override ChannelType[] ValidTypes {get; } = [
ChannelType.GuildVoice,
ChannelType.GuildStageVoice,
ChannelType.GuildMedia
];
internal GuildVoiceChannel(GuildChannelData data) : base(data)
{
}
internal override void Update(GuildChannelData data)
{
if(data.Bitrate != null){
Bitrate = data.Bitrate.Value;
}else{
Log.Warning("VoiceChannel data did not contain Bitrate");
}
base.Update(data);
}
}
+3 -5
View File
@@ -3,9 +3,8 @@ using Discord.API;
namespace Discord.Model; namespace Discord.Model;
public sealed class Role public sealed class Role : BaseType<RoleData>
{ {
public Snowflake Id { get; private set; }
public string Name { get; private set; } public string Name { get; private set; }
public uint Color { get; private set; } public uint Color { get; private set; }
public bool Hoist { get; private set; } public bool Hoist { get; private set; }
@@ -13,13 +12,12 @@ public sealed class Role
public bool Managed { get; private set; } public bool Managed { get; private set; }
public bool Mentionable { get; private set; } public bool Mentionable { get; private set; }
public Role(RoleData data){ public Role(RoleData data) : base(data.Id){
this.Id = data.Id;
Update(data); Update(data);
} }
[MemberNotNull(nameof(Name))] [MemberNotNull(nameof(Name))]
private void Update(RoleData data){ internal override void Update(RoleData data){
this.Name = data.Name; this.Name = data.Name;
this.Color = data.Color; this.Color = data.Color;
this.Hoist = data.Hoist; this.Hoist = data.Hoist;
+13
View File
@@ -0,0 +1,13 @@
using Discord.API;
namespace Discord.Model;
public class RoleCollection : DiscordCollection<RoleData, Role>
{
public RoleCollection(int capacity) : base(capacity)
{
}
protected override Role CreateInstance(RoleData data)
=> new Role(data);
}
+3 -5
View File
@@ -3,21 +3,19 @@ using Discord.API;
namespace Discord.Model; namespace Discord.Model;
public class User public class User : BaseType<UserData>
{ {
public Snowflake Id {get;}
public string Username {get; private set;} public string Username {get; private set;}
public string Discriminator {get; private set;} public string Discriminator {get; private set;}
public string? GlobalName {get; private set;} public string? GlobalName {get; private set;}
internal User(UserData data){ internal User(UserData data) : base(data.Id) {
this.Id = data.Id;
Update(data); Update(data);
} }
[MemberNotNull(nameof(Username))] [MemberNotNull(nameof(Username))]
[MemberNotNull(nameof(Discriminator))] [MemberNotNull(nameof(Discriminator))]
internal void Update(UserData data){ internal override void Update(UserData data){
this.Username = data.Username; this.Username = data.Username;
this.Discriminator = data.Discriminator; this.Discriminator = data.Discriminator;
this.GlobalName = data.GlobalName; this.GlobalName = data.GlobalName;
+13
View File
@@ -0,0 +1,13 @@
using Discord.API;
namespace Discord.Model;
public class UserCollection : DiscordCollection<UserData, User>
{
public UserCollection(int capacity) : base(capacity)
{
}
protected override User CreateInstance(UserData data)
=> new User(data);
}
+4 -3
View File
@@ -2,10 +2,11 @@
namespace Discord.Model; namespace Discord.Model;
public class VoiceState public class VoiceState : BaseType<VoiceStateData>
{ {
public override Snowflake Id => UserId;
public ulong? ChannelId { get; private set; } public ulong? ChannelId { get; private set; }
public ulong UserId { get; private set; } public Snowflake UserId { get; private set; }
public string? SessionId { get; private set; } public string? SessionId { get; private set; }
public bool Mute { get; private set; } public bool Mute { get; private set; }
public bool Deaf { get; private set; } public bool Deaf { get; private set; }
@@ -21,7 +22,7 @@ public class VoiceState
Update(data); Update(data);
} }
public void Update(VoiceStateData data){ internal override void Update(VoiceStateData data){
ChannelId = data.ChannelId; ChannelId = data.ChannelId;
SessionId = data.SessionId; SessionId = data.SessionId;
Mute = data.Mute; Mute = data.Mute;