diff --git a/.gitignore b/.gitignore
index 0bfbdf65..46529603 100644
--- a/.gitignore
+++ b/.gitignore
@@ -157,3 +157,4 @@ fabric.properties
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
+generated-docs
diff --git a/Writerside/topics/api-list.md b/Writerside/topics/api-list.md
index 39a2000c..61d8535b 100644
--- a/Writerside/topics/api-list.md
+++ b/Writerside/topics/api-list.md
@@ -1,4 +1,4 @@
-# API列表
+# API定义列表
此处会列举 `API 模块` 中、`love.forte.simbot.qguild.api` 包下定义的所有 `QQGuildApi` 实现。
@@ -6,259 +6,813 @@
> 因为那是最贴合真实情况且最全面的。
+
+
+`love.forte.simbot.qguild.api.GatewayApis`
+
+获取网关信息。
+
+通过`Normal] 或 [Shared`的形式根据bot信息获取使用 Websocket 接入时间通知的链接。
+
+> 参考文档
+
+
+
+
+
+
+
+`love.forte.simbot.qguild.api.Normal`
+
+获取通用 WSS 接入点
+
+> 参考文档
+
+
+
+
+
+`love.forte.simbot.qguild.api.Shared`
+
+获取带分片 WSS 接入点
+
+> 参考文档
+
+
+
+
+
+
-> `love.forte.simbot.qguild.api.announces.CreateAnnouncesApi`
+`love.forte.simbot.qguild.api.announces.CreateAnnouncesApi`
+
+机器人设置消息为指定子频道公告。
+
+创建子频道公告
+
+
+
-> `love.forte.simbot.qguild.api.announces.DeleteAnnouncesApi`
+`love.forte.simbot.qguild.api.announces.DeleteAnnouncesApi`
+
+删除子频道公告
+
+机器人删除指定子频道公告
+
+
-> `love.forte.simbot.qguild.api.apipermission.DemandApiPermissionApi`
+`love.forte.simbot.qguild.api.apipermission.DemandApiPermissionApi`
+
+创建频道 API 接口权限授权链接
+
+用于创建 API 接口权限授权链接,该链接指向 `guild_id` 对应的频道 。
+
+需要注意,私信场景中,当需要查询私信来源频道的权限时,应使用 `src_guild_id` ,即 message
+中的 `src_guild_id`
+
+每天只能在一个频道内发 `3` 条(默认值)频道权限授权链接。
+
+
-> `love.forte.simbot.qguild.api.apipermission.GetApiPermissionListApi`
+`love.forte.simbot.qguild.api.apipermission.GetApiPermissionListApi`
+
+获取频道可用权限列表
+
+用于获取机器人在频道 `guild_id` 内可以使用的权限列表。
+
+
-> `love.forte.simbot.qguild.api.channel.CreateChannelApi`
+`love.forte.simbot.qguild.api.channel.CreateChannelApi`
+
+创建子频道
+
+用于在 `guild_id` 指定的频道下创建一个子频道。
+
+- 要求操作人具有管理频道的权限,如果是机器人,则需要将机器人设置为管理员。
+- 创建成功后,返回创建成功的子频道对象,同时会触发一个频道创建的事件通知。
+
+
-> `love.forte.simbot.qguild.api.channel.DeleteChannelApi`
+`love.forte.simbot.qguild.api.channel.DeleteChannelApi`
+
+删除子频道
+
+用于删除 `channel_id` 指定的子频道。
+
+- 要求操作人具有 `管理子频道` 的权限,如果是机器人,则需要将机器人设置为管理员。
+- 修改成功后,会触发子频道删除事件。
+
+
+**注意**
+
+子频道的删除是无法撤回的,一旦删除,将无法恢复。
+
+
-> `love.forte.simbot.qguild.api.channel.GetChannelApi`
+`love.forte.simbot.qguild.api.channel.GetChannelApi`
+
+获取子频道信息
+
+
-> `love.forte.simbot.qguild.api.channel.GetChannelOnlineNumsApi`
+`love.forte.simbot.qguild.api.channel.GetChannelOnlineNumsApi`
+
+获取在线成员数
+
+用于查询音视频/直播子频道 channel_id 的在线成员数。
+
+
-> `love.forte.simbot.qguild.api.channel.GetGuildChannelListApi`
+`love.forte.simbot.qguild.api.channel.GetGuildChannelListApi`
+
+获取子频道列表
+
+用于获取 `guild_id` 指定的频道下的子频道列表。
+
+
-> `love.forte.simbot.qguild.api.channel.ModifyChannelApi`
+`love.forte.simbot.qguild.api.channel.ModifyChannelApi`
+
+修改子频道
+
+用于修改 `channel_id` 指定的子频道的信息。
+
+- 要求操作人具有 `管理子频道` 的权限,如果是机器人,则需要将机器人设置为管理员。
+- 修改成功后,会触发子频道更新事件。
+
+
-> `love.forte.simbot.qguild.api.channel.permissions.GetChannelMemberPermissionsApi`
+`love.forte.simbot.qguild.api.channel.permissions.GetChannelMemberPermissionsApi`
+
+获取指定子频道的权限
+
+用于获取 子频道 `channel_id` 下用户 `user_id` 的权限。
+
+- 获取子频道用户权限。
+- 要求操作人具有管理子频道的权限,如果是机器人,则需要将机器人设置为管理员。
+
+
-> `love.forte.simbot.qguild.api.channel.permissions.GetChannelRolePermissionsApi`
+`love.forte.simbot.qguild.api.channel.permissions.GetChannelRolePermissionsApi`
+
+获取子频道身份组权限
+
+用于获取子频道 `channel_id` 下身份组 `role_id` 的权限。
+
+- 要求操作人具有管理子频道的权限,如果是机器人,则需要将机器人设置为管理员。
+
+
-> `love.forte.simbot.qguild.api.channel.permissions.ModifyChannelMemberPermissionsApi`
+`love.forte.simbot.qguild.api.channel.permissions.ModifyChannelMemberPermissionsApi`
+
+修改子频道权限
+
+用于修改子频道 `channel_id` 下用户 `user_id` 的权限。
+
+- 要求操作人具有 `管理子频道` 的权限,如果是机器人,则需要将机器人设置为管理员。
+- 参数包括 `add` 和 `remove` 两个字段,分别表示授予的权限以及删除的权限。
+ 要授予用户权限即把 `add` 对应位置 1,删除用户权限即把 `remove` 对应位置 1。当两个字段同一位都为 1,表现为删除权限。
+- 本接口不支持修改 `可管理子频道` 权限。
+
+
-> `love.forte.simbot.qguild.api.channel.permissions.ModifyChannelRolePermissionsApi`
+`love.forte.simbot.qguild.api.channel.permissions.ModifyChannelRolePermissionsApi`
+
+修改子频道身份组权限
+
+用于修改子频道 `channel_id` 下身份组 `role_id` 的权限。
+
+- 要求操作人具有管理子频道的权限,如果是机器人,则需要将机器人设置为管理员。
+- 参数包括 `add` 和 `remove` 两个字段,分别表示授予的权限以及删除的权限。
+ 要授予身份组权限即把 `add` 对应位置 1,删除身份组权限即把 `remove` 对应位置 1。当两个字段同一位都为 1,表现为删除权限。
+- 本接口不支持修改 `可管理子频道` 权限。
+
+
-> `love.forte.simbot.qguild.api.channel.pins.AddPinsMessageApi`
+`love.forte.simbot.qguild.api.channel.pins.AddPinsMessageApi`
+
+添加精华消息
+
+用于添加子频道 `channel_id` 内的精华消息。
+
+- 精华消息在一个子频道内最多只能创建 `20` 条。
+- 只有可见的消息才能被设置为精华消息。
+- 接口返回对象中 `message_ids` 为当前请求后子频道内所有精华消息 `message_id` 数组。
+
+
-> `love.forte.simbot.qguild.api.channel.pins.DeletePinsMessageApi`
+`love.forte.simbot.qguild.api.channel.pins.DeletePinsMessageApi`
+
+删除精华消息
+
+用于删除子频道 `channel_id` 下指定 `message_id` 的精华消息。
+
+- 删除子频道内全部精华消息,请将 `message_id` 设置为 [`all`][DELETE_ALL_MESSAGE_ID]。
+
+
-> `love.forte.simbot.qguild.api.channel.pins.GetPinsMessageApi`
+`love.forte.simbot.qguild.api.channel.pins.GetPinsMessageApi`
+
+获取精华消息
+
+用于获取子频道 `channel_id` 内的精华消息。
+
+
-> `love.forte.simbot.qguild.api.channel.schedules.CreateScheduleApi`
+`love.forte.simbot.qguild.api.channel.schedules.CreateScheduleApi`
+
+创建日程
+
+用于在 `channel_id` 指定的 `日程子频道` 下创建一个日程。
+
+- 要求操作人具有 `管理频道` 的权限,如果是机器人,则需要将机器人设置为管理员。
+- 创建成功后,返回创建成功的日程对象。
+- 创建操作频次限制
+- 单个管理员每天限 `10` 次。
+- 单个频道每天 `100` 次。
+
+
-> `love.forte.simbot.qguild.api.channel.schedules.DeleteScheduleApi`
+`love.forte.simbot.qguild.api.channel.schedules.DeleteScheduleApi`
+
+修改日程
+
+用于修改日程子频道 `channel_id` 下 `schedule_id` 指定的日程的详情。
+
+- 要求操作人具有 `管理频道` 的权限,如果是机器人,则需要将机器人设置为管理员。
+
+
-> `love.forte.simbot.qguild.api.channel.schedules.GetScheduleApi`
+`love.forte.simbot.qguild.api.channel.schedules.GetScheduleApi`
+
+获取日程详情
+
+获取日程子频道 `channel_id` 下 `schedule_id` 指定的的日程的详情。
+
+
-> `love.forte.simbot.qguild.api.channel.schedules.GetScheduleListApi`
+`love.forte.simbot.qguild.api.channel.schedules.GetScheduleListApi`
+
+获取频道日程列表
+
+用于获取 `channel_id` 指定的子频道中当天的日程列表。
+
+- 若带了参数 `since`,则返回在 `since` 对应当天的日程列表;若未带参数 `since`,则默认返回今天的日程列表。
+
+
-> `love.forte.simbot.qguild.api.channel.schedules.ModifyScheduleApi`
+`love.forte.simbot.qguild.api.channel.schedules.ModifyScheduleApi`
+
+修改日程
+
+用于修改日程子频道 `channel_id` 下 `schedule_id` 指定的日程的详情。
+
+- 要求操作人具有 `管理频道` 的权限,如果是机器人,则需要将机器人设置为管理员。
+
+
-> `love.forte.simbot.qguild.api.forum.DeleteThreadApi`
+`love.forte.simbot.qguild.api.forum.DeleteThreadApi`
+
+删除帖子
+
+该接口用于删除指定子频道下的某个帖子。
+
+
-> `love.forte.simbot.qguild.api.forum.GetThreadApi`
+`love.forte.simbot.qguild.api.forum.GetThreadApi`
+
+获取帖子详情
+
+该接口用于获取子频道下的帖子详情。
+
+
+
-> `love.forte.simbot.qguild.api.forum.GetThreadListApi`
+`love.forte.simbot.qguild.api.forum.GetThreadListApi`
+
+获取帖子列表
+
+该接口用于获取子频道下的帖子列表。
+
+
-> `love.forte.simbot.qguild.api.forum.PublishThreadApi`
+`love.forte.simbot.qguild.api.forum.PublishThreadApi`
-
-
+发表帖子
-> `love.forte.simbot.qguild.api.GatewayApis$Normal`
-
-
-> `love.forte.simbot.qguild.api.GatewayApis$Shared`
-> `love.forte.simbot.qguild.api.guild.GetGuildApi`
+`love.forte.simbot.qguild.api.guild.GetGuildApi`
+
+获取频道详情
+
+用于获取 `guildId` 指定的频道的详情。
+
-> `love.forte.simbot.qguild.api.guild.mute.MuteAllApi`
+`love.forte.simbot.qguild.api.guild.mute.MuteAllApi`
+
+禁言全员
+
+用于将频道的全体成员(非管理员)禁言。
+
+需要使用的 `token` 对应的用户具备管理员权限。如果是机器人,要求被添加为管理员。
+该接口同样可用于解除禁言,将 `mute_end_timestamp` 或 `mute_seconds` 传值为字符串'0'即可。
+
+
+
+
-> `love.forte.simbot.qguild.api.guild.mute.MuteMemberApi`
+`love.forte.simbot.qguild.api.guild.mute.MuteMemberApi`
+
+禁言指定成员
+
+用于禁言频道 `guild_id` 下的成员 `user_id`。
+
+需要使用的 `token` 对应的用户具备管理员权限。如果是机器人,要求被添加为管理员。
+该接口同样可用于解除禁言,将 `mute_end_timestamp` 或 `mute_seconds` 传值为字符串'0'即可。
+
+
-> `love.forte.simbot.qguild.api.guild.mute.MuteMultiMemberApi`
+`love.forte.simbot.qguild.api.guild.mute.MuteMultiMemberApi`
+
+禁言批量成员
+
+用于将频道的指定批量成员(非管理员)禁言。
+
+需要使用的 `token` 对应的用户具备管理员权限。如果是机器人,要求被添加为管理员。
+该接口同样可用于批量解除禁言,将 `mute_end_timestamp` 或 `mute_seconds` 传值为字符串'0'即可,及需要批量解除禁言的成员的user_id列表user_ids'。
+
+
-> `love.forte.simbot.qguild.api.member.DeleteMemberApi`
+`love.forte.simbot.qguild.api.member.DeleteMemberApi`
+
+删除频道成员
+
+用于删除 guild_id 指定的频道下的成员 user_id。
+
+- 需要使用的 token 对应的用户具备踢人权限。如果是机器人,要求被添加为管理员。
+- 操作成功后,会触发频道成员删除事件。
+- 无法移除身份为管理员的成员
+
+
-> `love.forte.simbot.qguild.api.member.GetGuildMemberListApi`
+`love.forte.simbot.qguild.api.member.GetGuildMemberListApi`
+
+获取频道成员列表
+
+用于获取 guild_id 指定的频道中所有成员的详情列表,支持分页。
+
+
+**有关返回结果的说明**
+
+1. 在每次翻页的过程中,可能会返回上一次请求已经返回过的 `member` 信息,需要调用方自己根据 `user id` 来进行去重。
+2. 每次返回的 `member` 数量与 `limit` 不一定完全相等。翻页请使用最后一个 `member` 的 `user id` 作为下一次请求的 `after` 参数,直到回包为空,拉取结束。
+
+
+
-> `love.forte.simbot.qguild.api.member.GetGuildRoleMemberListApi`
+`love.forte.simbot.qguild.api.member.GetGuildRoleMemberListApi`
+
+获取频道身份组成员列表
+
+用于获取 `guild_id` 频道中指定 `role_id` 身份组下所有成员的详情列表,支持分页。
+
+
+**有关返回结果的说明**
+
+1. 每次返回的member数量与limit不一定完全相等。特定管理身份组下的成员可能存在一次性返回全部的情况
+
+
-> `love.forte.simbot.qguild.api.member.GetMemberApi`
+`love.forte.simbot.qguild.api.member.GetMemberApi`
+
+获取某个成员信息
+
+用于获取 `guild_id` 指定的频道中 `user_id` 对应成员的详细信息。
+
+
-> `love.forte.simbot.qguild.api.message.DeleteMessageApi`
+`love.forte.simbot.qguild.api.message.DeleteMessageApi`
+
+撤回消息
+
+用于撤回子频道 `channel_id` 下的消息 `message_id`。
+
+- 管理员可以撤回普通成员的消息。
+- 频道主可以撤回所有人的消息。
+
+
+
+
+
+
+`love.forte.simbot.qguild.api.message.GetMessageApi`
+
+获取指定消息
+
+用于获取子频道 `channel_id` 下的消息 `message_id` 的详情。
+
+
+
+
+
+
+`love.forte.simbot.qguild.api.message.MessageSendApi`
+
+发送消息
+
+用于向 `channel_id` 指定的子频道发送消息。
+
+- 要求操作人在该子频道具有 `发送消息` 的权限。
+- 主动消息在频道主或管理设置了情况下,按设置的数量进行限频。在未设置的情况遵循如下限制:
+- 主动推送消息,默认每天往每个子频道可推送的消息数是 `20` 条,超过会被限制。
+- 主动推送消息在每个频道中,每天可以往 `2` 个子频道推送消息。超过后会被限制。
+- 不论主动消息还是被动消息,在一个子频道中,每 `1s` 只能发送 `5` 条消息。
+- 被动回复消息有效期为 `5` 分钟。超时会报错。
+- 发送消息接口要求机器人接口需要连接到 websocket 上保持在线状态
+- 有关主动消息审核,可以通过 Intents
+ 中审核事件 `MESSAGE_AUDIT` 返回 MessageAudited 对象获取结果。
+
+
+
+
+**主动消息与被动消息**
+
+- 主动消息:发送消息时,未填充 `msg_id/event_id` 字段的消息。
+- 被动消息:发送消息时,填充了 `msg_id/event_id` 字段的消息。`msg_id` 和 `event_id` 两个字段任意填一个即为被动消息。
+ 接口使用此 `msg_id/event_id` 拉取用户的消息或事件,同时判断用户消息或事件的发送时间,如果超过被动消息回复时效,将会不允许发送该消息。
+
+更多参考 文档
+
+
+**发送 ARK 模板消息**
+
+通过指定 `ark` 字段发送模板消息。
+
+- 要求操作人在该子频道具有发送消息和 对应 `ARK 模板` 的权限。
+- 调用前需要先申请消息模板,这一步会得到一个模板 `id`,在请求时填在 `ark.template_id` 上。
+- 发送成功之后,会触发一个创建消息的事件。
+- 可用模板参考可用模板。
+
+更多参考 文档
+
+
+**发送引用消息**
+
+
+- 只支持引用机器人自己发送到的消息以及用户@机器人产生的消息。
+- 发送成功之后,会触发一个创建消息的事件。
+
+不能单独发送引用消息,引用消息需要和其他消息类型组合发送,参数请见发送消息。
+
+更多参考 文档
+
+
+**发送含有消息按钮组件的消息**
+
+
+通过指定 `keyboard` 字段发送带按钮的消息,支持 `keyboard 模版` 和 `自定义 keyboard` 两种请求格式。
+
+- 要求操作人在该子频道具有 `发送消息` 和 `对应消息按钮组件` 的权限。
+- 请求参数 `keyboard 模版` 和 `自定义 keyboard` 只能单一传值。
+- `keyboard 模版`
+- 调用前需要先申请消息按钮组件模板,这一步会得到一个模板 id,在请求时填在 `keyboard` 字段上。
+- 申请消息按钮组件模板需要提供响应的 json,具体格式参考 InlineKeyboard。
+- 仅 `markdown` 消息支持消息按钮。
+
+更多参考 文档
+
+
+**内嵌格式**
+
+利用 `content` 字段发送内嵌格式的消息。
+
+- 内嵌格式仅在 `content` 中会生效,在 `Ark` 和 `Embed` 中不生效。
+- 为了区分是文本还是内嵌格式,消息抄送和发送会对消息内容进行相关的转义。
+
+
+**转义内容**
+
+
+| **源字符** | **转义后** |
+|----------|----------|
+| `&` | `&` |
+| `<` | `<` |
+| `>` | `>` |
+
+可参考使用`ContentTextDecoder`和 [ContentTextEncoder]
+
+
+**消息审核**
+
+
+> 其中推送、回复消息的 code 错误码 `304023`、`304024` 会在 响应数据包 `data` 中返回 `MessageAudit` 审核消息的信息
+
+当响应结果为上述错误码时,请求实体对象结果的API时会抛出`MessageAuditedException`异常并携带相关的对象信息。
+
+详见文档 发送消息 中的相关描述以及
+[MessageAuditedException] 的文档描述。
+
+
+
+更多参考 文档
+
+
+
-> `love.forte.simbot.qguild.api.message.direct.CreateDmsApi`
+`love.forte.simbot.qguild.api.message.direct.CreateDmsApi`
+
+创建私信会话
+
+用于机器人和在同一个频道内的成员创建私信会话。
+
+机器人和用户存在共同频道才能创建私信会话。
+创建成功后,返回创建成功的频道 `id` ,子频道 `id` 和创建时间。
+
+
+**参数**
+
+| 字段名 | 类型 | 描述 |
+|-----|-----|-----|
+| `recipient_id` | `string` | 接收者 id |
+| `source_guild_id` | `string` | 源频道 id |
+
+
+
-> `love.forte.simbot.qguild.api.message.direct.DeleteDmsApi`
+`love.forte.simbot.qguild.api.message.direct.DeleteDmsApi`
+
+撤回私信
+
+用于撤回私信频道 `guild_id` 中 `message_id` 指定的私信消息。只能用于撤回机器人自己发送的私信。
+
+
+
-> `love.forte.simbot.qguild.api.message.direct.DmsSendApi`
+`love.forte.simbot.qguild.api.message.direct.DmsSendApi`
+
+发送私信
+
+**接口**
+
+`POST /dms/{guild_id}/messages`
+
+
+**功能描述**
+
+用于发送私信消息,前提是已经创建了私信会话。
+
+* 私信的 `guild_id` 在创建私信会话时以及私信消息事件中获取。
+* 私信场景下,每个机器人每天可以对一个用户发 `2` 条主动消息。
+* 私信场景下,每个机器人每天累计可以发 `200` 条主动消息。
+* 私信场景下,被动消息没有条数限制。
+
+
+**参数**
+
+和`发送消息][MessageSendApi`参数一致。
+
+
+**返回**
+
+和`发送消息][MessageSendApi`返回一致。
-
-
-> `love.forte.simbot.qguild.api.message.GetMessageApi`
-
-
-> `love.forte.simbot.qguild.api.message.MessageSendApi`
-> `love.forte.simbot.qguild.api.message.setting.GetMessageSettingApi`
+`love.forte.simbot.qguild.api.message.setting.GetMessageSettingApi`
+
+获取频道消息频率设置
+
+用于获取机器人在频道 `guild_id` 内的消息频率设置。
+
-> `love.forte.simbot.qguild.api.role.AddMemberRoleApi`
+`love.forte.simbot.qguild.api.role.AddMemberRoleApi`
+
+增加频道身份组成员
+
+用于将频道 `guild_id` 下的用户 `user_id` 添加到身份组 `role_id` 。
+
+- 需要使用的 `token` 对应的用户具备增加身份组成员权限。如果是机器人,要求被添加为管理员。
+- 如果要增加的身份组 `ID` 是 [`5-子频道管理员`][love.forte.simbot.qguild.model.Role.DEFAULT_ID_CHANNEL_ADMIN],
+ 需要增加 `channel` 对象来指定具体是哪个子频道。
+
+
-> `love.forte.simbot.qguild.api.role.CreateGuildRoleApi`
+`love.forte.simbot.qguild.api.role.CreateGuildRoleApi`
+
+创建频道身份组
+
+用于在 `guild_id` 指定的频道下创建一个身份组。
+
+- 需要使用的 `token` 对应的用户具备创建身份组权限。如果是机器人,要求被添加为管理员。
+- 参数为非必填,但至少需要传其中之一,默认为空或 0。
+
+
-> `love.forte.simbot.qguild.api.role.DeleteGuildRoleApi`
+`love.forte.simbot.qguild.api.role.DeleteGuildRoleApi`
+
+删除频道身份组
+
+用于删除频道 `guild_id` 下 `role_id` 对应的身份组。
+
+需要使用的 `token` 对应的用户具备删除身份组权限。如果是机器人,要求被添加为管理员。
+
-> `love.forte.simbot.qguild.api.role.GetGuildRoleListApi`
+`love.forte.simbot.qguild.api.role.GetGuildRoleListApi`
+
+获取频道身份组列表
+
+用于获取 `guild_id` 指定的频道下的身份组列表。
+
+
-> `love.forte.simbot.qguild.api.role.ModifyGuildRoleApi`
+`love.forte.simbot.qguild.api.role.ModifyGuildRoleApi`
+
+修改频道身份组
+
+用于修改频道 `guild_id` 下 `role_id` 指定的身份组。
+
+- 需要使用的 `token` 对应的用户具备修改身份组权限。如果是机器人,要求被添加为管理员。
+- 接口会修改传入的字段,不传入的默认不会修改,至少要传入一个参数。
+
+
-> `love.forte.simbot.qguild.api.role.RemoveMemberRoleApi`
+`love.forte.simbot.qguild.api.role.RemoveMemberRoleApi`
+
+删除频道身份组成员
+
+用于将 用户 `user_id` 从 频道 `guild_id` 的 `role_id` 身份组中移除。
+
+- 需要使用的 `token` 对应的用户具备删除身份组成员权限。如果是机器人,要求被添加为管理员。
+- 如果要删除的身份组 `ID` 是 [`5-子频道管理员`][love.forte.simbot.qguild.model.Role.DEFAULT_ID_CHANNEL_ADMIN],
+ 需要增加 `channel` 对象来指定具体是哪个子频道。
+
+
-> `love.forte.simbot.qguild.api.user.GetBotGuildListApi`
+`love.forte.simbot.qguild.api.user.GetBotGuildListApi`
+
+获取用户频道列表
+
+用于获取当前用户(机器人)所加入的频道列表,支持分页。
+
+当 `HTTP Authorization` 中填入 `Bot Token` 是获取机器人的数据,填入 `Bearer Token` 则获取用户的数据。
+
-> `love.forte.simbot.qguild.api.user.GetBotInfoApi`
+`love.forte.simbot.qguild.api.user.GetBotInfoApi`
+
+获取用户详情
+
+用于获取当前用户(机器人)详情。
+
+由于`GetBotInfoApi] 本身为 `object` 类型, 因此 [ApiDescription] 由内部对象 [Description`提供而不是伴生对象。
+
+[GetBotInfoApi] 得到的`User] 中,[User.isBot`始终为 `true`。
+
+
+
+
diff --git a/Writerside/topics/event.md b/Writerside/topics/event.md
index 2706dbd5..9d0377a1 100644
--- a/Writerside/topics/event.md
+++ b/Writerside/topics/event.md
@@ -13,44 +13,503 @@ API 模块所有的事件封装类型都在包 `love.forte.simbot.qguild.event`
所有事件封装类型均继承密封类 `love.forte.simbot.qguild.event.Signal.Dispatch`。
-
-子频道创建事件 CHANNEL_CREATE
-子频道修改事件 CHANNEL_UPDATE
-子频道删除事件 CHANNEL_DELETE
-主题创建事件 FORUM_THREAD_CREATE
-主题更新事件 FORUM_THREAD_UPDATE
-主题删除事件 FORUM_THREAD_DELETE
-帖子创建事件 FORUM_POST_CREATE
-帖子删除事件 FORUM_POST_DELETE
-回复创建事件 FORUM_REPLY_CREATE
-回复删除事件 FORUM_REPLY_DELETE
-帖子审核事件 FORUM_PUBLISH_AUDIT_RESULT
-"开放"创建主题事件 OPEN_FORUM_THREAD_CREATE
-"开放"更新主题事件 OPEN_FORUM_THREAD_UPDATE
-"开放"删除主题事件 OPEN_FORUM_THREAD_DELETE
-"开放"帖子创建(评论)事件 OPEN_FORUM_POST_CREATE
-"开放"帖子删除(评论)事件 OPEN_FORUM_POST_DELETE
-"开放"回复创建事件 OPEN_FORUM_REPLY_CREATE
-"开放"回复删除事件 OPEN_FORUM_REPLY_DELETE
-Bot加入频道事件 GUILD_CREATE
-频道信息变更事件 GUILD_UPDATE
-Bot因各种原因退出频道事件 GUILD_DELETE
-新用户加入频道事件 GUILD_MEMBER_ADD
-用户的频道属性发生变化事件 GUILD_MEMBER_UPDATE
-用户离开频道事件 GUILD_MEMBER_REMOVE
-收到公域at消息事件 AT_MESSAGE_CREATE
+
+
+
+`love.forte.simbot.qguild.event.Ready`
+
+事件类型名: `"READY"`
+
+鉴权成功之后,后台会下发的 Ready Event.
+
+
+
+
+`love.forte.simbot.qguild.event.Resumed`
+
+事件类型名: `"RESUMED"`
+
+4.恢复连接
+恢复成功之后,就开始补发遗漏事件,所有事件补发完成之后,会下发一个 `Resumed Event`
+
+
+
+
+`love.forte.simbot.qguild.event.ChannelDispatch`
+
+channel相关的事件类型。[data] 类型为 [EventChannel]。
+
+
+
+
+`love.forte.simbot.qguild.event.ChannelCreate`
+
+事件类型名: `"CHANNEL_CREATE"`
+
+子频道事件 CHANNEL_CREATE
+
+**发送时机**
+
+- 子频道被创建
+
+
+
+
+`love.forte.simbot.qguild.event.ChannelUpdate`
+
+事件类型名: `"CHANNEL_UPDATE"`
+
+子频道事件 CHANNEL_UPDATE
+
+**发送时机**
+
+- 子频道信息变更
+
+
+
+
+`love.forte.simbot.qguild.event.ChannelDelete`
+
+事件类型名: `"CHANNEL_DELETE"`
+
+子频道事件 CHANNEL_DELETE
+
+**发送时机**
+
+- 子频道被删除
+
+
+
+
+`love.forte.simbot.qguild.event.ForumDispatch`
+
+论坛事件(ForumEvent)
+
+**发送时机**
+
+用户在话题子频道内发帖、评论、回复评论时产生该事件
+
+**主题事件**
+
+- FORUM_THREAD_CREATE
+- FORUM_THREAD_UPDATE
+- FORUM_THREAD_DELETE
+ 事件内容为`Thread`对象
+
+**帖子事件**
+
+- FORUM_POST_CREATE
+- FORUM_POST_DELETE
+ 事件内容为`Post`对象
+
+**回复事件**
+
+- FORUM_REPLY_CREATE
+- FORUM_REPLY_DELETE
+ 事件内容为`Reply`对象
+
+**帖子审核事件**
+
+- FORUM_PUBLISH_AUDIT_RESULT
+ 事件内容为`AuditResult`对象
+
+
+
+
+`love.forte.simbot.qguild.event.ForumThreadDispatch`
+
+论坛事件:主题事件
+
+
+
+
+`love.forte.simbot.qguild.event.ForumThreadCreate`
+
+事件类型名: `"FORUM_THREAD_CREATE"`
+
+主题创建事件。
+
+
+
+
+`love.forte.simbot.qguild.event.ForumThreadUpdate`
+
+事件类型名: `"FORUM_THREAD_UPDATE"`
+
+主题更新事件。
+
+
+
+
+`love.forte.simbot.qguild.event.ForumThreadDelete`
+
+事件类型名: `"FORUM_THREAD_DELETE"`
+
+主题删除事件。
+
+
+
+
+`love.forte.simbot.qguild.event.ForumPostDispatch`
+
+论坛事件:帖子事件
+
+
+
+
+`love.forte.simbot.qguild.event.ForumPostCreate`
+
+事件类型名: `"FORUM_POST_CREATE"`
+
+帖子创建事件
+
+
+
+
+`love.forte.simbot.qguild.event.ForumPostDelete`
+
+事件类型名: `"FORUM_POST_DELETE"`
+
+帖子删除事件
+
+
+
+
+`love.forte.simbot.qguild.event.ForumReplyDispatch`
+
+论坛事件:回复事件
+
+
+
+
+`love.forte.simbot.qguild.event.ForumReplyCreate`
+
+事件类型名: `"FORUM_REPLY_CREATE"`
+
+回复创建事件
+
+
+
+
+`love.forte.simbot.qguild.event.ForumReplyDelete`
+
+事件类型名: `"FORUM_REPLY_DELETE"`
+
+回复删除事件
+
+
+
+
+`love.forte.simbot.qguild.event.ForumPublishAuditResult`
+
+事件类型名: `"FORUM_PUBLISH_AUDIT_RESULT"`
+
+帖子审核事件
+
+
+
+
+`love.forte.simbot.qguild.event.EventGuildDispatch`
+
+Guild相关事件类型。[data] 类型为 [EventGuild]。
+
+
+
+
+`love.forte.simbot.qguild.event.GuildCreate`
+
+事件类型名: `"GUILD_CREATE"`
+
+GUILD_CREATE
+
+**发送时机**
+
+- 机器人被加入到某个频道的时候
+
+
+
+
+`love.forte.simbot.qguild.event.GuildUpdate`
+
+事件类型名: `"GUILD_UPDATE"`
+
+GUILD_UPDATE
+
+**发送时机**
+
+- 频道信息变更
+- 事件内容为变更后的数据
+
+
+
+
+`love.forte.simbot.qguild.event.GuildDelete`
+
+事件类型名: `"GUILD_DELETE"`
+
+GUILD_DELETE
+
+**发送时机**
+
+- 频道被解散
+- 机器人被移除
+- 事件内容为变更前的数据
+
+
+
+
+`love.forte.simbot.qguild.event.GuildMemberAdd`
+
+事件类型名: `"GUILD_MEMBER_ADD"`
+
+`GUILD_MEMBER_ADD`
+
+**发送时机**
+
+- 新用户加入频道
+
+
+
+
+`love.forte.simbot.qguild.event.GuildMemberUpdate`
+
+事件类型名: `"GUILD_MEMBER_UPDATE"`
+
+`GUILD_MEMBER_UPDATE`
+
+**发送时机**
+
+- 用户的频道属性发生变化,如频道昵称,或者身份组
+
+
+
+
+`love.forte.simbot.qguild.event.GuildMemberRemove`
+
+事件类型名: `"GUILD_MEMBER_REMOVE"`
+
+`GUILD_MEMBER_REMOVE`
+
+**发送时机**
+
+- 用户离开频道
+
+
+
+
+`love.forte.simbot.qguild.event.MessageDispatch`
+
+与 `message` 相关的事件类型。[data] 类型为 [Message]
+
+
+
+
+`love.forte.simbot.qguild.event.AtMessageCreate`
+
+事件类型名: `"AT_MESSAGE_CREATE"`
+
+消息事件
+`AT_MESSAGE_CREATE(intents PUBLIC_GUILD_MESSAGES)`
+
+**发送时机**
+
+- 用户发送消息,@当前机器人或回复机器人消息时
+- 为保障消息投递的速度,消息顺序我们虽然会尽量有序,但是并不保证是严格有序的,
+ 如开发者对消息顺序有严格有序的需求,可以自行缓冲消息事件之后,基于`seq`进行排序
+
+
-PUBLIC_MESSAGE_DELETE
-此事件官网文档似乎没有详细说明,请慎重使用
+
+`love.forte.simbot.qguild.event.PublicMessageDeleteCreate`
+
+事件类型名: `"PUBLIC_MESSAGE_DELETE"`
+
+消息事件
+`PUBLIC_MESSAGE_DELETE_TYPE`
+
+
+
+
+`love.forte.simbot.qguild.event.DirectMessageCreate`
+
+事件类型名: `"DIRECT_MESSAGE_CREATE"`
+
+私信消息事件
+`DIRECT_MESSAGE_CREATE (intents DIRECT_MESSAGE)`
+
+**发送时机**
+
+- 用户通过私信发消息给机器人时
+
+
+
+
+`love.forte.simbot.qguild.event.MessageAuditedDispatch`
+
+与`MessageAudited] 相关的事件类型。[data`类型为 [MessageAudited]。
+
+
+
+
+`love.forte.simbot.qguild.event.MessageCreate`
+
+事件类型名: `"MESSAGE_CREATE"`
+
+发送消息事件,代表频道内的全部消息,而不只是 at 机器人的消息。内容与 AT_MESSAGE_CREATE 相同
+
-私信消息事件 DIRECT_MESSAGE_CREATE
-(私域)发送消息事件 MESSAGE_CREATE
-MESSAGE_DELETE
-此事件官网文档似乎没有详细说明,请慎重使用
+
+`love.forte.simbot.qguild.event.MessageDelete`
+
+事件类型名: `"MESSAGE_DELETE"`
+
+删除(撤回)消息事件
+
+
+
+
+`love.forte.simbot.qguild.event.MessageAuditPass`
+
+事件类型名: `"MESSAGE_AUDIT_PASS"`
+
+消息审核事件
+`MESSAGE_AUDIT_PASS(intents MESSAGE_AUDIT)`
+
+**发送时机**
+
+- 消息审核通过
+
+
+
+
+`love.forte.simbot.qguild.event.MessageAuditReject`
+
+事件类型名: `"MESSAGE_AUDIT_REJECT"`
+
+消息审核事件
+`MESSAGE_AUDIT_REJECT(intents MESSAGE_AUDIT)`
+
+**发送时机**
+
+- 消息审核不通过
+
+
+
+
+`love.forte.simbot.qguild.event.OpenForumDispatch`
+
+开放论坛事件(OpenForumEvent) 相关的事件父类。
+
+**发送时机**
+
+用户在话题子频道内发帖、评论、回复评论时产生该事件
+
+**主题事件**
+
+- OPEN_FORUM_THREAD_CREATE
+- OPEN_FORUM_THREAD_UPDATE
+- OPEN_FORUM_THREAD_DELETE
+ 参考 [OpenForumThreadDispatch]
+
+**帖子(评论)事件**
+
+- OPEN_FORUM_POST_CREATE
+- OPEN_FORUM_POST_DELETE
+ 参考 [OpenForumPostDispatch]
+
+**回复事件**
+
+- OPEN_FORUM_REPLY_CREATE
+- OPEN_FORUM_REPLY_DELETE
+ 参考 [OpenForumReplyDispatch]
+
+
+
+
+`love.forte.simbot.qguild.event.OpenForumThreadDispatch`
+
+开放论坛事件的 **_主题事件_**。
+
+
+
+
+`love.forte.simbot.qguild.event.OpenForumThreadCreate`
+
+事件类型名: `"OPEN_FORUM_THREAD_CREATE"`
+
+主题事件:创建主题
+
+
+
+
+`love.forte.simbot.qguild.event.OpenForumThreadUpdate`
+
+事件类型名: `"OPEN_FORUM_THREAD_UPDATE"`
+
+主题事件:更新主题
+
+
+
+
+`love.forte.simbot.qguild.event.OpenForumThreadDelete`
+
+事件类型名: `"OPEN_FORUM_THREAD_DELETE"`
+
+主题事件:删除主题
+
+
+
+
+`love.forte.simbot.qguild.event.OpenForumPostDispatch`
+
+开放论坛事件的 **_帖子(评论)事件_**。
+
+
+
+
+`love.forte.simbot.qguild.event.OpenForumPostCreate`
+
+事件类型名: `"OPEN_FORUM_POST_CREATE"`
+
+帖子事件:创建帖子(评论)
+
+
+
+
+`love.forte.simbot.qguild.event.OpenForumPostDelete`
+
+事件类型名: `"OPEN_FORUM_POST_DELETE"`
+
+帖子事件:删除帖子(评论)
+
+
+
+
+`love.forte.simbot.qguild.event.OpenForumReplyDispatch`
+
+开放论坛事件的 **_回复事件_**。
+
+
+
+
+`love.forte.simbot.qguild.event.OpenForumReplyCreate`
+
+事件类型名: `"OPEN_FORUM_REPLY_CREATE"`
+
+回复事件:创建回复
+
+
+
+
+`love.forte.simbot.qguild.event.OpenForumReplyDelete`
+
+事件类型名: `"OPEN_FORUM_REPLY_DELETE"`
+
+回复事件:删除回复
+
-消息审核通过事件 MESSAGE_AUDIT_PASS
-消息审核不通过事件 MESSAGE_AUDIT_REJECT
+
API 模块事件封装可以使用在 **标准库模块 (stdlib)** 中,使用 `Bot` 类型对他们进行监听与处理。
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 5bdae927..7684693e 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -13,6 +13,10 @@ reactor = "3.6.2"
simbot = "4.0.0-dev15"
suspendTransform = "0.7.0-beta1"
gradleCommon = "0.2.0"
+# ksp
+ksp = "1.9.22-1.0.17"
+# https://square.github.io/kotlinpoet/
+kotlinPoet = "1.16.0"
[libraries]
# simbot
@@ -92,6 +96,11 @@ log4j-slf4j2 = { group = "org.apache.logging.log4j", name = "log4j-slf4j2-impl",
openjdk-jmh-core = { group = "org.openjdk.jmh", name = "jmh-core", version.ref = "openjdk-jmh" }
openjdk-jmh-generator-annprocess = { group = "org.openjdk.jmh", name = "jmh-generator-annprocess", version.ref = "openjdk-jmh" }
+# ksp
+ksp = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
+# https://square.github.io/kotlinpoet/interop-ksp/
+kotlinPoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinPoet" }
+
# reactor
reactor-core = { group = "io.projectreactor", name = "reactor-core", version.ref = "reactor" }
@@ -106,6 +115,9 @@ gradle-common-core = { group = "love.forte.gradle.common", name = "gradle-common
gradle-common-multiplatform = { group = "love.forte.gradle.common", name = "gradle-common-kotlin-multiplatform", version.ref = "gradleCommon" }
gradle-common-publication = { group = "love.forte.gradle.common", name = "gradle-common-publication", version.ref = "gradleCommon" }
+[plugins]
+ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
+
[bundles]
gradle-common = ["gradle-common-core", "gradle-common-multiplatform", "gradle-common-publication"]
dokka = ["dokka-plugin", "dokka-base"]
diff --git a/internal-processors/api-reader/build.gradle.kts b/internal-processors/api-reader/build.gradle.kts
new file mode 100644
index 00000000..5f97a9cb
--- /dev/null
+++ b/internal-processors/api-reader/build.gradle.kts
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2024. ForteScarlet.
+ *
+ * This file is part of simbot-component-qq-guild.
+ *
+ * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild.
+ * If not, see .
+ */
+
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+}
+
+kotlin {
+ jvmToolchain(11)
+ compilerOptions {
+ javaParameters = true
+ jvmTarget.set(JvmTarget.JVM_11)
+ }
+}
+
+configJavaCompileWithModule()
+
+dependencies {
+// implementation(project(":annotations"))
+ api(libs.ksp)
+ api(libs.kotlinPoet.ksp)
+ testImplementation(kotlin("test-junit5"))
+}
+
+tasks.getByName("test") {
+ useJUnitPlatform()
+}
+
diff --git a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessor.kt b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessor.kt
new file mode 100644
index 00000000..10d395a7
--- /dev/null
+++ b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessor.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2024. ForteScarlet.
+ *
+ * This file is part of simbot-component-qq-guild.
+ *
+ * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild.
+ * If not, see .
+ */
+
+package qg.internal.processors.apireader
+
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.getKotlinClassByName
+import com.google.devtools.ksp.isAbstract
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import java.io.BufferedWriter
+import java.io.File
+import java.nio.file.StandardOpenOption
+import kotlin.io.path.bufferedWriter
+
+private const val QG_API_CLASS_NAME = "love.forte.simbot.qguild.api.QQGuildApi"
+
+private const val API_READ_TARGET_FILE_OPTION_KEY = "qg.api.finder.api.output"
+
+abstract class ReaderProcessor(private val environment: SymbolProcessorEnvironment) : SymbolProcessor {
+ abstract val optionName: String
+ abstract val targetClassName: String
+
+ @OptIn(KspExperimental::class)
+ override fun process(resolver: Resolver): List {
+ val targetFilePath: String? = environment.options[optionName]
+
+ val targetFile = File(targetFilePath ?: run {
+ val msg = "target output file option ['$optionName'] is null!"
+ environment.logger.error(msg)
+ throw NullPointerException(msg)
+ })
+
+ environment.logger.info("target output file: ${targetFile.absolutePath}")
+ val targetClass = resolver.getKotlinClassByName(targetClassName)
+ environment.logger.info("apiClass: $targetClassName")
+ targetClass ?: return emptyList()
+
+ val targetClasses = resolver.getAllFiles().flatMap { it.declarations }
+ .filterIsInstance()
+ .filter {
+ targetClass.asStarProjectedType().isAssignableFrom(it.asStarProjectedType())
+ }
+ .filter { !it.isAbstract() }
+ .toList()
+
+ if (!targetFile.exists()) {
+ targetFile.parentFile.mkdirs()
+ } else {
+ targetFile.delete()
+ }
+
+ targetFile.toPath().bufferedWriter(
+ options = arrayOf(
+ StandardOpenOption.WRITE,
+ StandardOpenOption.TRUNCATE_EXISTING,
+ StandardOpenOption.CREATE
+ )
+ ).use { writer ->
+ writer.writeDeflistTo(targetClasses)
+ }
+
+
+ return emptyList()
+ }
+
+}
+
+/**
+ *
+ * @author ForteScarlet
+ */
+class ApiReaderProcessor(private val environment: SymbolProcessorEnvironment) : ReaderProcessor(environment) {
+ override val optionName: String = API_READ_TARGET_FILE_OPTION_KEY
+ override val targetClassName: String = QG_API_CLASS_NAME
+}
+
+//class EventReaderProcessor(private val environment: SymbolProcessorEnvironment) : ReaderProcessor(environment) {
+// override val optionName: String = EVENT_READ_TARGET_FILE_OPTION_KEY
+// override val targetClassName: String = QG_EVENT_CLASS_NAME
+//}
+
+private fun BufferedWriter.writeDeflistTo(list: List) {
+ write("\n")
+ list.forEach { declaration ->
+ write("\n")
+ newLine()
+ write("`${declaration.packageName.asString()}.${declaration.simpleName.asString()}`\n")
+ newLine()
+
+ val lines = declaration.docString?.trim()
+ ?.lines()
+ ?.map { it.trim() }
+ ?.filter { !it.startsWith('@') }
+ ?.map { line ->
+ line
+ .replace(linkRegex, "$1")
+ .replace(refRegex, "`$1`")
+ .replace(titleRegex, "\n**$1**\n")
+ }
+
+ if (lines != null) {
+ lines.forEach { line ->
+ write(line)
+ newLine()
+ }
+ newLine()
+ }
+
+ newLine()
+
+ val sealedSub = declaration.getSealedSubclasses().toList()
+ if (sealedSub.isNotEmpty()) {
+ writeDeflistTo(sealedSub)
+ }
+
+ write("\n")
+ }
+ newLine()
+ write("\n")
+
+}
diff --git a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessorProvider.kt b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessorProvider.kt
new file mode 100644
index 00000000..22c03f8e
--- /dev/null
+++ b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessorProvider.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2024. ForteScarlet.
+ *
+ * This file is part of simbot-component-qq-guild.
+ *
+ * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild.
+ * If not, see .
+ */
+
+package qg.internal.processors.apireader
+
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
+import com.google.devtools.ksp.processing.SymbolProcessorProvider
+import com.google.devtools.ksp.symbol.KSAnnotated
+
+private object NonProcessor : SymbolProcessor {
+ override fun process(resolver: Resolver): List = emptyList()
+}
+
+private const val ENABLE_OPTION = "qg.api.reader.enable"
+
+/**
+ *
+ * @author ForteScarlet
+ */
+class ApiReaderProcessorProvider : SymbolProcessorProvider {
+ override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor =
+ if (environment.options[ENABLE_OPTION].toBoolean())
+ ApiReaderProcessor(environment)
+ else NonProcessor
+}
+
+/**
+ *
+ * @author ForteScarlet
+ */
+class EventReaderProcessorProvider : SymbolProcessorProvider {
+ override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor =
+ if (environment.options[ENABLE_OPTION].toBoolean())
+ EventReaderProcessor(environment)
+ else NonProcessor
+}
diff --git a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/EventReaderProcessor.kt b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/EventReaderProcessor.kt
new file mode 100644
index 00000000..9293d744
--- /dev/null
+++ b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/EventReaderProcessor.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2024. ForteScarlet.
+ *
+ * This file is part of simbot-component-qq-guild.
+ *
+ * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild.
+ * If not, see .
+ */
+
+package qg.internal.processors.apireader
+
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.getKotlinClassByName
+import com.google.devtools.ksp.isAbstract
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import java.io.BufferedWriter
+import java.io.File
+import java.nio.file.StandardOpenOption
+import kotlin.io.path.bufferedWriter
+
+private const val QG_EVENT_CLASS_NAME = "love.forte.simbot.qguild.event.Signal.Dispatch"
+private const val EVENT_READ_TARGET_FILE_OPTION_KEY = "qg.api.finder.event.output"
+
+
+
+/**
+ *
+ * @author ForteScarlet
+ */
+class EventReaderProcessor(private val environment: SymbolProcessorEnvironment) : SymbolProcessor {
+ @OptIn(KspExperimental::class)
+ override fun process(resolver: Resolver): List {
+ val targetFilePath: String? = environment.options[EVENT_READ_TARGET_FILE_OPTION_KEY]
+
+ val targetFile = File(targetFilePath ?: run {
+ val msg = "target output file option ['$EVENT_READ_TARGET_FILE_OPTION_KEY'] is null!"
+ environment.logger.error(msg)
+ throw NullPointerException(msg)
+ })
+
+ environment.logger.info("target output file: ${targetFile.absolutePath}")
+ val targetClass = resolver.getKotlinClassByName(QG_EVENT_CLASS_NAME)
+ environment.logger.info("apiClass: $QG_EVENT_CLASS_NAME")
+ targetClass ?: return emptyList()
+
+ val targetClasses = resolver.getAllFiles().flatMap { it.declarations }
+ .filterIsInstance()
+ .filter {
+ targetClass.asStarProjectedType().isAssignableFrom(it.asStarProjectedType())
+ }
+ .filter { !it.isAbstract() }
+ .toList()
+
+ if (!targetFile.exists()) {
+ targetFile.parentFile.mkdirs()
+ } else {
+ targetFile.delete()
+ }
+
+ targetFile.toPath().bufferedWriter(
+ options = arrayOf(
+ StandardOpenOption.WRITE,
+ StandardOpenOption.TRUNCATE_EXISTING,
+ StandardOpenOption.CREATE
+ )
+ ).use { writer ->
+ writer.writeDeflistTo(targetClasses)
+ }
+
+ return emptyList()
+ }
+
+ private fun BufferedWriter.writeDeflistTo(list: List) {
+ write("\n")
+ list.forEach { declaration ->
+ write("\n")
+ newLine()
+ write("`${declaration.packageName.asString()}.${declaration.simpleName.asString()}`\n")
+ newLine()
+ val serName = declaration.annotations.find {
+ it.shortName.asString() == "SerialName"
+ }?.arguments?.find { it.name?.asString() == "value" }?.value?.toString()
+
+ if (serName != null) {
+ write("事件类型名: `\"${serName}\"`\n")
+ newLine()
+ }
+
+ val lines = declaration.docString?.trim()
+ ?.lines()
+ ?.filter { it.isNotBlank() }
+ ?.map { it.trim() }
+ ?.filter { !it.startsWith('@') }
+ ?.map { line ->
+ line
+ .replace(linkRegex, "$1")
+ .replace(refRegex, "`$1`")
+ .replace(titleRegex, "\n**$1**\n")
+ }
+
+ if (lines != null) {
+ lines.forEach { line ->
+ write(line)
+ newLine()
+ }
+ newLine()
+ }
+
+ write("\n")
+ }
+ newLine()
+ write("\n")
+
+ }
+}
+
diff --git a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/utils.kt b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/utils.kt
new file mode 100644
index 00000000..46de0922
--- /dev/null
+++ b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/utils.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2024. ForteScarlet.
+ *
+ * This file is part of simbot-component-qq-guild.
+ *
+ * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild.
+ * If not, see .
+ */
+
+package qg.internal.processors.apireader
+
+val linkRegex = Regex("\\[(?.+)]\\((?.+)\\)")
+val refRegex = Regex(" \\[(.+)] ")
+val titleRegex = Regex("#+ (.+)")
diff --git a/internal-processors/api-reader/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/internal-processors/api-reader/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..a0451b37
--- /dev/null
+++ b/internal-processors/api-reader/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1,2 @@
+qg.internal.processors.apireader.ApiReaderProcessorProvider
+qg.internal.processors.apireader.EventReaderProcessorProvider
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 50389de1..ea6e0d22 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -17,6 +17,8 @@
rootProject.name = "qq-guild"
+// internals
+include(":internal-processors:api-reader")
//include(":builder-generator")
include(":simbot-component-qq-guild-api")
diff --git a/simbot-component-qq-guild-api/build.gradle.kts b/simbot-component-qq-guild-api/build.gradle.kts
index 4863a6ac..dc5f7da8 100644
--- a/simbot-component-qq-guild-api/build.gradle.kts
+++ b/simbot-component-qq-guild-api/build.gradle.kts
@@ -20,11 +20,13 @@ import love.forte.gradle.common.kotlin.multiplatform.applyTier1
import love.forte.gradle.common.kotlin.multiplatform.applyTier2
import love.forte.gradle.common.kotlin.multiplatform.applyTier3
import love.forte.plugin.suspendtrans.gradle.withKotlinTargets
+import util.isCi
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
`qq-guild-dokka-partial-configure`
+ alias(libs.plugins.ksp)
}
setup(P.ComponentQQGuild)
@@ -112,3 +114,12 @@ kotlin {
}
+dependencies {
+ add("kspJvm", project(":internal-processors:api-reader"))
+}
+
+ksp {
+ arg("qg.api.reader.enable", (!isCi).toString())
+ arg("qg.api.finder.api.output", rootDir.resolve("generated-docs/api-list.md").absolutePath)
+ arg("qg.api.finder.event.output", rootDir.resolve("generated-docs/event-list.md").absolutePath)
+}