Skip to content

Commit

Permalink
Roles download
Browse files Browse the repository at this point in the history
  • Loading branch information
gehongyan committed Oct 15, 2024
1 parent f1b221b commit 2127d9a
Show file tree
Hide file tree
Showing 21 changed files with 404 additions and 142 deletions.
9 changes: 8 additions & 1 deletion samples/QQBot.Net.Samples.SimpleBot/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@
{
LogLevel = LogSeverity.Debug,
AccessEnvironment = AccessEnvironment.Sandbox,
GatewayIntents = GatewayIntents.All
GatewayIntents = GatewayIntents.All,
StartupCacheFetchData = StartupCacheFetchData.All
});
client.Log += x => Task.Run(() => Console.WriteLine(x));
client.Ready += async () =>
{
Console.WriteLine("Ready!");
await client.DownloadUsersAsync();
};
client.MessageReceived += async message =>
{
if (message.Source is not MessageSource.User) return;
Expand All @@ -19,4 +25,5 @@
};
await client.LoginAsync(0, TokenType.BotToken, "");
await client.StartAsync();
await Task.Delay(TimeSpan.FromSeconds(10));
await Task.Delay(Timeout.Infinite);
37 changes: 36 additions & 1 deletion src/QQBot.Net.Core/Entities/Guilds/IGuild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public interface IGuild : IEntity<ulong>
int MemberCount { get; }

/// <summary>
/// 获取可以加入到此子频道的最大成员数量
/// 获取可以加入到此频道的最大成员数量
/// </summary>
int MaxMembers { get; }

Expand All @@ -40,6 +40,11 @@ public interface IGuild : IEntity<ulong>
/// </summary>
DateTimeOffset JoinedAt { get; }

/// <summary>
/// 获取此频道内可以同时拥有的角色的最大数量。
/// </summary>
int MaxRoles { get; }

/// <summary>
/// 确定此频道实体是否已准备就绪以供用户代码访问。
/// </summary>
Expand All @@ -52,6 +57,24 @@ public interface IGuild : IEntity<ulong>
/// </remarks>
bool IsAvailable { get; }

/// <summary>
/// 获取此服务器的所有角色。
/// </summary>
IReadOnlyCollection<IRole> Roles { get; }

#region Roles

/// <summary>
/// 获取此频道内的角色。
/// </summary>
/// <param name="id"> 要获取的角色的 ID。 </param>
/// <returns> 一个表示异步获取操作的任务。任务的结果包含与指定的 <paramref name="id"/> 关联的角色;如果未找到,则返回 <c>null</c>。 </returns>
IRole? GetRole(uint id);

#endregion

#region Users

/// <summary>
/// 获取此频道内的用户。
/// </summary>
Expand All @@ -66,4 +89,16 @@ public interface IGuild : IEntity<ulong>
/// <param name="options"> 发送请求时要使用的选项。 </param>
/// <returns> 一个表示异步获取操作的任务。任务的结果包含与指定的 <paramref name="id"/> 关联的用户;如果未找到,则返回 <c>null</c>。 </returns>
Task<IGuildMember?> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions? options = null);

/// <summary>
/// 下载此频道内的所有用户。
/// </summary>
/// <remarks>
/// 此方法会下载所有加入到此频道内的用户,并缓存它们。
/// </remarks>
/// <param name="options"> 发送请求时要使用的选项。 </param>
/// <returns> 一个表示异步下载操作的任务。 </returns>
Task DownloadUsersAsync(RequestOptions? options = null);

#endregion
}
5 changes: 5 additions & 0 deletions src/QQBot.Net.Core/Entities/Roles/IRole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
/// </summary>
public interface IRole : IEntity<uint>
{
/// <summary>
/// 获取拥有此角色的频道。
/// </summary>
IGuild Guild { get; }

/// <summary>
/// 获取此身份组的名称。
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/QQBot.Net.Core/Entities/Users/IGuildUser.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace QQBot;

/// <summary>
/// 表示一个通用的子频道内用户
/// 表示一个通用的频道上下文中的用户
/// </summary>
public interface IGuildUser : IUser, IEntity<ulong>
{
Expand Down
29 changes: 28 additions & 1 deletion src/QQBot.Net.Rest/Entities/Guilds/GuildHelper.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using QQBot.API;
using QQBot.API.Rest;

namespace QQBot.Rest;

internal static class GuildHelper
{

public static async Task<RestGuildMember> GetUserAsync(IGuild guild, BaseQQBotClient client,
ulong id, RequestOptions? options)
{
Expand All @@ -13,4 +13,31 @@ public static async Task<RestGuildMember> GetUserAsync(IGuild guild, BaseQQBotCl
throw new InvalidOperationException("User not found in guild.");
return RestGuildMember.Create(client, guild, model.User, model);
}

public static IAsyncEnumerable<IReadOnlyCollection<Member>> GetMembersAsync(
IGuild guild, BaseQQBotClient client, int? limit, RequestOptions? options)
{
return new PagedAsyncEnumerable<Member>(
QQBotConfig.MaxMembersPerBatch,
async (info, ct) =>
{
GetGuildMembersParams args = new()
{
Limit = info.PageSize
};
if (info.Position != null)
args.AfterId = info.Position.Value;
return [..await client.ApiClient.GetGuildMembersAsync(guild.Id, args, options).ConfigureAwait(false)];
},
nextPage: (info, lastPage) =>
{
if (lastPage.LastOrDefault()?.User?.Id is not { } lastId)
return false;
info.Position = lastId;
return true;
},
start: null,
count: limit
);
}
}
12 changes: 3 additions & 9 deletions src/QQBot.Net.Rest/Net/Converters/HexAlphaColorConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,9 @@ namespace QQBot.Net.Converters;

internal class HexAlphaColorConverter : JsonConverter<AlphaColor>
{
public override AlphaColor Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string? hex = reader.GetString()?.TrimStart('#');
if (hex == null || string.IsNullOrWhiteSpace(hex) || hex.Length < 8)
return AlphaColor.Default;

return new AlphaColor(uint.Parse(hex, NumberStyles.HexNumber));
}
public override AlphaColor Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
new(reader.GetUInt32());

public override void Write(Utf8JsonWriter writer, AlphaColor value, JsonSerializerOptions options) =>
writer.WriteStringValue($"#{value.RawValue:X8}");
writer.WriteNumberValue(value.RawValue);
}
5 changes: 3 additions & 2 deletions src/QQBot.Net.Rest/Net/Queue/ClientBucket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal enum ClientBucketType
SendEdit = 1
}

internal struct ClientBucket
internal record struct ClientBucket
{
private static readonly ImmutableDictionary<ClientBucketType, ClientBucket> DefsByType;
private static readonly ImmutableDictionary<BucketId, ClientBucket> DefsById;
Expand All @@ -29,7 +29,8 @@ static ClientBucket()

ImmutableDictionary<BucketId, ClientBucket>.Builder idBuilder =
ImmutableDictionary.CreateBuilder<BucketId, ClientBucket>();
foreach (ClientBucket bucket in buckets) idBuilder.Add(bucket.Id, bucket);
foreach (ClientBucket bucket in buckets)
idBuilder.Add(bucket.Id, bucket);
DefsById = idBuilder.ToImmutable();
}

Expand Down
22 changes: 11 additions & 11 deletions src/QQBot.Net.Rest/Net/Queue/RequestBucket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ public RequestBucket(RequestQueue queue, IRequest request, BucketId id)

_lock = new object();

if (request.Options.IsClientBucket && request.Options.BucketId != null)
WindowCount = ClientBucket.Get(request.Options.BucketId).WindowCount;
else if (request.Options.IsGatewayBucket && request.Options.BucketId != null)
WindowCount = GatewayBucket.Get(request.Options.BucketId).WindowCount;
if (request.Options.IsClientBucket)
WindowCount = ClientBucket.Get(request.Options.BucketId ?? throw new InvalidOperationException("Client bucket is not set.")).WindowCount;
else if (request.Options.IsGatewayBucket)
WindowCount = GatewayBucket.Get(request.Options.BucketId ?? throw new InvalidOperationException("Gateway bucket is not set.")).WindowCount;
else
WindowCount = 1; //Only allow one request until we get a header back
WindowCount = 117; // TODO: Preemptive rate limit
Debug.WriteLine($"WindowCount: {WindowCount}");

Check failure on line 45 in src/QQBot.Net.Rest/Net/Queue/RequestBucket.cs

View workflow job for this annotation

GitHub Actions / Push Packages / Push Packages

The name 'Debug' does not exist in the current context

Check failure on line 45 in src/QQBot.Net.Rest/Net/Queue/RequestBucket.cs

View workflow job for this annotation

GitHub Actions / Push Packages / Push Packages

The name 'Debug' does not exist in the current context

_semaphore = WindowCount;
_resetTick = null;
Expand Down Expand Up @@ -221,7 +222,7 @@ public async Task SendAsync(WebSocketRequest request)
internal async Task TriggerAsync(int id, IRequest request)
{
#if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Trigger Bucket");
Debug.WriteLine($"[{id}] Trigger Bucket");
#endif
await EnterAsync(id, request).ConfigureAwait(false);
UpdateRateLimit(id, request, default, false);
Expand Down Expand Up @@ -311,8 +312,7 @@ private async Task EnterAsync(int id, IRequest request)
continue;
}
#if DEBUG_LIMITS
else
Debug.WriteLine($"[{id}] Entered Semaphore ({semaphore}/{WindowCount} remaining)");
Debug.WriteLine($"[{id}] Entered Semaphore ({semaphore}/{WindowCount} remaining)");
#endif
break;
}
Expand Down Expand Up @@ -397,7 +397,7 @@ private void UpdateRateLimit(int id, IRequest request, RateLimitInfo info, bool
// Read the Reset-After header
resetTick = DateTimeOffset.UtcNow.Add(TimeSpan.FromSeconds(info.ResetAfter?.TotalSeconds ?? 0));
#if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Reset-After: {info.ResetAfter.Value} ({info.ResetAfter?.TotalMilliseconds} ms)");
Debug.WriteLine($"[{id}] Reset-After: {info.ResetAfter} ({info.ResetAfter?.TotalMilliseconds} ms)");
#endif
}
// if (info.RetryAfter.HasValue)
Expand All @@ -412,7 +412,7 @@ private void UpdateRateLimit(int id, IRequest request, RateLimitInfo info, bool
{
resetTick = DateTimeOffset.UtcNow.Add(info.ResetAfter.Value);
#if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Reset-After: {info.ResetAfter.Value} ({info.ResetAfter?.TotalMilliseconds} ms)");
Debug.WriteLine($"[{id}] Reset-After: {info.ResetAfter.Value} ({info.ResetAfter?.TotalMilliseconds} ms)");
#endif
}
// else if (info.Reset.HasValue)
Expand All @@ -433,7 +433,7 @@ private void UpdateRateLimit(int id, IRequest request, RateLimitInfo info, bool
{
resetTick = DateTimeOffset.UtcNow.AddSeconds(ClientBucket.Get(Id).WindowSeconds);
#if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Client Bucket ({ClientBucket.Get(Id).WindowSeconds * 1000} ms)");
Debug.WriteLine($"[{id}] Client Bucket ({ClientBucket.Get(Id).WindowSeconds * 1000} ms)");
#endif
}
else if (request.Options.IsGatewayBucket && request.Options.BucketId != null)
Expand Down
2 changes: 1 addition & 1 deletion src/QQBot.Net.Rest/QQBot.Net.Rest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DefineConstants>DEBUG_REST</DefineConstants>
<DefineConstants>DEBUG_REST;DEBUG_LIMITS</DefineConstants>
</PropertyGroup>

<ItemGroup>
Expand Down
61 changes: 34 additions & 27 deletions src/QQBot.Net.WebSocket/BaseSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,35 @@ public abstract partial class BaseSocketClient : BaseQQBotClient, IQQBotClient
protected set => base.CurrentUser = value;
}

// /// <summary>
// /// 获取当前用户所在的所有频道。
// /// </summary>
// public abstract IReadOnlyCollection<SocketGuild> Guilds { get; }
/// <summary>
/// 获取当前用户所在的所有频道。
/// </summary>
public abstract IReadOnlyCollection<SocketGuild> Guilds { get; }

internal BaseSocketClient(QQBotSocketConfig config, QQBotRestApiClient client)
: base(config, client)
{
BaseConfig = config;
}

// /// <summary>
// /// 获取用户。
// /// </summary>
// /// <remarks>
// /// 此方法可能返回 <c>null</c>,因为此方法仅会返回网关缓存中存在的用户,如果在当前 Bot
// /// 登录会话中,要获取的用户未引发过任何事件,那么该用户实体则不会存在于缓存中。
// /// </remarks>
// /// <param name="id"> 要获取的用户的 ID。 </param>
// /// <returns> 与指定的 <paramref name="id"/> 关联的用户;如果未找到,则返回 <c>null</c>。 </returns>
// public abstract SocketUser? GetUser(ulong id);
//
/// <summary>
/// 获取用户。
/// </summary>
/// <remarks>
/// 此方法可能返回 <c>null</c>,因为此方法仅会返回网关缓存中存在的用户,如果在当前 Bot
/// 登录会话中,要获取的用户未引发过任何事件,那么该用户实体则不会存在于缓存中。
/// </remarks>
/// <param name="id"> 要获取的用户的 ID。 </param>
/// <returns> 与指定的 <paramref name="id"/> 关联的用户;如果未找到,则返回 <c>null</c>。 </returns>
public abstract SocketUser? GetUser(string id);

/// <summary>
/// 获取频道上下文中的用户。
/// </summary>
/// <param name="id"> 要获取的用户的 ID。 </param>
/// <returns> 与指定的 <paramref name="id"/> 关联的用户;如果未找到,则返回 <c>null</c>。 </returns>
public abstract SocketGuildUser? GetGuildUser(string id);

// /// <summary>
// /// 获取用户。
// /// </summary>
Expand All @@ -69,7 +76,7 @@ internal BaseSocketClient(QQBotSocketConfig config, QQBotRestApiClient client)
// /// <param name="identifyNumber"> 用户的识别号。 </param>
// /// <returns> 与指定的名称和识别号关联的用户;如果未找到,则返回 <c>null</c>。 </returns>
// public abstract SocketUser? GetUser(string username, string identifyNumber);
//

// /// <summary>
// /// 获取一个频道子频道。
// /// </summary>
Expand All @@ -92,10 +99,10 @@ internal BaseSocketClient(QQBotSocketConfig config, QQBotRestApiClient client)
// public abstract SocketDMChannel? GetDMChannel(ulong userId);

/// <summary>
/// 获取一个子频道
/// 获取一个频道
/// </summary>
/// <param name="id"> 要获取的子频道的 ID。 </param>
/// <returns> 与指定的 <paramref name="id"/> 关联的子频道;如果未找到,则返回 <c>null</c>。 </returns>
/// <param name="id"> 要获取的频道的 ID。 </param>
/// <returns> 与指定的 <paramref name="id"/> 关联的频道;如果未找到,则返回 <c>null</c>。 </returns>
public abstract SocketGuild? GetGuild(ulong id);

/// <inheritdoc />
Expand All @@ -104,14 +111,14 @@ internal BaseSocketClient(QQBotSocketConfig config, QQBotRestApiClient client)
/// <inheritdoc />
public abstract Task StopAsync();

// /// <summary>
// /// 下载全部或指定频道的用户到缓存中。
// /// </summary>
// /// <param name="guilds"> 要下载用户的频道。如果为 <c>null</c>,则下载所有可用的频道。 </param>
// /// <param name="options"> 发送请求时要使用的选项。 </param>
// /// <returns> 一个表示异步下载操作的任务。 </returns>
// public abstract Task DownloadUsersAsync(IEnumerable<IGuild>? guilds = null, RequestOptions? options = null);
//
/// <summary>
/// 下载全部或指定频道的用户到缓存中。
/// </summary>
/// <param name="guilds"> 要下载用户的频道。如果为 <c>null</c>,则下载所有可用的频道。 </param>
/// <param name="options"> 发送请求时要使用的选项。 </param>
/// <returns> 一个表示异步下载操作的任务。 </returns>
public abstract Task DownloadUsersAsync(IEnumerable<SocketGuild>? guilds = null, RequestOptions? options = null);

// /// <summary>
// /// 下载全部或指定频道的语音状态到缓存中。
// /// </summary>
Expand Down
4 changes: 4 additions & 0 deletions src/QQBot.Net.WebSocket/ClientState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ internal SocketGroupChannel GetOrAddGroupChannel(Guid id, Func<Guid, SocketGroup

#region Global Users

internal SocketGlobalUser? GetGlobalUser(string id) => _globalUsers.GetValueOrDefault(id);

internal SocketGlobalUser GetOrAddGlobalUser(ulong id, Func<ulong, SocketGlobalUser> userFactory) =>
_globalUsers.GetOrAdd(id.ToIdString(), _ => userFactory(id));

Expand All @@ -90,6 +92,8 @@ internal SocketGlobalUser GetOrAddGlobalUser(Guid id, Func<Guid, SocketGlobalUse

#region Guild Users

internal SocketGuildUser? GetGuildUser(string id) => _guildUsers.GetValueOrDefault(id);

internal SocketGuildUser GetOrAddGuildUser(ulong id, Func<ulong, SocketGuildUser> userFactory) =>
_guildUsers.GetOrAdd(id.ToString(), _ => userFactory(id));

Expand Down
Loading

0 comments on commit 2127d9a

Please sign in to comment.