Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented Threads #1009

Merged
merged 50 commits into from
Sep 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
bb692ec
Incomplete implementation of threads
DWaffles Jun 11, 2021
905ec36
Merge branch 'DSharpPlus:master' into threads
DWaffles Jun 29, 2021
c6c3175
Merge branch 'DSharpPlus:master' into threads
DWaffles Jun 29, 2021
f95ffd3
.ToDiscordObject
VelvetToroyashi Jun 29, 2021
5406cc2
ToObject() -> ToDiscordObject() in Dispatch.cs
DWaffles Jun 29, 2021
52f0421
Fix query paremeters
VelvetToroyashi Jun 29, 2021
ffebebe
Merge remote-tracking branch 'Waffles/threads' into threads
VelvetToroyashi Jun 29, 2021
835a292
v9 bump
VelvetToroyashi Jun 29, 2021
7695eb3
Yes
VelvetToroyashi Jun 29, 2021
9f07db1
rename threadNew
VelvetToroyashi Jun 29, 2021
b0261eb
Hopefully fully implementing cache for threads
DWaffles Jun 29, 2021
9d78c38
Merge branch 'threads' of https://github.com/DWaffles/DSharpPlus into…
DWaffles Jun 29, 2021
8a533aa
Merge branch 'DSharpPlus:master' into threads
DWaffles Jul 1, 2021
f734838
WIP Commit
DWaffles Jul 22, 2021
c4676e4
Incomplete implementation of threads
DWaffles Jun 11, 2021
ff0e666
.ToDiscordObject
VelvetToroyashi Jun 29, 2021
e567072
Fix query paremeters
VelvetToroyashi Jun 29, 2021
ad29415
ToObject() -> ToDiscordObject() in Dispatch.cs
DWaffles Jun 29, 2021
85c82b5
Fixing merge errors
DWaffles Jul 23, 2021
e6a05bb
Yes
VelvetToroyashi Jun 29, 2021
2628af2
Hopefully fully implementing cache for threads
DWaffles Jun 29, 2021
ed1086d
rename threadNew
VelvetToroyashi Jun 29, 2021
ccedb6e
WIP Commit
DWaffles Jul 23, 2021
45b3402
Merge
DWaffles Jul 23, 2021
4ef3852
Merge branch 'DSharpPlus:master' into threads
DWaffles Jul 23, 2021
3612c31
WIP Commit
DWaffles Jul 27, 2021
d876de6
Merge branch 'DSharpPlus:master' into threads
DWaffles Jul 27, 2021
8b042ed
Tidying up comments & minor null check change
DWaffles Jul 27, 2021
d47d527
Merge branch 'threads' of https://github.com/DWaffles/DSharpPlus into…
DWaffles Jul 27, 2021
ac2be09
WIP Commit
DWaffles Jul 28, 2021
125f255
Added Member property to ThreadMember
DWaffles Jul 28, 2021
239fb85
Minor changes
DWaffles Jul 28, 2021
060c1d3
Removing ability to create news threads
DWaffles Jul 28, 2021
9dfc0ca
I am actually stupid
DWaffles Jul 28, 2021
a300abd
Apply changes from @VelvetThePanda review
DWaffles Jul 28, 2021
8656e9d
[ci skip] Resolving merge conflicts
DWaffles Aug 10, 2021
1a58c1d
Merge branch 'DSharpPlus-master' into threads
DWaffles Aug 10, 2021
2279c36
Fix message-related exceptions in threads (#3)
Aug 11, 2021
c4150fd
WIP of ListPublicArchived
DWaffles Aug 11, 2021
c3c6633
Merge branch 'DSharpPlus:master' into threads
DWaffles Aug 14, 2021
58589e8
Applying changes per review from @IDoEverything
DWaffles Aug 17, 2021
5a567a3
Forgot one, sue me. Ref: 58589e8
DWaffles Aug 17, 2021
dced561
Merge branch 'DSharpPlus:master' into threads
DWaffles Aug 21, 2021
cc367a4
WIP Implementation of remaining endpoints
DWaffles Aug 21, 2021
d2429fe
Resolving merge conflicts
DWaffles Aug 30, 2021
4fdd635
Revert "Resolving merge conflicts"
DWaffles Aug 30, 2021
075f2f3
Merge remote-tracking branch 'upstream/master' into threads
DWaffles Aug 30, 2021
e66c5ae
Thread events can now be unhooked
DWaffles Aug 31, 2021
4bb19d4
XML clean up and ThreadMember overloading
DWaffles Aug 31, 2021
1ec33ba
Removing misc comments
DWaffles Sep 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 44 additions & 0 deletions DSharpPlus.Test/TestBot.cs
Expand Up @@ -99,6 +99,13 @@ public TestBot(TestBotConfig cfg, int shardid)
// throw new Exception("Flippin' tables");
//};

this.Discord.ThreadCreated += this.Discord_ThreadCreated;
this.Discord.ThreadUpdated += this.Discord_ThreadUpdated;
this.Discord.ThreadDeleted += this.Discord_ThreadDeleted;
this.Discord.ThreadListSynced += this.Discord_ThreadListSynced;
this.Discord.ThreadMemberUpdated += this.Discord_ThreadMemberUpdated;
this.Discord.ThreadMembersUpdated += this.Discord_ThreadMembersUpdated;

// voice config and the voice service itself
var vcfg = new VoiceNextConfiguration
{
Expand Down Expand Up @@ -303,5 +310,42 @@ private async Task Discord_ChannelDeleted(DiscordClient client, ChannelDeleteEve
Console.WriteLine("TargetId: " + entry.Target.Id);
}
}

private Task Discord_ThreadCreated(DiscordClient client, ThreadCreateEventArgs e)
{
client.Logger.LogDebug(eventId: TestBotEventId, $"Thread created in {e.Guild.Name}. Thread Name: {e.Thread.Name}");
return Task.CompletedTask;
}

private Task Discord_ThreadUpdated(DiscordClient client, ThreadUpdateEventArgs e)
{
client.Logger.LogDebug(eventId: TestBotEventId, $"Thread updated in {e.Guild.Name}. New Thread Name: {e.ThreadAfter.Name}");
return Task.CompletedTask;
}

private Task Discord_ThreadDeleted(DiscordClient client, ThreadDeleteEventArgs e)
{
client.Logger.LogDebug(eventId: TestBotEventId, $"Thread deleted in {e.Guild.Name}. Thread Name: {e.Thread.Name ?? "Unknown"}");
return Task.CompletedTask;
}

private Task Discord_ThreadListSynced(DiscordClient client, ThreadListSyncEventArgs e)
{
client.Logger.LogDebug(eventId: TestBotEventId, $"Threads synced in {e.Guild.Name}.");
return Task.CompletedTask;
}

private Task Discord_ThreadMemberUpdated(DiscordClient client, ThreadMemberUpdateEventArgs e)
{
client.Logger.LogDebug(eventId: TestBotEventId, $"Thread member updated.");
Console.WriteLine($"Discord_ThreadMemberUpdated fired for thread {e.ThreadMember.ThreadId}. User ID {e.ThreadMember.Id}.");
return Task.CompletedTask;
}

private Task Discord_ThreadMembersUpdated(DiscordClient client, ThreadMembersUpdateEventArgs e)
{
client.Logger.LogDebug(eventId: TestBotEventId, $"Thread members updated in {e.Guild.Name}.");
return Task.CompletedTask;
}
}
}
6 changes: 6 additions & 0 deletions DSharpPlus.Test/TestBotCommands.cs
Expand Up @@ -534,5 +534,11 @@ public async Task PurgeChatAsync(CommandContext ctx)
.WithFooter($"foo");
await x.SendMessageAsync(embed: embed2);
}
[Command("ping"), Aliases("p")]
public async Task PingAsync(CommandContext ctx)
{
await ctx.TriggerTypingAsync();
await ctx.RespondAsync($"Pong: {ctx.Client.Ping}ms");
}
}
}
225 changes: 225 additions & 0 deletions DSharpPlus/Clients/DiscordClient.Dispatch.cs
Expand Up @@ -59,6 +59,7 @@ internal async Task HandleDispatchAsync(GatewayPayload payload)
}

DiscordChannel chn;
DiscordThreadChannel thread;
ulong gid;
ulong cid;
TransportUser usr = default;
Expand Down Expand Up @@ -346,6 +347,39 @@ internal async Task HandleDispatchAsync(GatewayPayload payload)

#endregion

#region Thread

case "thread_create":
thread = dat.ToDiscordObject<DiscordThreadChannel>();
await this.OnThreadCreateEventAsync(thread).ConfigureAwait(false);
break;

case "thread_update":
thread = dat.ToDiscordObject<DiscordThreadChannel>();
await this.OnThreadUpdateEventAsync(thread).ConfigureAwait(false);
break;

case "thread_delete":
thread = dat.ToDiscordObject<DiscordThreadChannel>();
await this.OnThreadDeleteEventAsync(thread).ConfigureAwait(false);
break;

case "thread_list_sync":
gid = (ulong)dat["guild_id"]; //get guild
await this.OnThreadListSyncEventAsync(this._guilds[gid], dat["channel_ids"].ToDiscordObject<IReadOnlyList<ulong>>(), dat["threads"].ToObject<IReadOnlyList<DiscordThreadChannel>>(), dat["members"].ToObject<IReadOnlyList<DiscordThreadChannelMember>>()).ConfigureAwait(false);
break;

case "thread_member_update":
await this.OnThreadMemberUpdateEventAsync(dat.ToDiscordObject<DiscordThreadChannelMember>()).ConfigureAwait(false);
break;

case "thread_members_update":
gid = (ulong)dat["guild_id"];
await this.OnThreadMembersUpdateEventAsync(this._guilds[gid], (ulong)dat["id"], dat["added_members"]?.ToObject<IReadOnlyList<DiscordThreadChannelMember>>(), dat["removed_member_ids"]?.ToObject<IReadOnlyList<ulong?>>(), (int)dat["member_count"]).ConfigureAwait(false);
break;

#endregion

#region Interaction/Integration/Application

case "interaction_create":
Expand Down Expand Up @@ -499,6 +533,8 @@ internal async Task OnReadyEventAsync(ReadyPayload ready, JArray rawGuilds, JArr

if (guild._channels == null)
guild._channels = new ConcurrentDictionary<ulong, DiscordChannel>();
if (guild._threads == null)
guild._threads = new ConcurrentDictionary<ulong, DiscordThreadChannel>();

foreach (var xc in guild.Channels.Values)
{
Expand All @@ -510,6 +546,11 @@ internal async Task OnReadyEventAsync(ReadyPayload ready, JArray rawGuilds, JArr
xo._channel_id = xc.Id;
}
}
foreach (var xt in guild.Threads.Values)
{
xt.GuildId = guild.Id;
xt.Discord = this;
}

if (guild._roles == null)
guild._roles = new ConcurrentDictionary<ulong, DiscordRole>();
Expand Down Expand Up @@ -723,6 +764,8 @@ internal async Task OnGuildCreateEventAsync(DiscordGuild guild, JArray rawMember

if (guild._channels == null)
guild._channels = new ConcurrentDictionary<ulong, DiscordChannel>();
if (guild._threads == null)
guild._threads = new ConcurrentDictionary<ulong, DiscordThreadChannel>();
if (guild._roles == null)
guild._roles = new ConcurrentDictionary<ulong, DiscordRole>();
if (guild._emojis == null)
Expand Down Expand Up @@ -761,6 +804,11 @@ internal async Task OnGuildCreateEventAsync(DiscordGuild guild, JArray rawMember
xo._channel_id = xc.Id;
}
}
foreach (var xt in guild._threads.Values)
{
xt.GuildId = guild.Id;
xt.Discord = this;
}
foreach (var xe in guild._emojis.Values)
xe.Discord = this;
foreach (var xs in guild._stickers.Values)
Expand Down Expand Up @@ -837,13 +885,15 @@ internal async Task OnGuildUpdateEventAsync(DiscordGuild guild, JArray rawMember
VoiceRegionId = gld.VoiceRegionId,
IsNSFW = gld.IsNSFW,
_channels = new ConcurrentDictionary<ulong, DiscordChannel>(),
_threads = new ConcurrentDictionary<ulong, DiscordThreadChannel>(),
_emojis = new ConcurrentDictionary<ulong, DiscordEmoji>(),
_members = new ConcurrentDictionary<ulong, DiscordMember>(),
_roles = new ConcurrentDictionary<ulong, DiscordRole>(),
_voiceStates = new ConcurrentDictionary<ulong, DiscordVoiceState>()
};

foreach (var kvp in gld._channels) oldGuild._channels[kvp.Key] = kvp.Value;
foreach (var kvp in gld._threads) oldGuild._threads[kvp.Key] = kvp.Value;
foreach (var kvp in gld._emojis) oldGuild._emojis[kvp.Key] = kvp.Value;
foreach (var kvp in gld._roles) oldGuild._roles[kvp.Key] = kvp.Value;
foreach (var kvp in gld._voiceStates) oldGuild._voiceStates[kvp.Key] = kvp.Value;
Expand All @@ -857,6 +907,8 @@ internal async Task OnGuildUpdateEventAsync(DiscordGuild guild, JArray rawMember

if (guild._channels == null)
guild._channels = new ConcurrentDictionary<ulong, DiscordChannel>();
if (guild._threads == null)
guild._threads = new ConcurrentDictionary<ulong, DiscordThreadChannel>();
if (guild._roles == null)
guild._roles = new ConcurrentDictionary<ulong, DiscordRole>();
if (guild._emojis == null)
Expand All @@ -878,6 +930,11 @@ internal async Task OnGuildUpdateEventAsync(DiscordGuild guild, JArray rawMember
xo._channel_id = xc.Id;
}
}
foreach (var xc in guild._threads.Values)
{
xc.GuildId = guild.Id;
xc.Discord = this;
}
foreach (var xe in guild._emojis.Values)
xe.Discord = this;
foreach (var xvs in guild._voiceStates.Values)
Expand Down Expand Up @@ -1764,6 +1821,174 @@ internal async Task OnVoiceServerUpdateEventAsync(string endpoint, string token,

#endregion

#region Thread

internal async Task OnThreadCreateEventAsync(DiscordThreadChannel thread)
{
thread.Discord = this;
this.InternalGetCachedGuild(thread.GuildId)._threads.AddOrUpdate(thread.Id, thread, (oldThread, newThread) => newThread);

await this._threadCreated.InvokeAsync(this, new ThreadCreateEventArgs { Thread = thread, Guild = thread.Guild, Parent = thread.Parent }).ConfigureAwait(false);
}

internal async Task OnThreadUpdateEventAsync(DiscordThreadChannel thread)
{
if (thread == null)
DWaffles marked this conversation as resolved.
Show resolved Hide resolved
return;

DiscordThreadChannel threadOld;
ThreadUpdateEventArgs updateEvent;

thread.Discord = this;

var guild = thread.Guild;
guild.Discord = this;

var cthread = this.InternalGetCachedThread(thread.Id);

if (cthread != null) //thread is cached
{
threadOld = new DiscordThreadChannel
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider a copy ctor.

{
Discord = this,
GuildId = cthread.GuildId,
CreatorId = cthread.CreatorId,
ParentId = cthread.ParentId,
Id = cthread.Id,
Name = cthread.Name,
Type = cthread.Type,
LastMessageId = cthread.LastMessageId,
MessageCount = cthread.MessageCount,
MemberCount = cthread.MemberCount,
ThreadMetadata = cthread.ThreadMetadata,
CurrentMember = cthread.CurrentMember,
};

updateEvent = new ThreadUpdateEventArgs
{
ThreadAfter = thread,
ThreadBefore = threadOld,
Guild = thread.Guild,
Parent = thread.Parent
};
}
else
{
updateEvent = new ThreadUpdateEventArgs
{
ThreadAfter = thread,
Guild = thread.Guild,
Parent = thread.Parent
};
guild._threads[thread.Id] = thread;
}

await this._threadUpdated.InvokeAsync(this, updateEvent).ConfigureAwait(false);
}

internal async Task OnThreadDeleteEventAsync(DiscordThreadChannel thread)
{
if (thread == null)
DWaffles marked this conversation as resolved.
Show resolved Hide resolved
return;

thread.Discord = this;

var gld = thread.Guild;
if (gld._threads.TryRemove(thread.Id, out var cachedThread))
thread = cachedThread;

await this._threadDeleted.InvokeAsync(this, new ThreadDeleteEventArgs { Thread = thread, Guild = thread.Guild, Parent = thread.Parent}).ConfigureAwait(false);
}

internal async Task OnThreadListSyncEventAsync(DiscordGuild guild, IReadOnlyList<ulong>? channel_ids, IReadOnlyList<DiscordThreadChannel> threads, IReadOnlyList<DiscordThreadChannelMember> members)
{
guild.Discord = this;
var channels = channel_ids.Select(x => guild.GetChannel(x) ?? new DiscordChannel{ Id = x, GuildId = guild.Id}); //getting channel objects

foreach (var channel in channels)
{
channel.Discord = this;
}

foreach(var thread in threads)
{
thread.Discord = this;
guild._threads[thread.Id] = thread;
}

foreach(var member in members)
{
member.Discord = this;
member._guild_id = guild.Id;

var thread = threads.SingleOrDefault(x => x.Id == member.ThreadId);
if (thread != null)
thread.CurrentMember = member;
}

await this._threadListSynced.InvokeAsync(this, new ThreadListSyncEventArgs { Guild = guild, Channels = channels.ToList().AsReadOnly(), Threads = threads, CurrentMembers = members.ToList().AsReadOnly() }).ConfigureAwait(false);
DWaffles marked this conversation as resolved.
Show resolved Hide resolved
}

internal async Task OnThreadMemberUpdateEventAsync(DiscordThreadChannelMember member)
{
member.Discord = this;

var thread = this.InternalGetCachedThread(member.ThreadId);
member._guild_id = thread.Guild.Id;
thread.CurrentMember = member;
DWaffles marked this conversation as resolved.
Show resolved Hide resolved
thread.Guild._threads.AddOrUpdate(member.ThreadId, thread, (oldThread, newThread) => newThread);

await this._threadMemberUpdated.InvokeAsync(this, new ThreadMemberUpdateEventArgs { ThreadMember = member, Thread = thread }).ConfigureAwait(false);
}

internal async Task OnThreadMembersUpdateEventAsync(DiscordGuild guild, ulong thread_id, IReadOnlyList<DiscordThreadChannelMember> addedMembers, IReadOnlyList<ulong?> removed_member_ids, int member_count)
{
var thread = this.InternalGetCachedThread(thread_id);
thread.Discord = this;
guild.Discord = this;

var removedMembers = new List<DiscordMember>();
if (removed_member_ids != null)
{
foreach (var removedId in removed_member_ids)
{
removedMembers.Add(guild._members.TryGetValue(removedId.Value, out var member) ? member: new DiscordMember { Id = removedId.Value, _guild_id = guild.Id, Discord = this });
}
}
else
removed_member_ids = Array.Empty<ulong?>();
if (addedMembers != null)
{
foreach (var threadMember in addedMembers)
{
threadMember.Discord = this;
threadMember._guild_id = guild.Id;

if(threadMember.Id == this.CurrentUser.Id)
thread.CurrentMember = threadMember;
}
}
else
addedMembers = Array.Empty<DiscordThreadChannelMember>();

if (removed_member_ids.Contains(this.CurrentUser.Id)) //indicates the bot was removed from the thread
thread.CurrentMember = null;
thread.MemberCount = member_count;

var threadMembersUpdateArg = new ThreadMembersUpdateEventArgs
{
Guild = guild,
Thread = thread,
AddedMembers = addedMembers,
RemovedMembers = removedMembers,
MemberCount = member_count
};

await this._threadMembersUpdated.InvokeAsync(this, threadMembersUpdateArg).ConfigureAwait(false);
}

#endregion

#region Commands

internal async Task OnApplicationCommandCreateAsync(DiscordApplicationCommand cmd, ulong? guild_id)
Expand Down