diff --git a/src/QQBot.Net.Core/Entities/Threads/IPost.cs b/src/QQBot.Net.Core/Entities/Threads/IPost.cs
new file mode 100644
index 0000000..515c094
--- /dev/null
+++ b/src/QQBot.Net.Core/Entities/Threads/IPost.cs
@@ -0,0 +1,42 @@
+namespace QQBot;
+
+///
+/// 表示一个通用的论坛主题评论。
+///
+public interface IPost : IEntity
+{
+ ///
+ /// 获取此主题评论所属的频道。
+ ///
+ IGuild Guild { get; }
+
+ ///
+ /// 获取此主题评论所属的论坛子频道。
+ ///
+ IForumChannel Channel { get; }
+
+ ///
+ /// 获取此主题评论的作者用户的 ID。
+ ///
+ ulong AuthorId { get; }
+
+ ///
+ /// 获取此主题评论所属的主题的 ID。
+ ///
+ string ThreadId { get; }
+
+ ///
+ /// 获取此主题的原始内容。
+ ///
+ string RawContent { get; }
+
+ ///
+ /// 获取此主题评论的富文本内容。
+ ///
+ RichText Content { get; }
+
+ ///
+ /// 获取此主题评论的创建时间。
+ ///
+ DateTimeOffset CreatedAt { get; }
+}
diff --git a/src/QQBot.Net.Core/Entities/Threads/IReply.cs b/src/QQBot.Net.Core/Entities/Threads/IReply.cs
new file mode 100644
index 0000000..4299d85
--- /dev/null
+++ b/src/QQBot.Net.Core/Entities/Threads/IReply.cs
@@ -0,0 +1,47 @@
+namespace QQBot;
+
+///
+/// 表示一个通用的论坛主题评论回复。
+///
+public interface IReply : IEntity
+{
+ ///
+ /// 获取此主题评论回复所属的频道。
+ ///
+ IGuild Guild { get; }
+
+ ///
+ /// 获取此主题评论回复所属的论坛子频道。
+ ///
+ IForumChannel Channel { get; }
+
+ ///
+ /// 获取此主题评论回复的作者用户的 ID。
+ ///
+ ulong AuthorId { get; }
+
+ ///
+ /// 获取此主题评论回复所属的主题的 ID。
+ ///
+ string ThreadId { get; }
+
+ ///
+ /// 获取此主题评论回复所属的主题评论的 ID。
+ ///
+ string PostId { get; }
+
+ ///
+ /// 获取此主题的原始内容。
+ ///
+ string RawContent { get; }
+
+ ///
+ /// 获取此主题评论回复的富文本内容。
+ ///
+ RichText Content { get; }
+
+ ///
+ /// 获取此主题评论回复的创建时间。
+ ///
+ DateTimeOffset CreatedAt { get; }
+}
diff --git a/src/QQBot.Net.Core/Entities/Threads/IThread.cs b/src/QQBot.Net.Core/Entities/Threads/IThread.cs
index 791d194..dbbffae 100644
--- a/src/QQBot.Net.Core/Entities/Threads/IThread.cs
+++ b/src/QQBot.Net.Core/Entities/Threads/IThread.cs
@@ -11,7 +11,7 @@ public interface IThread : IEntity, IUpdateable, IDeletable
IGuild Guild { get; }
///
- /// 获取次主题所属的论坛子频道。
+ /// 获取此主题所属的论坛子频道。
///
IForumChannel Channel { get; }
diff --git a/src/QQBot.Net.Rest/API/Common/PostInfo.cs b/src/QQBot.Net.Rest/API/Common/PostInfo.cs
new file mode 100644
index 0000000..e3c0323
--- /dev/null
+++ b/src/QQBot.Net.Rest/API/Common/PostInfo.cs
@@ -0,0 +1,18 @@
+using System.Text.Json.Serialization;
+
+namespace QQBot.API;
+
+internal class PostInfo
+{
+ [JsonPropertyName("thread_id")]
+ public required string ThreadId { get; init; }
+
+ [JsonPropertyName("post_id")]
+ public required string PostId { get; init; }
+
+ [JsonPropertyName("content")]
+ public required string Content { get; init; }
+
+ [JsonPropertyName("date_time")]
+ public required DateTimeOffset DateTime { get; init; }
+}
diff --git a/src/QQBot.Net.Rest/API/Common/ReplyInfo.cs b/src/QQBot.Net.Rest/API/Common/ReplyInfo.cs
new file mode 100644
index 0000000..812bf5b
--- /dev/null
+++ b/src/QQBot.Net.Rest/API/Common/ReplyInfo.cs
@@ -0,0 +1,21 @@
+using System.Text.Json.Serialization;
+
+namespace QQBot.API;
+
+internal class ReplyInfo
+{
+ [JsonPropertyName("thread_id")]
+ public required string ThreadId { get; init; }
+
+ [JsonPropertyName("post_id")]
+ public required string PostId { get; init; }
+
+ [JsonPropertyName("reply_id")]
+ public required string ReplyId { get; init; }
+
+ [JsonPropertyName("content")]
+ public required string Content { get; init; }
+
+ [JsonPropertyName("date_time")]
+ public required DateTimeOffset DateTime { get; init; }
+}
diff --git a/src/QQBot.Net.WebSocket/API/Gateway/AudioOrLiveChannelMemberEvent.cs b/src/QQBot.Net.WebSocket/API/Gateway/AudioOrLiveChannelMemberEvent.cs
index 550ebc5..e544022 100644
--- a/src/QQBot.Net.WebSocket/API/Gateway/AudioOrLiveChannelMemberEvent.cs
+++ b/src/QQBot.Net.WebSocket/API/Gateway/AudioOrLiveChannelMemberEvent.cs
@@ -15,4 +15,4 @@ internal class AudioOrLiveChannelMemberEvent
[JsonPropertyName("user_id")]
public required ulong UserId { get; init; }
-}
\ No newline at end of file
+}
diff --git a/src/QQBot.Net.WebSocket/API/Gateway/AuditType.cs b/src/QQBot.Net.WebSocket/API/Gateway/AuditType.cs
new file mode 100644
index 0000000..dbbefd1
--- /dev/null
+++ b/src/QQBot.Net.WebSocket/API/Gateway/AuditType.cs
@@ -0,0 +1,8 @@
+namespace QQBot.API.Gateway;
+
+internal enum AuditType
+{
+ Thread = 1,
+ Post = 2,
+ Reply = 3
+}
diff --git a/src/QQBot.Net.WebSocket/API/Gateway/ForumPostEvent.cs b/src/QQBot.Net.WebSocket/API/Gateway/ForumPostEvent.cs
new file mode 100644
index 0000000..2177732
--- /dev/null
+++ b/src/QQBot.Net.WebSocket/API/Gateway/ForumPostEvent.cs
@@ -0,0 +1,18 @@
+using System.Text.Json.Serialization;
+
+namespace QQBot.API.Gateway;
+
+internal class ForumPostEvent
+{
+ [JsonPropertyName("guild_id")]
+ public required ulong GuildId { get; init; }
+
+ [JsonPropertyName("channel_id")]
+ public required ulong ChannelId { get; init; }
+
+ [JsonPropertyName("author_id")]
+ public required ulong AuthorId { get; init; }
+
+ [JsonPropertyName("post_info")]
+ public required PostInfo PostInfo { get; set; }
+}
diff --git a/src/QQBot.Net.WebSocket/API/Gateway/ForumPublishAuditResultEvent.cs b/src/QQBot.Net.WebSocket/API/Gateway/ForumPublishAuditResultEvent.cs
new file mode 100644
index 0000000..648ffdf
--- /dev/null
+++ b/src/QQBot.Net.WebSocket/API/Gateway/ForumPublishAuditResultEvent.cs
@@ -0,0 +1,35 @@
+using System.Text.Json.Serialization;
+using QQBot.Net.Converters;
+
+namespace QQBot.API.Gateway;
+
+internal class ForumPublishAuditResultEvent
+{
+ [JsonPropertyName("guild_id")]
+ public required ulong GuildId { get; init; }
+
+ [JsonPropertyName("channel_id")]
+ public required ulong ChannelId { get; init; }
+
+ [JsonPropertyName("author_id")]
+ public required ulong AuthorId { get; init; }
+
+ [JsonPropertyName("thread_id")]
+ public required string? ThreadId { get; init; }
+
+ [JsonPropertyName("post_id")]
+ public required string? PostId { get; init; }
+
+ [JsonPropertyName("reply_id")]
+ public required string? ReplyId { get; init; }
+
+ [JsonPropertyName("type")]
+ public AuditType AuditType { get; init; }
+
+ [JsonPropertyName("result")]
+ [NumberBooleanConverter]
+ public bool Failed { get; init; }
+
+ [JsonPropertyName("err_msg")]
+ public string? ErrorMessage { get; init; }
+}
diff --git a/src/QQBot.Net.WebSocket/API/Gateway/ForumReplyEvent.cs b/src/QQBot.Net.WebSocket/API/Gateway/ForumReplyEvent.cs
new file mode 100644
index 0000000..32a7baf
--- /dev/null
+++ b/src/QQBot.Net.WebSocket/API/Gateway/ForumReplyEvent.cs
@@ -0,0 +1,18 @@
+using System.Text.Json.Serialization;
+
+namespace QQBot.API.Gateway;
+
+internal class ForumReplyEvent
+{
+ [JsonPropertyName("guild_id")]
+ public required ulong GuildId { get; init; }
+
+ [JsonPropertyName("channel_id")]
+ public required ulong ChannelId { get; init; }
+
+ [JsonPropertyName("author_id")]
+ public required ulong AuthorId { get; init; }
+
+ [JsonPropertyName("reply_info")]
+ public required ReplyInfo ReplyInfo { get; set; }
+}
diff --git a/src/QQBot.Net.WebSocket/API/Gateway/ForumThreadEvent.cs b/src/QQBot.Net.WebSocket/API/Gateway/ForumThreadEvent.cs
new file mode 100644
index 0000000..3439184
--- /dev/null
+++ b/src/QQBot.Net.WebSocket/API/Gateway/ForumThreadEvent.cs
@@ -0,0 +1,18 @@
+using System.Text.Json.Serialization;
+
+namespace QQBot.API.Gateway;
+
+internal class ForumThreadEvent
+{
+ [JsonPropertyName("guild_id")]
+ public required ulong GuildId { get; init; }
+
+ [JsonPropertyName("channel_id")]
+ public required ulong ChannelId { get; init; }
+
+ [JsonPropertyName("author_id")]
+ public required ulong AuthorId { get; init; }
+
+ [JsonPropertyName("thread_info")]
+ public required ThreadInfo ThreadInfo { get; set; }
+}
diff --git a/src/QQBot.Net.WebSocket/API/Gateway/GroupBotEvent.cs b/src/QQBot.Net.WebSocket/API/Gateway/GroupBotEvent.cs
new file mode 100644
index 0000000..0a85f97
--- /dev/null
+++ b/src/QQBot.Net.WebSocket/API/Gateway/GroupBotEvent.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+
+namespace QQBot.API.Gateway;
+
+internal class GroupBotEvent
+{
+ [JsonPropertyName("group_openid")]
+ public required Guid GroupOpenid { get; init; }
+
+ [JsonPropertyName("op_member_openid")]
+ public required string OpMemberOpenId { get; init; }
+
+ [JsonPropertyName("timestamp")]
+ public required int Timestamp { get; init; }
+}
diff --git a/src/QQBot.Net.WebSocket/API/Gateway/UserBotEvent.cs b/src/QQBot.Net.WebSocket/API/Gateway/UserBotEvent.cs
new file mode 100644
index 0000000..c7b38be
--- /dev/null
+++ b/src/QQBot.Net.WebSocket/API/Gateway/UserBotEvent.cs
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+
+namespace QQBot.API.Gateway;
+
+internal class UserBotEvent
+{
+ [JsonPropertyName("openid")]
+ public required Guid OpenId { get; init; }
+
+ [JsonPropertyName("timestamp")]
+ public required int Timestamp { get; init; }
+}
diff --git a/src/QQBot.Net.WebSocket/Entities/Channels/SocketUserChannel.cs b/src/QQBot.Net.WebSocket/Entities/Channels/SocketUserChannel.cs
index c3be592..2dfb7c0 100644
--- a/src/QQBot.Net.WebSocket/Entities/Channels/SocketUserChannel.cs
+++ b/src/QQBot.Net.WebSocket/Entities/Channels/SocketUserChannel.cs
@@ -13,20 +13,20 @@ public class SocketUserChannel : SocketChannel, IUserChannel, ISocketPrivateChan
public new Guid Id { get; }
///
- public SocketUser Recipient { get; }
+ public SocketUser? Recipient { get; }
///
public IReadOnlyCollection CachedMessages => [];
///
- internal SocketUserChannel(QQBotSocketClient client, Guid id, SocketUser recipient)
+ internal SocketUserChannel(QQBotSocketClient client, Guid id, SocketUser? recipient)
: base(client, id.ToIdString())
{
Id = id;
Recipient = recipient;
}
- internal static SocketUserChannel Create(QQBotSocketClient client, ClientState state, Guid id, SocketUser recipient)
+ internal static SocketUserChannel Create(QQBotSocketClient client, ClientState state, Guid id, SocketUser? recipient)
{
SocketUserChannel channel = new(client, id, recipient);
return channel;
@@ -51,7 +51,7 @@ public Task SendMessageAsync(string? content = null, IMarkdown? ma
///
protected override SocketUser? GetUserInternal(string id)
{
- if (id == Recipient.Id) return Recipient;
+ if (id == Recipient?.Id) return Recipient;
return id == Client.CurrentUser?.Id.ToIdString() ? Client.CurrentUser : null;
}
@@ -60,14 +60,14 @@ public Task SendMessageAsync(string? content = null, IMarkdown? ma
#region ISocketPrivateChannel
///
- IReadOnlyCollection ISocketPrivateChannel.Recipients => [Recipient];
+ IReadOnlyCollection ISocketPrivateChannel.Recipients => Recipient is not null ? [Recipient] : [];
#endregion
#region IPrivateChannel
///
- IReadOnlyCollection IPrivateChannel.Recipients => [Recipient];
+ IReadOnlyCollection IPrivateChannel.Recipients => Recipient is not null ? [Recipient] : [];
#endregion
diff --git a/src/QQBot.Net.WebSocket/Entities/Threads/SocketPost.cs b/src/QQBot.Net.WebSocket/Entities/Threads/SocketPost.cs
new file mode 100644
index 0000000..371f566
--- /dev/null
+++ b/src/QQBot.Net.WebSocket/Entities/Threads/SocketPost.cs
@@ -0,0 +1,72 @@
+using System.Diagnostics;
+using QQBot.API;
+using QQBot.Rest;
+
+namespace QQBot.WebSocket;
+
+///
+/// 表示一个基于网关的论坛主题评论。
+///
+[DebuggerDisplay("{DebuggerDisplay,nq}")]
+public class SocketPost : SocketEntity, IPost
+{
+ ///
+ public SocketGuild Guild { get; }
+
+ ///
+ public SocketForumChannel Channel { get; }
+
+ ///
+ public ulong AuthorId { get; }
+
+ ///
+ public string ThreadId { get; }
+
+ ///
+ public string RawContent { get; private set; }
+
+ ///
+ public RichText Content { get; private set; }
+
+ ///
+ public DateTimeOffset CreatedAt { get; private set; }
+
+ ///
+ private SocketPost(QQBotSocketClient client, string id, string threadId, SocketForumChannel channel, ulong authorId)
+ : base(client, id)
+ {
+ Guild = channel.Guild;
+ Channel = channel;
+ AuthorId = authorId;
+ ThreadId = threadId;
+ RawContent = string.Empty;
+ Content = RichText.Empty;
+ CreatedAt = DateTimeOffset.Now;
+ }
+
+ internal static SocketPost Create(QQBotSocketClient client,
+ SocketForumChannel channel, ulong authorId, API.PostInfo model)
+ {
+ SocketPost post = new(client, model.PostId, model.ThreadId, channel, authorId);
+ post.Update(model);
+ return post;
+ }
+
+ private void Update(API.PostInfo model)
+ {
+ RawContent = model.Content;
+ Content = ForumHelper.ParseContent(model.Content);
+ CreatedAt = model.DateTime;
+ }
+
+ private string DebuggerDisplay => $"{Content} ({Id}, {Content.DebuggerDisplay})";
+
+ ///
+ public override string ToString() => RawContent;
+
+ ///
+ IGuild IPost.Guild => Guild;
+
+ ///
+ IForumChannel IPost.Channel => Channel;
+}
diff --git a/src/QQBot.Net.WebSocket/Entities/Threads/SocketReply.cs b/src/QQBot.Net.WebSocket/Entities/Threads/SocketReply.cs
new file mode 100644
index 0000000..804ad52
--- /dev/null
+++ b/src/QQBot.Net.WebSocket/Entities/Threads/SocketReply.cs
@@ -0,0 +1,106 @@
+using System.Diagnostics;
+using QQBot.Rest;
+
+namespace QQBot.WebSocket;
+
+///
+/// 表示一个基于网关的论坛主题评论回复。
+///
+[DebuggerDisplay("{DebuggerDisplay,nq}")]
+public class SocketReply : SocketEntity, IReply
+{
+ ///
+ public SocketGuild Guild { get; }
+
+ ///
+ public SocketForumChannel Channel { get; }
+
+ ///
+ public ulong AuthorId { get; }
+
+ ///
+ public string ThreadId { get; }
+
+ ///
+ public string PostId { get; }
+
+ ///
+ public string RawContent { get; private set; }
+
+ ///
+ public RichText Content { get; private set; }
+
+ ///
+ public DateTimeOffset CreatedAt { get; private set; }
+
+ ///
+ private SocketReply(QQBotSocketClient client, string id, string threadId, string postId, SocketForumChannel channel, ulong authorId)
+ : base(client, id)
+ {
+ Guild = channel.Guild;
+ Channel = channel;
+ AuthorId = authorId;
+ ThreadId = threadId;
+ PostId = postId;
+ RawContent = string.Empty;
+ Content = RichText.Empty;
+ CreatedAt = DateTimeOffset.Now;
+ }
+
+ internal static SocketReply Create(QQBotSocketClient client,
+ SocketForumChannel channel, ulong authorId, API.ReplyInfo model)
+ {
+ SocketReply Reply = new(client, model.ReplyId, model.ThreadId, model.PostId, channel, authorId);
+ Reply.Update(model);
+ return Reply;
+ }
+
+ private void Update(API.ReplyInfo model)
+ {
+ RawContent = model.Content;
+ Content = ForumHelper.ParseContent(model.Content);
+ CreatedAt = model.DateTime;
+ }
+
+ private string DebuggerDisplay => $"{Content} ({Id}, {Content.DebuggerDisplay})";
+
+ ///
+ public override string ToString() => RawContent;
+
+ ///
+ IGuild IReply.Guild => Guild;
+
+ ///
+ IForumChannel IReply.Channel => Channel;
+}
+
+// internal class ForumPublishAuditResultEvent
+// {
+// [JsonPropertyName("guild_id")]
+// public required ulong GuildId { get; init; }
+//
+// [JsonPropertyName("channel_id")]
+// public required ulong ChannelId { get; init; }
+//
+// [JsonPropertyName("author_id")]
+// public required ulong AuthorId { get; init; }
+//
+// [JsonPropertyName("thread_id")]
+// public required string? ThreadId { get; init; }
+//
+// [JsonPropertyName("post_id")]
+// public required string? PostId { get; init; }
+//
+// [JsonPropertyName("reply_id")]
+// public required string? ReplyId { get; init; }
+//
+// [JsonPropertyName("type")]
+// public AuditType AuditType { get; init; }
+//
+// [JsonPropertyName("result")]
+// [NumberBooleanConverter]
+// public int Failed { get; init; }
+//
+// [JsonPropertyName("err_msg")]
+// public string? ErrorMessage { get; init; }
+// }
diff --git a/src/QQBot.Net.WebSocket/Entities/Threads/SocketThread.cs b/src/QQBot.Net.WebSocket/Entities/Threads/SocketThread.cs
new file mode 100644
index 0000000..96582a6
--- /dev/null
+++ b/src/QQBot.Net.WebSocket/Entities/Threads/SocketThread.cs
@@ -0,0 +1,83 @@
+using System.Diagnostics;
+using QQBot.Rest;
+
+namespace QQBot.WebSocket;
+
+///
+/// 表示一个基于网关的论坛主题。
+///
+[DebuggerDisplay("{DebuggerDisplay,nq}")]
+public class SocketThread : SocketEntity, IThread
+{
+ ///
+ public SocketGuild Guild { get; }
+
+ ///
+ public SocketForumChannel Channel { get; }
+
+ ///
+ public ulong AuthorId { get; }
+
+ ///
+ public string Title { get; private set; }
+
+ ///
+ public string RawContent { get; private set; }
+
+ ///
+ public RichText Content { get; private set; }
+
+ ///
+ public DateTimeOffset CreatedAt { get; private set; }
+
+ ///
+ private SocketThread(QQBotSocketClient client, string id, SocketForumChannel channel, ulong authorId)
+ : base(client, id)
+ {
+ Guild = channel.Guild;
+ Channel = channel;
+ AuthorId = authorId;
+ Title = string.Empty;
+ RawContent = string.Empty;
+ Content = RichText.Empty;
+ CreatedAt = DateTimeOffset.Now;
+ }
+
+ internal static SocketThread Create(QQBotSocketClient client,
+ SocketForumChannel channel, ulong authorId, API.ThreadInfo model)
+ {
+ SocketThread thread = new(client, model.ThreadId, channel, authorId);
+ thread.Update(model);
+ return thread;
+ }
+
+ internal void Update(API.ThreadInfo model)
+ {
+ Title = model.Title;
+ RawContent = model.Content;
+ Content = ForumHelper.ParseContent(model.Content);
+ CreatedAt = model.DateTime;
+ }
+
+ ///
+ public async Task UpdateAsync(RequestOptions? options = null)
+ {
+ API.Thread model = await ChannelHelper.GetThreadAsync(Channel, Client, Id, options).ConfigureAwait(false);
+ Update(model.ThreadInfo);
+ }
+
+ ///
+ public Task DeleteAsync(RequestOptions? options = null) =>
+ ChannelHelper.DeleteThreadAsync(Channel, Client, Id, options);
+
+ private string DebuggerDisplay => $"{Title} ({Id}, {Content.DebuggerDisplay})";
+
+ ///
+ public override string ToString() => RawContent;
+
+ ///
+ IGuild IThread.Guild => Guild;
+
+ ///
+ IForumChannel IThread.Channel => Channel;
+}
diff --git a/src/QQBot.Net.WebSocket/QQBot.Net.WebSocket.csproj.DotSettings b/src/QQBot.Net.WebSocket/QQBot.Net.WebSocket.csproj.DotSettings
index 656aaac..9e6838e 100644
--- a/src/QQBot.Net.WebSocket/QQBot.Net.WebSocket.csproj.DotSettings
+++ b/src/QQBot.Net.WebSocket/QQBot.Net.WebSocket.csproj.DotSettings
@@ -4,4 +4,5 @@
True
True
True
+ True
True
\ No newline at end of file
diff --git a/src/QQBot.Net.WebSocket/QQBotSocketClient.Events.cs b/src/QQBot.Net.WebSocket/QQBotSocketClient.Events.cs
index 22c4778..f136a01 100644
--- a/src/QQBot.Net.WebSocket/QQBotSocketClient.Events.cs
+++ b/src/QQBot.Net.WebSocket/QQBotSocketClient.Events.cs
@@ -293,4 +293,294 @@ public event Func, SocketGuildChannel, Task>
#endregion
+ #region Forums
+
+ ///
+ /// 当论坛主题被创建时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是新创建的论坛主题。
+ ///
+ ///
+ public event Func ForumThreadCreated
+ {
+ add => _forumThreadCreatedEvent.Add(value);
+ remove => _forumThreadCreatedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent> _forumThreadCreatedEvent = new();
+
+ ///
+ /// 当论坛主题被修改时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是修改后的论坛主题。
+ ///
+ ///
+ public event Func ForumThreadUpdated
+ {
+ add => _forumThreadUpdatedEvent.Add(value);
+ remove => _forumThreadUpdatedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent> _forumThreadUpdatedEvent = new();
+
+ ///
+ /// 当论坛主题被删除时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是被删除的论坛主题。
+ ///
+ ///
+ public event Func ForumThreadDeleted
+ {
+ add => _forumThreadDeletedEvent.Add(value);
+ remove => _forumThreadDeletedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent> _forumThreadDeletedEvent = new();
+
+ ///
+ /// 当论坛主题评论被创建时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是新创建的论坛主题评论。
+ ///
+ ///
+ public event Func ForumPostCreated
+ {
+ add => _forumPostCreatedEvent.Add(value);
+ remove => _forumPostCreatedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent> _forumPostCreatedEvent = new();
+
+ ///
+ /// 当论坛主题评论被删除时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是被删除的论坛主题评论。
+ ///
+ ///
+ public event Func ForumPostDeleted
+ {
+ add => _forumPostDeletedEvent.Add(value);
+ remove => _forumPostDeletedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent> _forumPostDeletedEvent = new();
+
+ ///
+ /// 当论坛主题评论回复被创建时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是新创建的论坛主题评论回复。
+ ///
+ ///
+ public event Func ForumReplyCreated
+ {
+ add => _forumReplyCreatedEvent.Add(value);
+ remove => _forumReplyCreatedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent> _forumReplyCreatedEvent = new();
+
+ ///
+ /// 当论坛主题评论回复被删除时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是被删除的论坛主题评论回复。
+ ///
+ ///
+ public event Func ForumReplyDeleted
+ {
+ add => _forumReplyDeletedEvent.Add(value);
+ remove => _forumReplyDeletedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent> _forumReplyDeletedEvent = new();
+
+ #endregion
+
+ #region Groups
+
+ ///
+ /// 当群组添加当前用户时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是添加当前用户的群组。
+ /// -
+ /// 参数是添加当前用户的群组的用户。如果缓存中存在此用户实体,那么该结构内包含该
+ /// 群组用户;否则,包含 用户 ID。
+ /// 如果网关没有提供实体的详细信息,由于目前无法通过 API 获取此用户实体,因此
+ /// 总会返回 null。
+ ///
+ ///
+ ///
+ public event Func, Task> JoinedGroup
+ {
+ add => _joinedGroupEvent.Add(value);
+ remove => _joinedGroupEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent, Task>> _joinedGroupEvent = new();
+
+ ///
+ /// 当群组移除当前用户时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是移除当前用户的群组。
+ /// -
+ /// 参数是添加当前用户的群组的用户。如果缓存中存在此用户实体,那么该结构内包含该
+ /// 群组用户;否则,包含 用户 ID。
+ /// 如果网关没有提供实体的详细信息,由于目前无法通过 API 获取此用户实体,因此
+ /// 总会返回 null。
+ ///
+ ///
+ ///
+ public event Func, Task> LeftGroup
+ {
+ add => _leftGroupEvent.Add(value);
+ remove => _leftGroupEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent, Task>> _leftGroupEvent = new();
+
+ ///
+ /// 当群组接受当前用户的主动消息时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是接受当前用户主动消息的群组。
+ /// -
+ /// 参数是添加当前用户的群组的用户。如果缓存中存在此用户实体,那么该结构内包含该
+ /// 群组用户;否则,包含 用户 ID。
+ /// 如果网关没有提供实体的详细信息,由于目前无法通过 API 获取此用户实体,因此
+ /// 总会返回 null。
+ ///
+ ///
+ ///
+ public event Func, Task> GroupActiveMessageAllowed
+ {
+ add => _groupActiveMessageAllowedEvent.Add(value);
+ remove => _groupActiveMessageAllowedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent, Task>> _groupActiveMessageAllowedEvent = new();
+
+ ///
+ /// 当群组接受当前用户的主动消息时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是接受当前用户主动消息的群组。
+ /// -
+ /// 参数是添加当前用户的群组的用户。如果缓存中存在此用户实体,那么该结构内包含该
+ /// 群组用户;否则,包含 用户 ID。
+ /// 如果网关没有提供实体的详细信息,由于目前无法通过 API 获取此用户实体,因此
+ /// 总会返回 null。
+ ///
+ ///
+ ///
+ public event Func, Task> GroupActiveMessageRejected
+ {
+ add => _groupActiveMessageRejectedEvent.Add(value);
+ remove => _groupActiveMessageRejectedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent, Task>> _groupActiveMessageRejectedEvent = new();
+
+ #endregion
+
+ #region Users
+
+ ///
+ /// 当用户添加当前用户时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是添加当前用户的用户频道。
+ ///
+ ///
+ public event Func UserAdded
+ {
+ add => _userAddedEvent.Add(value);
+ remove => _userAddedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent> _userAddedEvent = new();
+
+ ///
+ /// 当用户移除当前用户时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是移除当前用户的用户频道。
+ ///
+ ///
+ public event Func UserRemoved
+ {
+ add => _userRemovedEvent.Add(value);
+ remove => _userRemovedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent> _userRemovedEvent = new();
+
+ ///
+ /// 当用户接受当前用户的主动消息时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是接受当前用户主动消息的用户。
+ ///
+ ///
+ public event Func UserActiveMessageAllowed
+ {
+ add => _userActiveMessageAllowedEvent.Add(value);
+ remove => _userActiveMessageAllowedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent> _userActiveMessageAllowedEvent = new();
+
+ ///
+ /// 当用户接受当前用户的主动消息时引发。
+ ///
+ ///
+ /// 事件参数:
+ ///
+ /// - 参数是接受当前用户主动消息的用户。
+ ///
+ ///
+ public event Func UserActiveMessageRejected
+ {
+ add => _userActiveMessageRejectedEvent.Add(value);
+ remove => _userActiveMessageRejectedEvent.Remove(value);
+ }
+
+ internal readonly AsyncEvent> _userActiveMessageRejectedEvent = new();
+
+ #endregion
}
diff --git a/src/QQBot.Net.WebSocket/QQBotSocketClient.Messages.cs b/src/QQBot.Net.WebSocket/QQBotSocketClient.Messages.cs
index 9990a9b..d55cd7b 100644
--- a/src/QQBot.Net.WebSocket/QQBotSocketClient.Messages.cs
+++ b/src/QQBot.Net.WebSocket/QQBotSocketClient.Messages.cs
@@ -492,6 +492,229 @@ private async Task HandleAudioOrLiveChannelMemberExitAsync(object? payload)
#endregion
+ #region Forums
+
+ private async Task HandleForumThreadCreatedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetGuild(data.GuildId) is not { } guild)
+ {
+ await UnknownGuildAsync(nameof(ForumThreadCreated), data.GuildId, payload).ConfigureAwait(false);
+ return;
+ }
+ if (guild.GetForumChannel(data.ChannelId) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(ForumThreadCreated), data.ChannelId, payload).ConfigureAwait(false);
+ return;
+ }
+ SocketThread thread = SocketThread.Create(this, channel, data.AuthorId, data.ThreadInfo);
+ await TimedInvokeAsync(_forumThreadCreatedEvent, nameof(ForumThreadCreated), thread).ConfigureAwait(false);
+ }
+
+ private async Task HandleForumThreadUpdatedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetGuild(data.GuildId) is not { } guild)
+ {
+ await UnknownGuildAsync(nameof(ForumThreadUpdated), data.GuildId, payload).ConfigureAwait(false);
+ return;
+ }
+ if (guild.GetForumChannel(data.ChannelId) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(ForumThreadUpdated), data.ChannelId, payload).ConfigureAwait(false);
+ return;
+ }
+ SocketThread thread = SocketThread.Create(this, channel, data.AuthorId, data.ThreadInfo);
+ await TimedInvokeAsync(_forumThreadUpdatedEvent, nameof(ForumThreadUpdated), thread).ConfigureAwait(false);
+ }
+
+ private async Task HandleForumThreadDeletedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetGuild(data.GuildId) is not { } guild)
+ {
+ await UnknownGuildAsync(nameof(ForumThreadDeleted), data.GuildId, payload).ConfigureAwait(false);
+ return;
+ }
+ if (guild.GetForumChannel(data.ChannelId) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(ForumThreadDeleted), data.ChannelId, payload).ConfigureAwait(false);
+ return;
+ }
+ SocketThread thread = SocketThread.Create(this, channel, data.AuthorId, data.ThreadInfo);
+ await TimedInvokeAsync(_forumThreadDeletedEvent, nameof(ForumThreadDeleted), thread).ConfigureAwait(false);
+ }
+
+ private async Task HandleForumPostCreatedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetGuild(data.GuildId) is not { } guild)
+ {
+ await UnknownGuildAsync(nameof(ForumPostCreated), data.GuildId, payload).ConfigureAwait(false);
+ return;
+ }
+ if (guild.GetForumChannel(data.ChannelId) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(ForumPostCreated), data.ChannelId, payload).ConfigureAwait(false);
+ return;
+ }
+ SocketPost post = SocketPost.Create(this, channel, data.AuthorId, data.PostInfo);
+ await TimedInvokeAsync(_forumPostCreatedEvent, nameof(ForumPostCreated), post).ConfigureAwait(false);
+ }
+
+ private async Task HandleForumPostDeletedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetGuild(data.GuildId) is not { } guild)
+ {
+ await UnknownGuildAsync(nameof(ForumPostDeleted), data.GuildId, payload).ConfigureAwait(false);
+ return;
+ }
+ if (guild.GetForumChannel(data.ChannelId) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(ForumPostDeleted), data.ChannelId, payload).ConfigureAwait(false);
+ return;
+ }
+ SocketPost post = SocketPost.Create(this, channel, data.AuthorId, data.PostInfo);
+ await TimedInvokeAsync(_forumPostDeletedEvent, nameof(ForumPostDeleted), post).ConfigureAwait(false);
+ }
+
+ private async Task HandleForumReplyCreatedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetGuild(data.GuildId) is not { } guild)
+ {
+ await UnknownGuildAsync(nameof(ForumReplyCreated), data.GuildId, payload).ConfigureAwait(false);
+ return;
+ }
+ if (guild.GetForumChannel(data.ChannelId) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(ForumReplyCreated), data.ChannelId, payload).ConfigureAwait(false);
+ return;
+ }
+ SocketReply reply = SocketReply.Create(this, channel, data.AuthorId, data.ReplyInfo);
+ await TimedInvokeAsync(_forumReplyCreatedEvent, nameof(ForumReplyCreated), reply).ConfigureAwait(false);
+ }
+
+ private async Task HandleForumReplyDeletedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetGuild(data.GuildId) is not { } guild)
+ {
+ await UnknownGuildAsync(nameof(ForumReplyDeleted), data.GuildId, payload).ConfigureAwait(false);
+ return;
+ }
+ if (guild.GetForumChannel(data.ChannelId) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(ForumReplyDeleted), data.ChannelId, payload).ConfigureAwait(false);
+ return;
+ }
+ SocketReply reply = SocketReply.Create(this, channel, data.AuthorId, data.ReplyInfo);
+ await TimedInvokeAsync(_forumReplyDeletedEvent, nameof(ForumReplyDeleted), reply).ConfigureAwait(false);
+ }
+
+ #endregion
+
+ #region Groups
+
+ private async Task HandleGroupRobotAddedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetOrCreateGroupChannel(State, data.GroupOpenid) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(JoinedGroup), data.GroupOpenid, payload).ConfigureAwait(false);
+ return;
+ }
+ Cacheable user = new(null, data.OpMemberOpenId, false, () => Task.FromResult(null));
+ await TimedInvokeAsync(_joinedGroupEvent, nameof(JoinedGroup), channel, user).ConfigureAwait(false);
+ }
+
+ private async Task HandleGroupRobotRemovedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetOrCreateGroupChannel(State, data.GroupOpenid) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(LeftGroup), data.GroupOpenid, payload).ConfigureAwait(false);
+ return;
+ }
+ Cacheable user = new(null, data.OpMemberOpenId, false, () => Task.FromResult(null));
+ await TimedInvokeAsync(_leftGroupEvent, nameof(LeftGroup), channel, user).ConfigureAwait(false);
+ }
+
+ private async Task HandleGroupMessageRejectedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetOrCreateGroupChannel(State, data.GroupOpenid) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(GroupActiveMessageRejected), data.GroupOpenid, payload).ConfigureAwait(false);
+ return;
+ }
+ Cacheable user = new(null, data.OpMemberOpenId, false, () => Task.FromResult(null));
+ await TimedInvokeAsync(_groupActiveMessageRejectedEvent, nameof(GroupActiveMessageRejected), channel, user).ConfigureAwait(false);
+ }
+
+ private async Task HandleGroupMessageReceivedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetOrCreateGroupChannel(State, data.GroupOpenid) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(GroupActiveMessageAllowed), data.GroupOpenid, payload).ConfigureAwait(false);
+ return;
+ }
+ Cacheable user = new(null, data.OpMemberOpenId, false, () => Task.FromResult(null));
+ await TimedInvokeAsync(_groupActiveMessageAllowedEvent, nameof(GroupActiveMessageAllowed), channel, user).ConfigureAwait(false);
+ }
+
+ #endregion
+
+ #region Users
+
+ private async Task HandleFriendAddedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetOrCreateUserChannel(State, data.OpenId) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(UserAdded), data.OpenId, payload).ConfigureAwait(false);
+ return;
+ }
+ await TimedInvokeAsync(_userAddedEvent, nameof(UserAdded), channel).ConfigureAwait(false);
+ }
+
+ private async Task HandleFriendRemovedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetOrCreateUserChannel(State, data.OpenId) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(UserRemoved), data.OpenId, payload).ConfigureAwait(false);
+ return;
+ }
+ await TimedInvokeAsync(_userRemovedEvent, nameof(UserRemoved), channel).ConfigureAwait(false);
+ }
+
+ private async Task HandleUserMessageRejectedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetOrCreateUserChannel(State, data.OpenId) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(UserRemoved), data.OpenId, payload).ConfigureAwait(false);
+ return;
+ }
+ await TimedInvokeAsync(_userActiveMessageRejectedEvent, nameof(UserActiveMessageRejected), channel).ConfigureAwait(false);
+ }
+
+ private async Task HandleUserMessageReceivedAsync(object? payload)
+ {
+ if (DeserializePayload(payload) is not { } data) return;
+ if (GetOrCreateUserChannel(State, data.OpenId) is not { } channel)
+ {
+ await UnknownChannelAsync(nameof(UserRemoved), data.OpenId, payload).ConfigureAwait(false);
+ return;
+ }
+ await TimedInvokeAsync(_userActiveMessageAllowedEvent, nameof(UserActiveMessageAllowed), channel).ConfigureAwait(false);
+ }
+
+ #endregion
+
#region Raising Events
private async Task GuildAvailableAsync(SocketGuild guild)
@@ -501,7 +724,7 @@ private async Task GuildAvailableAsync(SocketGuild guild)
await TimedInvokeAsync(_guildAvailableEvent, nameof(GuildAvailable), guild).ConfigureAwait(false);
}
- internal async Task GuildUnavailableAsync(SocketGuild guild)
+ private async Task GuildUnavailableAsync(SocketGuild guild)
{
if (!guild.IsConnected) return;
guild.IsConnected = false;
@@ -514,6 +737,9 @@ private async Task UnknownGuildAsync(string dispatch, ulong guildId, object? pay
private async Task UnknownChannelAsync(string dispatch, ulong channelId, object? payload) =>
await LogGatewayErrorAsync(dispatch, $"Unknown ChannelId: {channelId}.", payload).ConfigureAwait(false);
+ private async Task UnknownChannelAsync(string dispatch, Guid channelId, object? payload) =>
+ await LogGatewayErrorAsync(dispatch, $"Unknown Group ChannelId: {channelId.ToIdString()}.", payload).ConfigureAwait(false);
+
private async Task UnknownUserAsync(string dispatch, object? payload) =>
await LogGatewayErrorAsync(dispatch, $"No User in payload.", payload).ConfigureAwait(false);
diff --git a/src/QQBot.Net.WebSocket/QQBotSocketClient.cs b/src/QQBot.Net.WebSocket/QQBotSocketClient.cs
index bc5de3f..64919e8 100644
--- a/src/QQBot.Net.WebSocket/QQBotSocketClient.cs
+++ b/src/QQBot.Net.WebSocket/QQBotSocketClient.cs
@@ -4,7 +4,6 @@
using System.Text.Json.Serialization;
using QQBot.API;
using QQBot.API.Gateway;
-using QQBot.API.Rest;
using QQBot.Logging;
using QQBot.Net.Queue;
using QQBot.Net.WebSockets;
@@ -273,7 +272,7 @@ internal SocketUserChannel AddUserChannel(ClientState state, Guid id, SocketUser
return channel;
}
- internal SocketUserChannel GetOrCreateUserChannel(ClientState state, Guid id, SocketUser recipient) =>
+ internal SocketUserChannel GetOrCreateUserChannel(ClientState state, Guid id, SocketUser? recipient = null) =>
state.GetOrAddUserChannel(id, _ => SocketUserChannel.Create(this, state, id, recipient));
internal SocketDMChannel GetOrCreateDMChannel(ClientState state, ulong id, SocketGuildUser recipient) =>
@@ -615,33 +614,33 @@ internal async Task ProcessGatewayEventAsync(int sequence, string type, object p
#endregion
- // #region Forums
- //
- // case "FORUM_THREAD_CREATE":
- // await HandleForumThreadCreatedAsync(payload).ConfigureAwait(false);
- // break;
- // case "FORUM_THREAD_UPDATE":
- // await HandleForumThreadUpdatedAsync(payload).ConfigureAwait(false);
- // break;
- // case "FORUM_THREAD_DELETE":
- // await HandleForumThreadDeletedAsync(payload).ConfigureAwait(false);
- // break;
- // case "FORUM_POST_CREATE":
- // await HandleForumPostCreatedAsync(payload).ConfigureAwait(false);
- // break;
- // case "FORUM_POST_DELETE":
- // await HandleForumPostDeletedAsync(payload).ConfigureAwait(false);
- // break;
- // case "FORUM_REPLY_CREATE":
- // await HandleForumReplyCreatedAsync(payload).ConfigureAwait(false);
- // break;
- // case "FORUM_REPLY_DELETE":
- // await HandleForumReplyDeletedAsync(payload).ConfigureAwait(false);
- // break;
+ #region Forums
+
+ case "FORUM_THREAD_CREATE":
+ await HandleForumThreadCreatedAsync(payload).ConfigureAwait(false);
+ break;
+ case "FORUM_THREAD_UPDATE":
+ await HandleForumThreadUpdatedAsync(payload).ConfigureAwait(false);
+ break;
+ case "FORUM_THREAD_DELETE":
+ await HandleForumThreadDeletedAsync(payload).ConfigureAwait(false);
+ break;
+ case "FORUM_POST_CREATE":
+ await HandleForumPostCreatedAsync(payload).ConfigureAwait(false);
+ break;
+ case "FORUM_POST_DELETE":
+ await HandleForumPostDeletedAsync(payload).ConfigureAwait(false);
+ break;
+ case "FORUM_REPLY_CREATE":
+ await HandleForumReplyCreatedAsync(payload).ConfigureAwait(false);
+ break;
+ case "FORUM_REPLY_DELETE":
+ await HandleForumReplyDeletedAsync(payload).ConfigureAwait(false);
+ break;
// case "FORUM_PUBLISH_AUDIT_RESULT":
// await HandleForumPublishAuditResultAsync(payload).ConfigureAwait(false);
// break;
- //
+
// case "OPEN_FORUM_THREAD_CREATE":
// await HandleOpenForumThreadCreatedAsync(payload).ConfigureAwait(false);
// break;
@@ -663,37 +662,42 @@ internal async Task ProcessGatewayEventAsync(int sequence, string type, object p
// case "OPEN_FORUM_REPLY_DELETE":
// await HandleOpenForumReplyDeletedAsync(payload).ConfigureAwait(false);
// break;
- //
- // #endregion
- //
- // #region Groups
- //
- // case "GROUP_ADD_ROBOT":
- // await HandleGroupRobotAddedAsync(payload).ConfigureAwait(false);
- // break;
- // case "GROUP_DEL_ROBOT":
- // await HandleGroupRobotRemovedAsync(payload).ConfigureAwait(false);
- // break;
- // case "GROUP_MSG_REJECT":
- // await HandleGroupMessageRejectedAsync(payload).ConfigureAwait(false);
- // break;
- // case "GROUP_MSG_RECEIVE":
- // await HandleGroupMessageReceivedAsync(payload).ConfigureAwait(false);
- // break;
- // case "FRIEND_ADD":
- // await HandleFriendAddedAsync(payload).ConfigureAwait(false);
- // break;
- // case "FRIEND_DEL":
- // await HandleFriendRemovedAsync(payload).ConfigureAwait(false);
- // break;
- // case "C2C_MSG_REJECT":
- // await HandleUserMessageRejectedAsync(payload).ConfigureAwait(false);
- // break;
- // case "C2C_MSG_RECEIVE":
- // await HandleUserMessageReceivedAsync(payload).ConfigureAwait(false);
- // break;
- //
- // #endregion
+
+ #endregion
+
+ #region Groups
+
+ case "GROUP_ADD_ROBOT":
+ await HandleGroupRobotAddedAsync(payload).ConfigureAwait(false);
+ break;
+ case "GROUP_DEL_ROBOT":
+ await HandleGroupRobotRemovedAsync(payload).ConfigureAwait(false);
+ break;
+ case "GROUP_MSG_REJECT":
+ await HandleGroupMessageRejectedAsync(payload).ConfigureAwait(false);
+ break;
+ case "GROUP_MSG_RECEIVE":
+ await HandleGroupMessageReceivedAsync(payload).ConfigureAwait(false);
+ break;
+
+ #endregion
+
+ #region Users
+
+ case "FRIEND_ADD":
+ await HandleFriendAddedAsync(payload).ConfigureAwait(false);
+ break;
+ case "FRIEND_DEL":
+ await HandleFriendRemovedAsync(payload).ConfigureAwait(false);
+ break;
+ case "C2C_MSG_REJECT":
+ await HandleUserMessageRejectedAsync(payload).ConfigureAwait(false);
+ break;
+ case "C2C_MSG_RECEIVE":
+ await HandleUserMessageReceivedAsync(payload).ConfigureAwait(false);
+ break;
+
+ #endregion
default:
if (!SuppressUnknownDispatchWarnings)