/// <exception cref="InvalidAuthKeyException"/> /// <param name="client">要进行请求的 <see cref="HttpClient"/></param> /// <param name="options">连接信息</param> /// <inheritdoc cref="ExecuteCommandAsync(string, string[], CancellationToken)"/> public static async Task ExecuteCommandAsync(HttpClient client, MiraiHttpSessionOptions options, string name, string[]?args, CancellationToken token) { var payload = new { authKey = options.AuthKey, name, args = args ?? Array.Empty <string>() }; string json = await client.PostAsJsonAsync($"{options.BaseUrl}/command/send", payload, token).GetStringAsync(token).ConfigureAwait(false); try { using JsonDocument j = JsonDocument.Parse(json); JsonElement root = j.RootElement; root.EnsureApiRespCode(); } catch (JsonException) // 返回值非json就是执行失败, 把响应正文重新抛出 { throw new InvalidOperationException(json); } catch (TargetNotFoundException e) // 指令不存在 { e.ActualMessage = "给定的指令不存在。"; throw; } }
/// <param name="client">要进行请求的 <see cref="HttpClient"/></param> /// <param name="options">连接信息</param> /// <inheritdoc cref="RegisterCommandAsync(string, string[], string, string, CancellationToken)"/> public static async Task RegisterCommandAsync(HttpClient client, MiraiHttpSessionOptions options, string name, string[]?alias = null, string?description = null, string?usage = null, CancellationToken token = default) { if (string.IsNullOrEmpty(name)) { throw new ArgumentException("指令名必须非空。", nameof(name)); } var payload = new { authKey = options.AuthKey, name, alias = alias ?? Array.Empty <string>(), description, usage }; string json = await client.PostAsJsonAsync($"{options.BaseUrl}/command/register", payload, token).GetStringAsync(token).ConfigureAwait(false); try { using JsonDocument j = JsonDocument.Parse(json); JsonElement root = j.RootElement; root.EnsureApiRespCode(); } catch (JsonException) // 返回值非json就是执行失败, 把响应正文重新抛出 { throw new InvalidOperationException(json); } }
private async Task <int> CommonSendMessageAsync(string action, long?qqNumber, long?groupNumber, ISharedChatMessage[] chain, int?quoteMsgId, CancellationToken token = default) { InternalSessionInfo session = SafeGetSession(); if (chain == null || chain.Length == 0) { throw new ArgumentException("消息链必须为非空且至少有1条消息。"); } int emptyPlainCount = 0; for (int i = 0; i < chain.Length; i++) { ISharedChatMessage msg = chain[i]; if (msg is not IChatMessage) { throw new ArgumentException($"不允许发送未实现 {typeof(IChatMessage).FullName} 接口的消息。"); } if (msg is ISourceMessage) { throw new ArgumentException("无法发送基本信息(SourceMessage)。"); } if (msg is IQuoteMessage) { throw new ArgumentException("无法发送引用信息(QuoteMessage), 请使用quoteMsgId参数进行引用。"); } if (msg is IPlainMessage pm && string.IsNullOrEmpty(pm.Message)) { emptyPlainCount++; } } if (emptyPlainCount == chain.Length) { throw new ArgumentException("消息链仅含文本消息且所有文本均为空。"); } // 前边已经检查了所有元素均实现了 IChatMessage 接口, 但直接强制转换数组类型会引发 InvalidCastException, 所以玩一点 trick IEnumerable <IChatMessage> convertedChain = Unsafe.As <ISharedChatMessage[], IEnumerable <IChatMessage> >(ref chain); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); var payload = new { sessionKey = session.SessionKey, qq = qqNumber, group = groupNumber, quote = quoteMsgId, messageChain = convertedChain }; using JsonDocument j = await _client.PostAsJsonAsync($"{_options.BaseUrl}/{action}", payload, _chatMessageSerializingOptions, token).GetJsonAsync(token).DisposeWhenCompleted(cts).ConfigureAwait(false); JsonElement root = j.RootElement; root.EnsureApiRespCode(); return(root.GetProperty("messageId").GetInt32()); }
/// <param name="client">要进行请求的 <see cref="HttpClient"/></param> /// <param name="options">连接信息</param> /// <inheritdoc cref="GetVersionAsync(CancellationToken)"/> public static async Task <Version> GetVersionAsync(HttpClient client, MiraiHttpSessionOptions options, CancellationToken token = default) { using JsonDocument j = await client.GetAsync($"{options.BaseUrl}/about", token).GetJsonAsync(token).ConfigureAwait(false); JsonElement root = j.RootElement; root.EnsureApiRespCode(); string version = root.GetProperty("data").GetProperty("version").GetString() !; int vIndex = version.IndexOf('v'); #if NETSTANDARD2_0 return(Version.Parse(vIndex != -1 ? version.Substring(vIndex) : version)); // v1.0.0 ~ v1.7.4, skip 'v' #else return(Version.Parse(vIndex != -1 ? version.AsSpan()[(vIndex + 1)..] : version)); // v1.0.0 ~ v1.7.4, skip 'v'
/// <inheritdoc/> /// <remarks> /// 当缓存失效, 或者未注册用于解析消息的 <see cref="IMiraiHttpMessageParser{TMessage}"/> 时, 本异步方法将返回 <see langword="null"/> /// </remarks> public async Task <IMiraiHttpMessage?> RetriveMessageAsync(int messageId, CancellationToken token = default) { InternalSessionInfo session = SafeGetSession(); IMiraiHttpMessageParserResolver resolver = _services.GetRequiredService <IMiraiHttpMessageParserResolver>(); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); JsonElement root = await _client.GetAsync($"{_options.BaseUrl}/messageFromId?sessionKey={session.SessionKey}&id={messageId}", token) .GetObjectAsync <JsonElement>(token) .DisposeWhenCompleted(cts); root.EnsureApiRespCode(); JsonElement data = root.GetProperty("data"); IMiraiHttpMessageParser?parser = resolver.ResolveParser(in data); if (parser != null && parser.CanParse(in data)) { return((IMiraiHttpMessage)parser.Parse(in data)); } return(null); }
private static async Task <string> AuthorizeAsync(HttpClient client, MiraiHttpSessionOptions options, Version apiVersion, CancellationToken token = default) { //https://github.com/project-mirai/mirai-api-http/blob/master/docs/misc/Migration2.md#%E8%AE%A4%E8%AF%81%E6%B5%81%E7%A8%8B string url; AuthorizePayload payload; if (apiVersion.Major >= 2) { url = $"{options.BaseUrl}/verify"; payload = new AuthorizePayloadv2(options.AuthKey); } else { url = $"{options.BaseUrl}/auth"; payload = new AuthorizePayloadv1(options.AuthKey); }; using JsonDocument j = await client.PostAsJsonAsync <object>(url, payload, token).GetJsonAsync(token).ConfigureAwait(false); JsonElement root = j.RootElement; root.EnsureApiRespCode(); return(root.GetProperty("session").GetString() !); }