diff --git a/DisCatSharp/Entities/ThreadAndForum/DiscordThreadChannel.cs b/DisCatSharp/Entities/ThreadAndForum/DiscordThreadChannel.cs
index c00fe6c19..d52c63d6f 100644
--- a/DisCatSharp/Entities/ThreadAndForum/DiscordThreadChannel.cs
+++ b/DisCatSharp/Entities/ThreadAndForum/DiscordThreadChannel.cs
@@ -1,697 +1,693 @@
// 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.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net.Models;
using DisCatSharp.Net.Serialization;
using Newtonsoft.Json;
namespace DisCatSharp.Entities;
///
/// Represents a discord thread channel.
///
public class DiscordThreadChannel : DiscordChannel, IEquatable
{
///
/// Gets ID of the owner that started this thread.
///
[JsonProperty("owner_id", NullValueHandling = NullValueHandling.Ignore)]
public ulong OwnerId { get; internal set; }
///
/// Gets the name of this thread.
///
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public new string Name { get; internal set; }
///
/// Gets the type of this thread.
///
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
public new ChannelType Type { get; internal set; }
[JsonProperty("total_message_sent", DefaultValueHandling = DefaultValueHandling.Ignore)]
public int TotalMessagesSent { get; internal set; }
///
/// Gets whether this thread is private.
///
[JsonIgnore]
public new bool IsPrivate
=> this.Type == ChannelType.PrivateThread;
///
/// Gets the ID of the last message sent in this thread.
///
[JsonProperty("last_message_id", NullValueHandling = NullValueHandling.Ignore)]
public new ulong? LastMessageId { get; internal set; }
///
/// Gets the slowmode delay configured for this thread.
/// All bots, as well as users with or permissions in the channel are exempt from slowmode.
///
[JsonProperty("rate_limit_per_user", NullValueHandling = NullValueHandling.Ignore)]
public new int? PerUserRateLimit { 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 when the last pinned message was pinned in this thread.
///
[JsonIgnore]
public new DateTimeOffset? LastPinTimestamp
=> !string.IsNullOrWhiteSpace(this.LastPinTimestampRaw) && DateTimeOffset.TryParse(this.LastPinTimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
dto : null;
///
/// Gets when the last pinned message was pinned in this thread as raw string.
///
[JsonProperty("last_pin_timestamp", NullValueHandling = NullValueHandling.Ignore)]
internal new string LastPinTimestampRaw { get; 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._appliedTagsIdsLazy.Value;
+ => this.AppliedTagIdsInternal;
///
/// List of applied tag ids.
///
[JsonProperty("applied_tags", NullValueHandling = NullValueHandling.Ignore)]
internal List AppliedTagIdsInternal;
- [JsonIgnore]
- private readonly Lazy> _appliedTagsIdsLazy;
///
/// Gets the list of applied tags.
/// Only applicable for forum channel posts.
///
[JsonIgnore]
public IEnumerable AppliedTags
=> this.AppliedTagIds.Select(id => this.Parent.GetForumPostTag(id)).Where(x => x != null);
///
/// Initializes a new instance of the class.
///
internal DiscordThreadChannel()
- {
- this._appliedTagsIdsLazy = new Lazy>(() => new ReadOnlyCollection(this.AppliedTagIdsInternal));
- }
+ { }
#region Methods
///
/// Deletes a thread.
///
/// 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 new Task DeleteAsync(string reason = null)
=> this.Discord.ApiClient.DeleteThreadAsync(this.Id, reason);
///
/// 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);
var canContinue = !mdl.AutoArchiveDuration.HasValue || !mdl.AutoArchiveDuration.Value.HasValue || Utilities.CheckThreadAutoArchiveDurationFeature(this.Guild, mdl.AutoArchiveDuration.Value.Value);
if (mdl.Invitable.HasValue)
{
canContinue = this.Guild.Features.CanCreatePrivateThreads;
}
return canContinue ? this.Discord.ApiClient.ModifyThreadAsync(this.Id, mdl.Name, mdl.Locked, mdl.Archived, mdl.PerUserRateLimit, mdl.AutoArchiveDuration, mdl.Invitable, mdl.AppliedTags, mdl.AuditLogReason) : throw new NotSupportedException($"Cannot modify ThreadAutoArchiveDuration. Guild needs boost tier {(mdl.AutoArchiveDuration.Value.Value == ThreadAutoArchiveDuration.ThreeDays ? "one" : "two")}.");
}
///
/// 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 async Task AddTagAsync(ForumPostTag tag, string reason = null)
=> await this.Discord.ApiClient.ModifyThreadAsync(this.Id, null, null, null, null, null, null, new List(this.AppliedTags) { tag }, reason: 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, null, null, null, null, null, null, new List(this.AppliedTags).Where(x => x != tag).ToList(), reason: 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, null, locked, true, null, null, null, null, reason: 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, null, null, false, null, null, null, null, reason: reason);
///
/// Gets the members of a thread. Needs the intent.
///
/// 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()
=> 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 member to be added.
/// 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)
=> this.Discord.ApiClient.GetThreadMemberAsync(this.Id, memberId);
///
/// Gets a member in this thread.
///
/// The member to be added.
/// 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)
=> this.Discord.ApiClient.GetThreadMemberAsync(this.Id, member.Id);
///
/// 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 to from 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);
///
/// Sends a message to this thread.
///
/// Content of the message to send.
/// The sent message.
/// Thrown when the client does not have the permission and if TTS is true or the thread is locked.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public new Task SendMessageAsync(string content) =>
!this.IsWritable()
? throw new ArgumentException("Cannot send a text message to a non-thread channel.")
: this.Discord.ApiClient.CreateMessageAsync(this.Id, content, null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
///
/// Sends a message to this thread.
///
/// Embed to attach to the message.
/// The sent message.
/// Thrown when the client does not have the permission and if TTS is true or the thread is locked.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public new Task SendMessageAsync(DiscordEmbed embed) =>
!this.IsWritable()
? throw new ArgumentException("Cannot send a text message to a non-thread channel.")
: this.Discord.ApiClient.CreateMessageAsync(this.Id, null, new[] { embed }, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
///
/// Sends a message to this thread.
///
/// Content of the message to send.
/// Embed to attach to the message.
/// The sent message.
/// Thrown when the client does not have the permission and if TTS is true or the thread is locked.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public new Task SendMessageAsync(string content, DiscordEmbed embed) =>
!this.IsWritable()
? throw new ArgumentException("Cannot send a text message to a non-thread channel.")
: this.Discord.ApiClient.CreateMessageAsync(this.Id, content, new[] { embed }, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
///
/// Sends a message to this thread.
///
/// The builder with all the items to thread.
/// The sent message.
/// Thrown when the client does not have the permission and if TTS is true or the thread is locked.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public new Task SendMessageAsync(DiscordMessageBuilder builder)
=> this.Discord.ApiClient.CreateMessageAsync(this.Id, builder);
///
/// Sends a message to this channel.
///
/// The builder with all the items to send.
/// The sent message.
/// Thrown when the client does not have the permission and if TTS is true or the thread is locked.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public new Task SendMessageAsync(Action action)
{
var builder = new DiscordMessageBuilder();
action(builder);
return !this.IsWritable()
? throw new ArgumentException("Cannot send a text message to a non-text channel.")
: this.Discord.ApiClient.CreateMessageAsync(this.Id, builder);
}
///
/// Returns a specific message
///
/// The id of the message
/// Thrown when the client does not have the permission and if TTS is true or the thread is locked.
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public new async Task GetMessageAsync(ulong id) =>
this.Discord.Configuration.MessageCacheSize > 0
&& this.Discord is DiscordClient dc
&& dc.MessageCache != null
&& dc.MessageCache.TryGet(xm => xm.Id == id && xm.ChannelId == this.Id, out var msg)
? msg
: await this.Discord.ApiClient.GetMessageAsync(this.Id, id).ConfigureAwait(false);
///
/// Returns a list of messages before a certain message.
/// The amount of messages to fetch.
/// Message to fetch before from.
///
/// Thrown when the client does not have the or 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 new Task> GetMessagesBeforeAsync(ulong before, int limit = 100)
=> this.GetMessagesInternalAsync(limit, before, null, null);
///
/// Returns a list of messages after a certain message.
/// The amount of messages to fetch.
/// Message to fetch after from.
///
/// Thrown when the client does not have the or 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 new Task> GetMessagesAfterAsync(ulong after, int limit = 100)
=> this.GetMessagesInternalAsync(limit, null, after, null);
///
/// Returns a list of messages around a certain message.
/// The amount of messages to fetch.
/// Message to fetch around from.
///
/// Thrown when the client does not have the or 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 new Task> GetMessagesAroundAsync(ulong around, int limit = 100)
=> this.GetMessagesInternalAsync(limit, null, null, around);
///
/// Returns a list of messages from the last message in the thread.
/// The amount of messages to fetch.
///
/// Thrown when the client does not have the or 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 new Task> GetMessagesAsync(int limit = 100) =>
this.GetMessagesInternalAsync(limit, null, null, null);
///
/// Returns a list of messages
///
/// How many messages should be returned.
/// Get messages before snowflake.
/// Get messages after snowflake.
/// Get messages around snowflake.
private async Task> GetMessagesInternalAsync(int limit = 100, ulong? before = null, ulong? after = null, ulong? around = null)
{
if (this.Type != ChannelType.PublicThread && this.Type != ChannelType.PrivateThread && this.Type != ChannelType.NewsThread)
throw new ArgumentException("Cannot get the messages of a non-thread channel.");
if (limit < 0)
throw new ArgumentException("Cannot get a negative number of messages.");
if (limit == 0)
return Array.Empty();
//return this.Discord.ApiClient.GetChannelMessagesAsync(this.Id, limit, before, after, around);
if (limit > 100 && around != null)
throw new InvalidOperationException("Cannot get more than 100 messages around the specified ID.");
var msgs = new List(limit);
var remaining = limit;
ulong? last = null;
var isAfter = after != null;
int lastCount;
do
{
var fetchSize = remaining > 100 ? 100 : remaining;
var fetch = await this.Discord.ApiClient.GetChannelMessagesAsync(this.Id, fetchSize, !isAfter ? last ?? before : null, isAfter ? last ?? after : null, around).ConfigureAwait(false);
lastCount = fetch.Count;
remaining -= lastCount;
if (!isAfter)
{
msgs.AddRange(fetch);
last = fetch.LastOrDefault()?.Id;
}
else
{
msgs.InsertRange(0, fetch);
last = fetch.FirstOrDefault()?.Id;
}
}
while (remaining > 0 && lastCount > 0);
return new ReadOnlyCollection(msgs);
}
///
/// Deletes multiple messages if they are less than 14 days old. If they are older, none of the messages will be deleted and you will receive a error.
///
/// A collection of messages to delete.
/// 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 new async Task DeleteMessagesAsync(IEnumerable messages, string reason = null)
{
// don't enumerate more than once
var msgs = messages.Where(x => x.Channel.Id == this.Id).Select(x => x.Id).ToArray();
if (messages == null || !msgs.Any())
throw new ArgumentException("You need to specify at least one message to delete.");
if (msgs.Length < 2)
{
await this.Discord.ApiClient.DeleteMessageAsync(this.Id, msgs.Single(), reason).ConfigureAwait(false);
return;
}
for (var i = 0; i < msgs.Length; i += 100)
await this.Discord.ApiClient.DeleteMessagesAsync(this.Id, msgs.Skip(i).Take(100), reason).ConfigureAwait(false);
}
///
/// Deletes a message
///
/// The message to be deleted.
/// 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 new Task DeleteMessageAsync(DiscordMessage message, string reason = null)
=> this.Discord.ApiClient.DeleteMessageAsync(this.Id, message.Id, reason);
///
/// Post a typing indicator
///
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public new Task TriggerTypingAsync() =>
this.Type != ChannelType.PublicThread && this.Type != ChannelType.PrivateThread && this.Type != ChannelType.NewsThread
? throw new ArgumentException("Cannot start typing in a non-text channel.")
: this.Discord.ApiClient.TriggerTypingAsync(this.Id);
///
/// Returns all pinned messages
///
/// Thrown when the client does not have the permission or the client is missing .
/// Thrown when the thread does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public new Task> GetPinnedMessagesAsync() =>
this.Type != ChannelType.PublicThread && this.Type != ChannelType.PrivateThread && this.Type != ChannelType.News
? throw new ArgumentException("A non-thread channel does not have pinned messages.")
: this.Discord.ApiClient.GetPinnedMessagesAsync(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
///
/// Checks whether this is equal to another object.
///
/// Object to compare to.
/// Whether the object is equal to this .
public override bool Equals(object obj) => this.Equals(obj as DiscordThreadChannel);
///
/// Checks whether this is equal to another .
///
/// to compare to.
/// Whether the is equal to this .
public bool Equals(DiscordThreadChannel e) => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
///
/// Gets the hash code for this .
///
/// The hash code for this .
public override int GetHashCode() => this.Id.GetHashCode();
///
/// Gets whether the two objects are equal.
///
/// First channel to compare.
/// Second channel to compare.
/// Whether the two channels are equal.
public static bool operator ==(DiscordThreadChannel e1, DiscordThreadChannel e2)
{
var o1 = e1 as object;
var o2 = e2 as object;
return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
}
///
/// Gets whether the two objects are not equal.
///
/// First channel to compare.
/// Second channel to compare.
/// Whether the two channels are not equal.
public static bool operator !=(DiscordThreadChannel e1, DiscordThreadChannel e2)
=> !(e1 == e2);
}