Compare commits
6
Commits
ad7597379a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b3c273008 | ||
|
|
12791a0c3e | ||
|
|
265647ca7c | ||
|
|
da80a3133d | ||
|
|
e925e541fd | ||
|
|
79dda056b1 |
@@ -150,6 +150,7 @@ public class JsonTests
|
||||
"user": {
|
||||
"id": "80351110224678912",
|
||||
"username": "Nelly",
|
||||
"global_name": null,
|
||||
"discriminator": "1337",
|
||||
"avatar": "8342729096ea3675442027381ff50dfe",
|
||||
"verified": true,
|
||||
@@ -457,11 +458,9 @@ public class JsonTests
|
||||
GuildId: 5678,
|
||||
Type: 2,
|
||||
Position: 3,
|
||||
Topic: "A very interesting topic",
|
||||
Nsfw: true,
|
||||
Bitrate: 420,
|
||||
ParentId: 5555,
|
||||
LastMessageId: 6969
|
||||
ParentId: 5555
|
||||
}
|
||||
});
|
||||
|
||||
@@ -503,15 +502,12 @@ public class JsonTests
|
||||
Data:
|
||||
{
|
||||
Id: 922243411795390570,
|
||||
Name: "voice csennel",
|
||||
GuildId: 5678,
|
||||
Type: 2,
|
||||
Position: 3,
|
||||
Topic: "A very interesting topic",
|
||||
Nsfw: true,
|
||||
Bitrate: 420,
|
||||
ParentId: 5555,
|
||||
LastMessageId: 6969
|
||||
ParentId: 5555
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,17 +2,9 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
public class ChannelData
|
||||
public class ChannelData : IId
|
||||
{
|
||||
public required ulong Id { get; init; }
|
||||
public required int Type { get; init; }
|
||||
public ulong? GuildId { get; init; }
|
||||
public int? Position { get; init; }
|
||||
public string? Name { get; init; }
|
||||
public string? Topic { get; init; }
|
||||
public bool? Nsfw { get; init; }
|
||||
public ulong? LastMessageId { get; init; }
|
||||
public int? Bitrate { get; init; }
|
||||
public ulong? ParentId { get; init; }
|
||||
//TODO: Missing fields
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Discord.API;
|
||||
|
||||
public class GuildChannelData : ChannelData
|
||||
{
|
||||
public ulong? GuildId { get; init; }
|
||||
public int? Position { get; init; }
|
||||
public required string Name { get; init; }
|
||||
public ulong? ParentId { get; init; }
|
||||
public int? Bitrate { get; init; }
|
||||
public bool? Nsfw { get; init; }
|
||||
}
|
||||
@@ -5,6 +5,14 @@ public class GuildCreateData : GuildData{
|
||||
public required bool Large { get; init; }
|
||||
public required uint MemberCount { get; init; }
|
||||
public required VoiceStateData[] VoiceStates { get; init; }
|
||||
public required GuildMemberData[] Members { get; init; }
|
||||
public required ChannelData[] Channels { get; init; }
|
||||
public required GuildMemberDataWithUser[] Members { get; init; }
|
||||
public required GuildChannelData[] Channels { get; init; }
|
||||
|
||||
public override void OnDeserialized()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(VoiceStates);
|
||||
ArgumentNullException.ThrowIfNull(Members);
|
||||
ArgumentNullException.ThrowIfNull(Channels);
|
||||
base.OnDeserialized();
|
||||
}
|
||||
}
|
||||
@@ -17,4 +17,11 @@ public abstract class GuildData : UnavailableGuildData
|
||||
public required uint SystemChannelFlags { get; init; }
|
||||
public required string? Description { get; init; }
|
||||
public required int NsfwLevel { get; init; }
|
||||
|
||||
public override void OnDeserialized()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(Name);
|
||||
ArgumentNullException.ThrowIfNull(Roles);
|
||||
base.OnDeserialized();
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,21 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
public class GuildMemberData
|
||||
[JsonPolymorphic]
|
||||
[JsonDerivedType(typeof(GuildMemberDataWithUser))]
|
||||
public class GuildMemberData : IJsonOnDeserialized
|
||||
{
|
||||
public UserData? User { get; init; }
|
||||
public string? Nick { get; init; }
|
||||
public ulong[]? Roles { get; init; }
|
||||
public DateTime? JoinedAt { get; init; }
|
||||
public bool? Deaf { get; init; }
|
||||
public bool? Mute { get; init; }
|
||||
public required ulong[] Roles { get; init; }
|
||||
public required DateTime JoinedAt { get; init; }
|
||||
public required bool Deaf { get; init; }
|
||||
public required bool Mute { get; init; }
|
||||
|
||||
public virtual void OnDeserialized()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(Roles);
|
||||
}
|
||||
//TODO: More fields
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
public class GuildMemberDataWithUser : GuildMemberData, IId
|
||||
{
|
||||
[JsonIgnore]
|
||||
public ulong Id => User.Id;
|
||||
public required UserData User { get; init; }
|
||||
|
||||
public override void OnDeserialized()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(User);
|
||||
base.OnDeserialized();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Discord.API;
|
||||
|
||||
public interface IId
|
||||
{
|
||||
public ulong Id {get;}
|
||||
}
|
||||
@@ -2,14 +2,18 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
public class RoleData
|
||||
public class RoleData : IJsonOnDeserialized, IId
|
||||
{
|
||||
[JsonRequired]
|
||||
public ulong Id { get; init; }
|
||||
public string? Name { get; init; }
|
||||
public uint? Color { get; init; }
|
||||
public bool? Hoist { get; init; }
|
||||
public int? Position { get; init; }
|
||||
public bool? Managed { get; init; }
|
||||
public bool? Mentionable { get; init; }
|
||||
public required ulong Id { get; init; }
|
||||
public required string Name { get; init; }
|
||||
public required uint Color { get; init; }
|
||||
public required bool Hoist { get; init; }
|
||||
public required int Position { get; init; }
|
||||
public required bool Managed { get; init; }
|
||||
public required bool Mentionable { get; init; }
|
||||
|
||||
public void OnDeserialized()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(Name);
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,12 @@ namespace Discord.API;
|
||||
|
||||
[JsonPolymorphic(UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor)]
|
||||
[JsonDerivedType(typeof(GuildData))]
|
||||
public class UnavailableGuildData
|
||||
public class UnavailableGuildData : IJsonOnDeserialized
|
||||
{
|
||||
public required ulong Id { get; init; }
|
||||
public virtual bool Unavailable => true;
|
||||
|
||||
public virtual void OnDeserialized()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,20 @@
|
||||
using System.Text.Json.Serialization;
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
public sealed class UserData
|
||||
public sealed class UserData : IJsonOnDeserialized, IId
|
||||
{
|
||||
public required ulong Id { get; init; }
|
||||
public string? Username { get; init; }
|
||||
public string? Discriminator { get; init; }
|
||||
public required string Username { get; init; }
|
||||
public required string Discriminator { get; init; }
|
||||
public string? GlobalName { get; init; }
|
||||
|
||||
public void OnDeserialized()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(Username);
|
||||
ArgumentNullException.ThrowIfNull(Discriminator);
|
||||
}
|
||||
//TODO More fields
|
||||
|
||||
|
||||
}
|
||||
@@ -4,18 +4,15 @@ namespace Discord.API;
|
||||
|
||||
public class VoiceStateData
|
||||
{
|
||||
public ulong? GuildId { get; init; }
|
||||
public ulong? ChannelId { get; init; }
|
||||
[JsonRequired]
|
||||
public required ulong? ChannelId { get; init; }
|
||||
public required ulong UserId { get; init; }
|
||||
public GuildMemberData? Member { get; init; }
|
||||
public string? SessionId { get; init; }
|
||||
public bool? Mute { get; init; }
|
||||
public bool? Deaf { get; init; }
|
||||
public bool? SelfMute { get; init; }
|
||||
public bool? SelfDeaf { get; init; }
|
||||
public required bool Mute { get; init; }
|
||||
public required bool Deaf { get; init; }
|
||||
public required bool SelfMute { get; init; }
|
||||
public required bool SelfDeaf { get; init; }
|
||||
public bool? SelfStream { get; init; }
|
||||
public bool? SelfVideo { get; init; }
|
||||
public bool? Suppress { get; init; }
|
||||
public required bool Suppress { get; init; }
|
||||
public DateTime? RequestToSpeakTimestamp { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Discord.API;
|
||||
|
||||
public class VoiceStateDataWithGuildId : VoiceStateData
|
||||
{
|
||||
public required ulong GuildId {get; init;}
|
||||
}
|
||||
@@ -96,6 +96,10 @@ public abstract class AbstractGateway {
|
||||
InstantHeartbeatCts?.Cancel();
|
||||
}
|
||||
|
||||
protected void ImmediateHeartbeat(){
|
||||
InstantHeartbeatCts?.Cancel();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public virtual async Task Close(){
|
||||
|
||||
@@ -59,6 +59,7 @@ public class GatewayClient : AbstractGateway {
|
||||
}
|
||||
protected override void MessageReceivedHandler(ResponseMessage msg){
|
||||
if(msg.MessageType != System.Net.WebSockets.WebSocketMessageType.Text) return;
|
||||
Log.Debug("GATEWAY PACKET: {packet}", msg);
|
||||
try{
|
||||
GatewayPacket packet = JsonSerializer.Deserialize(msg.Text!, SourceGenerationContext.Default.GatewayPacket)
|
||||
?? throw new Exception("Failed to deserialize packet"); // This can be optimized //TODO
|
||||
@@ -78,6 +79,9 @@ public class GatewayClient : AbstractGateway {
|
||||
case InvalidSessionPacket invalidSessionPacket:
|
||||
InvalidSessionHandler(invalidSessionPacket);
|
||||
break;
|
||||
case HeartbeatPacket:
|
||||
HeartbeatPacketHandler();
|
||||
break;
|
||||
default:
|
||||
Log.Debug("GATEWAY: Packet not handled {opcode}", packet.Op);
|
||||
break;
|
||||
@@ -126,6 +130,11 @@ public class GatewayClient : AbstractGateway {
|
||||
Log.Debug("GATEWAY: Resume url: {url}", packet.Data.ResumeGatewayUrl);
|
||||
}
|
||||
|
||||
private void HeartbeatPacketHandler(){
|
||||
ImmediateHeartbeat();
|
||||
Log.Debug("GATEWAY: Remote requested immediate heartbeat");
|
||||
}
|
||||
|
||||
protected override Task SendHeartbeat()
|
||||
{
|
||||
HeartbeatPacket packet = new(){
|
||||
|
||||
@@ -10,5 +10,5 @@ internal class ChannelCreatePacket : DispatchPacket
|
||||
|
||||
[JsonRequired]
|
||||
[JsonPropertyName("d")]
|
||||
public required ChannelData Data { get; init; }
|
||||
public required GuildChannelData Data { get; init; }
|
||||
}
|
||||
@@ -10,5 +10,5 @@ internal class ChannelUpdatePacket : DispatchPacket
|
||||
|
||||
[JsonRequired]
|
||||
[JsonPropertyName("d")]
|
||||
public required ChannelData Data { get; init; }
|
||||
public required GuildChannelData Data { get; init; }
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Discord.API\Discord.API.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
@@ -0,0 +1,22 @@
|
||||
using Discord.API;
|
||||
|
||||
namespace Discord.Model;
|
||||
|
||||
public sealed class DiscordModel
|
||||
{
|
||||
public DiscordClient DiscordClient {get;}
|
||||
private IDisposable Subscription;
|
||||
public DiscordModel(string api_key, Intents intents){
|
||||
DiscordClient = new DiscordClient(api_key, intents);
|
||||
Subscription = DiscordClient.PacketReceived.Subscribe(PacketHandler);
|
||||
}
|
||||
|
||||
private void PacketHandler(GatewayPacket packet){
|
||||
|
||||
}
|
||||
|
||||
public void Close(){
|
||||
Subscription.Dispose();
|
||||
DiscordClient.Close().Wait();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using Discord.API;
|
||||
using Serilog;
|
||||
|
||||
namespace Discord.Model;
|
||||
|
||||
public class GuildChannel : BaseType<GuildChannelData>
|
||||
{
|
||||
public string Name {get; private set;}
|
||||
public Snowflake? ParentId {get; private set;}
|
||||
public ChannelType Type {get; private set;}
|
||||
|
||||
//TODO: Optimise this shit
|
||||
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))]
|
||||
internal override void Update(GuildChannelData data)
|
||||
{
|
||||
if(!ValidTypes.Contains((ChannelType)data.Type))
|
||||
{
|
||||
Log.Warning("GuildChannel has type {type} that is not compatible", (ChannelType)data.Type);
|
||||
}
|
||||
|
||||
Name = data.Name;
|
||||
ParentId = data.ParentId;
|
||||
Type = (ChannelType) data.Type;
|
||||
}
|
||||
|
||||
public static GuildChannel Create(GuildChannelData data)
|
||||
=> (ChannelType)data.Type switch {
|
||||
ChannelType.GuildText
|
||||
or ChannelType.GuildAnnouncement
|
||||
or ChannelType.GuildForum
|
||||
=> 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);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Discord.API;
|
||||
|
||||
namespace Discord.Model;
|
||||
|
||||
public class GuildMember : BaseType<GuildMemberDataWithUser>
|
||||
{
|
||||
public override Snowflake Id => User.Id;
|
||||
public User User {get; }
|
||||
public string? Nick { get; private set; }
|
||||
public Snowflake[] Roles { get; private set; }
|
||||
public DateTime JoinedAt { get; private set; }
|
||||
public bool Deaf { get; private set; }
|
||||
public bool Mute { get; private set; }
|
||||
public VoiceState? VoiceState {get; private set; }
|
||||
|
||||
internal GuildMember(GuildMemberDataWithUser data, User user){
|
||||
User = user;
|
||||
Update(data); // This double-updates in some cases, but it's mostly fine //TODO
|
||||
}
|
||||
|
||||
[MemberNotNull(nameof(Roles))]
|
||||
internal override void Update(GuildMemberDataWithUser data){
|
||||
User.Update(data.User);
|
||||
Nick = data.Nick;
|
||||
Roles = data.Roles.Select(num => (Snowflake)num).ToArray();
|
||||
JoinedAt = data.JoinedAt;
|
||||
Deaf = data.Deaf;
|
||||
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));
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Discord.API;
|
||||
using Serilog;
|
||||
|
||||
namespace Discord.Model;
|
||||
|
||||
public class GuildTextChannel : GuildChannel
|
||||
{
|
||||
protected override ChannelType[] ValidTypes { get; } = [
|
||||
ChannelType.GuildText,
|
||||
ChannelType.GuildAnnouncement,
|
||||
ChannelType.GuildForum
|
||||
];
|
||||
|
||||
internal GuildTextChannel(GuildChannelData data) : base(data){
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Discord.API;
|
||||
|
||||
namespace Discord.Model;
|
||||
|
||||
public sealed class Role : BaseType<RoleData>
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
public uint Color { get; private set; }
|
||||
public bool Hoist { get; private set; }
|
||||
public int Position { get; private set; }
|
||||
public bool Managed { get; private set; }
|
||||
public bool Mentionable { get; private set; }
|
||||
|
||||
public Role(RoleData data) : base(data.Id){
|
||||
Update(data);
|
||||
}
|
||||
|
||||
[MemberNotNull(nameof(Name))]
|
||||
internal override void Update(RoleData data){
|
||||
this.Name = data.Name;
|
||||
this.Color = data.Color;
|
||||
this.Hoist = data.Hoist;
|
||||
this.Position = data.Position;
|
||||
this.Managed = data.Managed;
|
||||
this.Mentionable = data.Mentionable;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Discord.Model;
|
||||
|
||||
public readonly struct Snowflake
|
||||
{
|
||||
public readonly ulong Number;
|
||||
public Snowflake(ulong number){
|
||||
this.Number=number;
|
||||
}
|
||||
|
||||
public static bool operator ==(Snowflake left, Snowflake right){
|
||||
return left.Number == right.Number;
|
||||
}
|
||||
public static bool operator !=(Snowflake left, Snowflake right){
|
||||
return left.Number != right.Number;
|
||||
}
|
||||
|
||||
public static implicit operator Snowflake(ulong num){
|
||||
return new Snowflake(num);
|
||||
}
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
if(obj is ulong num) return this.Number == num;
|
||||
if(obj is Snowflake other) return this == other;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int)Number;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Number.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Discord.API;
|
||||
|
||||
namespace Discord.Model;
|
||||
|
||||
public class User : BaseType<UserData>
|
||||
{
|
||||
public string Username {get; private set;}
|
||||
public string Discriminator {get; private set;}
|
||||
public string? GlobalName {get; private set;}
|
||||
|
||||
internal User(UserData data) : base(data.Id) {
|
||||
Update(data);
|
||||
}
|
||||
|
||||
[MemberNotNull(nameof(Username))]
|
||||
[MemberNotNull(nameof(Discriminator))]
|
||||
internal override void Update(UserData data){
|
||||
this.Username = data.Username;
|
||||
this.Discriminator = data.Discriminator;
|
||||
this.GlobalName = data.GlobalName;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Discord.API;
|
||||
|
||||
namespace Discord.Model;
|
||||
|
||||
public class VoiceState : BaseType<VoiceStateData>
|
||||
{
|
||||
public override Snowflake Id => UserId;
|
||||
public ulong? ChannelId { get; private set; }
|
||||
public Snowflake UserId { get; private set; }
|
||||
public string? SessionId { get; private set; }
|
||||
public bool Mute { get; private set; }
|
||||
public bool Deaf { get; private set; }
|
||||
public bool SelfMute { get; private set; }
|
||||
public bool SelfDeaf { get; private set; }
|
||||
public bool? SelfStream { get; private set; }
|
||||
public bool? SelfVideo { get; private set; }
|
||||
public bool Suppress { get; private set; }
|
||||
public DateTime? RequestToSpeakTimestamp { get; private set; }
|
||||
|
||||
public VoiceState(VoiceStateData data){
|
||||
UserId = data.UserId;
|
||||
Update(data);
|
||||
}
|
||||
|
||||
internal override void Update(VoiceStateData data){
|
||||
ChannelId = data.ChannelId;
|
||||
SessionId = data.SessionId;
|
||||
Mute = data.Mute;
|
||||
Deaf = data.Deaf;
|
||||
SelfMute = data.SelfMute;
|
||||
SelfDeaf = data.SelfDeaf;
|
||||
SelfStream = data.SelfStream;
|
||||
SelfVideo = data.SelfVideo;
|
||||
Suppress = data.Suppress;
|
||||
RequestToSpeakTimestamp = data.RequestToSpeakTimestamp;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Model", "model\Discord.Model.csproj", "{2FA8D012-CC43-4FBC-9520-CF627AB4BBEA}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Model", "Discord.Model\Discord.Model.csproj", "{2FA8D012-CC43-4FBC-9520-CF627AB4BBEA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.API", "Discord.API\Discord.API.csproj", "{5C77661B-670F-400B-AF77-E3EC062B673D}"
|
||||
EndProject
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace model;
|
||||
|
||||
public class Class1
|
||||
{
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user