Started adding v6 support
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Discord.API
|
namespace Discord.API
|
||||||
{
|
{
|
||||||
@@ -7,8 +8,8 @@ namespace Discord.API
|
|||||||
//Shared
|
//Shared
|
||||||
[JsonProperty("id")]
|
[JsonProperty("id")]
|
||||||
public ulong Id { get; set; }
|
public ulong Id { get; set; }
|
||||||
[JsonProperty("is_private")]
|
[JsonProperty("type")]
|
||||||
public bool IsPrivate { get; set; }
|
public ChannelType Type { get; set; }
|
||||||
[JsonProperty("last_message_id")]
|
[JsonProperty("last_message_id")]
|
||||||
public ulong? LastMessageId { get; set; }
|
public ulong? LastMessageId { get; set; }
|
||||||
|
|
||||||
@@ -17,8 +18,6 @@ namespace Discord.API
|
|||||||
public Optional<ulong> GuildId { get; set; }
|
public Optional<ulong> GuildId { get; set; }
|
||||||
[JsonProperty("name")]
|
[JsonProperty("name")]
|
||||||
public Optional<string> Name { get; set; }
|
public Optional<string> Name { get; set; }
|
||||||
[JsonProperty("type")]
|
|
||||||
public Optional<ChannelType> Type { get; set; }
|
|
||||||
[JsonProperty("position")]
|
[JsonProperty("position")]
|
||||||
public Optional<int> Position { get; set; }
|
public Optional<int> Position { get; set; }
|
||||||
[JsonProperty("permission_overwrites")]
|
[JsonProperty("permission_overwrites")]
|
||||||
@@ -27,6 +26,8 @@ namespace Discord.API
|
|||||||
//TextChannel
|
//TextChannel
|
||||||
[JsonProperty("topic")]
|
[JsonProperty("topic")]
|
||||||
public Optional<string> Topic { get; set; }
|
public Optional<string> Topic { get; set; }
|
||||||
|
[JsonProperty("last_pin_timestamp")]
|
||||||
|
public Optional<DateTimeOffset?> LastPinTimestamp { get; set; }
|
||||||
|
|
||||||
//VoiceChannel
|
//VoiceChannel
|
||||||
[JsonProperty("bitrate")]
|
[JsonProperty("bitrate")]
|
||||||
@@ -35,7 +36,7 @@ namespace Discord.API
|
|||||||
public Optional<int> UserLimit { get; set; }
|
public Optional<int> UserLimit { get; set; }
|
||||||
|
|
||||||
//DMChannel
|
//DMChannel
|
||||||
[JsonProperty("recipient")]
|
[JsonProperty("recipients")]
|
||||||
public Optional<User> Recipient { get; set; }
|
public Optional<User[]> Recipients { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/Discord.Net/API/Common/MessageType.cs
Normal file
12
src/Discord.Net/API/Common/MessageType.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Discord.API.Common
|
||||||
|
{
|
||||||
|
public enum MessageType
|
||||||
|
{
|
||||||
|
Default = 0,
|
||||||
|
RecipientAdd = 1,
|
||||||
|
RecipientRemove = 2,
|
||||||
|
Call = 3,
|
||||||
|
ChannelNameChange = 4,
|
||||||
|
ChannelIconChange = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -211,7 +211,7 @@ namespace Discord.API
|
|||||||
if (_gatewayUrl == null)
|
if (_gatewayUrl == null)
|
||||||
{
|
{
|
||||||
var gatewayResponse = await GetGatewayAsync().ConfigureAwait(false);
|
var gatewayResponse = await GetGatewayAsync().ConfigureAwait(false);
|
||||||
_gatewayUrl = $"{gatewayResponse.Url}?v={DiscordConfig.GatewayAPIVersion}&encoding={DiscordConfig.GatewayEncoding}";
|
_gatewayUrl = $"{gatewayResponse.Url}?v={DiscordConfig.APIVersion}&encoding={DiscordConfig.GatewayEncoding}";
|
||||||
}
|
}
|
||||||
await _gatewayClient.ConnectAsync(_gatewayUrl).ConfigureAwait(false);
|
await _gatewayClient.ConnectAsync(_gatewayUrl).ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -1160,7 +1160,7 @@ namespace Discord.API
|
|||||||
{
|
{
|
||||||
return await SendAsync<IReadOnlyCollection<Connection>>("GET", "users/@me/connections", options: options).ConfigureAwait(false);
|
return await SendAsync<IReadOnlyCollection<Connection>>("GET", "users/@me/connections", options: options).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
public async Task<IReadOnlyCollection<Channel>> GetMyDMsAsync(RequestOptions options = null)
|
public async Task<IReadOnlyCollection<Channel>> GetMyPrivateChannelsAsync(RequestOptions options = null)
|
||||||
{
|
{
|
||||||
return await SendAsync<IReadOnlyCollection<Channel>>("GET", "users/@me/channels", options: options).ConfigureAwait(false);
|
return await SendAsync<IReadOnlyCollection<Channel>>("GET", "users/@me/channels", options: options).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
@@ -16,12 +17,19 @@ namespace Discord
|
|||||||
private readonly ConcurrentDictionary<ulong, CachedDMChannel> _dmChannels;
|
private readonly ConcurrentDictionary<ulong, CachedDMChannel> _dmChannels;
|
||||||
private readonly ConcurrentDictionary<ulong, CachedGuild> _guilds;
|
private readonly ConcurrentDictionary<ulong, CachedGuild> _guilds;
|
||||||
private readonly ConcurrentDictionary<ulong, CachedGlobalUser> _users;
|
private readonly ConcurrentDictionary<ulong, CachedGlobalUser> _users;
|
||||||
|
private readonly ConcurrentHashSet<ulong> _groupChannels;
|
||||||
|
|
||||||
internal IReadOnlyCollection<ICachedChannel> Channels => _channels.ToReadOnlyCollection();
|
internal IReadOnlyCollection<ICachedChannel> Channels => _channels.ToReadOnlyCollection();
|
||||||
internal IReadOnlyCollection<CachedDMChannel> DMChannels => _dmChannels.ToReadOnlyCollection();
|
internal IReadOnlyCollection<CachedDMChannel> DMChannels => _dmChannels.ToReadOnlyCollection();
|
||||||
|
internal IReadOnlyCollection<CachedGroupChannel> GroupChannels => _groupChannels.Select(x => GetChannel(x) as CachedGroupChannel).ToReadOnlyCollection(_groupChannels);
|
||||||
internal IReadOnlyCollection<CachedGuild> Guilds => _guilds.ToReadOnlyCollection();
|
internal IReadOnlyCollection<CachedGuild> Guilds => _guilds.ToReadOnlyCollection();
|
||||||
internal IReadOnlyCollection<CachedGlobalUser> Users => _users.ToReadOnlyCollection();
|
internal IReadOnlyCollection<CachedGlobalUser> Users => _users.ToReadOnlyCollection();
|
||||||
|
|
||||||
|
internal IReadOnlyCollection<ICachedPrivateChannel> PrivateChannels =>
|
||||||
|
_dmChannels.Select(x => x.Value as ICachedPrivateChannel).Concat(
|
||||||
|
_groupChannels.Select(x => GetChannel(x) as ICachedPrivateChannel))
|
||||||
|
.ToReadOnlyCollection(() => _dmChannels.Count + _groupChannels.Count);
|
||||||
|
|
||||||
public DataStore(int guildCount, int dmChannelCount)
|
public DataStore(int guildCount, int dmChannelCount)
|
||||||
{
|
{
|
||||||
double estimatedChannelCount = guildCount * AverageChannelsPerGuild + dmChannelCount;
|
double estimatedChannelCount = guildCount * AverageChannelsPerGuild + dmChannelCount;
|
||||||
@@ -30,6 +38,7 @@ namespace Discord
|
|||||||
_dmChannels = new ConcurrentDictionary<ulong, CachedDMChannel>(CollectionConcurrencyLevel, (int)(dmChannelCount * CollectionMultiplier));
|
_dmChannels = new ConcurrentDictionary<ulong, CachedDMChannel>(CollectionConcurrencyLevel, (int)(dmChannelCount * CollectionMultiplier));
|
||||||
_guilds = new ConcurrentDictionary<ulong, CachedGuild>(CollectionConcurrencyLevel, (int)(guildCount * CollectionMultiplier));
|
_guilds = new ConcurrentDictionary<ulong, CachedGuild>(CollectionConcurrencyLevel, (int)(guildCount * CollectionMultiplier));
|
||||||
_users = new ConcurrentDictionary<ulong, CachedGlobalUser>(CollectionConcurrencyLevel, (int)(estimatedUsersCount * CollectionMultiplier));
|
_users = new ConcurrentDictionary<ulong, CachedGlobalUser>(CollectionConcurrencyLevel, (int)(estimatedUsersCount * CollectionMultiplier));
|
||||||
|
_groupChannels = new ConcurrentHashSet<ulong>(CollectionConcurrencyLevel, (int)(10 * CollectionMultiplier));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ICachedChannel GetChannel(ulong id)
|
internal ICachedChannel GetChannel(ulong id)
|
||||||
@@ -39,18 +48,6 @@ namespace Discord
|
|||||||
return channel;
|
return channel;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
internal void AddChannel(ICachedChannel channel)
|
|
||||||
{
|
|
||||||
_channels[channel.Id] = channel;
|
|
||||||
}
|
|
||||||
internal ICachedChannel RemoveChannel(ulong id)
|
|
||||||
{
|
|
||||||
ICachedChannel channel;
|
|
||||||
if (_channels.TryRemove(id, out channel))
|
|
||||||
return channel;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal CachedDMChannel GetDMChannel(ulong userId)
|
internal CachedDMChannel GetDMChannel(ulong userId)
|
||||||
{
|
{
|
||||||
CachedDMChannel channel;
|
CachedDMChannel channel;
|
||||||
@@ -58,19 +55,25 @@ namespace Discord
|
|||||||
return channel;
|
return channel;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
internal void AddDMChannel(CachedDMChannel channel)
|
internal void AddChannel(ICachedChannel channel)
|
||||||
{
|
{
|
||||||
_channels[channel.Id] = channel;
|
_channels[channel.Id] = channel;
|
||||||
_dmChannels[channel.Recipient.Id] = channel;
|
var dmChannel = channel as CachedDMChannel;
|
||||||
|
if (dmChannel != null)
|
||||||
|
_dmChannels[dmChannel.Recipient.Id] = dmChannel;
|
||||||
}
|
}
|
||||||
internal CachedDMChannel RemoveDMChannel(ulong userId)
|
internal ICachedChannel RemoveChannel(ulong id)
|
||||||
{
|
{
|
||||||
CachedDMChannel channel;
|
ICachedChannel channel;
|
||||||
ICachedChannel ignored;
|
if (_channels.TryRemove(id, out channel))
|
||||||
if (_dmChannels.TryRemove(userId, out channel))
|
|
||||||
{
|
{
|
||||||
if (_channels.TryRemove(channel.Id, out ignored))
|
var dmChannel = channel as CachedDMChannel;
|
||||||
return channel;
|
if (dmChannel != null)
|
||||||
|
{
|
||||||
|
CachedDMChannel ignored;
|
||||||
|
_dmChannels.TryRemove(dmChannel.Recipient.Id, out ignored);
|
||||||
|
}
|
||||||
|
return channel;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
@@ -165,16 +166,26 @@ namespace Discord
|
|||||||
return guild.ToChannel(model);
|
return guild.ToChannel(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (model.Type == ChannelType.DM)
|
||||||
|
return new DMChannel(this, new User(model.Recipients.Value[0]), model);
|
||||||
|
else if (model.Type == ChannelType.Group)
|
||||||
|
{
|
||||||
|
var recipients = model.Recipients.Value;
|
||||||
|
var users = new ConcurrentDictionary<ulong, IUser>(1, recipients.Length);
|
||||||
|
for (int i = 0; i < recipients.Length; i++)
|
||||||
|
users[recipients[i].Id] = new User(recipients[i]);
|
||||||
|
return new GroupChannel(this, users, model);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return new DMChannel(this, new User(model.Recipient.Value), model);
|
throw new InvalidOperationException($"Unexpected channel type: {model.Type}");
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual async Task<IReadOnlyCollection<IDMChannel>> GetDMChannelsAsync()
|
public virtual async Task<IReadOnlyCollection<IPrivateChannel>> GetPrivateChannelsAsync()
|
||||||
{
|
{
|
||||||
var models = await ApiClient.GetMyDMsAsync().ConfigureAwait(false);
|
var models = await ApiClient.GetMyPrivateChannelsAsync().ConfigureAwait(false);
|
||||||
return models.Select(x => new DMChannel(this, new User(x.Recipient.Value), x)).ToImmutableArray();
|
return models.Select(x => new DMChannel(this, new User(x.Recipients.Value[0]), x)).ToImmutableArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -289,9 +300,9 @@ namespace Discord
|
|||||||
private async Task WriteInitialLog()
|
private async Task WriteInitialLog()
|
||||||
{
|
{
|
||||||
if (this is DiscordSocketClient)
|
if (this is DiscordSocketClient)
|
||||||
await _clientLogger.InfoAsync($"DiscordSocketClient v{DiscordConfig.Version} (Gateway v{DiscordConfig.GatewayAPIVersion}, {DiscordConfig.GatewayEncoding})").ConfigureAwait(false);
|
await _clientLogger.InfoAsync($"DiscordSocketClient v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion}, {DiscordConfig.GatewayEncoding})").ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await _clientLogger.InfoAsync($"DiscordClient v{DiscordConfig.Version}").ConfigureAwait(false);
|
await _clientLogger.InfoAsync($"DiscordClient v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion})").ConfigureAwait(false);
|
||||||
await _clientLogger.VerboseAsync($"Runtime: {RuntimeInformation.FrameworkDescription.Trim()} ({ToArchString(RuntimeInformation.ProcessArchitecture)})").ConfigureAwait(false);
|
await _clientLogger.VerboseAsync($"Runtime: {RuntimeInformation.FrameworkDescription.Trim()} ({ToArchString(RuntimeInformation.ProcessArchitecture)})").ConfigureAwait(false);
|
||||||
await _clientLogger.VerboseAsync($"OS: {RuntimeInformation.OSDescription.Trim()} ({ToArchString(RuntimeInformation.OSArchitecture)})").ConfigureAwait(false);
|
await _clientLogger.VerboseAsync($"OS: {RuntimeInformation.OSDescription.Trim()} ({ToArchString(RuntimeInformation.OSArchitecture)})").ConfigureAwait(false);
|
||||||
await _clientLogger.VerboseAsync($"Processors: {Environment.ProcessorCount}").ConfigureAwait(false);
|
await _clientLogger.VerboseAsync($"Processors: {Environment.ProcessorCount}").ConfigureAwait(false);
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ namespace Discord
|
|||||||
public static string Version { get; } = typeof(DiscordConfig).GetTypeInfo().Assembly?.GetName().Version.ToString(3) ?? "Unknown";
|
public static string Version { get; } = typeof(DiscordConfig).GetTypeInfo().Assembly?.GetName().Version.ToString(3) ?? "Unknown";
|
||||||
public static string UserAgent { get; } = $"DiscordBot (https://github.com/RogueException/Discord.Net, v{Version})";
|
public static string UserAgent { get; } = $"DiscordBot (https://github.com/RogueException/Discord.Net, v{Version})";
|
||||||
|
|
||||||
public const int GatewayAPIVersion = 5;
|
public const int APIVersion = 6;
|
||||||
public const string GatewayEncoding = "json";
|
public const string GatewayEncoding = "json";
|
||||||
|
|
||||||
public const string ClientAPIUrl = "https://discordapp.com/api/";
|
public static readonly string ClientAPIUrl = $"https://discordapp.com/api/v{APIVersion}/";
|
||||||
public const string CDNUrl = "https://cdn.discordapp.com/";
|
public const string CDNUrl = "https://cdn.discordapp.com/";
|
||||||
public const string InviteUrl = "https://discord.gg/";
|
public const string InviteUrl = "https://discord.gg/";
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ namespace Discord
|
|||||||
|
|
||||||
internal CachedSelfUser CurrentUser => _currentUser as CachedSelfUser;
|
internal CachedSelfUser CurrentUser => _currentUser as CachedSelfUser;
|
||||||
internal IReadOnlyCollection<CachedGuild> Guilds => DataStore.Guilds;
|
internal IReadOnlyCollection<CachedGuild> Guilds => DataStore.Guilds;
|
||||||
internal IReadOnlyCollection<CachedDMChannel> DMChannels => DataStore.DMChannels;
|
|
||||||
internal IReadOnlyCollection<VoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection();
|
internal IReadOnlyCollection<VoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection();
|
||||||
|
|
||||||
/// <summary> Creates a new REST/WebSocket discord client. </summary>
|
/// <summary> Creates a new REST/WebSocket discord client. </summary>
|
||||||
@@ -329,27 +328,42 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
return Task.FromResult<IChannel>(DataStore.GetChannel(id));
|
return Task.FromResult<IChannel>(DataStore.GetChannel(id));
|
||||||
}
|
}
|
||||||
public override Task<IReadOnlyCollection<IDMChannel>> GetDMChannelsAsync()
|
public override Task<IReadOnlyCollection<IPrivateChannel>> GetPrivateChannelsAsync()
|
||||||
{
|
{
|
||||||
return Task.FromResult<IReadOnlyCollection<IDMChannel>>(DMChannels);
|
return Task.FromResult<IReadOnlyCollection<IPrivateChannel>>(DataStore.PrivateChannels);
|
||||||
}
|
}
|
||||||
internal CachedDMChannel AddDMChannel(API.Channel model, DataStore dataStore)
|
internal ICachedChannel AddPrivateChannel(API.Channel model, DataStore dataStore)
|
||||||
{
|
{
|
||||||
var recipient = GetOrAddUser(model.Recipient.Value, dataStore);
|
switch (model.Type)
|
||||||
var channel = new CachedDMChannel(this, new CachedDMUser(recipient), model);
|
|
||||||
recipient.AddRef();
|
|
||||||
dataStore.AddDMChannel(channel);
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
internal CachedDMChannel RemoveDMChannel(ulong id)
|
|
||||||
{
|
|
||||||
var dmChannel = DataStore.RemoveDMChannel(id);
|
|
||||||
if (dmChannel != null)
|
|
||||||
{
|
{
|
||||||
var recipient = dmChannel.Recipient;
|
case ChannelType.DM:
|
||||||
recipient.User.RemoveRef(this);
|
{
|
||||||
|
var recipients = model.Recipients.Value;
|
||||||
|
var user = GetOrAddUser(recipients[0], dataStore);
|
||||||
|
var channel = new CachedDMChannel(this, new CachedPrivateUser(user), model);
|
||||||
|
dataStore.AddChannel(channel);
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
case ChannelType.Group:
|
||||||
|
{
|
||||||
|
var recipients = model.Recipients.Value;
|
||||||
|
var users = new ConcurrentDictionary<ulong, IUser>(1, recipients.Length);
|
||||||
|
for (int i = 0; i < recipients.Length; i++)
|
||||||
|
users[recipients[i].Id] = new CachedPrivateUser(GetOrAddUser(recipients[i], dataStore));
|
||||||
|
var channel = new CachedGroupChannel(this, users, model);
|
||||||
|
dataStore.AddChannel(channel);
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException($"Unexpected channel type: {model.Type}");
|
||||||
}
|
}
|
||||||
return dmChannel;
|
}
|
||||||
|
internal ICachedChannel RemovePrivateChannel(ulong id)
|
||||||
|
{
|
||||||
|
var channel = DataStore.RemoveChannel(id) as ICachedPrivateChannel;
|
||||||
|
foreach (var recipient in channel.Recipients)
|
||||||
|
recipient.User.RemoveRef(this);
|
||||||
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -362,6 +376,11 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
return Task.FromResult<IUser>(DataStore.Users.Where(x => x.Discriminator == discriminator && x.Username == username).FirstOrDefault());
|
return Task.FromResult<IUser>(DataStore.Users.Where(x => x.Discriminator == discriminator && x.Username == username).FirstOrDefault());
|
||||||
}
|
}
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<ISelfUser> GetCurrentUserAsync()
|
||||||
|
{
|
||||||
|
return Task.FromResult<ISelfUser>(_currentUser);
|
||||||
|
}
|
||||||
internal CachedGlobalUser GetOrAddUser(API.User model, DataStore dataStore)
|
internal CachedGlobalUser GetOrAddUser(API.User model, DataStore dataStore)
|
||||||
{
|
{
|
||||||
var user = dataStore.GetOrAddUser(model.Id, _ => new CachedGlobalUser(model));
|
var user = dataStore.GetOrAddUser(model.Id, _ => new CachedGlobalUser(model));
|
||||||
@@ -518,7 +537,7 @@ namespace Discord
|
|||||||
unavailableGuilds++;
|
unavailableGuilds++;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < data.PrivateChannels.Length; i++)
|
for (int i = 0; i < data.PrivateChannels.Length; i++)
|
||||||
AddDMChannel(data.PrivateChannels[i], dataStore);
|
AddPrivateChannel(data.PrivateChannels[i], dataStore);
|
||||||
|
|
||||||
_sessionId = data.SessionId;
|
_sessionId = data.SessionId;
|
||||||
_currentUser = currentUser;
|
_currentUser = currentUser;
|
||||||
@@ -686,7 +705,7 @@ namespace Discord
|
|||||||
|
|
||||||
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
|
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
|
||||||
ICachedChannel channel = null;
|
ICachedChannel channel = null;
|
||||||
if (!data.IsPrivate)
|
if (data.GuildId.IsSpecified)
|
||||||
{
|
{
|
||||||
var guild = DataStore.GetGuild(data.GuildId.Value);
|
var guild = DataStore.GetGuild(data.GuildId.Value);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
@@ -698,7 +717,8 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
channel = AddDMChannel(data, DataStore);
|
channel = AddPrivateChannel(data, DataStore);
|
||||||
|
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
await _channelCreatedEvent.InvokeAsync(channel).ConfigureAwait(false);
|
await _channelCreatedEvent.InvokeAsync(channel).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -728,7 +748,7 @@ namespace Discord
|
|||||||
|
|
||||||
ICachedChannel channel = null;
|
ICachedChannel channel = null;
|
||||||
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
|
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
|
||||||
if (!data.IsPrivate)
|
if (data.GuildId.IsSpecified)
|
||||||
{
|
{
|
||||||
var guild = DataStore.GetGuild(data.GuildId.Value);
|
var guild = DataStore.GetGuild(data.GuildId.Value);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
@@ -740,7 +760,8 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
channel = RemoveDMChannel(data.Recipient.Value.Id);
|
channel = RemovePrivateChannel(data.Id);
|
||||||
|
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
await _channelDestroyedEvent.InvokeAsync(channel).ConfigureAwait(false);
|
await _channelDestroyedEvent.InvokeAsync(channel).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
public enum ChannelType : byte
|
public enum ChannelType
|
||||||
{
|
{
|
||||||
DM,
|
Text = 0,
|
||||||
Text,
|
DM = 1,
|
||||||
Voice
|
Voice = 2,
|
||||||
|
Group = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ namespace Discord
|
|||||||
public IUser Recipient { get; private set; }
|
public IUser Recipient { get; private set; }
|
||||||
|
|
||||||
public virtual IReadOnlyCollection<IMessage> CachedMessages => ImmutableArray.Create<IMessage>();
|
public virtual IReadOnlyCollection<IMessage> CachedMessages => ImmutableArray.Create<IMessage>();
|
||||||
|
IReadOnlyCollection<IUser> IPrivateChannel.Recipients => ImmutableArray.Create(Recipient);
|
||||||
|
|
||||||
public DMChannel(DiscordClient discord, IUser recipient, Model model)
|
public DMChannel(DiscordClient discord, IUser recipient, Model model)
|
||||||
: base(model.Id)
|
: base(model.Id)
|
||||||
@@ -30,7 +31,7 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
if (/*source == UpdateSource.Rest && */IsAttached) return;
|
if (/*source == UpdateSource.Rest && */IsAttached) return;
|
||||||
|
|
||||||
(Recipient as User).Update(model.Recipient.Value, source);
|
(Recipient as User).Update(model.Recipients.Value[0], source);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateAsync()
|
public async Task UpdateAsync()
|
||||||
|
|||||||
140
src/Discord.Net/Entities/Channels/GroupChannel.cs
Normal file
140
src/Discord.Net/Entities/Channels/GroupChannel.cs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
using Discord.API.Rest;
|
||||||
|
using Discord.Extensions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Model = Discord.API.Channel;
|
||||||
|
|
||||||
|
namespace Discord
|
||||||
|
{
|
||||||
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
|
internal class GroupChannel : SnowflakeEntity, IGroupChannel
|
||||||
|
{
|
||||||
|
protected ConcurrentDictionary<ulong, IUser> _users;
|
||||||
|
|
||||||
|
public override DiscordClient Discord { get; }
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
public IReadOnlyCollection<IUser> Recipients => _users.ToReadOnlyCollection();
|
||||||
|
public virtual IReadOnlyCollection<IMessage> CachedMessages => ImmutableArray.Create<IMessage>();
|
||||||
|
|
||||||
|
public GroupChannel(DiscordClient discord, ConcurrentDictionary<ulong, IUser> recipients, Model model)
|
||||||
|
: base(model.Id)
|
||||||
|
{
|
||||||
|
Discord = discord;
|
||||||
|
_users = recipients;
|
||||||
|
|
||||||
|
Update(model, UpdateSource.Creation);
|
||||||
|
}
|
||||||
|
public void Update(Model model, UpdateSource source)
|
||||||
|
{
|
||||||
|
if (source == UpdateSource.Rest && IsAttached) return;
|
||||||
|
|
||||||
|
if (model.Name.IsSpecified)
|
||||||
|
Name = model.Name.Value;
|
||||||
|
|
||||||
|
if (source != UpdateSource.Creation && model.Recipients.IsSpecified)
|
||||||
|
UpdateUsers(model.Recipients.Value, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void UpdateUsers(API.User[] models, UpdateSource source)
|
||||||
|
{
|
||||||
|
if (!IsAttached)
|
||||||
|
{
|
||||||
|
var users = new ConcurrentDictionary<ulong, IUser>(1, (int)(models.Length * 1.05));
|
||||||
|
for (int i = 0; i < models.Length; i++)
|
||||||
|
users[models[i].Id] = new User(models[i]);
|
||||||
|
_users = users;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateAsync()
|
||||||
|
{
|
||||||
|
if (IsAttached) throw new NotSupportedException();
|
||||||
|
|
||||||
|
var model = await Discord.ApiClient.GetChannelAsync(Id).ConfigureAwait(false);
|
||||||
|
Update(model, UpdateSource.Rest);
|
||||||
|
}
|
||||||
|
public async Task LeaveAsync()
|
||||||
|
{
|
||||||
|
await Discord.ApiClient.DeleteChannelAsync(Id).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IUser> GetUserAsync(ulong id)
|
||||||
|
{
|
||||||
|
IUser user;
|
||||||
|
if (_users.TryGetValue(id, out user))
|
||||||
|
return user;
|
||||||
|
var currentUser = await Discord.GetCurrentUserAsync().ConfigureAwait(false);
|
||||||
|
if (id == currentUser.Id)
|
||||||
|
return currentUser;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public async Task<IReadOnlyCollection<IUser>> GetUsersAsync()
|
||||||
|
{
|
||||||
|
var currentUser = await Discord.GetCurrentUserAsync().ConfigureAwait(false);
|
||||||
|
return _users.Select(x => x.Value).Concat(ImmutableArray.Create(currentUser)).ToReadOnlyCollection(_users, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IMessage> SendMessageAsync(string text, bool isTTS)
|
||||||
|
{
|
||||||
|
var args = new CreateMessageParams { Content = text, IsTTS = isTTS };
|
||||||
|
var model = await Discord.ApiClient.CreateDMMessageAsync(Id, args).ConfigureAwait(false);
|
||||||
|
return new Message(this, new User(model.Author.Value), model);
|
||||||
|
}
|
||||||
|
public async Task<IMessage> SendFileAsync(string filePath, string text, bool isTTS)
|
||||||
|
{
|
||||||
|
string filename = Path.GetFileName(filePath);
|
||||||
|
using (var file = File.OpenRead(filePath))
|
||||||
|
{
|
||||||
|
var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS };
|
||||||
|
var model = await Discord.ApiClient.UploadDMFileAsync(Id, file, args).ConfigureAwait(false);
|
||||||
|
return new Message(this, new User(model.Author.Value), model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public async Task<IMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS)
|
||||||
|
{
|
||||||
|
var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS };
|
||||||
|
var model = await Discord.ApiClient.UploadDMFileAsync(Id, stream, args).ConfigureAwait(false);
|
||||||
|
return new Message(this, new User(model.Author.Value), model);
|
||||||
|
}
|
||||||
|
public virtual async Task<IMessage> GetMessageAsync(ulong id)
|
||||||
|
{
|
||||||
|
var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false);
|
||||||
|
if (model != null)
|
||||||
|
return new Message(this, new User(model.Author.Value), model);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit)
|
||||||
|
{
|
||||||
|
var args = new GetChannelMessagesParams { Limit = limit };
|
||||||
|
var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false);
|
||||||
|
return models.Select(x => new Message(this, new User(x.Author.Value), x)).ToImmutableArray();
|
||||||
|
}
|
||||||
|
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
|
||||||
|
{
|
||||||
|
var args = new GetChannelMessagesParams { Limit = limit };
|
||||||
|
var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false);
|
||||||
|
return models.Select(x => new Message(this, new User(x.Author.Value), x)).ToImmutableArray();
|
||||||
|
}
|
||||||
|
public async Task DeleteMessagesAsync(IEnumerable<IMessage> messages)
|
||||||
|
{
|
||||||
|
await Discord.ApiClient.DeleteDMMessagesAsync(Id, new DeleteMessagesParams { MessageIds = messages.Select(x => x.Id) }).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task TriggerTypingAsync()
|
||||||
|
{
|
||||||
|
await Discord.ApiClient.TriggerTypingIndicatorAsync(Id).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => Name;
|
||||||
|
private string DebuggerDisplay => $"@{Name} ({Id}, Group)";
|
||||||
|
|
||||||
|
IMessage IMessageChannel.GetCachedMessage(ulong id) => null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
public interface IDMChannel : IMessageChannel
|
public interface IDMChannel : IMessageChannel, IPrivateChannel
|
||||||
{
|
{
|
||||||
/// <summary> Gets the recipient of all messages in this channel. </summary>
|
/// <summary> Gets the recipient of all messages in this channel. </summary>
|
||||||
IUser Recipient { get; }
|
IUser Recipient { get; }
|
||||||
|
|||||||
10
src/Discord.Net/Entities/Channels/IGroupChannel.cs
Normal file
10
src/Discord.Net/Entities/Channels/IGroupChannel.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Discord
|
||||||
|
{
|
||||||
|
public interface IGroupChannel : IMessageChannel, IPrivateChannel
|
||||||
|
{
|
||||||
|
/// <summary> Leaves this group. </summary>
|
||||||
|
Task LeaveAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/Discord.Net/Entities/Channels/IPrivateChannel.cs
Normal file
9
src/Discord.Net/Entities/Channels/IPrivateChannel.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Discord
|
||||||
|
{
|
||||||
|
public interface IPrivateChannel
|
||||||
|
{
|
||||||
|
IReadOnlyCollection<IUser> Recipients { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -296,14 +296,14 @@ namespace Discord
|
|||||||
|
|
||||||
internal GuildChannel ToChannel(API.Channel model)
|
internal GuildChannel ToChannel(API.Channel model)
|
||||||
{
|
{
|
||||||
switch (model.Type.Value)
|
switch (model.Type)
|
||||||
{
|
{
|
||||||
case ChannelType.Text:
|
case ChannelType.Text:
|
||||||
return new TextChannel(this, model);
|
return new TextChannel(this, model);
|
||||||
case ChannelType.Voice:
|
case ChannelType.Voice:
|
||||||
return new VoiceChannel(this, model);
|
return new VoiceChannel(this, model);
|
||||||
default:
|
default:
|
||||||
throw new InvalidOperationException($"Unknown channel type: {model.Type.Value}");
|
throw new InvalidOperationException($"Unexpected channel type: {model.Type}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ namespace Discord
|
|||||||
var args = new CreateDMChannelParams { Recipient = this };
|
var args = new CreateDMChannelParams { Recipient = this };
|
||||||
var model = await Discord.ApiClient.CreateDMChannelAsync(args).ConfigureAwait(false);
|
var model = await Discord.ApiClient.CreateDMChannelAsync(args).ConfigureAwait(false);
|
||||||
|
|
||||||
return new DMChannel(Discord, User, model);
|
return new DMChannel(Discord, new User(model.Recipients.Value[0]), model);
|
||||||
}
|
}
|
||||||
|
|
||||||
IGuild IGuildUser.Guild => Guild;
|
IGuild IGuildUser.Guild => Guild;
|
||||||
|
|||||||
@@ -6,15 +6,16 @@ using Model = Discord.API.Channel;
|
|||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
internal class CachedDMChannel : DMChannel, IDMChannel, ICachedChannel, ICachedMessageChannel
|
internal class CachedDMChannel : DMChannel, IDMChannel, ICachedChannel, ICachedMessageChannel, ICachedPrivateChannel
|
||||||
{
|
{
|
||||||
private readonly MessageManager _messages;
|
private readonly MessageManager _messages;
|
||||||
|
|
||||||
public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient;
|
public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient;
|
||||||
public new CachedDMUser Recipient => base.Recipient as CachedDMUser;
|
public new CachedPrivateUser Recipient => base.Recipient as CachedPrivateUser;
|
||||||
public IReadOnlyCollection<ICachedUser> Members => ImmutableArray.Create<ICachedUser>(Discord.CurrentUser, Recipient);
|
public IReadOnlyCollection<ICachedUser> Members => ImmutableArray.Create<ICachedUser>(Discord.CurrentUser, Recipient);
|
||||||
|
IReadOnlyCollection<CachedPrivateUser> ICachedPrivateChannel.Recipients => ImmutableArray.Create(Recipient);
|
||||||
|
|
||||||
public CachedDMChannel(DiscordSocketClient discord, CachedDMUser recipient, Model model)
|
public CachedDMChannel(DiscordSocketClient discord, CachedPrivateUser recipient, Model model)
|
||||||
: base(discord, recipient, model)
|
: base(discord, recipient, model)
|
||||||
{
|
{
|
||||||
if (Discord.MessageCacheSize > 0)
|
if (Discord.MessageCacheSize > 0)
|
||||||
|
|||||||
80
src/Discord.Net/Entities/WebSocket/CachedGroupChannel.cs
Normal file
80
src/Discord.Net/Entities/WebSocket/CachedGroupChannel.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using Discord.Extensions;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MessageModel = Discord.API.Message;
|
||||||
|
using Model = Discord.API.Channel;
|
||||||
|
using Discord.API;
|
||||||
|
|
||||||
|
namespace Discord
|
||||||
|
{
|
||||||
|
internal class CachedGroupChannel : GroupChannel, IGroupChannel, ICachedChannel, ICachedMessageChannel
|
||||||
|
{
|
||||||
|
private readonly MessageManager _messages;
|
||||||
|
|
||||||
|
public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient;
|
||||||
|
public IReadOnlyCollection<ICachedUser> Members
|
||||||
|
=> _users.Select(x => x.Value).Concat(ImmutableArray.Create(Discord.CurrentUser)).Cast<ICachedUser>().ToReadOnlyCollection(_users, 1);
|
||||||
|
public new IReadOnlyCollection<CachedPrivateUser> Recipients => _users.Cast<CachedPrivateUser>().ToReadOnlyCollection(_users);
|
||||||
|
|
||||||
|
public CachedGroupChannel(DiscordSocketClient discord, ConcurrentDictionary<ulong, IUser> recipients, Model model)
|
||||||
|
: base(discord, recipients, model)
|
||||||
|
{
|
||||||
|
if (Discord.MessageCacheSize > 0)
|
||||||
|
_messages = new MessageCache(Discord, this);
|
||||||
|
else
|
||||||
|
_messages = new MessageManager(Discord, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateUsers(API.User[] models, UpdateSource source)
|
||||||
|
{
|
||||||
|
var users = new ConcurrentDictionary<ulong, IUser>(1, models.Length);
|
||||||
|
for (int i = 0; i < models.Length; i++)
|
||||||
|
users[models[i].Id] = new CachedPrivateUser(Discord.GetOrAddUser(models[i], Discord.DataStore));
|
||||||
|
_users = users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<IMessage> GetMessageAsync(ulong id)
|
||||||
|
{
|
||||||
|
return await _messages.DownloadAsync(id).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
public override async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit)
|
||||||
|
{
|
||||||
|
return await _messages.DownloadAsync(null, Direction.Before, limit).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
public override async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
|
||||||
|
{
|
||||||
|
return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
public CachedMessage AddMessage(ICachedUser author, MessageModel model)
|
||||||
|
{
|
||||||
|
var msg = new CachedMessage(this, author, model);
|
||||||
|
_messages.Add(msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
public CachedMessage GetMessage(ulong id)
|
||||||
|
{
|
||||||
|
return _messages.Get(id);
|
||||||
|
}
|
||||||
|
public CachedMessage RemoveMessage(ulong id)
|
||||||
|
{
|
||||||
|
return _messages.Remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CachedDMChannel Clone() => MemberwiseClone() as CachedDMChannel;
|
||||||
|
|
||||||
|
IMessage IMessageChannel.GetCachedMessage(ulong id) => GetMessage(id);
|
||||||
|
ICachedUser ICachedMessageChannel.GetUser(ulong id, bool skipCheck)
|
||||||
|
{
|
||||||
|
IUser user;
|
||||||
|
if (_users.TryGetValue(id, out user))
|
||||||
|
return user as ICachedUser;
|
||||||
|
if (id == Discord.CurrentUser.Id)
|
||||||
|
return Discord.CurrentUser;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ICachedChannel ICachedChannel.Clone() => Clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -311,14 +311,14 @@ namespace Discord
|
|||||||
|
|
||||||
new internal ICachedGuildChannel ToChannel(ChannelModel model)
|
new internal ICachedGuildChannel ToChannel(ChannelModel model)
|
||||||
{
|
{
|
||||||
switch (model.Type.Value)
|
switch (model.Type)
|
||||||
{
|
{
|
||||||
case ChannelType.Text:
|
case ChannelType.Text:
|
||||||
return new CachedTextChannel(this, model);
|
return new CachedTextChannel(this, model);
|
||||||
case ChannelType.Voice:
|
case ChannelType.Voice:
|
||||||
return new CachedVoiceChannel(this, model);
|
return new CachedVoiceChannel(this, model);
|
||||||
default:
|
default:
|
||||||
throw new InvalidOperationException($"Unknown channel type: {model.Type.Value}");
|
throw new InvalidOperationException($"Unexpected channel type: {model.Type}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using PresenceModel = Discord.API.Presence;
|
|||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
internal class CachedDMUser : ICachedUser
|
internal class CachedPrivateUser : ICachedUser
|
||||||
{
|
{
|
||||||
public CachedGlobalUser User { get; }
|
public CachedGlobalUser User { get; }
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ namespace Discord
|
|||||||
public string NicknameMention => User.NicknameMention;
|
public string NicknameMention => User.NicknameMention;
|
||||||
public string Username => User.Username;
|
public string Username => User.Username;
|
||||||
|
|
||||||
public CachedDMUser(CachedGlobalUser user)
|
public CachedPrivateUser(CachedGlobalUser user)
|
||||||
{
|
{
|
||||||
User = user;
|
User = user;
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ namespace Discord
|
|||||||
User.Update(model, source);
|
User.Update(model, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachedDMUser Clone() => MemberwiseClone() as CachedDMUser;
|
public CachedPrivateUser Clone() => MemberwiseClone() as CachedPrivateUser;
|
||||||
ICachedUser ICachedUser.Clone() => Clone();
|
ICachedUser ICachedUser.Clone() => Clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Discord
|
||||||
|
{
|
||||||
|
internal interface ICachedPrivateChannel : ICachedChannel, IPrivateChannel
|
||||||
|
{
|
||||||
|
new IReadOnlyCollection<CachedPrivateUser> Recipients { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -8,24 +9,26 @@ namespace Discord.Extensions
|
|||||||
internal static class CollectionExtensions
|
internal static class CollectionExtensions
|
||||||
{
|
{
|
||||||
public static IReadOnlyCollection<TValue> ToReadOnlyCollection<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> source)
|
public static IReadOnlyCollection<TValue> ToReadOnlyCollection<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> source)
|
||||||
=> new ConcurrentDictionaryWrapper<TValue, KeyValuePair<TKey, TValue>>(source, source.Select(x => x.Value));
|
=> new ConcurrentDictionaryWrapper<TValue>(source.Select(x => x.Value), () => source.Count);
|
||||||
public static IReadOnlyCollection<TValue> ToReadOnlyCollection<TValue, TSource>(this IEnumerable<TValue> query, IReadOnlyCollection<TSource> source)
|
public static IReadOnlyCollection<TValue> ToReadOnlyCollection<TValue, TSource>(this IEnumerable<TValue> query, IReadOnlyCollection<TSource> source, int countOffset = 0)
|
||||||
=> new ConcurrentDictionaryWrapper<TValue, TSource>(source, query);
|
=> new ConcurrentDictionaryWrapper<TValue>(query, () => source.Count + countOffset);
|
||||||
|
public static IReadOnlyCollection<TValue> ToReadOnlyCollection<TValue>(this IEnumerable<TValue> query, Func<int> countFunc)
|
||||||
|
=> new ConcurrentDictionaryWrapper<TValue>(query, countFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
internal struct ConcurrentDictionaryWrapper<TValue, TSource> : IReadOnlyCollection<TValue>
|
internal struct ConcurrentDictionaryWrapper<TValue> : IReadOnlyCollection<TValue>
|
||||||
{
|
{
|
||||||
private readonly IReadOnlyCollection<TSource> _source;
|
|
||||||
private readonly IEnumerable<TValue> _query;
|
private readonly IEnumerable<TValue> _query;
|
||||||
|
private readonly Func<int> _countFunc;
|
||||||
|
|
||||||
//It's okay that this count is affected by race conditions - we're wrapping a concurrent collection and that's to be expected
|
//It's okay that this count is affected by race conditions - we're wrapping a concurrent collection and that's to be expected
|
||||||
public int Count => _source.Count;
|
public int Count => _countFunc();
|
||||||
|
|
||||||
public ConcurrentDictionaryWrapper(IReadOnlyCollection<TSource> source, IEnumerable<TValue> query)
|
public ConcurrentDictionaryWrapper(IEnumerable<TValue> query, Func<int> countFunc)
|
||||||
{
|
{
|
||||||
_source = source;
|
|
||||||
_query = query;
|
_query = query;
|
||||||
|
_countFunc = countFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string DebuggerDisplay => $"Count = {Count}";
|
private string DebuggerDisplay => $"Count = {Count}";
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace Discord
|
|||||||
Task DisconnectAsync();
|
Task DisconnectAsync();
|
||||||
|
|
||||||
Task<IChannel> GetChannelAsync(ulong id);
|
Task<IChannel> GetChannelAsync(ulong id);
|
||||||
Task<IReadOnlyCollection<IDMChannel>> GetDMChannelsAsync();
|
Task<IReadOnlyCollection<IPrivateChannel>> GetPrivateChannelsAsync();
|
||||||
|
|
||||||
Task<IReadOnlyCollection<IConnection>> GetConnectionsAsync();
|
Task<IReadOnlyCollection<IConnection>> GetConnectionsAsync();
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Discord.Net.Converters
|
|
||||||
{
|
|
||||||
public class ChannelTypeConverter : JsonConverter
|
|
||||||
{
|
|
||||||
public static readonly ChannelTypeConverter Instance = new ChannelTypeConverter();
|
|
||||||
|
|
||||||
public override bool CanConvert(Type objectType) => true;
|
|
||||||
public override bool CanRead => true;
|
|
||||||
public override bool CanWrite => true;
|
|
||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
switch ((string)reader.Value)
|
|
||||||
{
|
|
||||||
case "text":
|
|
||||||
return ChannelType.Text;
|
|
||||||
case "voice":
|
|
||||||
return ChannelType.Voice;
|
|
||||||
default:
|
|
||||||
throw new JsonSerializationException("Unknown channel type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
switch ((ChannelType)value)
|
|
||||||
{
|
|
||||||
case ChannelType.Text:
|
|
||||||
writer.WriteValue("text");
|
|
||||||
break;
|
|
||||||
case ChannelType.Voice:
|
|
||||||
writer.WriteValue("voice");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new JsonSerializationException("Invalid channel type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -75,8 +75,6 @@ namespace Discord.Net.Converters
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Enums
|
//Enums
|
||||||
if (type == typeof(ChannelType))
|
|
||||||
return ChannelTypeConverter.Instance;
|
|
||||||
if (type == typeof(PermissionTarget))
|
if (type == typeof(PermissionTarget))
|
||||||
return PermissionTargetConverter.Instance;
|
return PermissionTargetConverter.Instance;
|
||||||
if (type == typeof(UserStatus))
|
if (type == typeof(UserStatus))
|
||||||
|
|||||||
Reference in New Issue
Block a user