Browse Source

Added initial work for member downloading

tags/1.0-rc
RogueException 9 years ago
parent
commit
0c3b02f5a4
6 changed files with 84 additions and 15 deletions
  1. +4
    -0
      src/Discord.Net/API/DiscordAPIClient.cs
  2. +2
    -1
      src/Discord.Net/API/Gateway/RequestMembersParams.cs
  3. +55
    -14
      src/Discord.Net/DiscordSocketClient.cs
  4. +1
    -0
      src/Discord.Net/Entities/Guilds/Guild.cs
  5. +3
    -0
      src/Discord.Net/Entities/Guilds/IGuild.cs
  6. +19
    -0
      src/Discord.Net/Entities/WebSocket/CachedGuild.cs

+ 4
- 0
src/Discord.Net/API/DiscordAPIClient.cs View File

@@ -367,6 +367,10 @@ namespace Discord.API
{
await SendGateway(GatewayOpCode.Heartbeat, lastSeq, options: options).ConfigureAwait(false);
}
public async Task SendRequestMembers(IEnumerable<ulong> guildIds, RequestOptions options = null)
{
await SendGateway(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false);
}

//Channels
public async Task<Channel> GetChannel(ulong channelId, RequestOptions options = null)


+ 2
- 1
src/Discord.Net/API/Gateway/RequestMembersParams.cs View File

@@ -1,11 +1,12 @@
using Newtonsoft.Json;
using System.Collections.Generic;

namespace Discord.API.Gateway
{
public class RequestMembersParams
{
[JsonProperty("guild_id")]
public ulong[] GuildId { get; set; }
public IEnumerable<ulong> GuildIds { get; set; }
[JsonProperty("query")]
public string Query { get; set; }
[JsonProperty("limit")]


+ 55
- 14
src/Discord.Net/DiscordSocketClient.cs View File

@@ -17,9 +17,8 @@ using System.Threading.Tasks;

namespace Discord
{
//TODO: Remove unnecessary `as` casts
//TODO: Add event docstrings
//TODO: Add reconnect logic (+ensure the heartbeat task shuts down)
//TODO: Add reconnect logic (+ensure the heartbeat task to shut down)
//TODO: Add resume logic
public class DiscordSocketClient : DiscordClient, IDiscordClient
{
@@ -32,7 +31,7 @@ namespace Discord
public event Func<IMessage, IMessage, Task> MessageUpdated;
public event Func<IRole, Task> RoleCreated, RoleDeleted;
public event Func<IRole, IRole, Task> RoleUpdated;
public event Func<IGuild, Task> JoinedGuild, LeftGuild, GuildAvailable, GuildUnavailable;
public event Func<IGuild, Task> JoinedGuild, LeftGuild, GuildAvailable, GuildUnavailable, GuildDownloadedMembers;
public event Func<IGuild, IGuild, Task> GuildUpdated;
public event Func<IUser, Task> UserJoined, UserLeft, UserBanned, UserUnbanned;
public event Func<IUser, IUser, Task> UserUpdated;
@@ -305,6 +304,47 @@ namespace Discord
return user;
}

/// <summary> Downloads the members list for all large guilds. </summary>
public Task DownloadAllMembers()
=> DownloadMembers(DataStore.Guilds.Where(x => !x.HasAllMembers));
/// <summary> Downloads the members list for the provided guilds, if they don't have a complete list. </summary>
public async Task DownloadMembers(IEnumerable<IGuild> guilds)
{
const short batchSize = 50;
var cachedGuilds = guilds.Select(x => x as CachedGuild).ToArray();
if (cachedGuilds.Length == 0)
return;
else if (cachedGuilds.Length == 1)
{
await cachedGuilds[0].DownloadMembers().ConfigureAwait(false);
return;
}

ulong[] batchIds = new ulong[Math.Min(batchSize, cachedGuilds.Length)];
Task[] batchTasks = new Task[batchIds.Length];
int batchCount = (cachedGuilds.Length + (batchSize - 1)) / batchSize;

for (int i = 0, k = 0; i < batchCount; i++)
{
bool isLast = i == batchCount - 1;
int count = isLast ? (batchIds.Length - (batchCount - 1) * batchSize) : batchSize;

for (int j = 0; j < count; j++, k++)
{
var guild = cachedGuilds[k];
batchIds[j] = guild.Id;
batchTasks[j] = guild.DownloaderPromise;
}

ApiClient.SendRequestMembers(batchIds);

if (isLast && batchCount > 1)
await Task.WhenAll(batchTasks.Take(count)).ConfigureAwait(false);
else
await Task.WhenAll(batchTasks).ConfigureAwait(false);
}
}

private async Task ProcessMessage(GatewayOpCode opCode, int? seq, string type, object payload)
{
if (seq != null)
@@ -367,11 +407,8 @@ namespace Discord
type = "GUILD_AVAILABLE";
else
await JoinedGuild.Raise(guild).ConfigureAwait(false);

if (!data.Large)
await GuildAvailable.Raise(guild);
else
_largeGuilds.Enqueue(data.Id);
await GuildAvailable.Raise(guild);
}
break;
case "GUILD_UPDATE":
@@ -781,15 +818,19 @@ namespace Discord
}
private async Task RunHeartbeat(int intervalMillis, CancellationToken cancelToken)
{
var state = ConnectionState;
while (state == ConnectionState.Connecting || state == ConnectionState.Connected)
try
{
//if (_heartbeatTime != 0) //TODO: Connection lost, reconnect
var state = ConnectionState;
while (state == ConnectionState.Connecting || state == ConnectionState.Connected)
{
//if (_heartbeatTime != 0) //TODO: Connection lost, reconnect

_heartbeatTime = Environment.TickCount;
await ApiClient.SendHeartbeat(_lastSeq).ConfigureAwait(false);
await Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false);
_heartbeatTime = Environment.TickCount;
await ApiClient.SendHeartbeat(_lastSeq).ConfigureAwait(false);
await Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException) { }
}
}
}

+ 1
- 0
src/Discord.Net/Entities/Guilds/Guild.cs View File

@@ -306,6 +306,7 @@ namespace Discord
IRole IGuild.EveryoneRole => EveryoneRole;
IReadOnlyCollection<Emoji> IGuild.Emojis => Emojis;
IReadOnlyCollection<string> IGuild.Features => Features;
Task IGuild.DownloadUsers() { throw new NotSupportedException(); }

IRole IGuild.GetRole(ulong id) => GetRole(id);
}


+ 3
- 0
src/Discord.Net/Entities/Guilds/IGuild.cs View File

@@ -90,6 +90,9 @@ namespace Discord
Task<IGuildUser> GetUser(ulong id);
/// <summary> Gets the current user for this guild. </summary>
Task<IGuildUser> GetCurrentUser();
/// <summary> Downloads all users for this guild if the current list is incomplete. </summary>
Task DownloadUsers();
/// <summary> Removes all users from this guild if they have not logged on in a provided number of days or, if simulate is true, returns the number of users that would be removed. </summary>
Task<int> PruneUsers(int days = 30, bool simulate = false);
}
}

+ 19
- 0
src/Discord.Net/Entities/WebSocket/CachedGuild.cs View File

@@ -16,6 +16,7 @@ namespace Discord
{
internal class CachedGuild : Guild, ICachedEntity<ulong>
{
private TaskCompletionSource<bool> _downloaderPromise;
private ConcurrentHashSet<ulong> _channels;
private ConcurrentDictionary<ulong, CachedGuildUser> _members;
private ConcurrentDictionary<ulong, Presence> _presences;
@@ -23,6 +24,9 @@ namespace Discord

public bool Available { get; private set; } //TODO: Add to IGuild

public bool HasAllMembers => _downloaderPromise.Task.IsCompleted;
public Task DownloaderPromise => _downloaderPromise.Task;

public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient;
public CachedGuildUser CurrentUser => GetCachedUser(Discord.CurrentUser.Id);
public IReadOnlyCollection<ICachedGuildChannel> Channels => _channels.Select(x => GetCachedChannel(x)).ToReadOnlyCollection(_channels);
@@ -30,6 +34,7 @@ namespace Discord

public CachedGuild(DiscordSocketClient discord, Model model) : base(discord, model)
{
_downloaderPromise = new TaskCompletionSource<bool>();
}

public void Update(ExtendedModel model, UpdateSource source, DataStore dataStore)
@@ -79,6 +84,9 @@ namespace Discord
{
for (int i = 0; i < model.Members.Length; i++)
AddCachedUser(model.Members[i], members, dataStore);
_downloaderPromise = new TaskCompletionSource<bool>();
if (!model.Large)
_downloaderPromise.SetResult(true);
}
_members = members;
}
@@ -153,6 +161,17 @@ namespace Discord
return null;
}

public async Task DownloadMembers()
{
if (!HasAllMembers)
await Discord.ApiClient.SendRequestMembers(new ulong[] { Id }).ConfigureAwait(false);
await _downloaderPromise.Task.ConfigureAwait(false);
}
public void CompleteDownloadMembers()
{
_downloaderPromise.SetResult(true);
}

public CachedGuild Clone() => MemberwiseClone() as CachedGuild;

new internal ICachedGuildChannel ToChannel(ChannelModel model)


Loading…
Cancel
Save