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) +}