node-karin 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +57 -0
- package/config/defSet/App.yaml +37 -0
- package/config/defSet/config.yaml +43 -0
- package/config/defSet/group.yaml +18 -0
- package/config/defSet/pm2.yaml +21 -0
- package/config/defSet/redis.yaml +18 -0
- package/config/defSet/server.yaml +42 -0
- package/config/view/App.yaml +74 -0
- package/config/view/config.yaml +100 -0
- package/config/view/group.yaml +62 -0
- package/config/view/pm2.yaml +41 -0
- package/config/view/redis.yaml +25 -0
- package/config/view/server.yaml +93 -0
- package/lib/adapter/onebot/onebot11.d.ts +430 -0
- package/lib/adapter/onebot/onebot11.js +1302 -0
- package/lib/core/init.d.ts +0 -0
- package/lib/core/init.js +4 -0
- package/lib/core/karin.d.ts +72 -0
- package/lib/core/karin.js +51 -0
- package/lib/core/listener.d.ts +121 -0
- package/lib/core/listener.js +188 -0
- package/lib/core/plugin.app.d.ts +15 -0
- package/lib/core/plugin.app.js +18 -0
- package/lib/core/plugin.d.ts +182 -0
- package/lib/core/plugin.js +138 -0
- package/lib/core/plugin.loader.d.ts +149 -0
- package/lib/core/plugin.loader.js +462 -0
- package/lib/core/server.d.ts +26 -0
- package/lib/core/server.js +213 -0
- package/lib/db/level.d.ts +20 -0
- package/lib/db/level.js +38 -0
- package/lib/db/redis.d.ts +41 -0
- package/lib/db/redis.js +137 -0
- package/lib/db/redis_level.d.ts +113 -0
- package/lib/db/redis_level.js +290 -0
- package/lib/event/event.d.ts +138 -0
- package/lib/event/event.handler.d.ts +29 -0
- package/lib/event/event.handler.js +142 -0
- package/lib/event/event.js +120 -0
- package/lib/event/message.d.ts +102 -0
- package/lib/event/message.handler.d.ts +25 -0
- package/lib/event/message.handler.js +240 -0
- package/lib/event/message.js +70 -0
- package/lib/event/notice.d.ts +49 -0
- package/lib/event/notice.js +15 -0
- package/lib/event/request.d.ts +49 -0
- package/lib/event/request.js +15 -0
- package/lib/event/review.handler.d.ts +54 -0
- package/lib/event/review.handler.js +382 -0
- package/lib/index.d.ts +23 -0
- package/lib/index.js +40 -0
- package/lib/renderer/app.d.ts +53 -0
- package/lib/renderer/app.js +93 -0
- package/lib/renderer/base.d.ts +30 -0
- package/lib/renderer/base.js +71 -0
- package/lib/renderer/client.d.ts +30 -0
- package/lib/renderer/client.js +159 -0
- package/lib/renderer/http.d.ts +19 -0
- package/lib/renderer/http.js +51 -0
- package/lib/renderer/server.d.ts +42 -0
- package/lib/renderer/server.js +112 -0
- package/lib/renderer/wormhole.d.ts +1 -0
- package/lib/renderer/wormhole.js +154 -0
- package/lib/types/adapter.d.ts +575 -0
- package/lib/types/adapter.js +1 -0
- package/lib/types/config.d.ts +327 -0
- package/lib/types/config.js +1 -0
- package/lib/types/element.d.ts +576 -0
- package/lib/types/element.js +1 -0
- package/lib/types/index.d.ts +8 -0
- package/lib/types/index.js +8 -0
- package/lib/types/logger.d.ts +109 -0
- package/lib/types/logger.js +1 -0
- package/lib/types/onebots11.d.ts +1371 -0
- package/lib/types/onebots11.js +1 -0
- package/lib/types/plugin.d.ts +282 -0
- package/lib/types/plugin.js +1 -0
- package/lib/types/render.d.ts +111 -0
- package/lib/types/render.js +1 -0
- package/lib/types/reply.d.ts +40 -0
- package/lib/types/reply.js +1 -0
- package/lib/types/types.d.ts +898 -0
- package/lib/types/types.js +1 -0
- package/lib/utils/YamlEditor.d.ts +62 -0
- package/lib/utils/YamlEditor.js +208 -0
- package/lib/utils/button.d.ts +49 -0
- package/lib/utils/button.js +79 -0
- package/lib/utils/common.d.ts +123 -0
- package/lib/utils/common.js +413 -0
- package/lib/utils/config.d.ts +72 -0
- package/lib/utils/config.js +254 -0
- package/lib/utils/exec.d.ts +22 -0
- package/lib/utils/exec.js +36 -0
- package/lib/utils/ffmpeg.d.ts +12 -0
- package/lib/utils/ffmpeg.js +25 -0
- package/lib/utils/handler.d.ts +76 -0
- package/lib/utils/handler.js +102 -0
- package/lib/utils/logger.d.ts +3 -0
- package/lib/utils/logger.js +104 -0
- package/lib/utils/segment.d.ts +276 -0
- package/lib/utils/segment.js +448 -0
- package/lib/utils/update.d.ts +69 -0
- package/lib/utils/update.js +151 -0
- package/lib/utils/updateVersion.d.ts +33 -0
- package/lib/utils/updateVersion.js +145 -0
- package/package.json +92 -0
|
@@ -0,0 +1,1302 @@
|
|
|
1
|
+
import { KarinMessage } from '../../event/message.js'
|
|
2
|
+
import { KarinNotice } from '../../event/notice.js'
|
|
3
|
+
import { KarinRequest } from '../../event/request.js'
|
|
4
|
+
import WebSocket from 'ws'
|
|
5
|
+
import { randomUUID } from 'crypto'
|
|
6
|
+
import logger from '../../utils/logger.js'
|
|
7
|
+
import common from '../../utils/common.js'
|
|
8
|
+
import listener from '../../core/listener.js'
|
|
9
|
+
import config from '../../utils/config.js'
|
|
10
|
+
import segment from '../../utils/segment.js'
|
|
11
|
+
/**
|
|
12
|
+
* @class OneBot11
|
|
13
|
+
* @extends KarinAdapter
|
|
14
|
+
*/
|
|
15
|
+
export class OneBot11 {
|
|
16
|
+
/**
|
|
17
|
+
* 是否初始化
|
|
18
|
+
*/
|
|
19
|
+
#init = false
|
|
20
|
+
/**
|
|
21
|
+
* 机器人QQ号
|
|
22
|
+
*/
|
|
23
|
+
self_id
|
|
24
|
+
/**
|
|
25
|
+
* - 重连次数 仅正向ws使用
|
|
26
|
+
*/
|
|
27
|
+
index
|
|
28
|
+
socket
|
|
29
|
+
account
|
|
30
|
+
adapter
|
|
31
|
+
version
|
|
32
|
+
constructor () {
|
|
33
|
+
this.self_id = ''
|
|
34
|
+
this.index = 0
|
|
35
|
+
this.account = { uid: '', uin: '', name: '' }
|
|
36
|
+
this.adapter = { id: 'QQ', name: 'OneBot11', type: 'ws', sub_type: 'internal', start_time: Date.now(), connect: '' }
|
|
37
|
+
this.version = { name: '', app_name: '', version: '' }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 反向ws初始化
|
|
42
|
+
*/
|
|
43
|
+
async server (socket, request) {
|
|
44
|
+
this.socket = socket
|
|
45
|
+
const self_id = String(request.headers['x-self-id'])
|
|
46
|
+
const connect = 'ws://' + request.headers.host + request.url
|
|
47
|
+
this.account.uin = self_id
|
|
48
|
+
this.account.uid = self_id
|
|
49
|
+
this.adapter.connect = connect
|
|
50
|
+
this.adapter.sub_type = 'server'
|
|
51
|
+
this.logger('info', `[反向WS][onebot11-${request.headers.upgrade}][${self_id}] ` + logger.green(connect))
|
|
52
|
+
await this.#initListener(connect)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 正向ws初始化
|
|
57
|
+
* @param connect - WebSocket连接地址
|
|
58
|
+
*/
|
|
59
|
+
async client (connect) {
|
|
60
|
+
/** 创建连接 */
|
|
61
|
+
this.socket = new WebSocket(connect)
|
|
62
|
+
this.socket.on('open', async () => {
|
|
63
|
+
this.adapter.sub_type = 'client'
|
|
64
|
+
this.adapter.connect = connect
|
|
65
|
+
logger.info('[正向WS][连接成功][onebot11] ' + logger.green(connect))
|
|
66
|
+
this.index = 0
|
|
67
|
+
this.#initListener(connect)
|
|
68
|
+
})
|
|
69
|
+
/** 监听断开 */
|
|
70
|
+
this.socket.on('close', async () => {
|
|
71
|
+
this.index++
|
|
72
|
+
logger.warn(`[正向WS][重连次数:${this.index}] 连接断开,将在5秒后重连:${connect}`)
|
|
73
|
+
/** 停止全部监听 */
|
|
74
|
+
this.socket.removeAllListeners()
|
|
75
|
+
await common.sleep(5000)
|
|
76
|
+
this.client(connect)
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 初始化监听事件
|
|
82
|
+
* @param connect - WebSocket连接地址
|
|
83
|
+
*/
|
|
84
|
+
async #initListener (connect) {
|
|
85
|
+
/** 监听事件 */
|
|
86
|
+
this.socket.on('message', data => {
|
|
87
|
+
this.logger('debug', `[收到事件]:${data}`)
|
|
88
|
+
const event = data.toString().trim() || '{"post_type":"error","error":"空事件"}'
|
|
89
|
+
const json = JSON.parse(event)
|
|
90
|
+
if (json.echo) {
|
|
91
|
+
return this.socket.emit(json.echo, json)
|
|
92
|
+
} else {
|
|
93
|
+
/** 未初始化 */
|
|
94
|
+
this.#init && this.#event(json)
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
/** 监听错误 */
|
|
98
|
+
this.socket.on('error', error => {
|
|
99
|
+
this.logger('debug', '[正向WS] 发生错误', error)
|
|
100
|
+
})
|
|
101
|
+
/** 监听断开 */
|
|
102
|
+
this.socket.once('close', async () => {
|
|
103
|
+
const type = this.adapter.sub_type === 'server' ? '反向WS' : '正向WS'
|
|
104
|
+
this.logger('warn', `[${type}] 连接断开:${connect}`)
|
|
105
|
+
/** 停止全部监听 */
|
|
106
|
+
this.socket.removeAllListeners()
|
|
107
|
+
/** 正向ws需要重连 */
|
|
108
|
+
if (this.adapter.sub_type === 'client') {
|
|
109
|
+
this.index++
|
|
110
|
+
this.logger('warn', `[正向WS][重连次数:${this.index}] 连接断开,将在5秒后重连:${connect}`)
|
|
111
|
+
await common.sleep(5000)
|
|
112
|
+
this.client(connect)
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
await this.getSelf()
|
|
116
|
+
this.#init = true
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 获取当前登录号信息
|
|
121
|
+
*/
|
|
122
|
+
async getSelf () {
|
|
123
|
+
const data = await this.GetCurrentAccount()
|
|
124
|
+
try {
|
|
125
|
+
const { app_name, app_version: version } = await this.GetVersion()
|
|
126
|
+
this.version.name = app_name
|
|
127
|
+
this.version.app_name = app_name
|
|
128
|
+
this.version.version = version
|
|
129
|
+
} catch (e) {
|
|
130
|
+
/** 兼容onebots */
|
|
131
|
+
const { app_name, app_version: version } = await this.SendApi('get_version')
|
|
132
|
+
this.version.name = app_name
|
|
133
|
+
this.version.app_name = app_name
|
|
134
|
+
this.version.version = version
|
|
135
|
+
}
|
|
136
|
+
this.account.uid = data.account_uid
|
|
137
|
+
this.account.uin = data.account_uin
|
|
138
|
+
this.account.name = data.account_name
|
|
139
|
+
this.logger('info', `[加载完成][app_name:${this.version.name}][version:${this.version.version}] ` + logger.green(this.adapter.connect))
|
|
140
|
+
/** 注册bot */
|
|
141
|
+
listener.emit('bot', { type: 'websocket', bot: this })
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** 是否初始化 */
|
|
145
|
+
get isInit () {
|
|
146
|
+
return new Promise(resolve => {
|
|
147
|
+
const timer = setInterval(() => {
|
|
148
|
+
if (this.account.name) {
|
|
149
|
+
const { name, version } = this.version
|
|
150
|
+
this.logger('info', `建立连接成功:[${name}(${version})] ${this.adapter.connect}`)
|
|
151
|
+
clearInterval(timer)
|
|
152
|
+
resolve(true)
|
|
153
|
+
}
|
|
154
|
+
}, 100)
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** 处理事件 */
|
|
159
|
+
#event (data) {
|
|
160
|
+
switch (data.post_type) {
|
|
161
|
+
case 'meta_event': {
|
|
162
|
+
switch (data.meta_event_type) {
|
|
163
|
+
case 'heartbeat':
|
|
164
|
+
this.logger('trace', `[心跳]:${JSON.stringify(data.status)}`)
|
|
165
|
+
break
|
|
166
|
+
case 'lifecycle': {
|
|
167
|
+
const typeMap = {
|
|
168
|
+
enable: 'OneBot启用',
|
|
169
|
+
disable: 'OneBot停用',
|
|
170
|
+
connect: 'WebSocket连接成功',
|
|
171
|
+
}
|
|
172
|
+
const sub_type = data.sub_type
|
|
173
|
+
this.logger('debug', `[生命周期]:${typeMap[sub_type]}`)
|
|
174
|
+
break
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
listener.emit('meta_event', data)
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
case 'message':
|
|
181
|
+
case 'message_sent': {
|
|
182
|
+
const message = {
|
|
183
|
+
event: (data.post_type + ''),
|
|
184
|
+
self_id: data.self_id + '',
|
|
185
|
+
user_id: data.sender.user_id + '',
|
|
186
|
+
time: data.time,
|
|
187
|
+
message_id: data.message_id + '',
|
|
188
|
+
message_seq: data.message_id + '',
|
|
189
|
+
sender: {
|
|
190
|
+
...data.sender,
|
|
191
|
+
uid: data.sender.user_id + '',
|
|
192
|
+
uin: data.sender.user_id + '',
|
|
193
|
+
nick: data.sender.nickname || '',
|
|
194
|
+
role: ('role' in data.sender ? data.sender.role || '' : ''),
|
|
195
|
+
},
|
|
196
|
+
elements: this.AdapterConvertKarin(data.message),
|
|
197
|
+
contact: {
|
|
198
|
+
scene: (data.message_type === 'private' ? 'private' : 'group'),
|
|
199
|
+
peer: data.message_type === 'private' ? data.sender.user_id : data.group_id,
|
|
200
|
+
sub_peer: '',
|
|
201
|
+
},
|
|
202
|
+
group_id: 'group_id' in data ? data.group_id : '',
|
|
203
|
+
raw_message: '',
|
|
204
|
+
}
|
|
205
|
+
const e = new KarinMessage(message)
|
|
206
|
+
e.bot = this
|
|
207
|
+
/**
|
|
208
|
+
* 快速回复 开发者不应该使用这个方法,应该使用由karin封装过后的reply方法
|
|
209
|
+
*/
|
|
210
|
+
e.replyCallback = async (elements) => {
|
|
211
|
+
if (data.message_type === 'private') {
|
|
212
|
+
return this.send_private_msg(data.user_id, elements)
|
|
213
|
+
} else {
|
|
214
|
+
return this.send_group_msg(data.group_id, elements)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
listener.emit('message', e)
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
case 'notice':
|
|
221
|
+
this.#notice_event(data)
|
|
222
|
+
break
|
|
223
|
+
case 'request':
|
|
224
|
+
this.#request_event(data)
|
|
225
|
+
break
|
|
226
|
+
default:
|
|
227
|
+
this.logger('info', `未知事件:${JSON.stringify(data)}`)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* 通知事件
|
|
233
|
+
*/
|
|
234
|
+
#notice_event (data) {
|
|
235
|
+
const time = data.time
|
|
236
|
+
const self_id = data.self_id + ''
|
|
237
|
+
let notice = {}
|
|
238
|
+
const user_id = data.user_id + ''
|
|
239
|
+
const event_id = `notice.${time}`
|
|
240
|
+
const sender = {
|
|
241
|
+
uid: data.user_id + '',
|
|
242
|
+
uin: data.user_id + '',
|
|
243
|
+
nick: '',
|
|
244
|
+
role: '',
|
|
245
|
+
}
|
|
246
|
+
const contact = {
|
|
247
|
+
scene: ('group_id' in data ? 'group' : 'private'),
|
|
248
|
+
peer: 'group_id' in data ? data.group_id : data.user_id,
|
|
249
|
+
sub_peer: '',
|
|
250
|
+
}
|
|
251
|
+
switch (data.notice_type) {
|
|
252
|
+
// 群文件上传
|
|
253
|
+
case 'group_upload': {
|
|
254
|
+
const content = {
|
|
255
|
+
group_id: data.group_id + '',
|
|
256
|
+
operator_uid: data.user_id + '',
|
|
257
|
+
operator_uin: data.user_id + '',
|
|
258
|
+
file_id: data.file.id,
|
|
259
|
+
file_sub_id: '',
|
|
260
|
+
file_name: data.file.name,
|
|
261
|
+
file_size: data.file.size,
|
|
262
|
+
bus_id: 0,
|
|
263
|
+
expire_time: 0,
|
|
264
|
+
url: '',
|
|
265
|
+
}
|
|
266
|
+
const options = {
|
|
267
|
+
time,
|
|
268
|
+
self_id,
|
|
269
|
+
user_id,
|
|
270
|
+
event_id,
|
|
271
|
+
content,
|
|
272
|
+
sender,
|
|
273
|
+
contact,
|
|
274
|
+
sub_event: 'group_file_uploaded',
|
|
275
|
+
}
|
|
276
|
+
notice = new KarinNotice(options)
|
|
277
|
+
break
|
|
278
|
+
}
|
|
279
|
+
// 群管理员变动
|
|
280
|
+
case 'group_admin': {
|
|
281
|
+
const content = {
|
|
282
|
+
group_id: data.group_id + '',
|
|
283
|
+
target_uid: data.user_id + '',
|
|
284
|
+
target_uin: data.user_id + '',
|
|
285
|
+
is_admin: data.sub_type === 'set',
|
|
286
|
+
}
|
|
287
|
+
const options = {
|
|
288
|
+
time,
|
|
289
|
+
self_id,
|
|
290
|
+
user_id,
|
|
291
|
+
event_id,
|
|
292
|
+
sender,
|
|
293
|
+
contact,
|
|
294
|
+
content,
|
|
295
|
+
sub_event: 'group_admin_changed',
|
|
296
|
+
}
|
|
297
|
+
notice = new KarinNotice(options)
|
|
298
|
+
break
|
|
299
|
+
}
|
|
300
|
+
// 群成员减少
|
|
301
|
+
case 'group_decrease': {
|
|
302
|
+
const content = {
|
|
303
|
+
group_id: data.group_id + '',
|
|
304
|
+
operator_uid: data.operator_id || '',
|
|
305
|
+
operator_uin: data.operator_id || '',
|
|
306
|
+
target_uid: data.user_id || '',
|
|
307
|
+
target_uin: data.user_id || '',
|
|
308
|
+
type: data.sub_type,
|
|
309
|
+
}
|
|
310
|
+
const options = {
|
|
311
|
+
time,
|
|
312
|
+
self_id,
|
|
313
|
+
user_id,
|
|
314
|
+
event_id,
|
|
315
|
+
sender,
|
|
316
|
+
contact,
|
|
317
|
+
content,
|
|
318
|
+
sub_event: 'group_member_decrease',
|
|
319
|
+
}
|
|
320
|
+
notice = new KarinNotice(options)
|
|
321
|
+
break
|
|
322
|
+
}
|
|
323
|
+
// 群成员增加
|
|
324
|
+
case 'group_increase': {
|
|
325
|
+
const content = {
|
|
326
|
+
group_id: data.group_id + '',
|
|
327
|
+
operator_uid: (data.operator_id || '') + '',
|
|
328
|
+
operator_uin: (data.operator_id || '') + '',
|
|
329
|
+
target_uid: (data.user_id || '') + '',
|
|
330
|
+
target_uin: (data.user_id || '') + '',
|
|
331
|
+
type: data.sub_type,
|
|
332
|
+
}
|
|
333
|
+
const options = {
|
|
334
|
+
time,
|
|
335
|
+
self_id,
|
|
336
|
+
user_id,
|
|
337
|
+
event_id,
|
|
338
|
+
sender,
|
|
339
|
+
contact,
|
|
340
|
+
content,
|
|
341
|
+
sub_event: 'group_member_increase',
|
|
342
|
+
}
|
|
343
|
+
notice = new KarinNotice(options)
|
|
344
|
+
break
|
|
345
|
+
}
|
|
346
|
+
// 群禁言事件
|
|
347
|
+
case 'group_ban': {
|
|
348
|
+
const content = {
|
|
349
|
+
group_id: data.group_id,
|
|
350
|
+
operator_uid: data.operator_id || '',
|
|
351
|
+
operator_uin: data.operator_id || '',
|
|
352
|
+
target_uid: data.user_id || '',
|
|
353
|
+
target_uin: data.user_id || '',
|
|
354
|
+
duration: data.duration,
|
|
355
|
+
type: data.sub_type,
|
|
356
|
+
}
|
|
357
|
+
const options = {
|
|
358
|
+
time,
|
|
359
|
+
self_id,
|
|
360
|
+
user_id,
|
|
361
|
+
event_id,
|
|
362
|
+
sender,
|
|
363
|
+
contact,
|
|
364
|
+
content,
|
|
365
|
+
sub_event: 'group_member_ban',
|
|
366
|
+
}
|
|
367
|
+
notice = new KarinNotice(options)
|
|
368
|
+
break
|
|
369
|
+
}
|
|
370
|
+
case 'friend_add':
|
|
371
|
+
// todo kritor没有这个事件
|
|
372
|
+
this.logger('info', `[好友添加]:${JSON.stringify(data)}`)
|
|
373
|
+
break
|
|
374
|
+
case 'group_recall': {
|
|
375
|
+
const content = {
|
|
376
|
+
group_id: data.group_id,
|
|
377
|
+
operator_uid: data.operator_id || '',
|
|
378
|
+
operator_uin: data.operator_id || '',
|
|
379
|
+
target_uid: data.user_id || '',
|
|
380
|
+
target_uin: data.user_id || '',
|
|
381
|
+
message_id: data.message_id,
|
|
382
|
+
tip_text: '撤回了一条消息',
|
|
383
|
+
}
|
|
384
|
+
const options = {
|
|
385
|
+
time,
|
|
386
|
+
self_id,
|
|
387
|
+
user_id,
|
|
388
|
+
event_id,
|
|
389
|
+
sender,
|
|
390
|
+
contact,
|
|
391
|
+
content,
|
|
392
|
+
sub_event: 'group_recall',
|
|
393
|
+
}
|
|
394
|
+
notice = new KarinNotice(options)
|
|
395
|
+
break
|
|
396
|
+
}
|
|
397
|
+
case 'friend_recall': {
|
|
398
|
+
const content = {
|
|
399
|
+
operator_uid: data.user_id || '',
|
|
400
|
+
operator_uin: data.user_id || '',
|
|
401
|
+
message_id: data.message_id,
|
|
402
|
+
tip_text: '撤回了一条消息',
|
|
403
|
+
}
|
|
404
|
+
const options = {
|
|
405
|
+
time,
|
|
406
|
+
self_id,
|
|
407
|
+
user_id,
|
|
408
|
+
event_id,
|
|
409
|
+
sender,
|
|
410
|
+
contact,
|
|
411
|
+
content,
|
|
412
|
+
sub_event: 'group_recall',
|
|
413
|
+
}
|
|
414
|
+
notice = new KarinNotice(options)
|
|
415
|
+
break
|
|
416
|
+
}
|
|
417
|
+
case 'notify':
|
|
418
|
+
switch (data.sub_type) {
|
|
419
|
+
case 'poke': {
|
|
420
|
+
const content = {
|
|
421
|
+
group_id: data.group_id + '',
|
|
422
|
+
operator_uid: data.user_id + '',
|
|
423
|
+
operator_uin: data.user_id + '',
|
|
424
|
+
target_uid: data.target_id + '',
|
|
425
|
+
target_uin: data.target_id + '',
|
|
426
|
+
action: '戳了戳',
|
|
427
|
+
suffix: '',
|
|
428
|
+
action_image: '',
|
|
429
|
+
}
|
|
430
|
+
const options = {
|
|
431
|
+
time,
|
|
432
|
+
self_id,
|
|
433
|
+
user_id,
|
|
434
|
+
event_id,
|
|
435
|
+
sender,
|
|
436
|
+
contact,
|
|
437
|
+
content,
|
|
438
|
+
sub_event: 'group_poke',
|
|
439
|
+
}
|
|
440
|
+
notice = new KarinNotice(options)
|
|
441
|
+
break
|
|
442
|
+
}
|
|
443
|
+
case 'lucky_king':
|
|
444
|
+
// todo kritor没有这个事件
|
|
445
|
+
this.logger('info', `[运气王]:${JSON.stringify(data)}`)
|
|
446
|
+
break
|
|
447
|
+
case 'honor':
|
|
448
|
+
// todo kritor没有这个事件
|
|
449
|
+
this.logger('info', `[群荣誉变更]:${JSON.stringify(data)}`)
|
|
450
|
+
break
|
|
451
|
+
}
|
|
452
|
+
break
|
|
453
|
+
case 'group_msg_emoji_like': {
|
|
454
|
+
const content = {
|
|
455
|
+
group_id: data.group_id + '',
|
|
456
|
+
message_id: data.message_id,
|
|
457
|
+
face_id: data.likes[0].emoji_id,
|
|
458
|
+
is_set: true,
|
|
459
|
+
}
|
|
460
|
+
const options = {
|
|
461
|
+
time,
|
|
462
|
+
self_id,
|
|
463
|
+
user_id,
|
|
464
|
+
event_id,
|
|
465
|
+
sender,
|
|
466
|
+
contact,
|
|
467
|
+
content,
|
|
468
|
+
sub_event: 'group_message_reaction',
|
|
469
|
+
}
|
|
470
|
+
notice = new KarinNotice(options)
|
|
471
|
+
break
|
|
472
|
+
}
|
|
473
|
+
default: {
|
|
474
|
+
return this.logger('error', '未知通知事件:', JSON.stringify(data))
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
listener.emit('notice', notice)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/** 请求事件 */
|
|
481
|
+
#request_event (data) {
|
|
482
|
+
switch (data.request_type) {
|
|
483
|
+
case 'friend': {
|
|
484
|
+
const request = new KarinRequest({
|
|
485
|
+
event_id: `request.${data.time}`,
|
|
486
|
+
self_id: data.self_id + '',
|
|
487
|
+
user_id: data.user_id + '',
|
|
488
|
+
time: data.time,
|
|
489
|
+
contact: {
|
|
490
|
+
scene: 'private',
|
|
491
|
+
peer: data.user_id + '',
|
|
492
|
+
sub_peer: '',
|
|
493
|
+
},
|
|
494
|
+
sender: {
|
|
495
|
+
uid: data.user_id + '',
|
|
496
|
+
uin: data.user_id + '',
|
|
497
|
+
nick: '',
|
|
498
|
+
role: '',
|
|
499
|
+
},
|
|
500
|
+
sub_event: 'friend_apply',
|
|
501
|
+
content: {
|
|
502
|
+
applier_uid: data.user_id + '',
|
|
503
|
+
applier_uin: data.user_id + '',
|
|
504
|
+
message: data.comment,
|
|
505
|
+
},
|
|
506
|
+
})
|
|
507
|
+
listener.emit('request', request)
|
|
508
|
+
return
|
|
509
|
+
}
|
|
510
|
+
case 'group': {
|
|
511
|
+
const request = new KarinRequest({
|
|
512
|
+
event_id: `request.${data.time}`,
|
|
513
|
+
self_id: data.self_id + '',
|
|
514
|
+
user_id: data.user_id + '',
|
|
515
|
+
time: data.time,
|
|
516
|
+
contact: {
|
|
517
|
+
scene: 'group',
|
|
518
|
+
peer: data.group_id + '',
|
|
519
|
+
sub_peer: '',
|
|
520
|
+
},
|
|
521
|
+
sender: {
|
|
522
|
+
uid: data.user_id + '',
|
|
523
|
+
uin: data.user_id + '',
|
|
524
|
+
nick: '',
|
|
525
|
+
role: '',
|
|
526
|
+
},
|
|
527
|
+
sub_event: data.sub_type === 'add' ? 'group_apply' : 'invited_group',
|
|
528
|
+
content: {
|
|
529
|
+
group_id: data.group_id + '',
|
|
530
|
+
applier_uid: data.user_id + '',
|
|
531
|
+
applier_uin: data.user_id + '',
|
|
532
|
+
inviter_uid: data.user_id + '',
|
|
533
|
+
inviter_uin: data.user_id + '',
|
|
534
|
+
message: data.comment,
|
|
535
|
+
},
|
|
536
|
+
})
|
|
537
|
+
listener.emit('request', request)
|
|
538
|
+
return
|
|
539
|
+
}
|
|
540
|
+
default: {
|
|
541
|
+
this.logger('info', `未知请求事件:${JSON.stringify(data)}`)
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* onebot11转karin
|
|
548
|
+
* @param {Array<{type: string, data: any}>} data onebot11格式消息
|
|
549
|
+
* @return karin格式消息
|
|
550
|
+
* */
|
|
551
|
+
AdapterConvertKarin (data) {
|
|
552
|
+
const elements = []
|
|
553
|
+
for (const i of data) {
|
|
554
|
+
switch (i.type) {
|
|
555
|
+
case 'text':
|
|
556
|
+
elements.push(segment.text(i.data.text))
|
|
557
|
+
break
|
|
558
|
+
case 'face':
|
|
559
|
+
elements.push(segment.face(Number(i.data.id)))
|
|
560
|
+
break
|
|
561
|
+
case 'image':
|
|
562
|
+
elements.push(segment.image(i.data.url || i.data.file, { file_type: i.data.type }))
|
|
563
|
+
break
|
|
564
|
+
case 'record':
|
|
565
|
+
elements.push(segment.voice(i.data.url || i.data.file, i.data.magic === 1))
|
|
566
|
+
break
|
|
567
|
+
case 'video':
|
|
568
|
+
elements.push(segment.video(i.data.url || i.data.file))
|
|
569
|
+
break
|
|
570
|
+
case 'at':
|
|
571
|
+
elements.push(segment.at(i.data.qq, i.data.qq))
|
|
572
|
+
break
|
|
573
|
+
case 'poke':
|
|
574
|
+
elements.push(segment.poke(Number(i.data.id), Number(i.data.type)))
|
|
575
|
+
break
|
|
576
|
+
case 'contact':
|
|
577
|
+
elements.push(segment.contact(i.data.type === 'qq' ? 'friend' : 'group', i.data.id))
|
|
578
|
+
break
|
|
579
|
+
case 'location':
|
|
580
|
+
elements.push(segment.location(Number(i.data.lat), Number(i.data.lon), i.data.title || '', i.data.content || ''))
|
|
581
|
+
break
|
|
582
|
+
case 'reply':
|
|
583
|
+
elements.push(segment.reply(i.data.id))
|
|
584
|
+
break
|
|
585
|
+
case 'forward':
|
|
586
|
+
elements.push(segment.forward(i.data.id))
|
|
587
|
+
break
|
|
588
|
+
case 'json':
|
|
589
|
+
elements.push(segment.json(i.data.data))
|
|
590
|
+
break
|
|
591
|
+
case 'xml':
|
|
592
|
+
elements.push(segment.xml(i.data.data))
|
|
593
|
+
break
|
|
594
|
+
default: {
|
|
595
|
+
elements.push(segment.text(JSON.stringify(i)))
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return elements
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* karin转onebot11
|
|
604
|
+
* @param data karin格式消息
|
|
605
|
+
* @return {Array<{type: string, data: any}>} onebot11格式消息
|
|
606
|
+
* */
|
|
607
|
+
KarinConvertAdapter (data) {
|
|
608
|
+
const elements = []
|
|
609
|
+
// const selfUin = this.account.uin
|
|
610
|
+
// const selfNick = this.account.name
|
|
611
|
+
for (const i of data) {
|
|
612
|
+
switch (i.type) {
|
|
613
|
+
case 'text':
|
|
614
|
+
elements.push({ type: 'text', data: { text: i.text } })
|
|
615
|
+
break
|
|
616
|
+
case 'face':
|
|
617
|
+
elements.push({ type: 'face', data: { id: i.id } })
|
|
618
|
+
break
|
|
619
|
+
case 'at':
|
|
620
|
+
elements.push({ type: 'at', data: { qq: String(i.uid || i.uin) } })
|
|
621
|
+
break
|
|
622
|
+
case 'reply':
|
|
623
|
+
elements.push({ type: 'reply', data: { id: i.message_id } })
|
|
624
|
+
break
|
|
625
|
+
case 'image':
|
|
626
|
+
case 'video':
|
|
627
|
+
case 'file': {
|
|
628
|
+
elements.push({ type: i.type, data: { file: i.file } })
|
|
629
|
+
break
|
|
630
|
+
}
|
|
631
|
+
case 'xml':
|
|
632
|
+
case 'json': {
|
|
633
|
+
elements.push({ type: i.type, data: { data: i.data } })
|
|
634
|
+
break
|
|
635
|
+
}
|
|
636
|
+
// case 'node': {
|
|
637
|
+
// let { type, user_id = selfUin, nickname = selfNick, content } = i
|
|
638
|
+
// content = this.KarinConvertAdapter(content)
|
|
639
|
+
// elements.push({ type, data: { uin: user_id, name: nickname, content } })
|
|
640
|
+
// break
|
|
641
|
+
// }
|
|
642
|
+
case 'forward': {
|
|
643
|
+
elements.push({ type: 'forward', data: { id: i.res_id } })
|
|
644
|
+
break
|
|
645
|
+
}
|
|
646
|
+
case 'voice': {
|
|
647
|
+
elements.push({ type: 'record', data: { file: i.file, magic: i.magic || false } })
|
|
648
|
+
break
|
|
649
|
+
}
|
|
650
|
+
case 'music': {
|
|
651
|
+
// if (i.platform) {
|
|
652
|
+
// elements.push({ type: 'music', data: { type: i.platform, id: i.id } })
|
|
653
|
+
// } else {
|
|
654
|
+
// const { url, audio, title, content, image } = i
|
|
655
|
+
// elements.push({ type: 'music', data: { type: 'custom', url, audio, title, content, image } })
|
|
656
|
+
// }
|
|
657
|
+
break
|
|
658
|
+
}
|
|
659
|
+
case 'button': {
|
|
660
|
+
// todo
|
|
661
|
+
// elements.push({ type: 'button', data: { buttons: i.buttons } })
|
|
662
|
+
break
|
|
663
|
+
}
|
|
664
|
+
case 'markdown': {
|
|
665
|
+
const { type, ...data } = i
|
|
666
|
+
elements.push({ type, data: { ...data } })
|
|
667
|
+
break
|
|
668
|
+
}
|
|
669
|
+
// case 'rows': {
|
|
670
|
+
// for (const val of i.rows) {
|
|
671
|
+
// elements.push({ type: 'button', data: { buttons: val.buttons } })
|
|
672
|
+
// }
|
|
673
|
+
// break
|
|
674
|
+
// }
|
|
675
|
+
case 'poke': {
|
|
676
|
+
elements.push({ type: 'poke', data: { type: i.poke_type, id: i.id } })
|
|
677
|
+
break
|
|
678
|
+
}
|
|
679
|
+
default: {
|
|
680
|
+
elements.push(i)
|
|
681
|
+
logger.info(i)
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return elements
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* 专属当前Bot的日志打印方法
|
|
690
|
+
*/
|
|
691
|
+
logger (level, ...args) {
|
|
692
|
+
logger.bot(level, this.account.uid || this.account.uin, ...args)
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* 获取头像url
|
|
697
|
+
* @param 头像大小,默认`0`
|
|
698
|
+
* @param 用户qq,默认为机器人QQ
|
|
699
|
+
* @returns 头像的url地址
|
|
700
|
+
*/
|
|
701
|
+
getAvatarUrl (uid = this.account.uid || this.account.uin, size = 0) {
|
|
702
|
+
return Number(uid) ? `https://q1.qlogo.cn/g?b=qq&s=${size}&nk=${uid}` : `https://q.qlogo.cn/qqapp/${uid}/${uid}/${size}`
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* 获取群头像
|
|
707
|
+
* @param group_id - 群号
|
|
708
|
+
* @param size - 头像大小,默认`0`
|
|
709
|
+
* @param history - 历史头像记录,默认`0`,若要获取历史群头像则填写1,2,3...
|
|
710
|
+
* @returns - 群头像的url地址
|
|
711
|
+
*/
|
|
712
|
+
getGroupAvatar (group_id, size = 0, history = 0) {
|
|
713
|
+
return `https://p.qlogo.cn/gh/${group_id}/${group_id}${history ? '_' + history : ''}/` + size
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* 发送私聊消息
|
|
718
|
+
* @param user_id - 用户ID
|
|
719
|
+
* @param message - 要发送的内容
|
|
720
|
+
* @returns - 消息ID
|
|
721
|
+
*/
|
|
722
|
+
async send_private_msg (user_id, message) {
|
|
723
|
+
const obMessage = this.KarinConvertAdapter(message)
|
|
724
|
+
// this.logger(`${logger.green(`Send private ${user_id}: `)}${this.logSend(message)}`))
|
|
725
|
+
return await this.SendApi('send_private_msg', { user_id, message: obMessage })
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* 发送群消息
|
|
730
|
+
* @param group_id - 群号
|
|
731
|
+
* @param message - 要发送的内容
|
|
732
|
+
* @returns - 消息ID
|
|
733
|
+
*/
|
|
734
|
+
async send_group_msg (group_id, message) {
|
|
735
|
+
const obMessages = this.KarinConvertAdapter(message)
|
|
736
|
+
return await this.SendApi('send_group_msg', { group_id, message: obMessages })
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* 发送消息
|
|
741
|
+
*
|
|
742
|
+
* @param contact
|
|
743
|
+
* @param elements
|
|
744
|
+
* @returns - 消息ID
|
|
745
|
+
*/
|
|
746
|
+
async SendMessage (contact, elements) {
|
|
747
|
+
const { scene, peer } = contact
|
|
748
|
+
const message_type = scene === 'group' ? 'group' : 'private'
|
|
749
|
+
const key = scene === 'group' ? 'group_id' : 'user_id'
|
|
750
|
+
const message = this.KarinConvertAdapter(elements)
|
|
751
|
+
const params = { [key]: peer, message_type, message }
|
|
752
|
+
return await this.SendApi('send_msg', params)
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* 上传合并转发消息
|
|
757
|
+
* @param contact - 联系人信息
|
|
758
|
+
* @param elements - nodes
|
|
759
|
+
* @returns - 资源id
|
|
760
|
+
* */
|
|
761
|
+
async UploadForwardMessage (contact, elements) {
|
|
762
|
+
if (!Array.isArray(elements)) { elements = [elements] }
|
|
763
|
+
if (elements.some((element) => element.type !== 'node')) {
|
|
764
|
+
throw new Error('elements should be all node type')
|
|
765
|
+
}
|
|
766
|
+
const { scene, peer } = contact
|
|
767
|
+
const message_type = scene === 'group' ? 'group_id' : 'user_id'
|
|
768
|
+
const messages = this.KarinConvertAdapter(elements)
|
|
769
|
+
const params = { [message_type]: String(peer), messages }
|
|
770
|
+
return await this.SendApi('send_forward_msg', params)
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* 通过资源id发送转发消息
|
|
775
|
+
* @param contact - 联系人信息
|
|
776
|
+
* @param id - 资源id
|
|
777
|
+
* */
|
|
778
|
+
async SendMessageByResId (contact, id) {
|
|
779
|
+
const { scene, peer } = contact
|
|
780
|
+
const message_type = scene === 'group' ? 'group' : 'private'
|
|
781
|
+
const key = scene === 'group' ? 'group_id' : 'user_id'
|
|
782
|
+
const message = [{ type: 'forward', data: { id } }]
|
|
783
|
+
const params = { [key]: peer, message_type, message }
|
|
784
|
+
const res = await this.SendApi('send_msg', params)
|
|
785
|
+
return { message_id: res.message_id, message_time: Date.now() }
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* 撤回消息
|
|
790
|
+
* @param {null} [_contact] - ob11无需提供contact参数
|
|
791
|
+
* @param message_id - 消息ID
|
|
792
|
+
* @returns {Promise<null>}
|
|
793
|
+
*/
|
|
794
|
+
async RecallMessage (_contact, message_id) {
|
|
795
|
+
return await this.SendApi('delete_msg', { message_id })
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* 获取消息
|
|
800
|
+
* @param {null} [_contact] - ob11无需提供contact参数
|
|
801
|
+
* @param message_id - 消息ID
|
|
802
|
+
* @returns {Promise<object>} - 消息内容
|
|
803
|
+
*/
|
|
804
|
+
async GetMessage (_contact, message_id) {
|
|
805
|
+
let res = await this.SendApi('get_msg', { message_id })
|
|
806
|
+
res = {
|
|
807
|
+
time: res.time,
|
|
808
|
+
message_id: res.message_id,
|
|
809
|
+
message_seq: res.message_id,
|
|
810
|
+
contact: {
|
|
811
|
+
scene: res.message_type === 'group' ? 'group' : 'private',
|
|
812
|
+
peer: res.sender.user_id, // 拿不到group_id...
|
|
813
|
+
},
|
|
814
|
+
sender: {
|
|
815
|
+
uid: res.sender.user_id,
|
|
816
|
+
uin: res.sender.user_id,
|
|
817
|
+
nick: res.sender.nickname,
|
|
818
|
+
},
|
|
819
|
+
elements: this.AdapterConvertKarin(res.message),
|
|
820
|
+
}
|
|
821
|
+
return res
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* 获取msg_id获取历史消息
|
|
826
|
+
* @description 此api各平台实现不同,暂时废弃
|
|
827
|
+
*/
|
|
828
|
+
async GetHistoryMessage (contact, start_message_id, count = 1) {
|
|
829
|
+
const type = contact.scene === 'group' ? 'group_id' : 'user_id'
|
|
830
|
+
const param = { [type]: contact.peer, message_id: start_message_id, count }
|
|
831
|
+
const api = contact.scene === 'group' ? 'get_group_msg_history' : 'get_friend_msg_history'
|
|
832
|
+
const res = await this.SendApi(api, param, 120)
|
|
833
|
+
const ret = []
|
|
834
|
+
for (const i of res.messages) {
|
|
835
|
+
let { time = Date.now(), message_id, message_seq = message_id, sender, message } = i
|
|
836
|
+
const { user_id, nickname } = sender
|
|
837
|
+
sender = { uid: user_id, uin: user_id, nick: nickname }
|
|
838
|
+
const elements = this.AdapterConvertKarin(message)
|
|
839
|
+
ret.push({ time, message_id, message_seq, contact, sender, elements })
|
|
840
|
+
}
|
|
841
|
+
return ret
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* 获取合并转发消息
|
|
846
|
+
*/
|
|
847
|
+
async get_forward_msg (id) {
|
|
848
|
+
return await this.SendApi('get_forward_msg', { id })
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* 发送好友赞
|
|
853
|
+
* @param target_uid_or_uin - 用户ID
|
|
854
|
+
* @param vote_count - 赞的次数,默认为`10`
|
|
855
|
+
*/
|
|
856
|
+
async VoteUser (target_uid_or_uin, vote_count = 10) {
|
|
857
|
+
const user_id = Number(target_uid_or_uin)
|
|
858
|
+
await this.SendApi('send_like', { user_id, times: vote_count })
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* 群组踢人
|
|
863
|
+
*/
|
|
864
|
+
async KickMember (group_id, target_uid_or_uin, reject_add_request = false, kick_reason = '') {
|
|
865
|
+
const user_id = Number(target_uid_or_uin)
|
|
866
|
+
await this.SendApi('set_group_kick', { group_id, user_id, reject_add_request })
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* 禁言用户
|
|
871
|
+
* @param group_id - 群号
|
|
872
|
+
* @param target_uid_or_uin - 用户ID
|
|
873
|
+
* @param duration - 禁言时长,单位秒,0 表示取消禁言
|
|
874
|
+
*/
|
|
875
|
+
async BanMember (group_id, target_uid_or_uin, duration) {
|
|
876
|
+
const user_id = Number(target_uid_or_uin)
|
|
877
|
+
await this.SendApi('set_group_ban', { group_id, user_id, duration })
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* 群组全员禁言
|
|
882
|
+
* @param group_id - 群号
|
|
883
|
+
* @param enable - 是否全员禁言
|
|
884
|
+
*/
|
|
885
|
+
async SetGroupWholeBan (group_id, enable = true) {
|
|
886
|
+
await this.SendApi('set_group_whole_ban', { group_id, enable })
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* 设置群管理员
|
|
891
|
+
* @param {{
|
|
892
|
+
* group_id:string,
|
|
893
|
+
* target_uid?:string,
|
|
894
|
+
* target_uin?:string,
|
|
895
|
+
* is_admin:boolean
|
|
896
|
+
* }} options - 设置管理员选项
|
|
897
|
+
* @param options.group_id - 群组ID
|
|
898
|
+
* @param options.target_uid - 要设置为管理员的用户uid
|
|
899
|
+
* @param options.target_uin - 要设置为管理员的用户uin
|
|
900
|
+
* @param options.is_admin - 是否设置为管理员
|
|
901
|
+
* @returns {Promise<SetGroupAdminResponse>} - 设置群管理员操作的响应
|
|
902
|
+
*/
|
|
903
|
+
async SetGroupAdmin (group_id, target_uid_or_uin, is_admin) {
|
|
904
|
+
const user_id = Number(target_uid_or_uin)
|
|
905
|
+
await this.SendApi('set_group_admin', { group_id, user_id, enable: is_admin })
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* 群组匿名
|
|
910
|
+
* @param group_id - 群号
|
|
911
|
+
* @param enable - 是否允许匿名聊天
|
|
912
|
+
*/
|
|
913
|
+
async set_group_anonymous (group_id, enable = true) {
|
|
914
|
+
await this.SendApi('set_group_anonymous', { group_id, enable })
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* 修改群名片
|
|
919
|
+
* @param group_id - 群号
|
|
920
|
+
* @param target_uid_or_uin - 目标用户ID
|
|
921
|
+
* @param card - 新名片
|
|
922
|
+
*/
|
|
923
|
+
async ModifyMemberCard (group_id, target_uid_or_uin, card) {
|
|
924
|
+
const user_id = Number(target_uid_or_uin)
|
|
925
|
+
await this.SendApi('set_group_card', { group_id, user_id, card })
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* 设置群名
|
|
930
|
+
* @param group_id - 群号
|
|
931
|
+
* @param group_name - 新群名
|
|
932
|
+
*/
|
|
933
|
+
async ModifyGroupName (group_id, group_name) {
|
|
934
|
+
await this.SendApi('set_group_name', { group_id, group_name })
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* 退出群组
|
|
939
|
+
* @param group_id - 群号
|
|
940
|
+
* @param is_dismiss - 是否解散,如果登录号是群主,则仅在此项为 true 时能够解散
|
|
941
|
+
*/
|
|
942
|
+
async LeaveGroup (group_id, is_dismiss = false) {
|
|
943
|
+
await this.SendApi('set_group_leave', { group_id, is_dismiss })
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* 设置群专属头衔
|
|
948
|
+
* @param group_id - 群号
|
|
949
|
+
* @param target_uid_or_uin - 目标用户ID
|
|
950
|
+
* @param special_title - 专属头衔
|
|
951
|
+
*/
|
|
952
|
+
async SetGroupUniqueTitle (group_id, target_uid_or_uin, unique_title) {
|
|
953
|
+
const user_id = Number(target_uid_or_uin)
|
|
954
|
+
const special_title = unique_title
|
|
955
|
+
const duration = -1
|
|
956
|
+
await this.SendApi('set_group_special_title', { group_id, user_id, special_title, duration })
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// /**
|
|
960
|
+
// * 处理加好友请求
|
|
961
|
+
// * @param flag - 加好友请求的 flag(需从上报的数据中获得)
|
|
962
|
+
// * @param {boolean} [approve=true] - 是否同意请求
|
|
963
|
+
// * @param [remark=''] - 添加后的好友备注(仅在同意时有效)
|
|
964
|
+
// */
|
|
965
|
+
// async set_friend_add_request(flag: string, approve = true, remark = '') {
|
|
966
|
+
// await this.SendApi('set_friend_add_request', { flag, approve, remark })
|
|
967
|
+
// }
|
|
968
|
+
// /**
|
|
969
|
+
// * 处理加群请求/邀请
|
|
970
|
+
// * @param flag - 加群请求的 flag(需从上报的数据中获得)
|
|
971
|
+
// * @param sub_type - add 或 invite,请求类型(需要和上报消息中的 sub_type 字段相符)
|
|
972
|
+
// * @param {boolean} [approve=true] - 是否同意请求/邀请
|
|
973
|
+
// * @param [reason=''] - 拒绝理由(仅在拒绝时有效)
|
|
974
|
+
// */
|
|
975
|
+
// async set_group_add_request(flag: string, sub_type: string, approve = true, reason = '') {
|
|
976
|
+
// await this.SendApi('set_group_add_request', { flag, sub_type, approve, reason })
|
|
977
|
+
// }
|
|
978
|
+
/**
|
|
979
|
+
* 获取登录号信息
|
|
980
|
+
*/
|
|
981
|
+
async GetCurrentAccount () {
|
|
982
|
+
const res = await this.SendApi('get_login_info')
|
|
983
|
+
return {
|
|
984
|
+
account_uid: res.user_id,
|
|
985
|
+
account_uin: res.user_id,
|
|
986
|
+
account_name: res.nickname,
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* 获取陌生人信息 不支持批量获取 只支持一个
|
|
992
|
+
* @param target_uid_or_uin - 目标用户ID
|
|
993
|
+
*/
|
|
994
|
+
async GetStrangerProfileCard (target_uid_or_uin) {
|
|
995
|
+
const user_id = Number(target_uid_or_uin[0]) || String(target_uid_or_uin[0])
|
|
996
|
+
const res = await this.SendApi('get_stranger_info', { user_id, no_cache: true })
|
|
997
|
+
return [res]
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* 获取好友列表
|
|
1002
|
+
* @returns {Promise<Array<IFriendInfo>>} - 好友列表
|
|
1003
|
+
*/
|
|
1004
|
+
async GetFriendList () {
|
|
1005
|
+
/** @type {{
|
|
1006
|
+
* user_id: number,
|
|
1007
|
+
* user_name: string?,
|
|
1008
|
+
* user_remark: string,
|
|
1009
|
+
* remark: string?,
|
|
1010
|
+
* nickname: string?,
|
|
1011
|
+
}[]} **/
|
|
1012
|
+
const friendList = await this.SendApi('get_friend_list')
|
|
1013
|
+
return friendList.map((friend) => {
|
|
1014
|
+
return {
|
|
1015
|
+
uin: friend.user_id,
|
|
1016
|
+
uid: friend.user_id,
|
|
1017
|
+
qid: '',
|
|
1018
|
+
nick: friend.nickname || friend.user_name,
|
|
1019
|
+
remark: friend.remark || friend.user_remark,
|
|
1020
|
+
}
|
|
1021
|
+
})
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* 获取群信息
|
|
1026
|
+
* @param group_id - 群号
|
|
1027
|
+
* @param no_cache - 是否不使用缓存
|
|
1028
|
+
* @returns {Promise<IGroupInfo>} - 群信息
|
|
1029
|
+
*/
|
|
1030
|
+
async GetGroupInfo (group_id, no_cache = false) {
|
|
1031
|
+
/**
|
|
1032
|
+
* @type {{
|
|
1033
|
+
* group_id: number,
|
|
1034
|
+
* group_name: string,
|
|
1035
|
+
* group_memo: string,
|
|
1036
|
+
* group_remark: string,
|
|
1037
|
+
* group_create_time: number,
|
|
1038
|
+
* group_level: number,
|
|
1039
|
+
* member_count: number,
|
|
1040
|
+
* max_member_count: number,
|
|
1041
|
+
* admins: number[]
|
|
1042
|
+
* }}
|
|
1043
|
+
*/
|
|
1044
|
+
const groupInfo = await this.SendApi('get_group_info', { group_id, no_cache })
|
|
1045
|
+
return {
|
|
1046
|
+
group_id: groupInfo.group_id,
|
|
1047
|
+
group_name: groupInfo.group_name,
|
|
1048
|
+
group_remark: groupInfo.group_memo || groupInfo.group_remark,
|
|
1049
|
+
max_member_count: groupInfo.max_member_count,
|
|
1050
|
+
member_count: groupInfo.member_count,
|
|
1051
|
+
group_uin: groupInfo.group_id,
|
|
1052
|
+
admins: groupInfo.admins,
|
|
1053
|
+
owner: groupInfo.admins[0],
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* 获取群列表
|
|
1059
|
+
*/
|
|
1060
|
+
async GetGroupList () {
|
|
1061
|
+
const groupList = await this.SendApi('get_group_list')
|
|
1062
|
+
return groupList?.map((groupInfo) => {
|
|
1063
|
+
return {
|
|
1064
|
+
group_id: groupInfo.group_id,
|
|
1065
|
+
group_name: groupInfo.group_name,
|
|
1066
|
+
group_remark: groupInfo.group_memo || groupInfo.group_remark,
|
|
1067
|
+
max_member_count: groupInfo.max_member_count,
|
|
1068
|
+
member_count: groupInfo.member_count,
|
|
1069
|
+
group_uin: groupInfo.group_id,
|
|
1070
|
+
admins: groupInfo.admins,
|
|
1071
|
+
}
|
|
1072
|
+
})
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* 获取群成员信息
|
|
1077
|
+
* @param group_id - 群号
|
|
1078
|
+
* @param target_uid_or_uin - 目标用户ID
|
|
1079
|
+
* @param refresh - 是否刷新缓存,默认为 false
|
|
1080
|
+
*/
|
|
1081
|
+
async GetGroupMemberInfo (group_id, target_uid_or_uin, refresh = false) {
|
|
1082
|
+
const user_id = Number(target_uid_or_uin)
|
|
1083
|
+
/**
|
|
1084
|
+
* @type {{
|
|
1085
|
+
* group_id: number,
|
|
1086
|
+
* user_id: number,
|
|
1087
|
+
* nickname: string,
|
|
1088
|
+
* card: string,
|
|
1089
|
+
* sex: string,
|
|
1090
|
+
* age: number,
|
|
1091
|
+
* area: string,
|
|
1092
|
+
* join_time: number,
|
|
1093
|
+
* last_sent_time: number,
|
|
1094
|
+
* level: string,
|
|
1095
|
+
* role: 'owner' | 'admin' | 'member',
|
|
1096
|
+
* unfriendly: boolean,
|
|
1097
|
+
* title: string,
|
|
1098
|
+
* title_expire_time: number,
|
|
1099
|
+
* card_changeable: boolean,
|
|
1100
|
+
* shut_up_timestamp: number
|
|
1101
|
+
* }}
|
|
1102
|
+
*/
|
|
1103
|
+
const groupMemberInfo = await this.SendApi('get_group_member_info', { group_id, user_id, no_cache: refresh })
|
|
1104
|
+
let level = 0
|
|
1105
|
+
try {
|
|
1106
|
+
level = parseInt(groupMemberInfo.level)
|
|
1107
|
+
} catch (e) { }
|
|
1108
|
+
return {
|
|
1109
|
+
uid: groupMemberInfo.user_id,
|
|
1110
|
+
uin: groupMemberInfo.user_id,
|
|
1111
|
+
nick: groupMemberInfo.nickname,
|
|
1112
|
+
age: groupMemberInfo.age,
|
|
1113
|
+
unique_title: groupMemberInfo.title,
|
|
1114
|
+
unique_title_expire_time: groupMemberInfo.title_expire_time,
|
|
1115
|
+
card: groupMemberInfo.card,
|
|
1116
|
+
join_time: groupMemberInfo.join_time,
|
|
1117
|
+
last_active_time: groupMemberInfo.last_sent_time,
|
|
1118
|
+
shut_up_time: 0,
|
|
1119
|
+
level,
|
|
1120
|
+
shut_up_timestamp: groupMemberInfo.shut_up_timestamp,
|
|
1121
|
+
unfriendly: groupMemberInfo.unfriendly,
|
|
1122
|
+
card_changeable: groupMemberInfo.card_changeable,
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* 获取群成员列表
|
|
1128
|
+
* @param group_id - 群号
|
|
1129
|
+
* @param refresh - 是否刷新缓存,默认为 false
|
|
1130
|
+
*/
|
|
1131
|
+
async GetGroupMemberList (group_id, refresh = false) {
|
|
1132
|
+
const gl = await this.SendApi('get_group_member_list', { group_id, refresh })
|
|
1133
|
+
return gl.map((groupMemberInfo) => {
|
|
1134
|
+
let level = 0
|
|
1135
|
+
try {
|
|
1136
|
+
level = parseInt(groupMemberInfo.level)
|
|
1137
|
+
} catch (e) { }
|
|
1138
|
+
return {
|
|
1139
|
+
uid: groupMemberInfo.user_id,
|
|
1140
|
+
uin: groupMemberInfo.user_id,
|
|
1141
|
+
nick: groupMemberInfo.nickname,
|
|
1142
|
+
age: groupMemberInfo.age,
|
|
1143
|
+
unique_title: groupMemberInfo.title,
|
|
1144
|
+
unique_title_expire_time: groupMemberInfo.title_expire_time,
|
|
1145
|
+
card: groupMemberInfo.card,
|
|
1146
|
+
join_time: groupMemberInfo.join_time,
|
|
1147
|
+
last_active_time: groupMemberInfo.last_sent_time,
|
|
1148
|
+
level,
|
|
1149
|
+
shut_up_timestamp: groupMemberInfo.shut_up_timestamp,
|
|
1150
|
+
unfriendly: groupMemberInfo.unfriendly,
|
|
1151
|
+
card_changeable: groupMemberInfo.card_changeable,
|
|
1152
|
+
}
|
|
1153
|
+
})
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* 获取群荣誉信息
|
|
1158
|
+
*/
|
|
1159
|
+
async GetGroupHonor (group_id, refresh = false) {
|
|
1160
|
+
/**
|
|
1161
|
+
* @typedef {{user_id: number, nickname: string, avatar: string, description: string}} GroupHonor
|
|
1162
|
+
*/
|
|
1163
|
+
/**
|
|
1164
|
+
* @type {{
|
|
1165
|
+
* group_id: number,
|
|
1166
|
+
* current_talkative: {user_id: number, nickname: string, avatar: string, day_count: number},
|
|
1167
|
+
* talkative_list: Array<GroupHonor>,
|
|
1168
|
+
* performer_list: Array<GroupHonor>,
|
|
1169
|
+
* legend_list: Array<GroupHonor>,
|
|
1170
|
+
* strong_newbie_list: Array<GroupHonor>,
|
|
1171
|
+
* emotion_list: Array<GroupHonor>,
|
|
1172
|
+
* }}
|
|
1173
|
+
*/
|
|
1174
|
+
const groupHonor = await this.SendApi('get_group_honor_info', { group_id, type: 'all' })
|
|
1175
|
+
const result = []
|
|
1176
|
+
groupHonor.talkative_list.forEach((honor) => {
|
|
1177
|
+
result.push({
|
|
1178
|
+
uin: honor.user_id,
|
|
1179
|
+
uid: honor.user_id,
|
|
1180
|
+
nick: honor.nickname,
|
|
1181
|
+
honor_name: '历史龙王',
|
|
1182
|
+
id: 0,
|
|
1183
|
+
avatar: honor.avatar,
|
|
1184
|
+
description: honor.description,
|
|
1185
|
+
})
|
|
1186
|
+
})
|
|
1187
|
+
groupHonor.performer_list.forEach((honor) => {
|
|
1188
|
+
result.push({
|
|
1189
|
+
uin: honor.user_id,
|
|
1190
|
+
uid: honor.user_id,
|
|
1191
|
+
nick: honor.nickname,
|
|
1192
|
+
honor_name: '群聊之火',
|
|
1193
|
+
avatar: honor.avatar,
|
|
1194
|
+
id: 0,
|
|
1195
|
+
description: honor.description,
|
|
1196
|
+
})
|
|
1197
|
+
})
|
|
1198
|
+
groupHonor.legend_list.forEach((honor) => {
|
|
1199
|
+
result.push({
|
|
1200
|
+
uin: honor.user_id,
|
|
1201
|
+
uid: honor.user_id,
|
|
1202
|
+
nick: honor.nickname,
|
|
1203
|
+
honor_name: '群聊炽焰',
|
|
1204
|
+
avatar: honor.avatar,
|
|
1205
|
+
id: 0,
|
|
1206
|
+
description: honor.description,
|
|
1207
|
+
})
|
|
1208
|
+
})
|
|
1209
|
+
groupHonor.strong_newbie_list.forEach((honor) => {
|
|
1210
|
+
result.push({
|
|
1211
|
+
uin: honor.user_id,
|
|
1212
|
+
uid: honor.user_id,
|
|
1213
|
+
nick: honor.nickname,
|
|
1214
|
+
honor_name: '冒尖小春笋',
|
|
1215
|
+
avatar: honor.avatar,
|
|
1216
|
+
id: 0,
|
|
1217
|
+
description: honor.description,
|
|
1218
|
+
})
|
|
1219
|
+
})
|
|
1220
|
+
groupHonor.emotion_list.forEach((honor) => {
|
|
1221
|
+
result.push({
|
|
1222
|
+
uin: honor.user_id,
|
|
1223
|
+
uid: honor.user_id,
|
|
1224
|
+
nick: honor.nickname,
|
|
1225
|
+
honor_name: '快乐之源',
|
|
1226
|
+
avatar: honor.avatar,
|
|
1227
|
+
id: 0,
|
|
1228
|
+
description: honor.description,
|
|
1229
|
+
})
|
|
1230
|
+
})
|
|
1231
|
+
return result
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// /**
|
|
1235
|
+
// * 对消息进行表情回应
|
|
1236
|
+
// * @param Contact - 联系人信息
|
|
1237
|
+
// * @param message_id - 消息ID
|
|
1238
|
+
// * @param face_id - 表情ID
|
|
1239
|
+
// */
|
|
1240
|
+
// async ReactMessageWithEmojiRequest(Contact: any, message_id: any, face_id: any, is_set = true) {
|
|
1241
|
+
// return await this.SendApi('set_msg_emoji_like', { message_id, emoji_id: face_id, is_set })
|
|
1242
|
+
// }
|
|
1243
|
+
/**
|
|
1244
|
+
* 获取版本信息
|
|
1245
|
+
*/
|
|
1246
|
+
async GetVersion () {
|
|
1247
|
+
return await this.SendApi('get_version_info')
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
async DownloadForwardMessage () {
|
|
1251
|
+
throw new Error('Method not implemented.')
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
async GetEssenceMessageList () {
|
|
1255
|
+
throw new Error('Method not implemented.')
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
async SetEssenceMessage () { }
|
|
1259
|
+
async DeleteEssenceMessage () { }
|
|
1260
|
+
async SetFriendApplyResult () { }
|
|
1261
|
+
async SetGroupApplyResultRequest () { }
|
|
1262
|
+
async SetInvitedJoinGroupResult () { }
|
|
1263
|
+
async ReactMessageWithEmojiRequest () { }
|
|
1264
|
+
async UploadPrivateFile () { }
|
|
1265
|
+
async UploadGroupFile () { }
|
|
1266
|
+
async sendForwardMessage () {
|
|
1267
|
+
return {}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
/**
|
|
1271
|
+
* 发送API请求
|
|
1272
|
+
* @param action - API断点
|
|
1273
|
+
* @param {object} params - API参数
|
|
1274
|
+
* @returns {Promise<any>} - API返回
|
|
1275
|
+
*/
|
|
1276
|
+
async SendApi (action, params = {}, time = 0) {
|
|
1277
|
+
if (!time) { time = config.timeout('ws') }
|
|
1278
|
+
const echo = randomUUID()
|
|
1279
|
+
const request = JSON.stringify({ echo, action, params })
|
|
1280
|
+
logger.debug(`[API请求] ${action}: ${request}`)
|
|
1281
|
+
return new Promise((resolve, reject) => {
|
|
1282
|
+
this.socket.send(request)
|
|
1283
|
+
this.socket.once(echo, data => {
|
|
1284
|
+
if (data.status === 'ok') {
|
|
1285
|
+
resolve(data.data)
|
|
1286
|
+
} else {
|
|
1287
|
+
this.logger('error', `[Api请求错误] ${action}: ${JSON.stringify(data, null, 2)}`)
|
|
1288
|
+
reject(data)
|
|
1289
|
+
}
|
|
1290
|
+
})
|
|
1291
|
+
/** 设置一个超时计时器 */
|
|
1292
|
+
setTimeout(() => {
|
|
1293
|
+
reject(new Error('API请求超时'))
|
|
1294
|
+
}, time * 1000)
|
|
1295
|
+
})
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
export default {
|
|
1299
|
+
type: 'websocket',
|
|
1300
|
+
path: '/onebot/v11/ws',
|
|
1301
|
+
adapter: OneBot11,
|
|
1302
|
+
}
|