/// <inheritdoc/> /// <exception cref="InvalidAuthKeyException"/> public override Task RegisterCommandAsync(string name, string[]?alias = null, string?description = null, string?usage = null, CancellationToken token = default) { InternalSessionInfo session = SafeGetSession(); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); return(RegisterCommandAsync(_client, _options, name, alias, description, usage, token).DisposeWhenCompleted(cts)); }
public override Task <long[]> GetManagersAsync(long qqNumber, CancellationToken token = default) { InternalSessionInfo session = SafeGetSession(); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); return(GetManagersAsync(_client, _options, qqNumber, token).DisposeWhenCompleted(cts)); }
/// <summary> /// 异步获取群列表 /// </summary> /// <exception cref="InvalidOperationException"/> public Task <IGroupInfo[]> GetGroupListAsync() { InternalSessionInfo session = SafeGetSession(); return(session.Client.GetAsync($"{session.Options.BaseUrl}/groupList?sessionKey={session.SessionKey}", session.Token) .AsApiRespAsync <IGroupInfo[], GroupInfo[]>(session.Token)); }
/// <summary> /// 异步获取群成员列表 /// </summary> /// <exception cref="InvalidOperationException"/> /// <exception cref="TargetNotFoundException"/> /// <param name="groupNumber">将要进行查询的群号</param> public Task <IGroupMemberInfo[]> GetGroupMemberListAsync(long groupNumber) { InternalSessionInfo session = SafeGetSession(); return(session.Client.GetAsync($"{session.Options.BaseUrl}/memberList?sessionKey={session.SessionKey}&target={groupNumber}", session.Token) .AsApiRespAsync <IGroupMemberInfo[], GroupMemberInfo[]>(session.Token)); }
/// <summary> /// 内部使用 /// </summary> /// <exception cref="NotSupportedException"/> /// <param name="type">目标类型</param> /// <param name="voiceStream">语音流</param> /// <returns>一个 <see cref="VoiceMessage"/> 实例, 可用于以后的消息发送</returns> private static Task <VoiceMessage> InternalUploadVoiceAsync(InternalSessionInfo session, UploadTarget type, Stream voiceStream) { if (session.ApiVersion < new Version(1, 8, 0)) { throw new NotSupportedException($"当前版本的mirai-api-http不支持上传语音。({session.ApiVersion}, 必须>=1.8.0)"); } HttpContent sessionKeyContent = new StringContent(session.SessionKey); sessionKeyContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "sessionKey" }; HttpContent typeContent = new StringContent(type.ToString().ToLower()); typeContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "type" }; HttpContent voiceContent = new StreamContent(voiceStream); voiceContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "voice", FileName = $"{Guid.NewGuid():n}.amr" }; HttpContent[] contents = new HttpContent[] { sessionKeyContent, typeContent, voiceContent }; return(session.Client.PostAsync($"{session.Options.BaseUrl}/uploadVoice", contents, session.Token) .AsApiRespAsync <VoiceMessage>(session.Token)); }
/// <summary> /// 异步获取给定群员的信息 /// </summary> /// <exception cref="InvalidOperationException"/> /// <exception cref="TargetNotFoundException"/> /// <param name="memberId">要获取信息的QQ号</param> /// <param name="groupNumber">该用户所在群号</param> public Task <IGroupMemberCardInfo> GetGroupMemberInfoAsync(long memberId, long groupNumber) { InternalSessionInfo session = SafeGetSession(); return(session.Client.GetAsync($"{session.Options.BaseUrl}/memberInfo?sessionKey={session.SessionKey}&target={groupNumber}&memberId={memberId}", session.Token) .AsApiRespAsync <IGroupMemberCardInfo, GroupMemberCardInfo>(session.Token)); }
/// <inheritdoc/> public override Task PublishGroupAnnouncementAsync(long groupNumber, ISharedPublishGroupAnnouncementRequest request, CancellationToken token = default) { if (string.IsNullOrEmpty(request.Content)) { throw new ArgumentException("群公告内容不能为空"); } InternalSessionInfo session = SafeGetSession(); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); if (request is not PublishGroupAnnouncementRequest payload) { payload = new PublishGroupAnnouncementRequest( request.GroupNumber, request.Content, request.SendToNewMember, request.Pinned, request.ShowEditMemberCard, request.AutoPopup, request.RequireConfirmation, request.ImageUrl, request.ImagePath, request.ImageBase64); } payload.SessionKey = session.SessionKey; return(_client.PostAsJsonAsync($"{_options.BaseUrl}/anno/publish", payload, token).AsApiRespAsync(token).DisposeWhenCompleted(cts)); }
/// <summary> /// 异步获取好友列表 /// </summary> /// <exception cref="InvalidOperationException"/> public Task <IFriendInfo[]> GetFriendListAsync() { InternalSessionInfo session = SafeGetSession(); return(session.Client.GetAsync($"{session.Options.BaseUrl}/friendList?sessionKey={session.SessionKey}", session.Token) .AsNoSuccCodeApiRespAsync <IFriendInfo[], FriendInfo[]>(session.Token)); }
/// <inheritdoc/> public override Task ExecuteCommandAsync(string name, string[]?args, CancellationToken token) { InternalSessionInfo session = SafeGetSession(); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); return(ExecuteCommandAsync(_client, _options, name, args, token).DisposeWhenCompleted(cts)); }
/// <inheritdoc/> public override Task<ISharedGroupMemberInfo[]> GetGroupMemberListAsync(long groupNumber, CancellationToken token = default) { InternalSessionInfo session = SafeGetSession(); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); return _client.GetAsync($"{_options.BaseUrl}/memberList?sessionKey={session.SessionKey}&target={groupNumber}", token) .AsApiRespV2Async<ISharedGroupMemberInfo[], GroupMemberInfo[]>(token) .DisposeWhenCompleted(cts); }
/// <inheritdoc/> public override Task<ISharedFriendInfo[]> GetFriendListAsync(CancellationToken token = default) { InternalSessionInfo session = SafeGetSession(); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); return _client.GetAsync($"{_options.BaseUrl}/friendList?sessionKey={session.SessionKey}", token) .AsApiRespV2Async<ISharedFriendInfo[], FriendInfo[]>(token) .DisposeWhenCompleted(cts); }
private async Task <IMiraiSessionConfig> GetConfigAsync(InternalSessionInfo session) { using JsonDocument j = await HttpHelper.HttpGetAsync($"{session.Options.BaseUrl}/config?sessionKey={WebUtility.UrlEncode(session.SessionKey)}", token : session.Canceller.Token); JsonElement root = j.RootElement; return(Utils.Deserialize <MiraiSessionConfig>(in root)); }
/// <inheritdoc/> public override Task <ISharedFriendProfile> GetFriendProfileAsync(long qqNumber, CancellationToken token = default) { InternalSessionInfo session = SafeGetSession(); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); return(_client.GetAsync($"{_options.BaseUrl}/friendProfile?sessionKey={session.SessionKey}&target={qqNumber}", token) .AsApiRespAsync <ISharedFriendProfile, FriendProfile>(token) .DisposeWhenCompleted(cts)); }
private Task SetConfigAsync(InternalSessionInfo session, IMiraiSessionConfig config) { byte[] payload = JsonSerializer.SerializeToUtf8Bytes(new { sessionKey = session.SessionKey, cacheSize = config.CacheSize, enableWebsocket = config.EnableWebSocket }, JsonSerializeOptionsFactory.IgnoreNulls); return(InternalHttpPostAsync($"{session.Options.BaseUrl}/config", payload, session.Canceller.Token)); }
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()); }
/// <summary> /// 异步撤回消息 /// </summary> /// <param name="messageId"> /// 请提供以下之一 /// <list type="bullet"> /// <item><see cref="SourceMessage.Id"/></item> /// <item><see cref="SendFriendMessageAsync(long, IMessageBase[], int?)"/> 的返回值</item> /// <item><see cref="SendTempMessageAsync(long, long, IMessageBase[], int?)"/> 的返回值</item> /// <item><see cref="SendGroupMessageAsync(long, IMessageBase[], int?)"/> 的返回值</item> /// </list> /// </param> /// <exception cref="InvalidOperationException"/> /// <exception cref="TargetNotFoundException"/> public Task RevokeMessageAsync(int messageId) { InternalSessionInfo session = SafeGetSession(); byte[] payload = JsonSerializer.SerializeToUtf8Bytes(new { sessionKey = session.SessionKey, target = messageId }); return(InternalHttpPostAsync(session.Client, $"{session.Options.BaseUrl}/recall", payload, session.Token)); }
/// <summary> /// 内部使用 /// </summary> /// <exception cref="InvalidOperationException"/> /// <exception cref="PermissionDeniedException"/> /// <exception cref="TargetNotFoundException"/> /// <param name="action">禁言为 <see langword="true"/>, 解禁为 <see langword="false"/></param> /// <param name="groupNumber">将要操作的群号</param> private Task InternalToggleMuteAllAsync(bool action, long groupNumber) { InternalSessionInfo session = SafeGetSession(); byte[] payload = JsonSerializer.SerializeToUtf8Bytes(new { sessionKey = session.SessionKey, target = groupNumber }); return(InternalHttpPostAsync(session.Client, $"{session.Options.BaseUrl}/{(action ? "muteAll" : "unmuteAll")}", payload, session.Token)); }
/// <summary> /// 异步使当前机器人退出给定的群 /// </summary> /// <exception cref="InvalidOperationException"/> /// <exception cref="TargetNotFoundException"/> /// <param name="groupNumber">将要退出的群号</param> public Task LeaveGroupAsync(long groupNumber) { InternalSessionInfo session = SafeGetSession(); byte[] payload = JsonSerializer.SerializeToUtf8Bytes(new { sessionKey = session.SessionKey, target = groupNumber, }); return(InternalHttpPostAsync(session.Client, $"{session.Options.BaseUrl}/quit", payload, session.Token)); }
/// <summary> /// 异步撤回消息 /// </summary> /// <param name="messageId"> /// 请提供以下之一 /// <list type="bullet"> /// <item><see cref="SourceMessage.Id"/></item> /// <item><see cref="SendFriendMessageAsync(long, IMessageBase[], int?)"/> 的返回值</item> /// <item><see cref="SendTempMessageAsync(long, long, IMessageBase[], int?)"/> 的返回值</item> /// <item><see cref="SendGroupMessageAsync(long, IMessageBase[], int?)"/> 的返回值</item> /// </list> /// </param> /// <exception cref="InvalidOperationException"/> /// <exception cref="TargetNotFoundException"/> public Task RevokeMessageAsync(int messageId) { InternalSessionInfo session = SafeGetSession(); var payload = new { sessionKey = session.SessionKey, target = messageId }; return(session.Client.PostAsJsonAsync($"{session.Options.BaseUrl}/recall", payload, session.Token).AsApiRespAsync(session.Token)); }
/// <inheritdoc/> public Task SetConfigAsync(IMiraiSessionConfig config, CancellationToken token = default) { InternalSessionInfo session = SafeGetSession(); if (session.ApiVersion.Major >= 2) { throw new NotSupportedException("自 mirai-api-http v2.0.0 起, 本接口不受支持"); } CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); return(SetConfigAsync(session, config, token).DisposeWhenCompleted(cts)); }
private Task SetConfigAsync(InternalSessionInfo session, IMiraiSessionConfig config, CancellationToken token = default) { var payload = new { sessionKey = session.SessionKey, cacheSize = config.CacheSize, enableWebsocket = config.EnableWebSocket }; return(_client.PostAsJsonAsync($"{_options.BaseUrl}/config", payload, JsonSerializeOptionsFactory.IgnoreNulls, session.Token).AsApiRespAsync(session.Token)); }
/// <summary> /// 异步使当前机器人退出给定的群 /// </summary> /// <exception cref="InvalidOperationException"/> /// <exception cref="TargetNotFoundException"/> /// <param name="groupNumber">将要退出的群号</param> public Task LeaveGroupAsync(long groupNumber) { InternalSessionInfo session = SafeGetSession(); var payload = new { sessionKey = session.SessionKey, target = groupNumber, }; return(session.Client.PostAsJsonAsync($"{session.Options.BaseUrl}/quit", payload, session.Token) .AsApiRespAsync(session.Token)); }
/// <summary> /// 内部使用 /// </summary> /// <exception cref="InvalidOperationException"/> /// <exception cref="PermissionDeniedException"/> /// <exception cref="TargetNotFoundException"/> /// <param name="action">禁言为 <see langword="true"/>, 解禁为 <see langword="false"/></param> /// <param name="groupNumber">将要操作的群号</param> private Task InternalToggleMuteAllAsync(bool action, long groupNumber) { InternalSessionInfo session = SafeGetSession(); var payload = new { sessionKey = session.SessionKey, target = groupNumber }; return(session.Client.PostAsJsonAsync($"{session.Options.BaseUrl}/{(action ? "muteAll" : "unmuteAll")}", payload, session.Token) .AsApiRespAsync(session.Token)); }
/// <summary> /// 异步修改群信息 /// </summary> /// <exception cref="InvalidOperationException"/> /// <exception cref="PermissionDeniedException"/> /// <exception cref="TargetNotFoundException"/> /// <param name="groupNumber">要进行修改的群号</param> /// <param name="config">群信息。其中不进行修改的值请置为 <see langword="null"/></param> public Task ChangeGroupConfigAsync(long groupNumber, IGroupConfig config) { InternalSessionInfo session = SafeGetSession(); byte[] payload = JsonSerializer.SerializeToUtf8Bytes(new { sessionKey = session.SessionKey, target = groupNumber, config }, JsonSerializeOptionsFactory.IgnoreNulls); return(InternalHttpPostAsync(session.Client, $"{session.Options.BaseUrl}/groupConfig", payload, session.Token)); }
/// <inheritdoc/> public override Task <ISharedUserProfile> GetUserProfileAsync(long qqNumber, CancellationToken token = default) { InternalSessionInfo session = SafeGetSession(); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); if (session.ApiVersion < new System.Version(2, 4)) { throw new System.NotSupportedException("本接口仅在 mirai-api-http v2.4.0 及以上版本提供"); } return(_client.GetAsync($"{_options.BaseUrl}/userProfile?sessionKey={session.SessionKey}&userId={qqNumber}", token) .AsApiRespAsync <ISharedUserProfile, UserProfile>(token) .DisposeWhenCompleted(cts)); }
/// <inheritdoc/> public override Task DeleteFriendAsync(long qqNumber, CancellationToken token = default) { InternalSessionInfo session = SafeGetSession(); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); var payload = new { sessionKey = session.SessionKey, target = qqNumber, }; return _client.PostAsJsonAsync($"{_options.BaseUrl}/deleteFriend", payload, token) .AsApiRespAsync(token) .DisposeWhenCompleted(cts); }
/// <summary> /// 异步将给定用户踢出给定的群 /// </summary> /// <exception cref="InvalidOperationException"/> /// <exception cref="PermissionDeniedException"/> /// <exception cref="TargetNotFoundException"/> /// <param name="memberId">将要被踢出的QQ号</param> /// <param name="groupNumber">该用户所在群号</param> /// <param name="msg">附加消息</param> public Task KickMemberAsync(long memberId, long groupNumber, string msg = "您已被移出群聊") { InternalSessionInfo session = SafeGetSession(); byte[] payload = JsonSerializer.SerializeToUtf8Bytes(new { sessionKey = session.SessionKey, target = groupNumber, memberId, msg }); return(InternalHttpPostAsync(session.Client, $"{session.Options.BaseUrl}/kick", payload, session.Token)); }
private async void ReceiveCommandLoop(InternalSessionInfo session, CancellationToken token) { using ClientWebSocket ws = new ClientWebSocket(); try { await ws.ConnectAsync(new Uri($"ws://{session.Options.Host}:{session.Options.Port}/command?authKey={session.Options.AuthKey}"), token); while (true) { using MemoryStream ms = await ws.ReceiveFullyAsync(token); ms.Seek(0, SeekOrigin.Begin); using JsonDocument j = await JsonDocument.ParseAsync(ms, default, token);
/// <inheritdoc/> public override Task SetEssenceMessageAsync(int id, CancellationToken token = default) { InternalSessionInfo session = SafeGetSession(); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); var payload = new { sessionKey = session.SessionKey, target = id, }; return _client.PostAsJsonAsync($"{_options.BaseUrl}/setEssence", payload, token) .AsApiRespAsync(token) .DisposeWhenCompleted(cts); }
/// <summary> /// 内部使用 /// </summary> private Task InternalToggleMuteAllAsync(bool action, long groupNumber, CancellationToken token = default) { InternalSessionInfo session = SafeGetSession(); CreateLinkedUserSessionToken(session.Token, token, out CancellationTokenSource? cts, out token); var payload = new { sessionKey = session.SessionKey, target = groupNumber }; return _client.PostAsJsonAsync($"{_options.BaseUrl}/{(action ? "muteAll" : "unmuteAll")}", payload, token) .AsApiRespAsync(token) .DisposeWhenCompleted(cts); }