diff --git a/DisCatSharp/Entities/Guild/ThreadAndForum/DiscordThreadChannel.cs b/DisCatSharp/Entities/Guild/ThreadAndForum/DiscordThreadChannel.cs
index 5cabcd242..264d369aa 100644
--- a/DisCatSharp/Entities/Guild/ThreadAndForum/DiscordThreadChannel.cs
+++ b/DisCatSharp/Entities/Guild/ThreadAndForum/DiscordThreadChannel.cs
@@ -1,388 +1,388 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Exceptions;
using DisCatSharp.Net.Models;
using DisCatSharp.Net.Serialization;
using Newtonsoft.Json;
namespace DisCatSharp.Entities;
///
/// Represents a discord thread channel.
///
public class DiscordThreadChannel : DiscordChannel
{
///
/// Gets ID of the owner that started this thread.
///
[JsonProperty("owner_id", NullValueHandling = NullValueHandling.Ignore)]
public ulong OwnerId { get; internal set; }
[JsonProperty("total_message_sent", DefaultValueHandling = DefaultValueHandling.Ignore)]
public int TotalMessagesSent { get; internal set; }
///
/// Gets an approximate count of messages in a thread, stops counting at 50.
///
[JsonProperty("message_count", NullValueHandling = NullValueHandling.Ignore)]
public int? MessageCount { get; internal set; }
///
/// Gets an approximate count of users in a thread, stops counting at 50.
///
[JsonProperty("member_count", NullValueHandling = NullValueHandling.Ignore)]
public int? MemberCount { get; internal set; }
///
/// Represents the current member for this thread. This will have a value if the user has joined the thread.
///
[JsonProperty("member", NullValueHandling = NullValueHandling.Ignore)]
public DiscordThreadChannelMember CurrentMember { get; internal set; }
///
/// Gets the threads metadata.
///
[JsonProperty("thread_metadata", NullValueHandling = NullValueHandling.Ignore)]
public DiscordThreadChannelMetadata ThreadMetadata { get; internal set; }
///
/// Gets the thread members object.
///
[JsonIgnore]
public IReadOnlyDictionary ThreadMembers => new ReadOnlyConcurrentDictionary(this.ThreadMembersInternal);
[JsonProperty("thread_member", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
internal ConcurrentDictionary ThreadMembersInternal;
///
/// List of applied tag ids.
///
[JsonIgnore]
internal IReadOnlyList AppliedTagIds
=> this.AppliedTagIdsInternal;
///
/// List of applied tag ids.
///
[JsonProperty("applied_tags", NullValueHandling = NullValueHandling.Ignore)]
internal List AppliedTagIdsInternal;
///
/// Gets the list of applied tags.
/// Only applicable for forum channel posts.
///
[JsonIgnore]
public IReadOnlyList AppliedTags
=> this.AppliedTagIds?.Select(id => this.Parent.GetForumPostTag(id)).Where(x => x != null).ToList();
///
/// Initializes a new instance of the class.
///
internal DiscordThreadChannel()
{ }
#region Methods
///
/// Modifies the current thread.
///
/// Action to perform on this thread
/// Thrown when the client does not have the permission.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
/// Thrown when the cannot be modified. This happens, when the guild hasn't reached a certain boost .
public Task ModifyAsync(Action action)
{
var mdl = new ThreadEditModel();
action(mdl);
return this.Parent.Type == ChannelType.Forum && mdl.AppliedTags.HasValue && mdl.AppliedTags.Value.Count() > 5
? throw new NotSupportedException("Cannot have more than 5 applied tags.")
: this.Discord.ApiClient.ModifyThreadAsync(this.Id, this.Parent.Type, mdl.Name, mdl.Locked, mdl.Archived, mdl.PerUserRateLimit, mdl.AutoArchiveDuration, mdl.Invitable, mdl.AppliedTags, mdl.Pinned, mdl.AuditLogReason);
}
///
/// Add a tag to the current thread.
///
/// The tag to add.
/// The reason for the audit logs.
/// Thrown when the client does not have the permission.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task AddTagAsync(ForumPostTag tag, string reason = null)
=> this.AppliedTagIds.Count == 5 ?
throw new NotSupportedException("Cannot have more than 5 applied tags.") :
this.Discord.ApiClient.ModifyThreadAsync(this.Id, this.Parent.Type, null, null, null, null, null, null, new List(this.AppliedTags) { tag }, null, reason);
///
/// Remove a tag from the current thread.
///
/// The tag to remove.
/// The reason for the audit logs.
/// Thrown when the client does not have the permission.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task RemoveTagAsync(ForumPostTag tag, string reason = null)
=> await this.Discord.ApiClient.ModifyThreadAsync(this.Id, this.Parent.Type, null, null, null, null, null, null, new List(this.AppliedTags).Where(x => x != tag).ToList(), null, reason);
///
/// Archives a thread.
///
/// Whether the thread should be locked.
/// Reason for audit logs.
/// Thrown when the client does not have the permission.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task ArchiveAsync(bool locked = true, string reason = null)
=> this.Discord.ApiClient.ModifyThreadAsync(this.Id, this.Parent.Type, null, locked, true, null, null, null, null, null, reason);
///
/// Unarchives a thread.
///
/// Reason for audit logs.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task UnarchiveAsync(string reason = null)
=> this.Discord.ApiClient.ModifyThreadAsync(this.Id, this.Parent.Type, null, null, false, null, null, null, null, null, reason);
///
/// Gets the members of a thread. Needs the intent.
///
/// Whether to request the member object. (If set to true, will paginate the result)
/// Request all members after the specified. (Currently only utilized if withMember is set to true)
/// The amount of members to fetch. (Currently only utilized if withMember is set to true)
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task> GetMembersAsync(bool withMember = false, ulong? after = null, int? limit = null)
=> await this.Discord.ApiClient.GetThreadMembersAsync(this.Id);
///
/// Adds a member to this thread.
///
/// The member id to be added.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task AddMemberAsync(ulong memberId)
=> this.Discord.ApiClient.AddThreadMemberAsync(this.Id, memberId);
///
/// Adds a member to this thread.
///
/// The member to be added.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task AddMemberAsync(DiscordMember member)
=> this.AddMemberAsync(member.Id);
///
/// Gets a member in this thread.
///
/// The id of the member to get.
/// Whether to request the member object. (If set to true, will paginate the result)
/// Thrown when the member is not part of the thread.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task GetMemberAsync(ulong memberId, bool withMember = false)
=> this.Discord.ApiClient.GetThreadMemberAsync(this.Id, memberId, withMember);
///
/// Tries to get a member in this thread.
///
/// The id of the member to get.
- /// Whether to request the member object. (If set to true, will paginate the result)
+ /// Whether to request the member object.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
public async Task TryGetMemberAsync(ulong memberId, bool withMember = false)
#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
try
{
return await this.GetMemberAsync(memberId, withMember).ConfigureAwait(false);
}
catch (NotFoundException)
{
return null;
}
}
///
/// Gets a member in this thread.
///
/// The member to get.
/// Whether to request the member object.
/// Thrown when the member is not part of the thread.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task GetMemberAsync(DiscordMember member, bool withMember = false)
=> this.Discord.ApiClient.GetThreadMemberAsync(this.Id, member.Id, withMember);
///
/// Tries to get a member in this thread.
///
/// The member to get.
/// Whether to request the member object. (If set to true, will paginate the result)
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
public async Task TryGetMemberAsync(DiscordMember member, bool withMember = false)
#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
try
{
return await this.GetMemberAsync(member, withMember).ConfigureAwait(false);
}
catch (NotFoundException)
{
return null;
}
}
///
/// Removes a member from this thread.
///
/// The member id to be removed.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task RemoveMemberAsync(ulong memberId)
=> this.Discord.ApiClient.RemoveThreadMemberAsync(this.Id, memberId);
///
/// Removes a member from this thread. Only applicable to private threads.
///
/// The member to be removed.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task RemoveMemberAsync(DiscordMember member)
=> this.RemoveMemberAsync(member.Id);
///
/// Adds a role to this thread. Only applicable to private threads.
///
/// The role id to be added.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task AddRoleAsync(ulong roleId)
{
var role = this.Guild.GetRole(roleId);
var members = await this.Guild.GetAllMembersAsync();
var roleMembers = members.Where(m => m.Roles.Contains(role));
foreach (var member in roleMembers)
{
await this.Discord.ApiClient.AddThreadMemberAsync(this.Id, member.Id);
}
}
///
/// Adds a role to this thread. Only applicable to private threads.
///
/// The role to be added.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task AddRoleAsync(DiscordRole role)
=> this.AddRoleAsync(role.Id);
///
/// Removes a role from this thread. Only applicable to private threads.
///
/// The role id to be removed.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task RemoveRoleAsync(ulong roleId)
{
var role = this.Guild.GetRole(roleId);
var members = await this.Guild.GetAllMembersAsync();
var roleMembers = members.Where(m => m.Roles.Contains(role));
foreach (var member in roleMembers)
{
await this.Discord.ApiClient.RemoveThreadMemberAsync(this.Id, member.Id);
}
}
///
/// Removes a role from this thread. Only applicable to private threads.
///
/// The role to be removed.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task RemoveRoleAsync(DiscordRole role)
=> this.RemoveRoleAsync(role.Id);
///
/// Joins a thread.
///
/// Thrown when the client has no access to this thread.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task JoinAsync()
=> this.Discord.ApiClient.JoinThreadAsync(this.Id);
///
/// Leaves a thread.
///
/// Thrown when the client has no access to this thread.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task LeaveAsync()
=> this.Discord.ApiClient.LeaveThreadAsync(this.Id);
///
/// Returns a string representation of this thread.
///
/// String representation of this thread.
public override string ToString()
=> this.Type switch
{
ChannelType.NewsThread => $"News thread {this.Name} ({this.Id})",
ChannelType.PublicThread => $"Thread {this.Name} ({this.Id})",
ChannelType.PrivateThread => $"Private thread {this.Name} ({this.Id})",
_ => $"Thread {this.Name} ({this.Id})",
};
#endregion
}