diff --git a/src/Discord.Net.Core/Discord.Net.Core.xml b/src/Discord.Net.Core/Discord.Net.Core.xml
index b351ad49d..1ab7ab78d 100644
--- a/src/Discord.Net.Core/Discord.Net.Core.xml
+++ b/src/Discord.Net.Core/Discord.Net.Core.xml
@@ -1003,6 +1003,51 @@
A message was unpinned from this guild.
+
+
+ A integration was created
+
+
+
+
+ A integration was updated
+
+
+
+
+ An integration was deleted
+
+
+
+
+ A stage instance was created.
+
+
+
+
+ A stage instance was updated.
+
+
+
+
+ A stage instance was deleted.
+
+
+
+
+ A sticker was created.
+
+
+
+
+ A sticker was updated.
+
+
+
+
+ A sticker was deleted.
+
+
Represents data applied to an .
@@ -6105,7 +6150,7 @@
The built embed object.
Total embed length exceeds .
- Any Url must be well formatted include its protocols (i.e http:// or https://).
+ Any Url must include its protocols (i.e http:// or https://).
diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs
index 51e98c1a3..d34a6a4b9 100644
--- a/src/Discord.Net.Rest/DiscordRestApiClient.cs
+++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs
@@ -463,7 +463,9 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options);
- await SendAsync("PUT", $"channels/{channelId}/thread-members/{userId}", options: options).ConfigureAwait(false);
+ var bucket = new BucketIds(channelId: channelId);
+
+ await SendAsync("PUT", () => $"channels/{channelId}/thread-members/{userId}", bucket, options: options).ConfigureAwait(false);
}
public async Task LeaveThreadAsync(ulong channelId, RequestOptions options = null)
diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml
index 8c6f031fa..b17f73024 100644
--- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml
+++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xml
@@ -2187,6 +2187,38 @@
+
+
+ Creates a thread within this .
+
+
+ When is the thread type will be based off of the
+ channel its created in. When called on a , it creates a .
+ When called on a , it creates a . The id of the created
+ thread will be the same as the id of the message, and as such a message can only have a
+ single thread created from it.
+
+ The name of the thread.
+
+ The type of the thread.
+
+ Note: This parameter is not used if the parameter is not specified.
+
+
+
+ The duration on which this thread archives after.
+
+ Note: Options and
+ are only available for guilds that are boosted. You can check in the to see if the
+ guild has the THREE_DAY_THREAD_ARCHIVE and SEVEN_DAY_THREAD_ARCHIVE.
+
+
+ The message which to start the thread from.
+ The options to be used when sending the request.
+
+ A task that represents the asynchronous create operation. The task result contains a
+
+
@@ -2352,6 +2384,9 @@
+
+
+
@@ -2390,6 +2425,9 @@
Represents a thread channel inside of a guild.
+
+
+
Gets the owner of the current thread.
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
index 0c55eeab6..12802247d 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
@@ -1992,12 +1992,15 @@ namespace Discord.WebSocket
if ((threadChannel = guild.ThreadChannels.FirstOrDefault(x => x.Id == data.Id)) != null)
{
threadChannel.Update(this.State, data);
- threadChannel.AddOrUpdateThreadMember(data.ThreadMember.Value, guild.CurrentUser);
+
+ if(data.ThreadMember.IsSpecified)
+ threadChannel.AddOrUpdateThreadMember(data.ThreadMember.Value, guild.CurrentUser);
}
else
{
threadChannel = (SocketThreadChannel)guild.AddChannel(this.State, data);
- threadChannel.AddOrUpdateThreadMember(data.ThreadMember.Value, guild.CurrentUser);
+ if (data.ThreadMember.IsSpecified)
+ threadChannel.AddOrUpdateThreadMember(data.ThreadMember.Value, guild.CurrentUser);
await TimedInvokeAsync(_threadCreated, nameof(ThreadCreated), threadChannel).ConfigureAwait(false);
}
}
@@ -2092,12 +2095,19 @@ namespace Discord.WebSocket
var data = (payload as JToken).ToObject(_serializer);
- //var guild = State.GetGuild(data.)
+ var thread = (SocketThreadChannel)State.GetChannel(data.Id.Value);
+
+ if (thread == null)
+ {
+ await UnknownChannelAsync(type, data.Id.Value);
+ return;
+ }
+ thread.AddOrUpdateThreadMember(data, thread.Guild.CurrentUser);
}
break;
- case "THREAD_MEMBERS_UPDATE": // based on intents
+ case "THREAD_MEMBERS_UPDATE":
{
await _gatewayLogger.DebugAsync("Received Dispatch (THREAD_MEMBERS_UPDATE)").ConfigureAwait(false);
diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs
index ca9164a46..a0d14fdab 100644
--- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs
+++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs
@@ -72,7 +72,7 @@ namespace Discord.WebSocket
{
base.Update(state, model);
CategoryId = model.CategoryId;
- Topic = model.Topic.Value;
+ Topic = model.Topic.GetValueOrDefault();
SlowModeInterval = model.SlowMode.GetValueOrDefault(); // some guilds haven't been patched to include this yet?
_nsfw = model.Nsfw.GetValueOrDefault();
}
@@ -115,7 +115,12 @@ namespace Discord.WebSocket
ThreadArchiveDuration autoArchiveDuration = ThreadArchiveDuration.OneDay, IMessage message = null, RequestOptions options = null)
{
var model = await ThreadHelper.CreateThreadAsync(Discord, this, name, type, autoArchiveDuration, message, options);
- return SocketThreadChannel.Create(this.Guild, Discord.State, model);
+
+ var thread = (SocketThreadChannel)Guild.AddOrUpdateChannel(Discord.State, model);
+
+ await thread.DownloadUsersAsync();
+
+ return thread;
}
//Messages
diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketThreadChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketThreadChannel.cs
index 141d7535c..8065a8b2b 100644
--- a/src/Discord.Net.WebSocket/Entities/Channels/SocketThreadChannel.cs
+++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketThreadChannel.cs
@@ -146,7 +146,6 @@ namespace Discord.WebSocket
return member;
}
- //Users
///
public new SocketThreadUser GetUser(ulong id)
{
@@ -310,7 +309,7 @@ namespace Discord.WebSocket
/// This method is not supported in threads.
///
public override Task ModifyAsync(Action func, RequestOptions options = null)
- => throw new NotImplementedException();
+ => ThreadHelper.ModifyAsync(this, Discord, func, options);
///
///
@@ -339,5 +338,7 @@ namespace Discord.WebSocket
///
public override Task SyncPermissionsAsync(RequestOptions options = null)
=> throw new NotImplementedException();
+
+ string IChannel.Name => this.Name;
}
}
diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
index 58e2c152f..f720db018 100644
--- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
+++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
@@ -32,7 +32,7 @@ namespace Discord.WebSocket
private readonly SemaphoreSlim _audioLock;
private TaskCompletionSource _syncPromise, _downloaderPromise;
private TaskCompletionSource _audioConnectPromise;
- private ConcurrentHashSet _channels;
+ private ConcurrentDictionary _channels;
private ConcurrentDictionary _members;
private ConcurrentDictionary _roles;
private ConcurrentDictionary _voiceStates;
@@ -307,7 +307,7 @@ namespace Discord.WebSocket
{
var channels = _channels;
var state = Discord.State;
- return channels.Select(x => state.GetChannel(x) as SocketGuildChannel).Where(x => x != null).ToReadOnlyCollection(channels);
+ return channels.Select(x => x.Value).Where(x => x != null).ToReadOnlyCollection(channels);
}
}
///
@@ -363,7 +363,7 @@ namespace Discord.WebSocket
if (!IsAvailable)
{
if (_channels == null)
- _channels = new ConcurrentHashSet();
+ _channels = new ConcurrentDictionary();
if (_members == null)
_members = new ConcurrentDictionary();
if (_roles == null)
@@ -379,20 +379,20 @@ namespace Discord.WebSocket
Update(state, model as Model);
- var channels = new ConcurrentHashSet(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.Channels.Length * 1.05));
+ var channels = new ConcurrentDictionary(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.Channels.Length * 1.05));
{
for (int i = 0; i < model.Channels.Length; i++)
{
var channel = SocketGuildChannel.Create(this, state, model.Channels[i]);
state.AddChannel(channel);
- channels.TryAdd(channel.Id);
+ channels.TryAdd(channel.Id, channel);
}
for(int i = 0; i < model.Threads.Length; i++)
{
var threadChannel = SocketThreadChannel.Create(this, state, model.Threads[i]);
state.AddChannel(threadChannel);
- channels.TryAdd(threadChannel.Id);
+ channels.TryAdd(threadChannel.Id, threadChannel);
}
}
@@ -703,20 +703,34 @@ namespace Discord.WebSocket
internal SocketGuildChannel AddChannel(ClientState state, ChannelModel model)
{
var channel = SocketGuildChannel.Create(this, state, model);
- _channels.TryAdd(model.Id);
+ _channels.TryAdd(model.Id, channel);
state.AddChannel(channel);
return channel;
}
+
+ internal SocketGuildChannel AddOrUpdateChannel(ClientState state, ChannelModel model)
+ {
+ if (_channels.TryGetValue(model.Id, out SocketGuildChannel channel))
+ channel.Update(Discord.State, model);
+ else
+ {
+ channel = SocketGuildChannel.Create(this, Discord.State, model);
+ _channels[channel.Id] = channel;
+ state.AddChannel(channel);
+ }
+ return channel;
+ }
+
internal SocketGuildChannel RemoveChannel(ClientState state, ulong id)
{
- if (_channels.TryRemove(id))
+ if (_channels.TryRemove(id, out var _))
return state.RemoveChannel(id) as SocketGuildChannel;
return null;
}
internal void PurgeChannelCache(ClientState state)
{
foreach (var channelId in _channels)
- state.RemoveChannel(channelId);
+ state.RemoveChannel(channelId.Key);
_channels.Clear();
}