mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-04 18:34:59 +08:00
Remove old files
This commit is contained in:
parent
69abf7a5a0
commit
205432db10
@ -1,727 +0,0 @@
|
|||||||
|
|
||||||
#### Bot登录成功
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "BotOnlineEvent",
|
|
||||||
"qq": 123456
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ---- | ---- | ------------------- |
|
|
||||||
| qq | Long | 登录成功的Bot的QQ号 |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Bot主动离线
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "BotOfflineEventActive",
|
|
||||||
"qq": 123456
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ---- | ---- | ------------------- |
|
|
||||||
| qq | Long | 主动离线的Bot的QQ号 |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Bot被挤下线
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "BotOfflineEventForce",
|
|
||||||
"qq": 123456
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ---- | ---- | ------------------- |
|
|
||||||
| qq | Long | 被挤下线的Bot的QQ号 |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Bot被服务器断开或因网络问题而掉线
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "BotOfflineEventDropped",
|
|
||||||
"qq": 123456
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ---- | ---- | ----------------------------------------- |
|
|
||||||
| qq | Long | 被服务器断开或因网络问题而掉线的Bot的QQ号 |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Bot主动重新登录.
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "BotReloginEvent",
|
|
||||||
"qq": 123456
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ---- | ---- | ----------------------- |
|
|
||||||
| qq | Long | 主动重新登录的Bot的QQ号 |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Bot在群里的权限被改变. 操作人一定是群主
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "BotGroupPermissionChangeEvent",
|
|
||||||
"origin": "MEMBER",
|
|
||||||
"new": "ADMINISTRATOR",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "ADMINISTRATOR"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ---------------- | ------ | --------------------------------------------- |
|
|
||||||
| origin | String | Bot的原权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| new | String | Bot的新权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| group | Object | 权限改变所在的群信息 |
|
|
||||||
| group.id | Long | 群号 |
|
|
||||||
| group.name | String | 群名 |
|
|
||||||
| group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Bot被禁言
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "BotMuteEvent",
|
|
||||||
"durationSeconds": 600,
|
|
||||||
"operator": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是管理员",
|
|
||||||
"permission": "ADMINISTRATOR",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ------------------------- | ------ | ------------------------------------------------ |
|
|
||||||
| durationSeconds | Int | 禁言时长,单位为秒 |
|
|
||||||
| operator | Object | 操作的管理员或群主信息 |
|
|
||||||
| operator.id | Long | 操作者的QQ号 |
|
|
||||||
| operator.memberName | String | 操作者的群名片 |
|
|
||||||
| operator.permission | String | 操作者在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| operator.group | Object | Bot被禁言所在群的信息 |
|
|
||||||
| operator.group.id | Long | 群号 |
|
|
||||||
| operator.group.name | String | 群名 |
|
|
||||||
| operator.group.permission | String | Bot在群中的权限,OWNER或ADMINISTRATOR |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Bot被取消禁言
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "BotUnmuteEvent",
|
|
||||||
"operator": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是管理员",
|
|
||||||
"permission": "ADMINISTRATOR",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ------------------------- | ------ | ------------------------------------------------ |
|
|
||||||
| operator | Object | 操作的管理员或群主信息 |
|
|
||||||
| operator.id | Long | 操作者的QQ号 |
|
|
||||||
| operator.memberName | String | 操作者的群名片 |
|
|
||||||
| operator.permission | String | 操作者在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| operator.group | Object | Bot被取消禁言所在群的信息 |
|
|
||||||
| operator.group.id | Long | 群号 |
|
|
||||||
| operator.group.name | String | 群名 |
|
|
||||||
| operator.group.permission | String | Bot在群中的权限,OWNER或ADMINISTRATOR |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Bot加入了一个新群
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "BotJoinGroupEvent",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ---------------- | ------ | ------------------------------------------------------------ |
|
|
||||||
| group | Object | Bot新加入群的信息 |
|
|
||||||
| group.id | Long | 群号 |
|
|
||||||
| group.name | String | 群名 |
|
|
||||||
| group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER(新加入群通常是Member) |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 某个群名改变
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "GroupNameChangeEvent",
|
|
||||||
"origin": "miral technology",
|
|
||||||
"new": "MIRAI TECHNOLOGY",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "MIRAI TECHNOLOGY",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
},
|
|
||||||
"isByBot": false
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ---------------- | ------- | --------------------------------------------- |
|
|
||||||
| origin | String | 原群名 |
|
|
||||||
| new | String | 新群名 |
|
|
||||||
| group | Object | 群名改名的群信息 |
|
|
||||||
| group.id | Long | 群号 |
|
|
||||||
| group.name | String | 群名 |
|
|
||||||
| group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| isByBot | Boolean | 是否Bot进行该操作 |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 某群入群公告改变
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "GroupEntranceAnnouncementChangeEvent",
|
|
||||||
"origin": "abc",
|
|
||||||
"new": "cba",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
},
|
|
||||||
"operator": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是管理员",
|
|
||||||
"permission": "ADMINISTRATOR",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ------------------- | ------- | --------------------------------------------- |
|
|
||||||
| origin | String | 原公告 |
|
|
||||||
| new | String | 新公告 |
|
|
||||||
| group | Object | 公告改变的群信息 |
|
|
||||||
| group.id | Long | 群号 |
|
|
||||||
| group.name | String | 群名 |
|
|
||||||
| group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| operator | Object? | 操作的管理员或群主信息,当null时为Bot操作 |
|
|
||||||
| operator.id | Long | 操作者的QQ号 |
|
|
||||||
| operator.memberName | String | 操作者的群名片 |
|
|
||||||
| operator.permission | String | 操作者在群中的权限,OWNER或ADMINISTRATOR |
|
|
||||||
| operator.group | Object | 同group |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 全员禁言
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "GroupMuteAllEvent",
|
|
||||||
"origin": false,
|
|
||||||
"new": true,
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
},
|
|
||||||
"operator": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是管理员",
|
|
||||||
"permission": "ADMINISTRATOR",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ------------------- | ------- | --------------------------------------------- |
|
|
||||||
| origin | Boolean | 原本是否处于全员禁言 |
|
|
||||||
| new | Boolean | 现在是否处于全员禁言 |
|
|
||||||
| group | Object | 全员禁言的群信息 |
|
|
||||||
| group.id | Long | 群号 |
|
|
||||||
| group.name | String | 群名 |
|
|
||||||
| group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| operator | Object? | 操作的管理员或群主信息,当null时为Bot操作 |
|
|
||||||
| operator.id | Long | 操作者的QQ号 |
|
|
||||||
| operator.memberName | String | 操作者的群名片 |
|
|
||||||
| operator.permission | String | 操作者在群中的权限,OWNER或ADMINISTRATOR |
|
|
||||||
| operator.group | Object | 同group |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 匿名聊天
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "GroupAllowAnonymousChatEvent",
|
|
||||||
"origin": false,
|
|
||||||
"new": true,
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
},
|
|
||||||
"operator": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是管理员",
|
|
||||||
"permission": "ADMINISTRATOR",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ------------------- | ------- | --------------------------------------------- |
|
|
||||||
| origin | Boolean | 原本匿名聊天是否开启 |
|
|
||||||
| new | Boolean | 现在匿名聊天是否开启 |
|
|
||||||
| group | Object | 匿名聊天状态改变的群信息 |
|
|
||||||
| group.id | Long | 群号 |
|
|
||||||
| group.name | String | 群名 |
|
|
||||||
| group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| operator | Object? | 操作的管理员或群主信息,当null时为Bot操作 |
|
|
||||||
| operator.id | Long | 操作者的QQ号 |
|
|
||||||
| operator.memberName | String | 操作者的群名片 |
|
|
||||||
| operator.permission | String | 操作者在群中的权限,OWNER或ADMINISTRATOR |
|
|
||||||
| operator.group | Object | 同group |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 坦白说
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "GroupAllowConfessTalkEvent",
|
|
||||||
"origin": false,
|
|
||||||
"new": true,
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
},
|
|
||||||
"isByBot": false
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ---------------- | ------- | --------------------------------------------- |
|
|
||||||
| origin | Boolean | 原本坦白说是否开启 |
|
|
||||||
| new | Boolean | 现在坦白说是否开启 |
|
|
||||||
| group | Object | 坦白说状态改变的群信息 |
|
|
||||||
| group.id | Long | 群号 |
|
|
||||||
| group.name | String | 群名 |
|
|
||||||
| group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| isByBot | Boolean | 是否Bot进行该操作 |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 允许群员邀请好友加群
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "GroupAllowMemberInviteEvent",
|
|
||||||
"origin": false,
|
|
||||||
"new": true,
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
},
|
|
||||||
"operator": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是管理员",
|
|
||||||
"permission": "ADMINISTRATOR",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ------------------- | ------- | --------------------------------------------- |
|
|
||||||
| origin | Boolean | 原本是否允许群员邀请好友加群 |
|
|
||||||
| new | Boolean | 现在是否允许群员邀请好友加群 |
|
|
||||||
| group | Object | 允许群员邀请好友加群状态改变的群信息 |
|
|
||||||
| group.id | Long | 群号 |
|
|
||||||
| group.name | String | 群名 |
|
|
||||||
| group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| operator | Object? | 操作的管理员或群主信息,当null时为Bot操作 |
|
|
||||||
| operator.id | Long | 操作者的QQ号 |
|
|
||||||
| operator.memberName | String | 操作者的群名片 |
|
|
||||||
| operator.permission | String | 操作者在群中的权限,OWNER或ADMINISTRATOR |
|
|
||||||
| operator.group | Object | 同group |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 新人入群的事件
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "MemberJoinEvent",
|
|
||||||
"member": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是新人",
|
|
||||||
"permission": "MEMBER",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ----------------------- | ------ | ------------------------------------------------------------ |
|
|
||||||
| member | Object | 新人信息 |
|
|
||||||
| member.id | Long | 新人的QQ号 |
|
|
||||||
| member.memberName | String | 新人的群名片 |
|
|
||||||
| member.permission | String | 新人在群中的权限,OWNER、ADMINISTRATOR或MEMBER(新入群通常是MEMBER) |
|
|
||||||
| member.group | Object | 新人入群的群信息 |
|
|
||||||
| member.group.id | Long | 群号 |
|
|
||||||
| member.group.name | String | 群名 |
|
|
||||||
| member.group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 成员被踢出群(该成员不是Bot)
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "MemberLeaveEventKick",
|
|
||||||
"member": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是被踢的",
|
|
||||||
"permission": "MEMBER",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"operator": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是管理员",
|
|
||||||
"permission": "ADMINISTRATOR",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ----------------------- | ------- | --------------------------------------------- |
|
|
||||||
| member | Object | 被踢者的信息 |
|
|
||||||
| member.id | Long | 被踢者的QQ号 |
|
|
||||||
| member.memberName | String | 被踢者的群名片 |
|
|
||||||
| member.permission | String | 被踢者在群中的权限,ADMINISTRATOR或MEMBER |
|
|
||||||
| member.group | Object | 被踢者所在的群 |
|
|
||||||
| member.group.id | Long | 群号 |
|
|
||||||
| member.group.name | String | 群名 |
|
|
||||||
| member.group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| operator | Object? | 操作的管理员或群主信息,当null时为Bot操作 |
|
|
||||||
| operator.id | Long | 操作者的QQ号 |
|
|
||||||
| operator.memberName | String | 操作者的群名片 |
|
|
||||||
| operator.permission | String | 操作者在群中的权限,OWNER或ADMINISTRATOR |
|
|
||||||
| operator.group | Object | 同member.group |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 成员主动离群(该成员不是Bot)
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "MemberLeaveEventQuit",
|
|
||||||
"member": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是被踢的",
|
|
||||||
"permission": "MEMBER",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ----------------------- | ------ | --------------------------------------------- |
|
|
||||||
| member | Object | 退群群员的信息 |
|
|
||||||
| member.id | Long | 退群群员的QQ号 |
|
|
||||||
| member.memberName | String | 退群群员的群名片 |
|
|
||||||
| member.permission | String | 退群群员在群中的权限,ADMINISTRATOR或MEMBER |
|
|
||||||
| member.group | Object | 退群群员所在的群信息 |
|
|
||||||
| member.group.id | Long | 群号 |
|
|
||||||
| member.group.name | String | 群名 |
|
|
||||||
| member.group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 群名片改动
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "MemberCardChangeEvent",
|
|
||||||
"origin": "origin name",
|
|
||||||
"new": "我是被改名的",
|
|
||||||
"member": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是被改名的",
|
|
||||||
"permission": "MEMBER",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"operator": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是管理员,也可能是我自己",
|
|
||||||
"permission": "ADMINISTRATOR",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ----------------------- | ------- | -------------------------------------------------------- |
|
|
||||||
| member | Object | 名片改动的群员的信息 |
|
|
||||||
| member.id | Long | 名片改动的群员的QQ号 |
|
|
||||||
| member.memberName | String | 名片改动的群员的群名片 |
|
|
||||||
| member.permission | String | 名片改动的群员在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| member.group | Object | 名片改动的群员所在群的信息 |
|
|
||||||
| member.group.id | Long | 群号 |
|
|
||||||
| member.group.name | String | 群名 |
|
|
||||||
| member.group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| operator | Object? | 操作者的信息,可能为该群员自己,当null时为Bot操作 |
|
|
||||||
| operator.id | Long | 操作者的QQ号 |
|
|
||||||
| operator.memberName | String | 操作者的群名片 |
|
|
||||||
| operator.permission | String | 操作者在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| operator.group | Object | 同member.group |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 群头衔改动(只有群主有操作限权)
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "MemberSpecialTitleChangeEvent",
|
|
||||||
"origin": "origin title",
|
|
||||||
"new": "new title",
|
|
||||||
"member": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是被改头衔的",
|
|
||||||
"permission": "MEMBER",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ----------------------- | ------ | -------------------------------------------------------- |
|
|
||||||
| origin | String | 原头衔 |
|
|
||||||
| new | String | 现头衔 |
|
|
||||||
| member | Object | 头衔改动的群员的信息 |
|
|
||||||
| member.id | Long | 头衔改动的群员的QQ号 |
|
|
||||||
| member.memberName | String | 头衔改动的群员的群名片 |
|
|
||||||
| member.permission | String | 头衔改动的群员在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| member.group | Object | 头衔改动的群员所在群的信息 |
|
|
||||||
| member.group.id | Long | 群号 |
|
|
||||||
| member.group.name | String | 群名 |
|
|
||||||
| member.group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 成员权限改变的事件(该成员不可能是Bot,见BotGroupPermissionChangeEvent)
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "MemberPermissionChangeEvent",
|
|
||||||
"origin": "MEMBER",
|
|
||||||
"new": "ADMINISTRATOR",
|
|
||||||
"member": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是被改权限的",
|
|
||||||
"permission": "ADMINISTRATOR",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ----------------------- | ------ | ------------------------------------------------- |
|
|
||||||
| origin | String | 原权限 |
|
|
||||||
| new | String | 现权限 |
|
|
||||||
| member | Object | 权限改动的群员的信息 |
|
|
||||||
| member.id | Long | 权限改动的群员的QQ号 |
|
|
||||||
| member.memberName | String | 权限改动的群员的群名片 |
|
|
||||||
| member.permission | String | 权限改动的群员在群中的权限,ADMINISTRATOR或MEMBER |
|
|
||||||
| member.group | Object | 权限改动的群员所在群的信息 |
|
|
||||||
| member.group.id | Long | 群号 |
|
|
||||||
| member.group.name | String | 群名 |
|
|
||||||
| member.group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 群成员被禁言事件(该成员不可能是Bot,见BotMuteEvent)
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "MemberMuteEvent",
|
|
||||||
"durationSeconds": 600,
|
|
||||||
"member": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是被禁言的",
|
|
||||||
"permission": "MEMBER",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"operator": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是管理员",
|
|
||||||
"permission": "ADMINISTRATOR",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ----------------------- | ------- | ----------------------------------------------- |
|
|
||||||
| durationSeconds | Long | 禁言时长,单位为秒 |
|
|
||||||
| member | Object | 被禁言的群员的信息 |
|
|
||||||
| member.id | Long | 被禁言的群员的QQ号 |
|
|
||||||
| member.memberName | String | 被禁言的群员的群名片 |
|
|
||||||
| member.permission | String | 被禁言的群员在群中的权限,ADMINISTRATOR或MEMBER |
|
|
||||||
| member.group | Object | 被禁言的群员所在群的信息 |
|
|
||||||
| member.group.id | Long | 群号 |
|
|
||||||
| member.group.name | String | 群名 |
|
|
||||||
| member.group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| operator | Object? | 操作者的信息,当null时为Bot操作 |
|
|
||||||
| operator.id | Long | 操作者的QQ号 |
|
|
||||||
| operator.memberName | String | 操作者的群名片 |
|
|
||||||
| operator.permission | String | 操作者在群中的权限,OWNER、ADMINISTRATOR |
|
|
||||||
| operator.group | Object | 同member.group |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 群成员被取消禁言事件(该成员不可能是Bot,见BotUnmuteEvent)
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "MemberUnmuteEvent",
|
|
||||||
"member": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是被取消禁言的",
|
|
||||||
"permission": "MEMBER",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"operator": {
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "我是管理员",
|
|
||||||
"permission": "ADMINISTRATOR",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ----------------------- | ------- | --------------------------------------------------- |
|
|
||||||
| member | Object | 被取消禁言的群员的信息 |
|
|
||||||
| member.id | Long | 被取消禁言的群员的QQ号 |
|
|
||||||
| member.memberName | String | 被取消禁言的群员的群名片 |
|
|
||||||
| member.permission | String | 被取消禁言的群员在群中的权限,ADMINISTRATOR或MEMBER |
|
|
||||||
| member.group | Object | 被取消禁言的群员所在群的信息 |
|
|
||||||
| member.group.id | Long | 群号 |
|
|
||||||
| member.group.name | String | 群名 |
|
|
||||||
| member.group.permission | String | Bot在群中的权限,OWNER、ADMINISTRATOR或MEMBER |
|
|
||||||
| operator | Object? | 操作者的信息,当null时为Bot操作 |
|
|
||||||
| operator.id | Long | 操作者的QQ号 |
|
|
||||||
| operator.memberName | String | 操作者的群名片 |
|
|
||||||
| operator.permission | String | 操作者在群中的权限,OWNER、ADMINISTRATOR |
|
|
||||||
| operator.group | Object | 同member.group |
|
|
||||||
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
|||||||
# mirai-api-http
|
|
||||||
|
|
||||||
<b>
|
|
||||||
Mirai-API-http provides adapter for ALL langugae to access mirai via HTTP protocol.<br>
|
|
||||||
</b>
|
|
||||||
|
|
||||||
**[中文](README_CH.md)**
|
|
||||||
|
|
||||||
|
|
||||||
### Start Session-Authorize
|
|
||||||
|
|
||||||
```php
|
|
||||||
Path: /auth
|
|
||||||
Method: POST
|
|
||||||
```
|
|
||||||
this verify your session to one bot and you could have full access to that bot<br>
|
|
||||||
NOTE that only 1 bot could be control under 1 session, you could have multiple session to control all bots.
|
|
||||||
|
|
||||||
#### Request:<br>
|
|
||||||
|
|
||||||
| name | type | optional|example|note|
|
|
||||||
| --- | --- | --- | --- | --- |
|
|
||||||
| key | String |false|U9HSaDXl39ksd918273hU|MIRAI API HTTP key, this could be found after initialize|
|
|
||||||
| qq | String |false|1040400290|bot QQ number you want to access|
|
|
||||||
|
|
||||||
|
|
||||||
#### Response if success:<br>
|
|
||||||
|
|
||||||
| name | type | example|note|
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| success |Boolean |true|if this session is authorized|
|
|
||||||
| session |String |UANSHDKSLAOISN|your session key|
|
|
||||||
|
|
||||||
|
|
||||||
#### Response if failed:<br>
|
|
||||||
|
|
||||||
| name | type | example|note|
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| success |Boolean |false|if this session is authorized|
|
|
||||||
| session |String |null|your session key|
|
|
||||||
| error |int |0|error code|
|
|
||||||
|
|
||||||
#### Error:<br>
|
|
||||||
|
|
||||||
| code | reason|
|
|
||||||
| --- | --- |
|
|
||||||
| 0 | wrong MIRAI API HTTP key |
|
|
||||||
| 1 | unknown bot number |
|
|
||||||
|
|
||||||
|
|
||||||
without session key, you are not able to access any method below.</br>
|
|
||||||
session key should be attached to your <b>cookies</b> like this:
|
|
||||||
|
|
||||||
| name | value |
|
|
||||||
| --- | --- |
|
|
||||||
| session |your session key here |
|
|
||||||
|
|
||||||
if you were getting HTTP error code 403, you should ask for a new session key.
|
|
@ -1,927 +0,0 @@
|
|||||||
# mirai-api-http
|
|
||||||
|
|
||||||
<b>Mirai-API-http 提供HTTP API供所有语言使用mirai</b>
|
|
||||||
|
|
||||||
## 快速开始
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
fun main() {
|
|
||||||
val bot = Bot(123456789, "password")
|
|
||||||
|
|
||||||
bot.login()
|
|
||||||
|
|
||||||
MiraiHttpAPIServer.start()
|
|
||||||
|
|
||||||
bot.join()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 认证相关
|
|
||||||
|
|
||||||
### 开始会话-认证(Authorize)
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /auth
|
|
||||||
```
|
|
||||||
使用此方法验证你的身份,并返回一个会话
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"authKey": "U9HSaDXl39ksd918273hU"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
|
||||||
| ------- | ------ | ----- | ----------------------- | ---------------------------------------------------------- |
|
|
||||||
| authKey | String | false | "U9HSaDXl39ksd918273hU" | 创建Mirai-Http-Server时生成的key,可在启动时指定或随机生成 |
|
|
||||||
|
|
||||||
#### 响应: 返回(成功):
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"session": "UnVerifiedSession"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 举例 | 说明 |
|
|
||||||
| ------- | ------ | ------------------- | --------------- |
|
|
||||||
| code | Int | 0 | 返回状态码 |
|
|
||||||
| session | String | "UnVerifiedSession" | 你的session key |
|
|
||||||
|
|
||||||
#### 状态码:
|
|
||||||
|
|
||||||
| 代码 | 原因 |
|
|
||||||
| ---- | ----------------------------- |
|
|
||||||
| 0 | 正常 |
|
|
||||||
| 1 | 错误的MIRAI API HTTP auth key |
|
|
||||||
|
|
||||||
session key 是使用以下方法必须携带的
|
|
||||||
session key 使用前必须进行校验和绑定指定的Bot,**每个Session只能绑定一个Bot,但一个Bot可有多个Session**
|
|
||||||
session Key 在未进行校验的情况下,一定时间后将会被自动释放
|
|
||||||
|
|
||||||
|
|
||||||
### 校验Session
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /verify
|
|
||||||
```
|
|
||||||
|
|
||||||
使用此方法校验并激活你的Session,同时将Session与一个**已登录**的Bot绑定
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "UnVerifiedSession",
|
|
||||||
"qq": 123456789
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
|
||||||
| ---------- | ------ | ----- | ------------------- | -------------------------- |
|
|
||||||
| sessionKey | String | false | "UnVerifiedSession" | 你的session key |
|
|
||||||
| qq | Long | false | 123456789 | Session将要绑定的Bot的qq号 |
|
|
||||||
|
|
||||||
#### 响应: 返回统一状态码(后续不再赘述)
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"msg": "success"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 状态码 | 原因 |
|
|
||||||
| ------ | ---------------------------------------- |
|
|
||||||
| 0 | 正常 |
|
|
||||||
| 1 | 错误的auth key |
|
|
||||||
| 2 | 指定的Bot不存在(常发生在Session认证时) |
|
|
||||||
| 3 | Session失效或不存在 |
|
|
||||||
| 4 | Session未认证(未激活) |
|
|
||||||
| 5 | 发送消息目标不存在(指定对象不存在) |
|
|
||||||
| 10 | 无操作权限,指Bot没有对应操作的限权 |
|
|
||||||
| 20 | Bot被禁言,指Bot当前无法向指定群发送消息 |
|
|
||||||
| 400 | 错误的访问,如参数错误等 |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 释放Session
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /release
|
|
||||||
```
|
|
||||||
|
|
||||||
使用此方式释放session及其相关资源(Bot不会被释放)
|
|
||||||
**不使用的Session应当被释放,否则Session持续保存Bot收到的消息**
|
|
||||||
**长时间(30分钟)未被使用的Session会被系统自动释放,以避免内存泄露**
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "YourSessionKey",
|
|
||||||
"qq": 123456789
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
|
||||||
| ---------- | ------ | ----- | -----------------| -------------------------- |
|
|
||||||
| sessionKey | String | false | "YourSessionKey" | 你的session key |
|
|
||||||
| qq | Long | false | 123456789 | 与该Session绑定Bot的QQ号码 |
|
|
||||||
|
|
||||||
#### 响应: 返回统一状态码
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"msg": "success"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
> SessionKey与Bot 对应错误时将会返回状态码5:指定对象不存在
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 消息相关
|
|
||||||
|
|
||||||
|
|
||||||
### 发送好友消息
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /sendFriendMessage
|
|
||||||
```
|
|
||||||
|
|
||||||
使用此方法向指定好友发送消息
|
|
||||||
|
|
||||||
#### 请求
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "YourSession",
|
|
||||||
"target": 987654321,
|
|
||||||
"messageChain": [
|
|
||||||
{ "type": "Plain", "text":"hello\n" },
|
|
||||||
{ "type": "Plain", "text":"world" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
|
||||||
| ------------ | ------ | ----- | ----------- | -------------------------------- |
|
|
||||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
|
||||||
| target | Long | false | 987654321 | 发送消息目标好友的QQ号 |
|
|
||||||
| quote | Long | true | 135798642 | 引用一条消息的messageId进行回复 |
|
|
||||||
| messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 |
|
|
||||||
|
|
||||||
#### 响应: 返回统一状态码(并携带messageId)
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"msg": "success",
|
|
||||||
"messageId": 1234567890 // 一个Long类型属性,标识本条消息,用于撤回和引用回复
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 发送群消息
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /sendGroupMessage
|
|
||||||
```
|
|
||||||
|
|
||||||
使用此方法向指定群发送消息
|
|
||||||
|
|
||||||
#### 请求
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "YourSession",
|
|
||||||
"target": 987654321,
|
|
||||||
"messageChain": [
|
|
||||||
{ "type": "Plain", "text":"hello\n" },
|
|
||||||
{ "type": "Plain", "text":"world" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
|
||||||
| ------------ | ------ | ----- | ----------- | -------------------------------- |
|
|
||||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
|
||||||
| target | Long | false | 987654321 | 发送消息目标群的群号 |
|
|
||||||
| quote | Long | true | 135798642 | 引用一条消息的messageId进行回复 |
|
|
||||||
| messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 |
|
|
||||||
|
|
||||||
#### 响应: 返回统一状态码(并携带messageId)
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"msg": "success",
|
|
||||||
"messageId": 1234567890 // 一个Long类型属性,标识本条消息,用于撤回和引用回复
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 发送图片消息(通过URL)
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /sendImageMessage
|
|
||||||
```
|
|
||||||
|
|
||||||
使用此方法向指定对象(群或好友)发送图片消息
|
|
||||||
|
|
||||||
#### 请求
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "YourSession",
|
|
||||||
"target": 987654321,
|
|
||||||
"qq": 1234567890,
|
|
||||||
"group": 987654321,
|
|
||||||
"urls": [
|
|
||||||
"https://xxx.yyy.zzz/",
|
|
||||||
"https://aaa.bbb.ccc/"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
|
||||||
| ------------ | ------ | ----- | ----------- | ---------------------------------- |
|
|
||||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
|
||||||
| target | Long | true | 987654321 | 发送对象的QQ号或群号,可能存在歧义 |
|
|
||||||
| qq | Long | true | 123456789 | 发送对象的QQ号 |
|
|
||||||
| group | Long | true | 987654321 | 发送对象的群号 |
|
|
||||||
| urls | Array | false | [] | 是一个url字符串构成的数组 |
|
|
||||||
|
|
||||||
#### 响应: 图片的imageId数组
|
|
||||||
|
|
||||||
```json5
|
|
||||||
[
|
|
||||||
"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}.jpg",
|
|
||||||
"{YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY}.jpg"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 图片文件上传
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /uploadImage
|
|
||||||
```
|
|
||||||
|
|
||||||
使用此方法上传图片文件至服务器并返回ImageId
|
|
||||||
|
|
||||||
#### 请求
|
|
||||||
|
|
||||||
Content-Type:multipart/form-data
|
|
||||||
|
|
||||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
|
||||||
| ------------ | ------ | ----- | ----------- | ---------------------------------- |
|
|
||||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
|
||||||
| type | String | false | "friend " | "friend" 或 "group" |
|
|
||||||
| img | File | false | - | 图片文件 |
|
|
||||||
|
|
||||||
|
|
||||||
#### 响应: 图片的imageId(好友图片与群聊图片Id不同)
|
|
||||||
|
|
||||||
```
|
|
||||||
{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 撤回消息
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /recall
|
|
||||||
```
|
|
||||||
|
|
||||||
使用此方法撤回指定消息。对于bot发送的消息,又2分钟时间限制。对于撤回群聊中群员的消息,需要有相应权限
|
|
||||||
|
|
||||||
#### 请求
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "YourSession",
|
|
||||||
"target": 987654321
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
|
||||||
| ------------ | ------ | ----- | ----------- | -------------------------------- |
|
|
||||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
|
||||||
| target | Long | false | 987654321 | 需要撤回的消息的messageId |
|
|
||||||
|
|
||||||
#### 响应: 返回统一状态码
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"msg": "success"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 获取Bot收到的消息和事件
|
|
||||||
|
|
||||||
```
|
|
||||||
[GET] /fetchMessage?sessionKey=YourSessionKey&count=10
|
|
||||||
```
|
|
||||||
|
|
||||||
使用此方法获取bot接收到的消息和各类事件
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
| 名字 | 可选 | 举例 | 说明 |
|
|
||||||
| ---------- | ----- | -------------- | -------------------- |
|
|
||||||
| sessionKey | false | YourSessionKey | 你的session key |
|
|
||||||
| count | false | 10 | 获取消息和事件的数量 |
|
|
||||||
|
|
||||||
#### 响应: 返回JSON对象
|
|
||||||
|
|
||||||
```json5
|
|
||||||
[{
|
|
||||||
"type": "GroupMessage", // 消息类型:GroupMessage或FriendMessage或各类Event
|
|
||||||
"messageChain": [{ // 消息链,是一个消息对象构成的数组
|
|
||||||
"type": "Source",
|
|
||||||
"uid": 123456
|
|
||||||
},{
|
|
||||||
"type": "Plain",
|
|
||||||
"text": "Miral牛逼"
|
|
||||||
}],
|
|
||||||
"sender": { // 发送者信息
|
|
||||||
"id": 123456789, // 发送者的QQ号码
|
|
||||||
"memberName": "化腾", // 发送者的群名片
|
|
||||||
"permission": "MEMBER", // 发送者的群限权:OWNER、ADMINISTRATOR或MEMBER
|
|
||||||
"group": { // 消息发送群的信息
|
|
||||||
"id": 1234567890, // 发送群的群号
|
|
||||||
"name": "Miral Technology", // 发送群的群名称
|
|
||||||
"permission": "MEMBER" // 发送群中,Bot的群限权
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},{
|
|
||||||
"type": "FriendMessage", // 消息类型:GroupMessage或FriendMessage或各类Event
|
|
||||||
"messageChain": [{ // 消息链,是一个消息对象构成的数组
|
|
||||||
"type": "Source",
|
|
||||||
"uid": 123456
|
|
||||||
},{
|
|
||||||
"type": "Plain",
|
|
||||||
"text": "Miral牛逼"
|
|
||||||
}],
|
|
||||||
"sender": { // 发送者信息
|
|
||||||
"id": 1234567890, // 发送者的QQ号码
|
|
||||||
"nickName": "", // 发送者的昵称
|
|
||||||
"remark": "" // 发送者的备注
|
|
||||||
}
|
|
||||||
},{
|
|
||||||
"type": "MemberMuteEvent", // 消息类型:GroupMessage或FriendMessage或各类Event
|
|
||||||
"durationSeconds": 600,
|
|
||||||
"member":{
|
|
||||||
"id": 123456789,
|
|
||||||
"memberName": "禁言对象",
|
|
||||||
"permission": "MEMBER",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"operator":{
|
|
||||||
"id": 987654321,
|
|
||||||
"memberName": "群主大人",
|
|
||||||
"permission": "OWNER",
|
|
||||||
"group": {
|
|
||||||
"id": 123456789,
|
|
||||||
"name": "Miral Technology",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 事件类型一览
|
|
||||||
[事件类型一览](EventType_CH.md)
|
|
||||||
|
|
||||||
> 事件为Bot被动接收的信息,无法主动构建
|
|
||||||
|
|
||||||
|
|
||||||
### 消息类型一览
|
|
||||||
|
|
||||||
#### 消息是构成消息链的基本对象,目前支持的消息类型有
|
|
||||||
|
|
||||||
+ [x] At,@消息
|
|
||||||
+ [x] AtAll,@全体成员
|
|
||||||
+ [x] Face,表情消息
|
|
||||||
+ [x] Plain,文字消息
|
|
||||||
+ [x] Image,图片消息
|
|
||||||
+ [ ] Xml,Xml卡片消息
|
|
||||||
+ [ ] 敬请期待
|
|
||||||
|
|
||||||
#### Source
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "Source",
|
|
||||||
"id": 123456
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ---- | ---- | ------------------------------------------------------------ |
|
|
||||||
| id | Long | 消息的识别号,用于引用回复(Source类型只在群消息中返回,且永远为chain的第一个元素) |
|
|
||||||
|
|
||||||
#### At
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "At",
|
|
||||||
"target": 123456,
|
|
||||||
"display": "@Mirai"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ------- | ------ | ---------------------------------------------- |
|
|
||||||
| target | Long | 群员QQ号 |
|
|
||||||
| dispaly | String | At时显示的文字,发送消息时无效,自动使用群名片 |
|
|
||||||
|
|
||||||
#### AtAll
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "AtAll"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ------- | ------ | ------------------------- |
|
|
||||||
| - | - | - |
|
|
||||||
|
|
||||||
#### Face
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "Face",
|
|
||||||
"faceId": 123
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ------ | ---- | ---------- |
|
|
||||||
| faceId | Int | QQ表情编号 |
|
|
||||||
|
|
||||||
#### Plain
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "Plain",
|
|
||||||
"text": "Mirai牛逼"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ---- | ------ | -------- |
|
|
||||||
| text | String | 文字消息 |
|
|
||||||
|
|
||||||
#### Image
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "Image",
|
|
||||||
"imageId": "{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png" //群图片格式
|
|
||||||
//"imageId": "/f8f1ab55-bf8e-4236-b55e-955848d7069f" //好友图片格式
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ------- | ------ | --------------------------------------- |
|
|
||||||
| imageId | String | 图片的imageId,群图片与好友图片格式不同 |
|
|
||||||
|
|
||||||
#### Xml
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"type": "Xml",
|
|
||||||
"xml": "XML"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
|
||||||
| ---- | ------ | ------- |
|
|
||||||
| xml | String | XML文本 |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 管理相关
|
|
||||||
|
|
||||||
### 获取好友列表
|
|
||||||
|
|
||||||
使用此方法获取bot的好友列表
|
|
||||||
|
|
||||||
```
|
|
||||||
[GET] /friendList?sessionKey=YourSessionKey
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
| 名字 | 可选 | 举例 | 说明 |
|
|
||||||
| ---------- | ----- | -------------- | --------------- |
|
|
||||||
| sessionKey | false | YourSessionKey | 你的session key |
|
|
||||||
|
|
||||||
#### 响应: 返回JSON对象
|
|
||||||
|
|
||||||
```json5
|
|
||||||
[{
|
|
||||||
"id":123456789,
|
|
||||||
"nickName":"",
|
|
||||||
"remark":""
|
|
||||||
},{
|
|
||||||
"id":987654321,
|
|
||||||
"nickName":"",
|
|
||||||
"remark":""
|
|
||||||
}]
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 获取群列表
|
|
||||||
|
|
||||||
使用此方法获取bot的群列表
|
|
||||||
|
|
||||||
```
|
|
||||||
[GET] /groupList?sessionKey=YourSessionKey
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
| 名字 | 可选 | 举例 | 说明 |
|
|
||||||
| ---------- | ----- | -------------- | --------------- |
|
|
||||||
| sessionKey | false | YourSessionKey | 你的session key |
|
|
||||||
|
|
||||||
#### 响应: 返回JSON对象
|
|
||||||
|
|
||||||
```json5
|
|
||||||
[{
|
|
||||||
"id":123456789,
|
|
||||||
"name":"群名1",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
},{
|
|
||||||
"id":987654321,
|
|
||||||
"name":"群名2",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}]
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 获取群成员列表
|
|
||||||
|
|
||||||
使用此方法获取bot指定群种的成员列表
|
|
||||||
|
|
||||||
```
|
|
||||||
[GET] /memberList?sessionKey=YourSessionKey
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
| 名字 | 可选 | 举例 | 说明 |
|
|
||||||
| ---------- | ----- | -------------- | --------------- |
|
|
||||||
| sessionKey | false | YourSessionKey | 你的session key |
|
|
||||||
| target | false | 123456789 | 指定群的群号 |
|
|
||||||
|
|
||||||
#### 响应: 返回JSON对象
|
|
||||||
|
|
||||||
```json5
|
|
||||||
[{
|
|
||||||
"id":1234567890,
|
|
||||||
"memberName":"",
|
|
||||||
"permission":"MEMBER",
|
|
||||||
"group":{
|
|
||||||
"id":12345,
|
|
||||||
"name":"群名1",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
},{
|
|
||||||
"id":9876543210,
|
|
||||||
"memberName":"",
|
|
||||||
"permission":"OWNER",
|
|
||||||
"group":{
|
|
||||||
"id":54321,
|
|
||||||
"name":"群名2",
|
|
||||||
"permission": "MEMBER"
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 群全体禁言
|
|
||||||
|
|
||||||
使用此方法令指定群进行全体禁言(需要有相关限权)
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /muteAll
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "YourSessionKey",
|
|
||||||
"target": 123456789,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
|
||||||
| ---------- | ----- | ------ | ---------------- | --------------- |
|
|
||||||
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
|
||||||
| target | false | Long | 123456789 | 指定群的群号 |
|
|
||||||
|
|
||||||
|
|
||||||
#### 响应: 返回统一状态码
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"msg": "success"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 群解除全体禁言
|
|
||||||
|
|
||||||
使用此方法令指定群解除全体禁言(需要有相关限权)
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /unmuteAll
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
同全体禁言
|
|
||||||
|
|
||||||
#### 响应
|
|
||||||
|
|
||||||
同全体禁言
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 群禁言群成员
|
|
||||||
|
|
||||||
使用此方法指定群禁言指定群员(需要有相关限权)
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /mute
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "YourSessionKey",
|
|
||||||
"target": 123456789,
|
|
||||||
"memberId": 987654321,
|
|
||||||
"time": 1800
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
|
||||||
| ---------- | ----- | ------ | ---------------- | ------------------------------------- |
|
|
||||||
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
|
||||||
| target | false | Long | 123456789 | 指定群的群号 |
|
|
||||||
| memberId | false | Long | 987654321 | 指定群员QQ号 |
|
|
||||||
| time | true | Int | 1800 | 禁言时长,单位为秒,最多30天,默认为0 |
|
|
||||||
|
|
||||||
#### 响应: 返回统一状态码
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"msg": "success"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 群解除群成员禁言
|
|
||||||
|
|
||||||
使用此方法令指定群解除全体禁言(需要有相关限权)
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /unmute
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "YourSessionKey",
|
|
||||||
"target": 123456789,
|
|
||||||
"memberId": 987654321
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 响应
|
|
||||||
|
|
||||||
同群禁言群成员
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 移除群成员
|
|
||||||
|
|
||||||
使用此方法移除指定群成员(需要有相关限权)
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /kick
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "YourSessionKey",
|
|
||||||
"target": 123456789,
|
|
||||||
"memberId": 987654321,
|
|
||||||
"msg": "您已被移出群聊"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
|
||||||
| ---------- | ----- | ------ | ---------------- | --------------- |
|
|
||||||
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
|
||||||
| target | false | Long | 123456789 | 指定群的群号 |
|
|
||||||
| memberId | false | Long | 987654321 | 指定群员QQ号 |
|
|
||||||
| msg | true | String | "" | 信息 |
|
|
||||||
|
|
||||||
#### 响应
|
|
||||||
|
|
||||||
#### 响应: 返回统一状态码
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"msg": "success"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 群设置
|
|
||||||
|
|
||||||
使用此方法修改群设置(需要有相关限权)
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /groupConfig
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "YourSessionKey",
|
|
||||||
"target": 123456789,
|
|
||||||
"config": {
|
|
||||||
"name": "群名称",
|
|
||||||
"announcement": "群公告",
|
|
||||||
"confessTalk": true,
|
|
||||||
"allowMemberInvite": true,
|
|
||||||
"autoApprove": true,
|
|
||||||
"anonymousChat": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
|
||||||
| ----------------- | ----- | ------- | ---------------- | -------------------- |
|
|
||||||
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
|
||||||
| target | false | Long | 123456789 | 指定群的群号 |
|
|
||||||
| config | false | Object | {} | 群设置 |
|
|
||||||
| name | true | String | "Name" | 群名 |
|
|
||||||
| announcement | true | Boolean | true | 群公告 |
|
|
||||||
| confessTalk | true | Boolean | true | 是否开启坦白说 |
|
|
||||||
| allowMemberInvite | true | Boolean | true | 是否运行群员邀请 |
|
|
||||||
| autoApprove | true | Boolean | true | 是否开启自动审批入群 |
|
|
||||||
| anonymousChat | true | Boolean | true | 是否允许匿名聊天 |
|
|
||||||
|
|
||||||
#### 响应: 返回统一状态码
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"msg": "success"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 获取群设置
|
|
||||||
|
|
||||||
使用此方法获取群设置
|
|
||||||
|
|
||||||
```
|
|
||||||
[Get] /groupConfig?sessionKey=YourSessionKey&target=123456789
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
|
||||||
| ----------------- | ----- | ------- | ---------------- | -------------------- |
|
|
||||||
| sessionKey | false | String | YourSessionKey | 你的session key |
|
|
||||||
| target | false | Long | 123456789 | 指定群的群号 |
|
|
||||||
|
|
||||||
|
|
||||||
#### 响应
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"name": "群名称",
|
|
||||||
"announcement": "群公告",
|
|
||||||
"confessTalk": true,
|
|
||||||
"allowMemberInvite": true,
|
|
||||||
"autoApprove": true,
|
|
||||||
"anonymousChat": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 修改群员资料
|
|
||||||
|
|
||||||
使用此方法修改群员资料(需要有相关限权)
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /memberInfo
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "YourSessionKey",
|
|
||||||
"target": 123456789,
|
|
||||||
"memberId": 987654321,
|
|
||||||
"info": {
|
|
||||||
"name": "群名片",
|
|
||||||
"specialTitle": "群头衔"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
|
||||||
| ----------------- | ----- | ------- | ---------------- | -------------------- |
|
|
||||||
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
|
||||||
| target | false | Long | 123456789 | 指定群的群号 |
|
|
||||||
| memberId | false | Long | 987654321 | 群员QQ号 |
|
|
||||||
| info | false | Object | {} | 群员资料 |
|
|
||||||
| name | true | String | "Name" | 群名片,即群昵称 |
|
|
||||||
| specialTitle | true | String | "Title" | 群头衔 |
|
|
||||||
|
|
||||||
#### 响应: 返回统一状态码
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"msg": "success"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 获取群员资料
|
|
||||||
|
|
||||||
使用此方法获取群员资料
|
|
||||||
|
|
||||||
```
|
|
||||||
[Get] /memberInfo?sessionKey=YourSessionKey&target=123456789
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 请求:
|
|
||||||
|
|
||||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
|
||||||
| ----------------- | ----- | ------- | ---------------- | -------------------- |
|
|
||||||
| sessionKey | false | String | YourSessionKey | 你的session key |
|
|
||||||
| target | false | Long | 123456789 | 指定群的群号 |
|
|
||||||
| memberId | false | Long | 987654321 | 群员QQ号 |
|
|
||||||
|
|
||||||
|
|
||||||
#### 响应
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"name": "群名片",
|
|
||||||
"announcement": "群头衔"
|
|
||||||
}
|
|
||||||
```
|
|
@ -1,73 +0,0 @@
|
|||||||
@file:Suppress("UNUSED_VARIABLE")
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id("kotlinx-atomicfu")
|
|
||||||
kotlin("jvm")
|
|
||||||
id("kotlinx-serialization")
|
|
||||||
}
|
|
||||||
|
|
||||||
group = "net.mamoe.mirai"
|
|
||||||
version = rootProject.ext["mirai_version"].toString()
|
|
||||||
|
|
||||||
description = "Mirai Http Api"
|
|
||||||
|
|
||||||
val kotlinVersion: String by rootProject.ext
|
|
||||||
val atomicFuVersion: String by rootProject.ext
|
|
||||||
val coroutinesVersion: String by rootProject.ext
|
|
||||||
val kotlinXIoVersion: String by rootProject.ext
|
|
||||||
val coroutinesIoVersion: String by rootProject.ext
|
|
||||||
|
|
||||||
val klockVersion: String by rootProject.ext
|
|
||||||
val ktorVersion: String by rootProject.ext
|
|
||||||
|
|
||||||
val serializationVersion: String by rootProject.ext
|
|
||||||
|
|
||||||
fun kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$version"
|
|
||||||
|
|
||||||
fun ktor(id: String, version: String = this@Build_gradle.ktorVersion) = "io.ktor:ktor-$id:$version"
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
|
|
||||||
|
|
||||||
sourceSets["main"].apply {
|
|
||||||
dependencies {
|
|
||||||
implementation(project(":mirai-core-qqandroid"))
|
|
||||||
|
|
||||||
implementation(kotlin("stdlib-jdk8", kotlinVersion))
|
|
||||||
implementation(kotlin("stdlib-jdk7", kotlinVersion))
|
|
||||||
implementation(kotlin("reflect", kotlinVersion))
|
|
||||||
|
|
||||||
implementation(ktor("server-cio"))
|
|
||||||
implementation(kotlinx("io-jvm", kotlinXIoVersion))
|
|
||||||
implementation(ktor("http-jvm"))
|
|
||||||
implementation("org.slf4j:slf4j-simple:1.7.30")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets["test"].apply {
|
|
||||||
dependencies {
|
|
||||||
}
|
|
||||||
kotlin.outputDir = file("build/classes/kotlin/jvm/test")
|
|
||||||
kotlin.setSrcDirs(listOf("src/$name/kotlin"))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets.all {
|
|
||||||
languageSettings.enableLanguageFeature("InlineClasses")
|
|
||||||
|
|
||||||
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(kotlin("stdlib", kotlinVersion))
|
|
||||||
implementation(kotlin("serialization", kotlinVersion))
|
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlinx:atomicfu:$atomicFuVersion")
|
|
||||||
implementation(kotlinx("io", kotlinXIoVersion))
|
|
||||||
implementation(kotlinx("coroutines-io", coroutinesIoVersion))
|
|
||||||
implementation(kotlinx("coroutines-core", coroutinesVersion))
|
|
||||||
implementation(kotlinx("serialization-runtime", serializationVersion))
|
|
||||||
implementation(ktor("server-core"))
|
|
||||||
implementation(ktor("http"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http
|
|
||||||
|
|
||||||
import io.ktor.application.Application
|
|
||||||
import io.ktor.server.cio.CIO
|
|
||||||
import io.ktor.server.engine.applicationEngineEnvironment
|
|
||||||
import io.ktor.server.engine.connector
|
|
||||||
import io.ktor.server.engine.embeddedServer
|
|
||||||
import io.ktor.util.KtorExperimentalAPI
|
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import net.mamoe.mirai.api.http.route.mirai
|
|
||||||
import net.mamoe.mirai.utils.DefaultLogger
|
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
|
||||||
import org.slf4j.helpers.NOPLoggerFactory
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
|
|
||||||
object MiraiHttpAPIServer : CoroutineScope {
|
|
||||||
|
|
||||||
var logger: MiraiLogger = DefaultLogger("Mirai HTTP API")
|
|
||||||
|
|
||||||
override val coroutineContext: CoroutineContext =
|
|
||||||
CoroutineExceptionHandler { _, throwable -> logger.error(throwable) }
|
|
||||||
|
|
||||||
init {
|
|
||||||
SessionManager.authKey = generateSessionKey()//用于验证的key, 使用和SessionKey相同的方法生成, 但意义不同
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setAuthKey(key: String) {
|
|
||||||
SessionManager.authKey = key
|
|
||||||
}
|
|
||||||
|
|
||||||
@UseExperimental(KtorExperimentalAPI::class)
|
|
||||||
fun start(
|
|
||||||
port: Int = 8080,
|
|
||||||
authKey: String,
|
|
||||||
callback: (() -> Unit)? = null
|
|
||||||
) {
|
|
||||||
require(authKey.length in 8..128) { "Expected authKey length is between 8 to 128" }
|
|
||||||
SessionManager.authKey = authKey
|
|
||||||
|
|
||||||
// TODO: start是无阻塞的,理应获取启动状态后再执行后续代码
|
|
||||||
launch {
|
|
||||||
embeddedServer(CIO, environment = applicationEngineEnvironment {
|
|
||||||
this.parentCoroutineContext = coroutineContext
|
|
||||||
this.log = NOPLoggerFactory().getLogger("NMYSL")
|
|
||||||
this.module(Application::mirai)
|
|
||||||
|
|
||||||
connector {
|
|
||||||
this.port = port
|
|
||||||
}
|
|
||||||
}).start(wait = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("Http api server is running with authKey: ${SessionManager.authKey}")
|
|
||||||
callback?.invoke()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,138 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http
|
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import net.mamoe.mirai.Bot
|
|
||||||
import net.mamoe.mirai.api.http.queue.MessageQueue
|
|
||||||
import net.mamoe.mirai.event.Listener
|
|
||||||
import net.mamoe.mirai.event.events.BotEvent
|
|
||||||
import net.mamoe.mirai.event.subscribeAlways
|
|
||||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
|
||||||
|
|
||||||
tailrec fun generateSessionKey(): String {
|
|
||||||
fun generateRandomSessionKey(): String {
|
|
||||||
val all = "QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm"
|
|
||||||
return buildString(capacity = 8) {
|
|
||||||
repeat(8) {
|
|
||||||
append(all.random())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val key = generateRandomSessionKey()
|
|
||||||
if (!SessionManager.allSession.containsKey(key)) {
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
return generateSessionKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal object SessionManager {
|
|
||||||
|
|
||||||
val allSession: MutableMap<String, Session> = mutableMapOf()
|
|
||||||
|
|
||||||
lateinit var authKey: String
|
|
||||||
|
|
||||||
|
|
||||||
fun createTempSession(): TempSession = TempSession(EmptyCoroutineContext).also { newTempSession ->
|
|
||||||
allSession[newTempSession.key] = newTempSession
|
|
||||||
//设置180000ms后检测并回收
|
|
||||||
newTempSession.launch {
|
|
||||||
delay(180000)
|
|
||||||
allSession[newTempSession.key]?.run {
|
|
||||||
if (this is TempSession)
|
|
||||||
closeSession(newTempSession.key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createAuthedSession(bot: Bot, originKey: String): AuthedSession = AuthedSession(bot, originKey, EmptyCoroutineContext).also { session ->
|
|
||||||
closeSession(originKey)
|
|
||||||
allSession[originKey] = session
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(sessionKey: String) = allSession[sessionKey]?.also {
|
|
||||||
if (it is AuthedSession) it.latestUsed = currentTimeSeconds }
|
|
||||||
|
|
||||||
fun containSession(sessionKey: String): Boolean = allSession.containsKey(sessionKey)
|
|
||||||
|
|
||||||
fun closeSession(sessionKey: String) = allSession.remove(sessionKey)?.also { it.close() }
|
|
||||||
|
|
||||||
fun closeSession(session: Session) = closeSession(session.key)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 管理不同 Client 与 Mirai HTTP 的会话
|
|
||||||
*
|
|
||||||
* [Session] 均为内部操作用类
|
|
||||||
* @see [SessionManager]
|
|
||||||
* @author NaturalHG
|
|
||||||
*/
|
|
||||||
internal abstract class Session internal constructor(
|
|
||||||
coroutineContext: CoroutineContext,
|
|
||||||
val key: String = generateSessionKey()
|
|
||||||
) : CoroutineScope {
|
|
||||||
private val supervisorJob = SupervisorJob(coroutineContext[Job])
|
|
||||||
final override val coroutineContext: CoroutineContext = supervisorJob + coroutineContext
|
|
||||||
|
|
||||||
internal open fun close() {
|
|
||||||
supervisorJob.complete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 任何新链接建立后分配一个[TempSession]
|
|
||||||
*
|
|
||||||
* TempSession在建立180s内没有转变为[AuthedSession]应被清除
|
|
||||||
*/
|
|
||||||
internal class TempSession internal constructor(coroutineContext: CoroutineContext) : Session(coroutineContext)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 任何[TempSession]认证后转化为一个[AuthedSession]
|
|
||||||
* 在这一步[AuthedSession]应该已经有assigned的bot
|
|
||||||
*/
|
|
||||||
internal class AuthedSession internal constructor(val bot: Bot, originKey: String, coroutineContext: CoroutineContext) : Session(coroutineContext, originKey) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val CHECK_TIME = 1800L // 1800s aka 30min
|
|
||||||
}
|
|
||||||
|
|
||||||
val messageQueue = MessageQueue()
|
|
||||||
private val _listener: Listener<BotEvent>
|
|
||||||
private val releaseJob: Job //手动释放将会在下一次检查时回收Session
|
|
||||||
|
|
||||||
internal var latestUsed = currentTimeSeconds
|
|
||||||
|
|
||||||
init {
|
|
||||||
_listener = bot.subscribeAlways{ this.run(messageQueue::add) }
|
|
||||||
releaseJob = launch {
|
|
||||||
while (true) {
|
|
||||||
delay(CHECK_TIME * 1000)
|
|
||||||
if (currentTimeSeconds - latestUsed >= CHECK_TIME) {
|
|
||||||
SessionManager.closeSession(this@AuthedSession)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
messageQueue.clear()
|
|
||||||
_listener.complete()
|
|
||||||
super.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.data
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import net.mamoe.mirai.api.http.data.common.DTO
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
open class StateCode(val code: Int, var msg: String) : DTO {
|
|
||||||
object Success : StateCode(0, "success") // 成功
|
|
||||||
object NoBot : StateCode(2, "指定Bot不存在")
|
|
||||||
object IllegalSession : StateCode(3, "Session失效或不存在")
|
|
||||||
object NotVerifySession : StateCode(4, "Session未认证")
|
|
||||||
object NoElement : StateCode(5, "指定对象不存在")
|
|
||||||
object PermissionDenied : StateCode(10, "无操作权限")
|
|
||||||
object BotMuted : StateCode(20, "Bot被禁言")
|
|
||||||
|
|
||||||
// KS bug: 主构造器中不能有非字段参数 https://github.com/Kotlin/kotlinx.serialization/issues/575
|
|
||||||
@Serializable
|
|
||||||
class IllegalAccess() : StateCode(400, "") { // 非法访问
|
|
||||||
constructor(msg: String) : this() {
|
|
||||||
this.msg = msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,126 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.data.common
|
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import net.mamoe.mirai.contact.MemberPermission
|
|
||||||
import net.mamoe.mirai.event.events.*
|
|
||||||
import net.mamoe.mirai.message.MessagePacket
|
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
sealed class BotEventDTO : EventDTO()
|
|
||||||
|
|
||||||
@UseExperimental(MiraiExperimentalAPI::class)
|
|
||||||
suspend fun BotEvent.toDTO() = when(this) {
|
|
||||||
is MessagePacket<*, *> -> toDTO()
|
|
||||||
else -> when(this) {
|
|
||||||
is BotOnlineEvent -> BotOnlineEventDTO(bot.uin)
|
|
||||||
is BotOfflineEvent.Active -> BotOfflineEventActiveDTO(bot.uin)
|
|
||||||
is BotOfflineEvent.Force -> BotOfflineEventForceDTO(bot.uin, title, message)
|
|
||||||
is BotOfflineEvent.Dropped -> BotOfflineEventDroppedDTO(bot.uin)
|
|
||||||
is BotReloginEvent -> BotReloginEventDTO(bot.uin)
|
|
||||||
// is MessageSendEvent.GroupMessageSendEvent -> {}
|
|
||||||
// is MessageSendEvent.FriendMessageSendEvent -> {}
|
|
||||||
// is BeforeImageUploadEvent -> {}
|
|
||||||
// is ImageUploadEvent.Succeed -> {}
|
|
||||||
is BotGroupPermissionChangeEvent -> BotGroupPermissionChangeEventDTO(origin, new, GroupDTO(group))
|
|
||||||
is BotMuteEvent -> BotMuteEventDTO(durationSeconds, MemberDTO(operator))
|
|
||||||
is BotUnmuteEvent -> BotUnmuteEventDTO(MemberDTO(operator))
|
|
||||||
is BotJoinGroupEvent -> BotJoinGroupEventDTO(GroupDTO(group))
|
|
||||||
// is GroupSettingChangeEvent<*> -> {} // 不知道会改什么
|
|
||||||
is GroupNameChangeEvent -> GroupNameChangeEventDTO(origin, new, GroupDTO(group), isByBot)
|
|
||||||
is GroupEntranceAnnouncementChangeEvent -> GroupEntranceAnnouncementChangeEventDTO(origin, new, GroupDTO(group), operator?.let(::MemberDTO))
|
|
||||||
is GroupMuteAllEvent -> GroupMuteAllEventDTO(origin, new, GroupDTO(group), operator?.let(::MemberDTO))
|
|
||||||
is GroupAllowAnonymousChatEvent -> GroupAllowAnonymousChatEventDTO(origin, new, GroupDTO(group), operator?.let(::MemberDTO))
|
|
||||||
is GroupAllowConfessTalkEvent -> GroupAllowConfessTalkEventDTO(origin, new, GroupDTO(group), isByBot)
|
|
||||||
is GroupAllowMemberInviteEvent -> GroupAllowMemberInviteEventDTO(origin, new, GroupDTO(group), operator?.let(::MemberDTO))
|
|
||||||
is MemberJoinEvent -> MemberJoinEventDTO(MemberDTO(member))
|
|
||||||
is MemberLeaveEvent.Kick -> MemberLeaveEventKickDTO(MemberDTO(member), operator?.let(::MemberDTO))
|
|
||||||
is MemberLeaveEvent.Quit -> MemberLeaveEventQuitDTO(MemberDTO(member))
|
|
||||||
is MemberCardChangeEvent -> MemberCardChangeEventDTO(origin, new, MemberDTO(member), operator?.let(::MemberDTO))
|
|
||||||
is MemberSpecialTitleChangeEvent -> MemberSpecialTitleChangeEventDTO(origin, new, MemberDTO(member))
|
|
||||||
is MemberPermissionChangeEvent -> MemberPermissionChangeEventDTO(origin, new, MemberDTO(member))
|
|
||||||
is MemberMuteEvent -> MemberMuteEventDTO(durationSeconds, MemberDTO(member), operator?.let(::MemberDTO))
|
|
||||||
is MemberUnmuteEvent -> MemberUnmuteEventDTO(MemberDTO(member), operator?.let(::MemberDTO))
|
|
||||||
else -> IgnoreEventDTO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
@SerialName("BotOnlineEvent")
|
|
||||||
data class BotOnlineEventDTO(val qq: Long) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("BotOfflineEventActive")
|
|
||||||
data class BotOfflineEventActiveDTO(val qq: Long) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("BotOfflineEventForce")
|
|
||||||
data class BotOfflineEventForceDTO(val qq: Long, val title: String, val message: String) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("BotOfflineEventDropped")
|
|
||||||
data class BotOfflineEventDroppedDTO(val qq: Long) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("BotReloginEvent")
|
|
||||||
data class BotReloginEventDTO(val qq: Long) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("BotGroupPermissionChangeEvent")
|
|
||||||
data class BotGroupPermissionChangeEventDTO(val origin: MemberPermission, val new: MemberPermission, val group: GroupDTO) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("BotMuteEvent")
|
|
||||||
data class BotMuteEventDTO(val durationSeconds: Int, val operator: MemberDTO) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("BotUnmuteEvent")
|
|
||||||
data class BotUnmuteEventDTO(val operator: MemberDTO) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("BotJoinGroupEvent")
|
|
||||||
data class BotJoinGroupEventDTO(val group: GroupDTO) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("GroupNameChangeEvent")
|
|
||||||
data class GroupNameChangeEventDTO(val origin: String, val new: String, val group: GroupDTO, val isByBot: Boolean) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("GroupEntranceAnnouncementChangeEvent")
|
|
||||||
data class GroupEntranceAnnouncementChangeEventDTO(val origin: String, val new: String, val group: GroupDTO, val operator: MemberDTO?) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("GroupMuteAllEvent")
|
|
||||||
data class GroupMuteAllEventDTO(val origin: Boolean, val new: Boolean, val group: GroupDTO, val operator: MemberDTO?) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("GroupAllowAnonymousChatEvent")
|
|
||||||
data class GroupAllowAnonymousChatEventDTO(val origin: Boolean, val new: Boolean, val group: GroupDTO, val operator: MemberDTO?) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("GroupAllowConfessTalkEvent")
|
|
||||||
data class GroupAllowConfessTalkEventDTO(val origin: Boolean, val new: Boolean, val group: GroupDTO, val isByBot: Boolean) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("GroupAllowMemberInviteEvent")
|
|
||||||
data class GroupAllowMemberInviteEventDTO(val origin: Boolean, val new: Boolean, val group: GroupDTO, val operator: MemberDTO?) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("MemberJoinEvent")
|
|
||||||
data class MemberJoinEventDTO(val member: MemberDTO) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("MemberLeaveEventKick")
|
|
||||||
data class MemberLeaveEventKickDTO(val member: MemberDTO, val operator: MemberDTO?) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("MemberLeaveEventQuit")
|
|
||||||
data class MemberLeaveEventQuitDTO(val member: MemberDTO) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("MemberCardChangeEvent")
|
|
||||||
data class MemberCardChangeEventDTO(val origin: String, val new: String, val member: MemberDTO, val operator: MemberDTO?) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("MemberSpecialTitleChangeEvent")
|
|
||||||
data class MemberSpecialTitleChangeEventDTO(val origin: String, val new: String, val member: MemberDTO) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("MemberPermissionChangeEvent")
|
|
||||||
data class MemberPermissionChangeEventDTO(val origin: MemberPermission, val new: MemberPermission, val member: MemberDTO) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("MemberMuteEvent")
|
|
||||||
data class MemberMuteEventDTO(val durationSeconds: Int, val member: MemberDTO, val operator: MemberDTO?) : BotEventDTO()
|
|
||||||
@Serializable
|
|
||||||
@SerialName("MemberUnmuteEvent")
|
|
||||||
data class MemberUnmuteEventDTO(val member: MemberDTO, val operator: MemberDTO?) : BotEventDTO()
|
|
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.data.common
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import net.mamoe.mirai.contact.*
|
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
abstract class ContactDTO : DTO {
|
|
||||||
abstract val id: Long
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class QQDTO(
|
|
||||||
override val id: Long,
|
|
||||||
val nickName: String,
|
|
||||||
val remark: String
|
|
||||||
) : ContactDTO() {
|
|
||||||
// TODO: queryProfile.nickname & queryRemark.value not support now
|
|
||||||
constructor(qq: QQ) : this(qq.id, "", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class MemberDTO(
|
|
||||||
override val id: Long,
|
|
||||||
val memberName: String,
|
|
||||||
val permission: MemberPermission,
|
|
||||||
val group: GroupDTO
|
|
||||||
) : ContactDTO() {
|
|
||||||
constructor(member: Member) : this(
|
|
||||||
member.id, member.nameCardOrNick, member.permission,
|
|
||||||
GroupDTO(member.group)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class GroupDTO(
|
|
||||||
override val id: Long,
|
|
||||||
val name: String,
|
|
||||||
val permission: MemberPermission
|
|
||||||
) : ContactDTO() {
|
|
||||||
@UseExperimental(MiraiExperimentalAPI::class)
|
|
||||||
constructor(group: Group) : this(group.id, group.name, group.botPermission)
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.data.common
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.Transient
|
|
||||||
import net.mamoe.mirai.api.http.AuthedSession
|
|
||||||
|
|
||||||
interface DTO
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class AuthDTO(val authKey: String) : DTO
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
abstract class VerifyDTO : DTO {
|
|
||||||
abstract val sessionKey: String
|
|
||||||
@Transient
|
|
||||||
internal lateinit var session: AuthedSession // 反序列化验证成功后传入
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
abstract class EventDTO : DTO
|
|
||||||
|
|
||||||
object IgnoreEventDTO : EventDTO()
|
|
@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.data.common
|
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import net.mamoe.mirai.contact.Contact
|
|
||||||
import net.mamoe.mirai.contact.Group
|
|
||||||
import net.mamoe.mirai.message.FriendMessage
|
|
||||||
import net.mamoe.mirai.message.GroupMessage
|
|
||||||
import net.mamoe.mirai.message.MessagePacket
|
|
||||||
import net.mamoe.mirai.message.data.*
|
|
||||||
import net.mamoe.mirai.message.uploadImage
|
|
||||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
/*
|
|
||||||
* DTO data class
|
|
||||||
* */
|
|
||||||
|
|
||||||
// MessagePacket
|
|
||||||
@Serializable
|
|
||||||
@SerialName("FriendMessage")
|
|
||||||
data class FriendMessagePacketDTO(val sender: QQDTO) : MessagePacketDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
@SerialName("GroupMessage")
|
|
||||||
data class GroupMessagePacketDTO(val sender: MemberDTO) : MessagePacketDTO()
|
|
||||||
|
|
||||||
|
|
||||||
// Message
|
|
||||||
@Serializable
|
|
||||||
@SerialName("Source")
|
|
||||||
data class MessageSourceDTO(val id: Long) : MessageDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
@SerialName("At")
|
|
||||||
data class AtDTO(val target: Long, val display: String = "") : MessageDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
@SerialName("AtAll")
|
|
||||||
data class AtAllDTO(val target: Long = 0) : MessageDTO() // target为保留字段
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
@SerialName("Face")
|
|
||||||
data class FaceDTO(val faceId: Int) : MessageDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
@SerialName("Plain")
|
|
||||||
data class PlainDTO(val text: String) : MessageDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
@SerialName("Image")
|
|
||||||
data class ImageDTO(val imageId: String? = null, val url: String? = null) : MessageDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
@SerialName("Xml")
|
|
||||||
data class XmlDTO(val xml: String) : MessageDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
@SerialName("Unknown")
|
|
||||||
object UnknownMessageDTO : MessageDTO()
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Abstract Class
|
|
||||||
* */
|
|
||||||
@Serializable
|
|
||||||
sealed class MessagePacketDTO : EventDTO() {
|
|
||||||
lateinit var messageChain: MessageChainDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
typealias MessageChainDTO = List<MessageDTO>
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
sealed class MessageDTO : DTO
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Extend function
|
|
||||||
*/
|
|
||||||
suspend fun MessagePacket<*, *>.toDTO() = when (this) {
|
|
||||||
is FriendMessage -> FriendMessagePacketDTO(QQDTO(sender))
|
|
||||||
is GroupMessage -> GroupMessagePacketDTO(MemberDTO(sender))
|
|
||||||
else -> IgnoreEventDTO
|
|
||||||
}.apply {
|
|
||||||
if (this is MessagePacketDTO) {
|
|
||||||
// 将MessagePacket中的所有Message转为DTO对象,并添加到messageChain
|
|
||||||
// foreachContent会忽略MessageSource,一次主动获取
|
|
||||||
messageChain = mutableListOf(messageDTO(message[MessageSource])).apply {
|
|
||||||
message.foreachContent { content -> messageDTO(content).takeUnless { it == UnknownMessageDTO }?.let(::add) }
|
|
||||||
}
|
|
||||||
// else: `this` is bot event
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun MessageChainDTO.toMessageChain(contact: Contact) =
|
|
||||||
buildMessageChain { this@toMessageChain.forEach { it.toMessage(contact)?.let(::add) } }
|
|
||||||
|
|
||||||
|
|
||||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
|
||||||
suspend fun MessagePacket<*, *>.messageDTO(message: Message) = when (message) {
|
|
||||||
is MessageSource -> MessageSourceDTO(message.id)
|
|
||||||
is At -> AtDTO(message.target, message.display)
|
|
||||||
is AtAll -> AtAllDTO(0L)
|
|
||||||
is Face -> FaceDTO(message.id)
|
|
||||||
is PlainText -> PlainDTO(message.stringValue)
|
|
||||||
is Image -> ImageDTO(message.imageId, message.url())
|
|
||||||
is XMLMessage -> XmlDTO(message.stringValue)
|
|
||||||
else -> UnknownMessageDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
|
||||||
suspend fun MessageDTO.toMessage(contact: Contact) = when (this) {
|
|
||||||
is AtDTO -> At((contact as Group)[target])
|
|
||||||
is AtAllDTO -> AtAll
|
|
||||||
is FaceDTO -> Face(faceId)
|
|
||||||
is PlainDTO -> PlainText(text)
|
|
||||||
is ImageDTO -> when {
|
|
||||||
!imageId.isNullOrBlank() -> Image(imageId)
|
|
||||||
!url.isNullOrBlank() -> contact.uploadImage(URL(url))
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
is XmlDTO -> XMLMessage(xml)
|
|
||||||
is MessageSourceDTO, is UnknownMessageDTO -> null
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.data
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 错误请求. 抛出这个异常后将会返回错误给一个请求
|
|
||||||
*/
|
|
||||||
@Suppress("unused")
|
|
||||||
open class IllegalAccessException : Exception {
|
|
||||||
override val message: String get() = super.message!!
|
|
||||||
|
|
||||||
constructor(message: String) : super(message, null)
|
|
||||||
constructor(cause: Throwable) : super(cause.toString(), cause)
|
|
||||||
constructor(message: String, cause: Throwable?) : super(message, cause)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session失效或不存在
|
|
||||||
*/
|
|
||||||
object IllegalSessionException : IllegalAccessException("Session失效或不存在")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session未激活
|
|
||||||
*/
|
|
||||||
object NotVerifiedSessionException : IllegalAccessException("Session未激活")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 指定Bot不存在
|
|
||||||
*/
|
|
||||||
object NoSuchBotException: IllegalAccessException("指定Bot不存在")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 错误参数
|
|
||||||
*/
|
|
||||||
class IllegalParamException(message: String) : IllegalAccessException(message)
|
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.queue
|
|
||||||
|
|
||||||
import net.mamoe.mirai.api.http.data.common.EventDTO
|
|
||||||
import net.mamoe.mirai.api.http.data.common.IgnoreEventDTO
|
|
||||||
import net.mamoe.mirai.api.http.data.common.toDTO
|
|
||||||
import net.mamoe.mirai.event.events.BotEvent
|
|
||||||
import net.mamoe.mirai.message.MessagePacket
|
|
||||||
import net.mamoe.mirai.message.data.MessageSource
|
|
||||||
import net.mamoe.mirai.utils.firstKey
|
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque
|
|
||||||
|
|
||||||
class MessageQueue : ConcurrentLinkedDeque<BotEvent>() {
|
|
||||||
|
|
||||||
val cacheSize = 4096
|
|
||||||
val cache = LinkedHashMap<Long, MessagePacket<*, *>>()
|
|
||||||
|
|
||||||
suspend fun fetch(size: Int): List<EventDTO> {
|
|
||||||
var count = size
|
|
||||||
|
|
||||||
val ret = ArrayList<EventDTO>(count)
|
|
||||||
while (!this.isEmpty() && count > 0) {
|
|
||||||
val event = pop()
|
|
||||||
|
|
||||||
event.toDTO().also {
|
|
||||||
if (it != IgnoreEventDTO) {
|
|
||||||
ret.add(it)
|
|
||||||
count--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event is MessagePacket<*, *>) {
|
|
||||||
addQuoteCache(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cache(messageId: Long) =
|
|
||||||
cache[messageId] ?: throw NoSuchElementException()
|
|
||||||
|
|
||||||
fun addQuoteCache(msg: MessagePacket<*, *>) {
|
|
||||||
cache[msg.message[MessageSource].id] = msg
|
|
||||||
if (cache.size > cacheSize) {
|
|
||||||
cache.remove(cache.firstKey())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.route
|
|
||||||
|
|
||||||
import io.ktor.application.Application
|
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.routing.routing
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import net.mamoe.mirai.Bot
|
|
||||||
import net.mamoe.mirai.api.http.AuthedSession
|
|
||||||
import net.mamoe.mirai.api.http.SessionManager
|
|
||||||
import net.mamoe.mirai.api.http.data.NoSuchBotException
|
|
||||||
import net.mamoe.mirai.api.http.data.StateCode
|
|
||||||
import net.mamoe.mirai.api.http.data.common.DTO
|
|
||||||
import net.mamoe.mirai.api.http.data.common.VerifyDTO
|
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
|
||||||
|
|
||||||
|
|
||||||
fun Application.authModule() {
|
|
||||||
routing {
|
|
||||||
miraiAuth("/auth") {
|
|
||||||
if (it.authKey != SessionManager.authKey) {
|
|
||||||
call.respondStateCode(StateCode(1, "Auth Key错误"))
|
|
||||||
} else {
|
|
||||||
call.respondDTO(AuthRetDTO(0, SessionManager.createTempSession().key))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<BindDTO>("/verify", verifiedSessionKey = false) {
|
|
||||||
val bot = getBotOrThrow(it.qq)
|
|
||||||
SessionManager.createAuthedSession(bot, it.sessionKey)
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<BindDTO>("/release") {
|
|
||||||
val bot = getBotOrThrow(it.qq)
|
|
||||||
val session = SessionManager[it.sessionKey] as AuthedSession
|
|
||||||
if (bot.uin == session.bot.uin) {
|
|
||||||
SessionManager.closeSession(it.sessionKey)
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
} else {
|
|
||||||
throw NoSuchElementException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class AuthRetDTO(val code: Int, val session: String) : DTO
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class BindDTO(override val sessionKey: String, val qq: Long) : VerifyDTO()
|
|
||||||
|
|
||||||
private fun getBotOrThrow(qq: Long) = try {
|
|
||||||
Bot.getInstance(qq)
|
|
||||||
} catch (e: NoSuchElementException) {
|
|
||||||
throw NoSuchBotException
|
|
||||||
}
|
|
@ -1,214 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.route
|
|
||||||
|
|
||||||
import io.ktor.application.Application
|
|
||||||
import io.ktor.application.ApplicationCall
|
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.application.install
|
|
||||||
import io.ktor.features.CallLogging
|
|
||||||
import io.ktor.features.DefaultHeaders
|
|
||||||
import io.ktor.http.ContentType
|
|
||||||
import io.ktor.http.HttpMethod
|
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import io.ktor.http.content.PartData
|
|
||||||
import io.ktor.request.receive
|
|
||||||
import io.ktor.response.defaultTextContentType
|
|
||||||
import io.ktor.response.respond
|
|
||||||
import io.ktor.response.respondText
|
|
||||||
import io.ktor.routing.Route
|
|
||||||
import io.ktor.routing.route
|
|
||||||
import io.ktor.util.pipeline.ContextDsl
|
|
||||||
import io.ktor.util.pipeline.PipelineContext
|
|
||||||
import net.mamoe.mirai.api.http.AuthedSession
|
|
||||||
import net.mamoe.mirai.api.http.SessionManager
|
|
||||||
import net.mamoe.mirai.api.http.TempSession
|
|
||||||
import net.mamoe.mirai.api.http.data.*
|
|
||||||
import net.mamoe.mirai.api.http.data.common.AuthDTO
|
|
||||||
import net.mamoe.mirai.api.http.data.common.DTO
|
|
||||||
import net.mamoe.mirai.api.http.data.common.VerifyDTO
|
|
||||||
import net.mamoe.mirai.api.http.util.jsonParseOrNull
|
|
||||||
import net.mamoe.mirai.api.http.util.toJson
|
|
||||||
import net.mamoe.mirai.contact.PermissionDeniedException
|
|
||||||
import org.slf4j.helpers.NOPLoggerFactory
|
|
||||||
|
|
||||||
fun Application.mirai() {
|
|
||||||
install(DefaultHeaders)
|
|
||||||
install(CallLogging) {
|
|
||||||
logger = NOPLoggerFactory().getLogger("NMSL")
|
|
||||||
|
|
||||||
}
|
|
||||||
authModule()
|
|
||||||
messageModule()
|
|
||||||
infoModule()
|
|
||||||
groupManageModule()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auth,处理http server的验证
|
|
||||||
* 为闭包传入一个AuthDTO对象
|
|
||||||
*/
|
|
||||||
@ContextDsl
|
|
||||||
internal fun Route.miraiAuth(
|
|
||||||
path: String,
|
|
||||||
body: suspend PipelineContext<Unit, ApplicationCall>.(AuthDTO) -> Unit
|
|
||||||
): Route {
|
|
||||||
return route(path, HttpMethod.Post) {
|
|
||||||
intercept {
|
|
||||||
val dto = context.receiveDTO<AuthDTO>() ?: throw IllegalParamException("参数格式错误")
|
|
||||||
this.body(dto)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get,用于获取bot的属性
|
|
||||||
* 验证请求参数中sessionKey参数的有效性
|
|
||||||
*/
|
|
||||||
@ContextDsl
|
|
||||||
internal fun Route.miraiGet(
|
|
||||||
path: String,
|
|
||||||
body: suspend PipelineContext<Unit, ApplicationCall>.(AuthedSession) -> Unit
|
|
||||||
): Route {
|
|
||||||
return route(path, HttpMethod.Get) {
|
|
||||||
intercept {
|
|
||||||
val sessionKey = call.parameters["sessionKey"] ?: throw IllegalParamException("参数格式错误")
|
|
||||||
if (!SessionManager.containSession(sessionKey)) throw IllegalSessionException
|
|
||||||
|
|
||||||
when (val session = SessionManager[sessionKey]) {
|
|
||||||
is TempSession -> throw NotVerifiedSessionException
|
|
||||||
is AuthedSession -> this.body(session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify,用于处理bot的行为请求
|
|
||||||
* 验证数据传输对象(DTO)中是否包含sessionKey字段
|
|
||||||
* 且验证sessionKey的有效性
|
|
||||||
*
|
|
||||||
* @param verifiedSessionKey 是否验证sessionKey是否被激活
|
|
||||||
*
|
|
||||||
* it 为json解析出的DTO对象
|
|
||||||
*/
|
|
||||||
@ContextDsl
|
|
||||||
internal inline fun <reified T : VerifyDTO> Route.miraiVerify(
|
|
||||||
path: String,
|
|
||||||
verifiedSessionKey: Boolean = true,
|
|
||||||
crossinline body: suspend PipelineContext<Unit, ApplicationCall>.(T) -> Unit
|
|
||||||
): Route {
|
|
||||||
return route(path, HttpMethod.Post) {
|
|
||||||
intercept {
|
|
||||||
val dto = context.receiveDTO<T>() ?: throw IllegalParamException("参数格式错误")
|
|
||||||
SessionManager[dto.sessionKey]?.let {
|
|
||||||
when {
|
|
||||||
it is TempSession && verifiedSessionKey -> throw NotVerifiedSessionException
|
|
||||||
it is AuthedSession -> dto.session = it
|
|
||||||
}
|
|
||||||
} ?: throw IllegalSessionException
|
|
||||||
|
|
||||||
this.body(dto)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 统一捕获并处理异常
|
|
||||||
*/
|
|
||||||
internal inline fun Route.intercept(crossinline blk: suspend PipelineContext<Unit, ApplicationCall>.() -> Unit) = handle {
|
|
||||||
try {
|
|
||||||
blk(this)
|
|
||||||
} catch (e: NoSuchBotException) { // Bot不存在
|
|
||||||
call.respondStateCode(StateCode.NoBot)
|
|
||||||
} catch (e: IllegalSessionException) { // Session过期
|
|
||||||
call.respondStateCode(StateCode.IllegalSession)
|
|
||||||
} catch (e: NotVerifiedSessionException) { // Session未认证
|
|
||||||
call.respondStateCode(StateCode.NotVerifySession)
|
|
||||||
} catch (e: NoSuchElementException) { // 指定对象不存在
|
|
||||||
call.respondStateCode(StateCode.NoElement)
|
|
||||||
} catch (e: PermissionDeniedException) { // 缺少权限
|
|
||||||
call.respondStateCode(StateCode.PermissionDenied)
|
|
||||||
} catch (e: IllegalStateException) { // Bot被禁言
|
|
||||||
call.respondStateCode(StateCode.BotMuted)
|
|
||||||
} catch (e: IllegalAccessException) { // 错误访问
|
|
||||||
call.respondStateCode(StateCode(400, e.message), HttpStatusCode.BadRequest)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
e.printStackTrace()
|
|
||||||
call.respond(HttpStatusCode.InternalServerError, e.message!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
extend function
|
|
||||||
*/
|
|
||||||
internal suspend inline fun <reified T : StateCode> ApplicationCall.respondStateCode(code: T, status: HttpStatusCode = HttpStatusCode.OK) =
|
|
||||||
respondJson(code.toJson(StateCode.serializer()), status)
|
|
||||||
|
|
||||||
internal suspend inline fun <reified T : DTO> ApplicationCall.respondDTO(dto: T, status: HttpStatusCode = HttpStatusCode.OK) =
|
|
||||||
respondJson(dto.toJson(), status)
|
|
||||||
|
|
||||||
internal suspend fun ApplicationCall.respondJson(json: String, status: HttpStatusCode = HttpStatusCode.OK) =
|
|
||||||
respondText(json, defaultTextContentType(ContentType("application", "json")), status)
|
|
||||||
|
|
||||||
internal suspend inline fun <reified T : DTO> ApplicationCall.receiveDTO(): T? = receive<String>().jsonParseOrNull()
|
|
||||||
|
|
||||||
|
|
||||||
fun PipelineContext<Unit, ApplicationCall>.illegalParam(
|
|
||||||
expectingType: String?,
|
|
||||||
paramName: String,
|
|
||||||
actualValue: String? = call.parameters[paramName]
|
|
||||||
): Nothing = throw IllegalParamException("Illegal param. A $expectingType is required for `$paramName` while `$actualValue` is given")
|
|
||||||
|
|
||||||
|
|
||||||
@Suppress("IMPLICIT_CAST_TO_ANY")
|
|
||||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
|
||||||
internal inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNull(name: String): R =
|
|
||||||
when (R::class) {
|
|
||||||
Byte::class -> call.parameters[name]?.toByte()
|
|
||||||
Int::class -> call.parameters[name]?.toInt()
|
|
||||||
Short::class -> call.parameters[name]?.toShort()
|
|
||||||
Float::class -> call.parameters[name]?.toFloat()
|
|
||||||
Long::class -> call.parameters[name]?.toLong()
|
|
||||||
Double::class -> call.parameters[name]?.toDouble()
|
|
||||||
Boolean::class -> when (call.parameters[name]) {
|
|
||||||
"true" -> true
|
|
||||||
"false" -> false
|
|
||||||
"0" -> false
|
|
||||||
"1" -> true
|
|
||||||
null -> null
|
|
||||||
else -> illegalParam("boolean", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
String::class -> call.parameters[name]
|
|
||||||
|
|
||||||
UByte::class -> call.parameters[name]?.toUByte()
|
|
||||||
UInt::class -> call.parameters[name]?.toUInt()
|
|
||||||
UShort::class -> call.parameters[name]?.toUShort()
|
|
||||||
|
|
||||||
else -> error(name::class.simpleName + " is not supported")
|
|
||||||
} as R ?: illegalParam(R::class.simpleName, name)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* multi part
|
|
||||||
*/
|
|
||||||
internal fun List<PartData>.value(name: String) =
|
|
||||||
try {
|
|
||||||
(filter { it.name == name }[0] as PartData.FormItem).value
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw IllegalParamException("参数格式错误")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun List<PartData>.file(name: String) =
|
|
||||||
try {
|
|
||||||
filter { it.name == name }[0] as? PartData.FileItem
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw IllegalParamException("参数格式错误")
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.route
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.route
|
|
||||||
|
|
||||||
import io.ktor.application.Application
|
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.routing.routing
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import net.mamoe.mirai.api.http.data.StateCode
|
|
||||||
import net.mamoe.mirai.api.http.data.common.DTO
|
|
||||||
import net.mamoe.mirai.api.http.data.common.VerifyDTO
|
|
||||||
import net.mamoe.mirai.contact.Group
|
|
||||||
import net.mamoe.mirai.contact.Member
|
|
||||||
|
|
||||||
|
|
||||||
fun Application.groupManageModule() {
|
|
||||||
routing {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 禁言(需要相关权限)
|
|
||||||
*/
|
|
||||||
miraiVerify<MuteDTO>("/muteAll") {
|
|
||||||
it.session.bot.getGroup(it.target).isMuteAll = true
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<MuteDTO>("/unmuteAll") {
|
|
||||||
it.session.bot.getGroup(it.target).isMuteAll = false
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<MuteDTO>("/mute") {
|
|
||||||
it.session.bot.getGroup(it.target)[it.memberId].mute(it.time)
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<MuteDTO>("/unmute") {
|
|
||||||
it.session.bot.getGroup(it.target).members[it.memberId].unmute()
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 移出群聊(需要相关权限)
|
|
||||||
*/
|
|
||||||
miraiVerify<KickDTO>("/kick") {
|
|
||||||
it.session.bot.getGroup(it.target)[it.memberId].kick(it.msg)
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 群设置(需要相关权限)
|
|
||||||
*/
|
|
||||||
miraiGet("/groupConfig") {
|
|
||||||
val group = it.bot.getGroup(paramOrNull("target"))
|
|
||||||
call.respondDTO(GroupDetailDTO(group))
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<GroupConfigDTO>("/groupConfig") { dto ->
|
|
||||||
val group = dto.session.bot.getGroup(dto.target)
|
|
||||||
with(dto.config) {
|
|
||||||
name?.let { group.name = it }
|
|
||||||
announcement?.let { group.entranceAnnouncement = it }
|
|
||||||
confessTalk?.let { group.isConfessTalkEnabled = it }
|
|
||||||
allowMemberInvite?.let { group.isAllowMemberInvite = it }
|
|
||||||
// TODO: 待core接口实现设置可改
|
|
||||||
// autoApprove?.let { group.autoApprove = it }
|
|
||||||
// anonymousChat?.let { group.anonymousChat = it }
|
|
||||||
}
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 群员信息管理(需要相关权限)
|
|
||||||
*/
|
|
||||||
miraiGet("/memberInfo") {
|
|
||||||
val member = it.bot.getGroup(paramOrNull("target"))[paramOrNull("memberId")]
|
|
||||||
call.respondDTO(MemberDetailDTO(member))
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<MemberInfoDTO>("/memberInfo") { dto ->
|
|
||||||
val member = dto.session.bot.getGroup(dto.target)[dto.memberId]
|
|
||||||
with(dto.info) {
|
|
||||||
name?.let { member.nameCard = it }
|
|
||||||
specialTitle?.let { member.specialTitle = it }
|
|
||||||
}
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class MuteDTO(
|
|
||||||
override val sessionKey: String,
|
|
||||||
val target: Long,
|
|
||||||
val memberId: Long = 0,
|
|
||||||
val time: Int = 0
|
|
||||||
) : VerifyDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class KickDTO(
|
|
||||||
override val sessionKey: String,
|
|
||||||
val target: Long,
|
|
||||||
val memberId: Long,
|
|
||||||
val msg: String = ""
|
|
||||||
) : VerifyDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class GroupConfigDTO(
|
|
||||||
override val sessionKey: String,
|
|
||||||
val target: Long,
|
|
||||||
val config: GroupDetailDTO
|
|
||||||
) : VerifyDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class GroupDetailDTO(
|
|
||||||
val name: String? = null,
|
|
||||||
val announcement: String? = null,
|
|
||||||
val confessTalk: Boolean? = null,
|
|
||||||
val allowMemberInvite: Boolean? = null,
|
|
||||||
val autoApprove: Boolean? = null,
|
|
||||||
val anonymousChat: Boolean? = null
|
|
||||||
) : DTO {
|
|
||||||
constructor(group: Group) : this(
|
|
||||||
group.name, group.entranceAnnouncement, group.isConfessTalkEnabled, group.isAllowMemberInvite,
|
|
||||||
group.isAutoApproveEnabled, group.isAnonymousChatEnabled
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class MemberInfoDTO(
|
|
||||||
override val sessionKey: String,
|
|
||||||
val target: Long,
|
|
||||||
val memberId: Long,
|
|
||||||
val info: MemberDetailDTO
|
|
||||||
) : VerifyDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class MemberDetailDTO(
|
|
||||||
val name: String? = null,
|
|
||||||
val specialTitle: String? = null
|
|
||||||
) : DTO {
|
|
||||||
constructor(member: Member) : this(member.nameCard, member.specialTitle)
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.route
|
|
||||||
|
|
||||||
import io.ktor.application.Application
|
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.routing.routing
|
|
||||||
import net.mamoe.mirai.api.http.data.common.GroupDTO
|
|
||||||
import net.mamoe.mirai.api.http.data.common.MemberDTO
|
|
||||||
import net.mamoe.mirai.api.http.data.common.QQDTO
|
|
||||||
import net.mamoe.mirai.api.http.util.toJson
|
|
||||||
import net.mamoe.mirai.contact.toMutableList
|
|
||||||
|
|
||||||
fun Application.infoModule() {
|
|
||||||
routing {
|
|
||||||
|
|
||||||
miraiGet("/friendList") {
|
|
||||||
val ls = it.bot.qqs.toMutableList().map { qq -> QQDTO(qq) }
|
|
||||||
call.respondJson(ls.toJson())
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiGet("/groupList") {
|
|
||||||
val ls = it.bot.groups.toMutableList().map { group -> GroupDTO(group) }
|
|
||||||
call.respondJson(ls.toJson())
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiGet("/memberList") {
|
|
||||||
val ls = it.bot.getGroup(paramOrNull("target")).members.toMutableList().map { member -> MemberDTO(member) }
|
|
||||||
call.respondJson(ls.toJson())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,168 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.route
|
|
||||||
|
|
||||||
import io.ktor.application.Application
|
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import io.ktor.http.content.readAllParts
|
|
||||||
import io.ktor.http.content.streamProvider
|
|
||||||
import io.ktor.request.receiveMultipart
|
|
||||||
import io.ktor.response.respond
|
|
||||||
import io.ktor.response.respondText
|
|
||||||
import io.ktor.routing.post
|
|
||||||
import io.ktor.routing.routing
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import net.mamoe.mirai.api.http.AuthedSession
|
|
||||||
import net.mamoe.mirai.api.http.SessionManager
|
|
||||||
import net.mamoe.mirai.api.http.data.*
|
|
||||||
import net.mamoe.mirai.api.http.data.common.DTO
|
|
||||||
import net.mamoe.mirai.api.http.data.common.MessageChainDTO
|
|
||||||
import net.mamoe.mirai.api.http.data.common.VerifyDTO
|
|
||||||
import net.mamoe.mirai.api.http.data.common.toMessageChain
|
|
||||||
import net.mamoe.mirai.api.http.util.toJson
|
|
||||||
import net.mamoe.mirai.contact.Contact
|
|
||||||
import net.mamoe.mirai.message.FriendMessage
|
|
||||||
import net.mamoe.mirai.message.GroupMessage
|
|
||||||
import net.mamoe.mirai.message.MessageReceipt
|
|
||||||
import net.mamoe.mirai.message.data.*
|
|
||||||
import net.mamoe.mirai.message.uploadImage
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
fun Application.messageModule() {
|
|
||||||
routing {
|
|
||||||
|
|
||||||
miraiGet("/fetchMessage") {
|
|
||||||
val count: Int = paramOrNull("count")
|
|
||||||
val fetch = it.messageQueue.fetch(count)
|
|
||||||
|
|
||||||
call.respondJson(fetch.toJson())
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun <C : Contact> sendMessage(
|
|
||||||
quote: QuoteReplyToSend?,
|
|
||||||
messageChain: MessageChain,
|
|
||||||
target: C
|
|
||||||
): MessageReceipt<out Contact> {
|
|
||||||
val send = if (quote == null) {
|
|
||||||
messageChain
|
|
||||||
} else {
|
|
||||||
(quote + messageChain).toChain()
|
|
||||||
}
|
|
||||||
return target.sendMessage(send)
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<SendDTO>("/sendFriendMessage") {
|
|
||||||
val quote = it.quote?.let { q ->
|
|
||||||
it.session.messageQueue.cache(q).run {
|
|
||||||
this[MessageSource].quote(sender)
|
|
||||||
}}
|
|
||||||
|
|
||||||
it.session.bot.getFriend(it.target).apply {
|
|
||||||
val receipt = sendMessage(quote, it.messageChain.toMessageChain(this), this)
|
|
||||||
receipt.source.ensureSequenceIdAvailable()
|
|
||||||
|
|
||||||
it.session.messageQueue.addQuoteCache(FriendMessage(bot.selfQQ, receipt.source.toChain()))
|
|
||||||
call.respondDTO(SendRetDTO(messageId = receipt.source.id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<SendDTO>("/sendGroupMessage") {
|
|
||||||
val quote = it.quote?.let { q ->
|
|
||||||
it.session.messageQueue.cache(q).run {
|
|
||||||
this[MessageSource].quote(sender)
|
|
||||||
}}
|
|
||||||
|
|
||||||
it.session.bot.getGroup(it.target).apply {
|
|
||||||
val receipt = sendMessage(quote, it.messageChain.toMessageChain(this), this)
|
|
||||||
receipt.source.ensureSequenceIdAvailable()
|
|
||||||
|
|
||||||
it.session.messageQueue.addQuoteCache(GroupMessage("", botPermission, botAsMember, receipt.source.toChain()))
|
|
||||||
call.respondDTO(SendRetDTO(messageId = receipt.source.id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<SendImageDTO>("sendImageMessage") {
|
|
||||||
val bot = it.session.bot
|
|
||||||
val contact = when {
|
|
||||||
it.target != null -> bot[it.target]
|
|
||||||
it.qq != null -> bot.getFriend(it.qq)
|
|
||||||
it.group != null -> bot.getGroup(it.group)
|
|
||||||
else -> throw IllegalParamException("target、qq、group不可全为null")
|
|
||||||
}
|
|
||||||
val ls = it.urls.map { url -> contact.uploadImage(URL(url)) }
|
|
||||||
contact.sendMessage(MessageChain(ls))
|
|
||||||
call.respondJson(ls.map { image -> image.imageId }.toJson())
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 重构
|
|
||||||
post("uploadImage") {
|
|
||||||
val parts = call.receiveMultipart().readAllParts()
|
|
||||||
val sessionKey = parts.value("sessionKey")
|
|
||||||
if (!SessionManager.containSession(sessionKey)) throw IllegalSessionException
|
|
||||||
val session = try {
|
|
||||||
SessionManager[sessionKey] as AuthedSession
|
|
||||||
} catch (e: TypeCastException) {
|
|
||||||
throw NotVerifiedSessionException
|
|
||||||
}
|
|
||||||
|
|
||||||
val type = parts.value("type")
|
|
||||||
parts.file("img")?.apply {
|
|
||||||
val image = streamProvider().use {
|
|
||||||
when (type) {
|
|
||||||
"group" -> session.bot.groups.firstOrNull()?.uploadImage(it)
|
|
||||||
"friend" -> session.bot.qqs.firstOrNull()?.uploadImage(it)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
image?.apply {
|
|
||||||
call.respondText(imageId)
|
|
||||||
} ?: throw IllegalAccessException("图片上传错误")
|
|
||||||
} ?: throw IllegalAccessException("未知错误")
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<RecallDTO>("recall") {
|
|
||||||
it.session.messageQueue.cache(it.target).apply {
|
|
||||||
it.session.bot.recall(get(MessageSource))
|
|
||||||
}
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class SendDTO(
|
|
||||||
override val sessionKey: String,
|
|
||||||
val quote: Long? = null,
|
|
||||||
val target: Long,
|
|
||||||
val messageChain: MessageChainDTO
|
|
||||||
) : VerifyDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class SendImageDTO(
|
|
||||||
override val sessionKey: String,
|
|
||||||
val target: Long? = null,
|
|
||||||
val qq: Long? = null,
|
|
||||||
val group: Long? = null,
|
|
||||||
val urls: List<String>
|
|
||||||
) : VerifyDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private class SendRetDTO(
|
|
||||||
val code: Int = 0,
|
|
||||||
val msg: String = "success",
|
|
||||||
val messageId: Long
|
|
||||||
) : DTO
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class RecallDTO(
|
|
||||||
override val sessionKey: String,
|
|
||||||
val target: Long
|
|
||||||
) : VerifyDTO()
|
|
@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.api.http.util
|
|
||||||
|
|
||||||
import kotlinx.serialization.*
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlinx.serialization.modules.SerializersModule
|
|
||||||
import net.mamoe.mirai.api.http.data.common.*
|
|
||||||
|
|
||||||
// 解析失败时直接返回null,由路由判断响应400状态
|
|
||||||
@UseExperimental(ImplicitReflectionSerializer::class)
|
|
||||||
inline fun <reified T : Any> String.jsonParseOrNull(
|
|
||||||
serializer: DeserializationStrategy<T>? = null
|
|
||||||
): T? = try {
|
|
||||||
if(serializer == null) MiraiJson.json.parse(this) else Json.parse(this)
|
|
||||||
} catch (e: Exception) { null }
|
|
||||||
|
|
||||||
|
|
||||||
@UseExperimental(ImplicitReflectionSerializer::class, UnstableDefault::class)
|
|
||||||
inline fun <reified T : Any> T.toJson(
|
|
||||||
serializer: SerializationStrategy<T>? = null
|
|
||||||
): String = if (serializer == null) MiraiJson.json.stringify(this)
|
|
||||||
else MiraiJson.json.stringify(serializer, this)
|
|
||||||
|
|
||||||
|
|
||||||
// 序列化列表时,stringify需要使用的泛型是T,而非List<T>
|
|
||||||
// 因为使用的stringify的stringify(objs: List<T>)重载
|
|
||||||
@UseExperimental(ImplicitReflectionSerializer::class, UnstableDefault::class)
|
|
||||||
inline fun <reified T : Any> List<T>.toJson(
|
|
||||||
serializer: SerializationStrategy<List<T>>? = null
|
|
||||||
): String = if (serializer == null) MiraiJson.json.stringify(this)
|
|
||||||
else MiraiJson.json.stringify(serializer, this)
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Json解析规则,需要注册支持的多态的类
|
|
||||||
*/
|
|
||||||
object MiraiJson {
|
|
||||||
val json = Json(context = SerializersModule {
|
|
||||||
|
|
||||||
polymorphic(EventDTO.serializer()) {
|
|
||||||
GroupMessagePacketDTO::class with GroupMessagePacketDTO.serializer()
|
|
||||||
FriendMessagePacketDTO::class with FriendMessagePacketDTO.serializer()
|
|
||||||
|
|
||||||
BotOnlineEventDTO::class with BotOnlineEventDTO.serializer()
|
|
||||||
BotOfflineEventActiveDTO::class with BotOfflineEventActiveDTO.serializer()
|
|
||||||
BotOfflineEventForceDTO::class with BotOfflineEventForceDTO.serializer()
|
|
||||||
BotOfflineEventDroppedDTO::class with BotOfflineEventDroppedDTO.serializer()
|
|
||||||
BotReloginEventDTO::class with BotReloginEventDTO.serializer()
|
|
||||||
BotGroupPermissionChangeEventDTO::class with BotGroupPermissionChangeEventDTO.serializer()
|
|
||||||
BotMuteEventDTO::class with BotMuteEventDTO.serializer()
|
|
||||||
BotUnmuteEventDTO::class with BotUnmuteEventDTO.serializer()
|
|
||||||
BotJoinGroupEventDTO::class with BotJoinGroupEventDTO.serializer()
|
|
||||||
GroupNameChangeEventDTO::class with GroupNameChangeEventDTO.serializer()
|
|
||||||
GroupEntranceAnnouncementChangeEventDTO::class with GroupEntranceAnnouncementChangeEventDTO.serializer()
|
|
||||||
GroupMuteAllEventDTO::class with GroupMuteAllEventDTO.serializer()
|
|
||||||
GroupAllowAnonymousChatEventDTO::class with GroupAllowAnonymousChatEventDTO.serializer()
|
|
||||||
GroupAllowConfessTalkEventDTO::class with GroupAllowConfessTalkEventDTO.serializer()
|
|
||||||
GroupAllowMemberInviteEventDTO::class with GroupAllowMemberInviteEventDTO.serializer()
|
|
||||||
MemberJoinEventDTO::class with MemberJoinEventDTO.serializer()
|
|
||||||
MemberLeaveEventKickDTO::class with MemberLeaveEventKickDTO.serializer()
|
|
||||||
MemberLeaveEventQuitDTO::class with MemberLeaveEventQuitDTO.serializer()
|
|
||||||
MemberCardChangeEventDTO::class with MemberCardChangeEventDTO.serializer()
|
|
||||||
MemberSpecialTitleChangeEventDTO::class with MemberSpecialTitleChangeEventDTO.serializer()
|
|
||||||
MemberPermissionChangeEventDTO::class with MemberPermissionChangeEventDTO.serializer()
|
|
||||||
MemberMuteEventDTO::class with MemberMuteEventDTO.serializer()
|
|
||||||
MemberUnmuteEventDTO::class with MemberUnmuteEventDTO.serializer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message Polymorphic
|
|
||||||
// polymorphic(MessageDTO.serializer()) {
|
|
||||||
// MessageSourceDTO::class with MessageSourceDTO.serializer()
|
|
||||||
// AtDTO::class with AtDTO.serializer()
|
|
||||||
// AtAllDTO::class with AtAllDTO.serializer()
|
|
||||||
// FaceDTO::class with FaceDTO.serializer()
|
|
||||||
// PlainDTO::class with PlainDTO.serializer()
|
|
||||||
// ImageDTO::class with ImageDTO.serializer()
|
|
||||||
// XmlDTO::class with XmlDTO.serializer()
|
|
||||||
// UnknownMessageDTO::class with UnknownMessageDTO.serializer()
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
}
|
|
Binary file not shown.
@ -1,50 +0,0 @@
|
|||||||
plugins {
|
|
||||||
kotlin("jvm")
|
|
||||||
java
|
|
||||||
id("com.github.johnrengelman.shadow")
|
|
||||||
}
|
|
||||||
|
|
||||||
version = "1.0.0"
|
|
||||||
|
|
||||||
val kotlinVersion: String by rootProject.ext
|
|
||||||
val atomicFuVersion: String by rootProject.ext
|
|
||||||
val coroutinesVersion: String by rootProject.ext
|
|
||||||
val kotlinXIoVersion: String by rootProject.ext
|
|
||||||
val coroutinesIoVersion: String by rootProject.ext
|
|
||||||
val serializationVersion: String by rootProject.ext
|
|
||||||
|
|
||||||
val klockVersion: String by rootProject.ext
|
|
||||||
val ktorVersion: String by rootProject.ext
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
sourceSets {
|
|
||||||
all {
|
|
||||||
languageSettings.enableLanguageFeature("InlineClasses")
|
|
||||||
|
|
||||||
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$version"
|
|
||||||
|
|
||||||
fun ktor(id: String, version: String) = "io.ktor:ktor-$id:$version"
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
api(project(":mirai-core"))
|
|
||||||
api(project(":mirai-console"))
|
|
||||||
runtimeOnly(files("../mirai-core/build/classes/kotlin/jvm/main")) // classpath is not added correctly by IDE
|
|
||||||
|
|
||||||
api(kotlin("stdlib", kotlinVersion))
|
|
||||||
api(kotlinx("io-jvm", kotlinXIoVersion))
|
|
||||||
api(kotlinx("io", kotlinXIoVersion))
|
|
||||||
api(kotlinx("coroutines-io", coroutinesIoVersion))
|
|
||||||
api(kotlinx("coroutines-core", coroutinesVersion))
|
|
||||||
api("org.jsoup:jsoup:1.12.1")
|
|
||||||
api(group = "com.alibaba", name = "fastjson", version = "1.2.62")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType<JavaCompile>() {
|
|
||||||
options.encoding = "UTF-8"
|
|
||||||
}
|
|
@ -1,155 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.imageplugin
|
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import net.mamoe.mirai.console.MiraiConsole
|
|
||||||
import net.mamoe.mirai.console.command.registerCommand
|
|
||||||
import net.mamoe.mirai.console.plugins.Config
|
|
||||||
import net.mamoe.mirai.console.plugins.ConfigSection
|
|
||||||
import net.mamoe.mirai.console.plugins.PluginBase
|
|
||||||
import net.mamoe.mirai.console.plugins.withDefaultWriteSave
|
|
||||||
import net.mamoe.mirai.contact.Contact
|
|
||||||
import net.mamoe.mirai.contact.sendMessage
|
|
||||||
import net.mamoe.mirai.event.events.BotOnlineEvent
|
|
||||||
import net.mamoe.mirai.event.events.MemberPermissionChangeEvent
|
|
||||||
import net.mamoe.mirai.event.subscribeAlways
|
|
||||||
import net.mamoe.mirai.event.subscribeGroupMessages
|
|
||||||
import net.mamoe.mirai.event.subscribeMessages
|
|
||||||
import net.mamoe.mirai.message.data.Image
|
|
||||||
import net.mamoe.mirai.message.data.sendTo
|
|
||||||
import net.mamoe.mirai.message.upload
|
|
||||||
import net.mamoe.mirai.message.uploadAsImage
|
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
|
||||||
import org.jsoup.Jsoup
|
|
||||||
import java.awt.RenderingHints
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
import javax.imageio.ImageIO
|
|
||||||
|
|
||||||
|
|
||||||
class ImageSenderMain : PluginBase() {
|
|
||||||
|
|
||||||
lateinit var images: Config
|
|
||||||
lateinit var normal: List<ConfigSection>
|
|
||||||
lateinit var r18: List<ConfigSection>
|
|
||||||
|
|
||||||
|
|
||||||
val config by lazy {
|
|
||||||
loadConfig("setting.yml")
|
|
||||||
}
|
|
||||||
|
|
||||||
val Normal_Image_Trigger by config.withDefaultWriteSave { "色图" }
|
|
||||||
val R18_Image_Trigger by config.withDefaultWriteSave { "不够色" }
|
|
||||||
val Image_Resize_Max_Width_Height by config.withDefaultWriteSave { 800 }
|
|
||||||
|
|
||||||
val groupsAllowNormal by lazy {
|
|
||||||
config.getLongList("Allow_Normal_Image_Groups").toMutableList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val groupsAllowR18 by lazy {
|
|
||||||
config.getLongList("Allow_R18_Image_Groups").toMutableList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDisable() {
|
|
||||||
config["Allow_R18_Image_Groups"] = groupsAllowR18
|
|
||||||
config["Allow_Normal_Image_Groups"] = groupsAllowNormal
|
|
||||||
config.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onEnable() {
|
|
||||||
logger.info("Image Sender plugin enabled")
|
|
||||||
registerCommands()
|
|
||||||
subscribeAlways<MemberPermissionChangeEvent> {
|
|
||||||
logger.info("${this.bot.uin} login succeed, it will be controlled by Image Sender Plugin")
|
|
||||||
this.bot.subscribeGroupMessages {
|
|
||||||
(contains(Normal_Image_Trigger)) {
|
|
||||||
sendImage(subject, normal.random())
|
|
||||||
}
|
|
||||||
(contains(R18_Image_Trigger)) {
|
|
||||||
sendImage(subject, r18.random())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sendImage(contact: Contact, configSection: ConfigSection) {
|
|
||||||
launch {
|
|
||||||
try {
|
|
||||||
logger.info("正在推送图片")
|
|
||||||
getImage(
|
|
||||||
contact, configSection.getString("url"), configSection.getString("pid"), 800
|
|
||||||
).plus(configSection.getString("tags")).sendTo(contact)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
contact.sendMessage(e.message ?: "unknown error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun getImage(contact: Contact, url: String, pid: String, maxWidthOrHeight: Int): Image {
|
|
||||||
val bodyStream = withTimeoutOrNull(20 * 1000) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
Jsoup
|
|
||||||
.connect(url)
|
|
||||||
.followRedirects(true)
|
|
||||||
.timeout(180_000)
|
|
||||||
.ignoreContentType(true)
|
|
||||||
.userAgent("Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; ja-jp) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27")
|
|
||||||
.referrer("https://www.pixiv.net/member_illust.php?mode=medium&illust_id=$pid")
|
|
||||||
.ignoreHttpErrors(true)
|
|
||||||
.maxBodySize(100000000)
|
|
||||||
.execute().also { check(it.statusCode() == 200) { "Failed to download image" } }
|
|
||||||
}
|
|
||||||
}?.bodyStream() ?: error("Failed to download image")
|
|
||||||
if (maxWidthOrHeight < 1) {
|
|
||||||
return bodyStream.uploadAsImage(contact)
|
|
||||||
}
|
|
||||||
val image = withContext(Dispatchers.IO) {
|
|
||||||
ImageIO.read(bodyStream)
|
|
||||||
}
|
|
||||||
if (image.width.coerceAtLeast(image.height) <= maxWidthOrHeight) {
|
|
||||||
return image.upload(contact)
|
|
||||||
}
|
|
||||||
val rate = (maxWidthOrHeight.toFloat() / image.width.coerceAtLeast(image.height))
|
|
||||||
val newWidth = (image.width * rate).toInt()
|
|
||||||
val newHeight = (image.height * rate).toInt()
|
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
val dimg = BufferedImage(newWidth, newHeight, image.type)
|
|
||||||
val g = dimg.createGraphics()
|
|
||||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR)
|
|
||||||
g.drawImage(image, 0, 0, newWidth, newHeight, 0, 0, image.width, image.height, null)
|
|
||||||
g.dispose()
|
|
||||||
dimg
|
|
||||||
}.upload(contact)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun onLoad() {
|
|
||||||
logger.info("loading local image data")
|
|
||||||
|
|
||||||
try {
|
|
||||||
images = getResourcesConfig("data.yml")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
logger.info("无法加载本地图片")
|
|
||||||
}
|
|
||||||
logger.info("本地图片版本" + images.getString("version"))
|
|
||||||
r18 = images.getConfigSectionList("R18")
|
|
||||||
normal = images.getConfigSectionList("normal")
|
|
||||||
logger.info("Normal * " + normal.size)
|
|
||||||
logger.info("R18 * " + r18.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun registerCommands() {
|
|
||||||
registerCommand {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
import com.alibaba.fastjson.JSONObject
|
|
||||||
import com.google.gson.JsonObject
|
|
||||||
import net.mamoe.mirai.console.plugins.*
|
|
||||||
import net.mamoe.mirai.utils.cryptor.contentToString
|
|
||||||
import org.jsoup.Connection
|
|
||||||
import org.jsoup.Jsoup
|
|
||||||
import java.io.File
|
|
||||||
import kotlin.concurrent.thread
|
|
||||||
|
|
||||||
object Data {
|
|
||||||
val section = (File(System.getProperty("user.dir") + "/setu.yml")).loadAsConfig()
|
|
||||||
|
|
||||||
|
|
||||||
val abstract = section.getStringList("abstract").toMutableList()
|
|
||||||
val R18 = section.getConfigSectionList("R18").toMutableList()
|
|
||||||
val normal = section.getConfigSectionList("normal").toMutableList()
|
|
||||||
|
|
||||||
fun init() {
|
|
||||||
section.setIfAbsent("abstract", mutableListOf<String>())
|
|
||||||
section.setIfAbsent("R18", mutableListOf<ConfigSection>())
|
|
||||||
section.setIfAbsent("Normal", mutableListOf<ConfigSection>())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun save() {
|
|
||||||
section["abstract"] = abstract
|
|
||||||
section["R18"] = R18
|
|
||||||
section["normal"] = normal
|
|
||||||
section.save()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
|
|
||||||
val abstract_file = (File(System.getProperty("user.dir") + "/abstractSetu.yml")).loadAsConfig()
|
|
||||||
|
|
||||||
abstract_file.setIfAbsent("R18", mutableListOf<ConfigSection>())
|
|
||||||
abstract_file.setIfAbsent("normal", mutableListOf<ConfigSection>())
|
|
||||||
|
|
||||||
val r18 = abstract_file.getConfigSectionList("R18").toMutableList()
|
|
||||||
val normal = abstract_file.getConfigSectionList("normal").toMutableList()
|
|
||||||
|
|
||||||
Data.R18.forEach {
|
|
||||||
val forbid = with(it.getString("tags")) {
|
|
||||||
this.contains("初音ミク") || this.contains("VOCALOID") || this.contains("Miku")
|
|
||||||
||
|
|
||||||
this.contains("东方") || this.contains("東方")
|
|
||||||
}
|
|
||||||
if (forbid) {
|
|
||||||
println("过滤掉了一张图")
|
|
||||||
} else {
|
|
||||||
r18.add(
|
|
||||||
ConfigSectionImpl().apply {
|
|
||||||
this["pid"] = it["pid"]!!
|
|
||||||
this["author"] = it["author"]!!
|
|
||||||
this["uid"] = it["uid"]!!
|
|
||||||
this["tags"] = it["tags"]!!
|
|
||||||
this["url"] = it["url"]!!
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Data.normal.forEach {
|
|
||||||
val forbid = with(it.getString("tags")) {
|
|
||||||
this.contains("初音ミク") || this.contains("VOCALOID") || this.contains("Miku")
|
|
||||||
||
|
|
||||||
this.contains("东方") || this.contains("東方")
|
|
||||||
}
|
|
||||||
if (forbid) {
|
|
||||||
println("过滤掉了一张图")
|
|
||||||
} else {
|
|
||||||
normal.add(
|
|
||||||
ConfigSectionImpl().apply {
|
|
||||||
this["pid"] = it["pid"]!!
|
|
||||||
this["author"] = it["author"]!!
|
|
||||||
this["uid"] = it["uid"]!!
|
|
||||||
this["tags"] = it["tags"]!!
|
|
||||||
this["url"] = it["url"]!!
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract_file.set("R18", r18)
|
|
||||||
abstract_file.set("normal", normal)
|
|
||||||
abstract_file.save()
|
|
||||||
|
|
||||||
/**
|
|
||||||
Data.init()
|
|
||||||
Runtime.getRuntime().addShutdownHook(thread(start = false) {
|
|
||||||
Data.save()
|
|
||||||
})
|
|
||||||
|
|
||||||
while (true){
|
|
||||||
try {
|
|
||||||
val val0 = JSONObject.parseObject(Jsoup
|
|
||||||
.connect("https://api.lolicon.app/setu/")
|
|
||||||
.ignoreContentType(true)
|
|
||||||
.method(Connection.Method.GET)
|
|
||||||
.data("r18","1")
|
|
||||||
.data("num","10")
|
|
||||||
.execute().body())
|
|
||||||
|
|
||||||
val val1 = val0.getJSONArray("data")
|
|
||||||
for(index in 0 until val1.size - 1){
|
|
||||||
val content = val1.getJSONObject(index)
|
|
||||||
|
|
||||||
val pid = content.getString("pid")
|
|
||||||
if(Data.abstract.contains(pid)){
|
|
||||||
println("获取到了一张重复图$pid")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val configSection = ConfigSectionImpl()
|
|
||||||
val isR18 = content.getBoolean("r18")
|
|
||||||
configSection["author"] = content.getString("author")
|
|
||||||
configSection["pid"] = pid
|
|
||||||
configSection["uid"] = content.getInteger("uid")
|
|
||||||
configSection["width"] = content.getInteger("width")
|
|
||||||
configSection["height"] = content.getInteger("height")
|
|
||||||
configSection["tags"] = content.getJSONArray("tags").map {
|
|
||||||
it.toString()
|
|
||||||
}.joinToString(",")
|
|
||||||
configSection["url"] = content.getString("url")
|
|
||||||
if(isR18){
|
|
||||||
Data.R18.add(configSection)
|
|
||||||
print("获取到了一张R18")
|
|
||||||
}else{
|
|
||||||
Data.normal.add(configSection)
|
|
||||||
print("获取到了一张Normal")
|
|
||||||
}
|
|
||||||
Data.abstract.add(pid)
|
|
||||||
println(configSection.contentToString())
|
|
||||||
}
|
|
||||||
}catch (e:Exception){
|
|
||||||
println(e.message)
|
|
||||||
}
|
|
||||||
Data.save()
|
|
||||||
println("SAVED")
|
|
||||||
Thread.sleep(1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +0,0 @@
|
|||||||
name: ImageSender
|
|
||||||
main: net.mamoe.mirai.imageplugin.ImageSenderMain
|
|
||||||
version: 1.0.0
|
|
||||||
author: 不想写代码
|
|
||||||
info: a demo[hso] plugin of mirai
|
|
Loading…
Reference in New Issue
Block a user