node-karin 0.0.3 → 0.1.1

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.
Files changed (109) hide show
  1. package/LICENSE +674 -674
  2. package/README.md +57 -57
  3. package/config/defSet/App.yaml +37 -37
  4. package/config/defSet/config.yaml +43 -43
  5. package/config/defSet/group.yaml +18 -18
  6. package/config/defSet/pm2.yaml +21 -21
  7. package/config/defSet/server.yaml +42 -42
  8. package/config/view/App.yaml +74 -74
  9. package/config/view/config.yaml +100 -100
  10. package/config/view/group.yaml +62 -62
  11. package/config/view/pm2.yaml +41 -41
  12. package/config/view/redis.yaml +25 -25
  13. package/config/view/server.yaml +93 -93
  14. package/lib/adapter/onebot/onebot11.d.ts +430 -430
  15. package/lib/adapter/onebot/onebot11.js +1265 -1302
  16. package/lib/core/dir.d.ts +1 -0
  17. package/lib/core/dir.js +4 -0
  18. package/lib/core/init.js +4 -4
  19. package/lib/core/karin.d.ts +72 -72
  20. package/lib/core/karin.js +51 -51
  21. package/lib/core/listener.d.ts +121 -121
  22. package/lib/core/listener.js +178 -178
  23. package/lib/core/plugin.app.d.ts +15 -15
  24. package/lib/core/plugin.app.js +18 -18
  25. package/lib/core/plugin.d.ts +182 -182
  26. package/lib/core/plugin.js +132 -132
  27. package/lib/core/plugin.loader.d.ts +149 -149
  28. package/lib/core/plugin.loader.js +451 -451
  29. package/lib/core/server.d.ts +26 -26
  30. package/lib/core/server.js +209 -209
  31. package/lib/db/level.d.ts +20 -20
  32. package/lib/db/level.js +36 -36
  33. package/lib/db/redis.d.ts +41 -41
  34. package/lib/db/redis.js +131 -131
  35. package/lib/db/redis_level.d.ts +113 -113
  36. package/lib/db/redis_level.js +274 -274
  37. package/lib/event/event.d.ts +138 -138
  38. package/lib/event/event.handler.d.ts +29 -29
  39. package/lib/event/event.handler.js +138 -138
  40. package/lib/event/event.js +120 -120
  41. package/lib/event/message.d.ts +102 -102
  42. package/lib/event/message.handler.d.ts +25 -25
  43. package/lib/event/message.handler.js +237 -237
  44. package/lib/event/message.js +69 -69
  45. package/lib/event/notice.d.ts +49 -49
  46. package/lib/event/notice.js +14 -14
  47. package/lib/event/request.d.ts +49 -49
  48. package/lib/event/request.js +14 -14
  49. package/lib/event/review.handler.d.ts +54 -54
  50. package/lib/event/review.handler.js +374 -374
  51. package/lib/index.d.ts +24 -23
  52. package/lib/index.js +40 -40
  53. package/lib/renderer/app.d.ts +53 -53
  54. package/lib/renderer/app.js +88 -88
  55. package/lib/renderer/base.d.ts +30 -30
  56. package/lib/renderer/base.js +68 -68
  57. package/lib/renderer/client.d.ts +30 -30
  58. package/lib/renderer/client.js +155 -155
  59. package/lib/renderer/http.d.ts +19 -19
  60. package/lib/renderer/http.js +50 -50
  61. package/lib/renderer/server.d.ts +42 -42
  62. package/lib/renderer/server.js +110 -110
  63. package/lib/renderer/wormhole.d.ts +1 -1
  64. package/lib/renderer/wormhole.js +154 -154
  65. package/lib/tools/init.d.ts +2 -0
  66. package/lib/tools/init.js +31 -0
  67. package/lib/types/adapter.d.ts +575 -575
  68. package/lib/types/adapter.js +1 -1
  69. package/lib/types/config.d.ts +327 -327
  70. package/lib/types/config.js +1 -1
  71. package/lib/types/element.d.ts +576 -576
  72. package/lib/types/element.js +1 -1
  73. package/lib/types/index.d.ts +8 -8
  74. package/lib/types/index.js +8 -8
  75. package/lib/types/logger.d.ts +109 -109
  76. package/lib/types/logger.js +1 -1
  77. package/lib/types/onebots11.d.ts +1371 -1371
  78. package/lib/types/onebots11.js +1 -1
  79. package/lib/types/plugin.d.ts +282 -282
  80. package/lib/types/plugin.js +1 -1
  81. package/lib/types/render.d.ts +111 -111
  82. package/lib/types/render.js +1 -1
  83. package/lib/types/reply.d.ts +40 -40
  84. package/lib/types/reply.js +1 -1
  85. package/lib/types/types.d.ts +898 -898
  86. package/lib/types/types.js +1 -1
  87. package/lib/utils/YamlEditor.d.ts +62 -62
  88. package/lib/utils/YamlEditor.js +197 -197
  89. package/lib/utils/button.d.ts +49 -49
  90. package/lib/utils/button.js +75 -75
  91. package/lib/utils/common.d.ts +123 -123
  92. package/lib/utils/common.js +396 -396
  93. package/lib/utils/config.d.ts +72 -72
  94. package/lib/utils/config.js +259 -254
  95. package/lib/utils/exec.d.ts +22 -22
  96. package/lib/utils/exec.js +36 -36
  97. package/lib/utils/ffmpeg.d.ts +12 -12
  98. package/lib/utils/ffmpeg.js +25 -25
  99. package/lib/utils/handler.d.ts +76 -76
  100. package/lib/utils/handler.js +98 -98
  101. package/lib/utils/logger.d.ts +3 -3
  102. package/lib/utils/logger.js +104 -104
  103. package/lib/utils/segment.d.ts +276 -276
  104. package/lib/utils/segment.js +420 -420
  105. package/lib/utils/update.d.ts +69 -69
  106. package/lib/utils/update.js +145 -145
  107. package/lib/utils/updateVersion.d.ts +33 -33
  108. package/lib/utils/updateVersion.js +140 -140
  109. package/package.json +95 -91
@@ -1,112 +1,112 @@
1
- import Renderer from './app.js'
2
- import RenderBase from './base.js'
3
- import { randomUUID } from 'crypto'
4
- import logger from '../utils/logger.js'
5
- import listener from '../core/listener.js'
6
- class Puppeteer extends RenderBase {
7
- socket
8
- id
9
- type
10
- host
11
- url
12
- index
13
- constructor () {
14
- super()
15
- this.id = 0
16
- this.index = 0
17
- this.type = ''
18
- this.host = ''
19
- this.url = ''
1
+ import Renderer from './app.js'
2
+ import RenderBase from './base.js'
3
+ import { randomUUID } from 'crypto'
4
+ import logger from '../utils/logger.js'
5
+ import listener from '../core/listener.js'
6
+ class Puppeteer extends RenderBase {
7
+ socket
8
+ id
9
+ type
10
+ host
11
+ url
12
+ index
13
+ constructor () {
14
+ super()
15
+ this.id = 0
16
+ this.index = 0
17
+ this.type = ''
18
+ this.host = ''
19
+ this.url = ''
20
20
  }
21
-
22
- async server (socket, request) {
23
- this.socket = socket
24
- this.id = request.headers['renderer-id']
25
- this.type = request.headers['renderer-type']
26
- /** 注册渲染器 */
27
- this.host = request.headers.host
28
- this.url = `ws://${this.host + request.url}`
29
- logger.info(`[渲染器:${this.id}] 收到新的连接请求:` + logger.green(this.url))
30
- /** 监听上报事件 */
31
- this.socket.on('message', data => {
32
- const json = JSON.parse(data.toString())
33
- if (json.echo) {
34
- listener.emit(json.echo, json)
35
- } else if (json.action === 'heartbeat') {
36
- logger.debug(`[渲染器:${this.id}] 收到心跳:${this.url}`)
37
- } else {
38
- logger.warn(`[渲染器:${this.id}] 收到未知数据:`, data)
39
- }
40
- })
41
- /** 监听断开 */
42
- this.socket.on('close', () => {
43
- logger.warn(`[渲染器:${this.id}] 连接断开:${this.url}`)
44
- /** 卸载渲染器 */
45
- this.index && Renderer.unapp(this.index)
46
- this.index = 0
47
- })
48
- /** 注册渲染器 */
49
- try {
50
- const index = Renderer.app({
51
- id: this.id,
52
- type: this.type,
53
- render: this.render.bind(this),
54
- })
55
- this.index = index
56
- } catch (error) {
57
- logger.error(`[渲染器:${this.id}] 注册渲染器失败:`, error)
58
- /** 断开连接 */
59
- this.socket.close()
60
- }
21
+
22
+ async server (socket, request) {
23
+ this.socket = socket
24
+ this.id = request.headers['renderer-id']
25
+ this.type = request.headers['renderer-type']
26
+ /** 注册渲染器 */
27
+ this.host = request.headers.host
28
+ this.url = `ws://${this.host + request.url}`
29
+ logger.info(`[渲染器:${this.id}] 收到新的连接请求:` + logger.green(this.url))
30
+ /** 监听上报事件 */
31
+ this.socket.on('message', data => {
32
+ const json = JSON.parse(data.toString())
33
+ if (json.echo) {
34
+ listener.emit(json.echo, json)
35
+ } else if (json.action === 'heartbeat') {
36
+ logger.debug(`[渲染器:${this.id}] 收到心跳:${this.url}`)
37
+ } else {
38
+ logger.warn(`[渲染器:${this.id}] 收到未知数据:`, data)
39
+ }
40
+ })
41
+ /** 监听断开 */
42
+ this.socket.on('close', () => {
43
+ logger.warn(`[渲染器:${this.id}] 连接断开:${this.url}`)
44
+ /** 卸载渲染器 */
45
+ this.index && Renderer.unapp(this.index)
46
+ this.index = 0
47
+ })
48
+ /** 注册渲染器 */
49
+ try {
50
+ const index = Renderer.app({
51
+ id: this.id,
52
+ type: this.type,
53
+ render: this.render.bind(this),
54
+ })
55
+ this.index = index
56
+ } catch (error) {
57
+ logger.error(`[渲染器:${this.id}] 注册渲染器失败:`, error)
58
+ /** 断开连接 */
59
+ this.socket.close()
60
+ }
61
61
  }
62
-
63
- /**
64
- * 渲染模板
65
- * @param {object} options 渲染参数
66
- * @param {string} options.file http地址或本地文件路径
67
- * @param {string} [options.name] 模板名称
68
- * @param {string} [options.fileID] art-template后的文件名
69
- * @param {object} [options.data] 传递给模板的数据 template.render(data)
70
- * @param {'png'|'jpeg'|'webp'} [options.type] 截图类型 默认'webp'
71
- * @param {number} [options.quality] 截图质量 默认90 1-100
72
- * @param {boolean} options.omitBackground 是否隐藏背景 默认false
73
- * @param {object} [options.setViewport] 设置视窗大小和设备像素比 默认1920*1080、1
74
- * @param {number} [options.setViewport.width] 视窗宽度
75
- * @param {number} [options.setViewport.height] 视窗高度
76
- * @param {string} [options.setViewport.deviceScaleFactor] 设备像素比
77
- * @param {number|boolean} [options.multiPage] 分页截图 传递数字则视为视窗高度 返回数组
78
- * @param {object} [options.pageGotoParams] 页面goto时的参数
79
- * @param {number} [options.pageGotoParams.timeout] 页面加载超时时间
80
- * @param {'load'|'domcontentloaded'|'networkidle0'|'networkidle2'} [options.pageGotoParams.waitUntil] 页面加载状态
81
- * @returns {Promise<string|string[]>} 返回图片base64或数组
82
- */
83
- async render (options) {
84
- /** 渲染模板 */
85
- let file = ''
86
- if (options.file.includes('http') || options.vue) {
87
- file = options.file
88
- } else {
89
- file = 'file://' + this.dealTpl(options)
90
- }
91
- if (!file) { throw new Error(`[渲染器:${this.id}] 模板文件不存在:${options.name}`) }
92
- const echo = randomUUID()
93
- const action = 'render'
94
- const data = options
95
- /** 移除掉模板参数 */
96
- if (data.data) { delete data.data }
97
- data.file = file
98
- logger.debug(`[渲染器:${this.id}][反向WS] \n请求:${this.url} \nhtml: ${options.file} \ndata: ${JSON.stringify(data)}`)
99
- this.socket.send(JSON.stringify({ echo, action, data }))
100
- return new Promise((resolve, reject) => {
101
- listener.once(echo, data => {
102
- if (data.ok) { return resolve(data.data) }
103
- reject(new Error(JSON.stringify(data)))
104
- })
105
- })
106
- }
107
- }
108
- export default {
109
- type: 'render',
110
- path: '/puppeteer',
111
- adapter: Puppeteer,
112
- }
62
+
63
+ /**
64
+ * 渲染模板
65
+ * @param {object} options 渲染参数
66
+ * @param {string} options.file http地址或本地文件路径
67
+ * @param {string} [options.name] 模板名称
68
+ * @param {string} [options.fileID] art-template后的文件名
69
+ * @param {object} [options.data] 传递给模板的数据 template.render(data)
70
+ * @param {'png'|'jpeg'|'webp'} [options.type] 截图类型 默认'webp'
71
+ * @param {number} [options.quality] 截图质量 默认90 1-100
72
+ * @param {boolean} options.omitBackground 是否隐藏背景 默认false
73
+ * @param {object} [options.setViewport] 设置视窗大小和设备像素比 默认1920*1080、1
74
+ * @param {number} [options.setViewport.width] 视窗宽度
75
+ * @param {number} [options.setViewport.height] 视窗高度
76
+ * @param {string} [options.setViewport.deviceScaleFactor] 设备像素比
77
+ * @param {number|boolean} [options.multiPage] 分页截图 传递数字则视为视窗高度 返回数组
78
+ * @param {object} [options.pageGotoParams] 页面goto时的参数
79
+ * @param {number} [options.pageGotoParams.timeout] 页面加载超时时间
80
+ * @param {'load'|'domcontentloaded'|'networkidle0'|'networkidle2'} [options.pageGotoParams.waitUntil] 页面加载状态
81
+ * @returns {Promise<string|string[]>} 返回图片base64或数组
82
+ */
83
+ async render (options) {
84
+ /** 渲染模板 */
85
+ let file = ''
86
+ if (options.file.includes('http') || options.vue) {
87
+ file = options.file
88
+ } else {
89
+ file = 'file://' + this.dealTpl(options)
90
+ }
91
+ if (!file) { throw new Error(`[渲染器:${this.id}] 模板文件不存在:${options.name}`) }
92
+ const echo = randomUUID()
93
+ const action = 'render'
94
+ const data = options
95
+ /** 移除掉模板参数 */
96
+ if (data.data) { delete data.data }
97
+ data.file = file
98
+ logger.debug(`[渲染器:${this.id}][反向WS] \n请求:${this.url} \nhtml: ${options.file} \ndata: ${JSON.stringify(data)}`)
99
+ this.socket.send(JSON.stringify({ echo, action, data }))
100
+ return new Promise((resolve, reject) => {
101
+ listener.once(echo, data => {
102
+ if (data.ok) { return resolve(data.data) }
103
+ reject(new Error(JSON.stringify(data)))
104
+ })
105
+ })
106
+ }
107
+ }
108
+ export default {
109
+ type: 'render',
110
+ path: '/puppeteer',
111
+ adapter: Puppeteer,
112
+ }
@@ -1 +1 @@
1
- export default function connect(): void;
1
+ export default function connect(): void;
@@ -1,154 +1,154 @@
1
- import WebSocket from 'ws'
2
- import fs from 'fs'
3
- import path from 'path'
4
- import { URL } from 'url'
5
- import HttpRenderer from './http.js'
6
- import config from '../utils/config.js'
7
- import { render } from '../index.js'
8
- let ws
9
- let reConnect
10
- const chunkSize = 1024 * 1024 * 3 // 文件分片大小
11
- export default function connect () {
12
- let heartbeat
13
- let index = 0
14
- reConnect = undefined
15
- const wsUrl = config.Server.HttpRender.WormholeClient
16
- ws = new WebSocket(wsUrl)
17
- ws.on('open', function open () {
18
- logger.info('连接到wormhole服务器' + wsUrl)
19
- // 发送心跳
20
- heartbeat = setInterval(() => {
21
- ws.send(JSON.stringify({ type: 'heartbeat', date: new Date() }))
22
- }, 30000) // 每30秒发送一次心跳
23
- })
24
- ws.on('message', msg => {
25
- let data = ''
26
- try {
27
- data = JSON.parse(msg.toString())
28
- } catch (error) {
29
- logger.warn(`收到非法消息${data}`)
30
- }
31
- const echo = data.echo
32
- switch (data.type) {
33
- case 'msg': {
34
- const { post, token, WormholeClient } = config.Server.HttpRender
35
- const parsedUrl = new URL(WormholeClient)
36
- const { hostname, port } = parsedUrl
37
- const ishttps = WormholeClient.includes('wss://')
38
- const host = `${ishttps ? 'https' : 'http'}://${hostname}${port ? `:${port}` : ''}/web/${data.date}`
39
- logger.mark(`web渲染器已连接,地址:${host}`)
40
- /** 注册渲染器 */
41
- const rd = new HttpRenderer(host, post, token)
42
- index = render.app({ id: 'puppeteer', type: 'image', render: rd.render.bind(rd) })
43
- break
44
- }
45
- case 'web':
46
- if (data.path) {
47
- const filePath = data.path
48
- const query = data.query
49
- if (query.html) {
50
- ws.send(JSON.stringify({ type: 'web', command: 'redirect', path: filePath, target: query.html.startsWith('/') ? query.html.slice(1) : query.html, echo }))
51
- return
52
- }
53
- const list = ['.css', '.html', '.ttf', '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.ico', '.woff', '.woff2']
54
- if (!list.some(ext => path.extname(filePath).endsWith(ext))) {
55
- logger.warn(`拦截非资源文件${filePath}`)
56
- ws.send(JSON.stringify({ type: 'web', state: 'error', error: '非资源文件', echo }))
57
- return
58
- }
59
- /** 判断一下是否为html 如果是需要特殊处理 */
60
- if (path.extname(filePath) === '.html') {
61
- let html = filePath
62
- /** 获取文件路径 对路径进行处理,去掉../、./ */
63
- html = `./${html.replace(/\\/g, '/').replace(/(\.\/|\.\.\/)/g, '')}`
64
- /** 判断是否为html文件且路径存在 */
65
- if (!fs.existsSync(html)) {
66
- return ws.send(JSON.stringify({ type: 'web', state: 'error', error: '文件不存在', echo }))
67
- }
68
- let content = fs.readFileSync(html, 'utf-8')
69
- /** 处理所有绝对路径、相对路径 */
70
- content = content.replace(new RegExp(`(${process.cwd()}|${process.cwd().replace(/\\/g, '/')})`, 'g'), '')
71
- // 保存到本地
72
- // filePath = './1.html'
73
- // fs.writeFileSync(filePath, content, 'utf-8')
74
- return ws.send(JSON.stringify({
75
- type: 'web',
76
- path: data.path,
77
- command: 'resource',
78
- data: Buffer.from(content),
79
- state: 'complete',
80
- part: 0,
81
- echo,
82
- }))
83
- }
84
- logger.info(`获取网页文件数据:${filePath}`)
85
- // 获取文件
86
- const stream = fs.createReadStream(filePath, { highWaterMark: chunkSize })
87
- let part = 0
88
- stream.on('data', chunk => {
89
- part++
90
- const message = {
91
- type: 'web',
92
- path: data.path,
93
- command: 'resource',
94
- data: chunk,
95
- state: 'part',
96
- part,
97
- echo,
98
- }
99
- ws.send(JSON.stringify(message))
100
- })
101
- stream.on('end', () => {
102
- part++
103
- // 如果是最后一片段,则更新状态为 'complete'
104
- if (stream.readableEnded) {
105
- ws.send(JSON.stringify({
106
- type: 'web',
107
- path: data.path,
108
- command: 'resource',
109
- data: '',
110
- state: 'complete',
111
- part,
112
- echo,
113
- }))
114
- }
115
- })
116
- stream.on('error', err => {
117
- ws.send(JSON.stringify({ type: 'web', state: 'error', error: err.message, echo }))
118
- })
119
- } else {
120
- ws.send(JSON.stringify({ type: 'web', state: 'error', error: '错误的文件路径', echo }))
121
- }
122
- break
123
- default:
124
- logger.warn(`未知消息类型${JSON.stringify(data)}`)
125
- break
126
- }
127
- })
128
- ws.on('close', function close () {
129
- /** 卸载渲染器 */
130
- index && render.unapp(index)
131
- index = 0
132
- if (heartbeat) {
133
- clearInterval(heartbeat)
134
- heartbeat = null
135
- }
136
- logger.warn('连接关闭,10秒后尝试重新连接')
137
- if (!reConnect) {
138
- reConnect = setTimeout(connect, 10000)
139
- }
140
- })
141
- ws.on('error', function error () {
142
- /** 卸载渲染器 */
143
- index && render.unapp(index)
144
- index = 0
145
- if (heartbeat) {
146
- clearInterval(heartbeat)
147
- heartbeat = null
148
- }
149
- logger.warn('连接错误,10秒后尝试重新连接')
150
- if (!reConnect) {
151
- reConnect = setTimeout(connect, 10000)
152
- }
153
- })
154
- }
1
+ import WebSocket from 'ws'
2
+ import fs from 'fs'
3
+ import path from 'path'
4
+ import { URL } from 'url'
5
+ import HttpRenderer from './http.js'
6
+ import config from '../utils/config.js'
7
+ import { render } from '../index.js'
8
+ let ws
9
+ let reConnect
10
+ const chunkSize = 1024 * 1024 * 3 // 文件分片大小
11
+ export default function connect () {
12
+ let heartbeat
13
+ let index = 0
14
+ reConnect = undefined
15
+ const wsUrl = config.Server.HttpRender.WormholeClient
16
+ ws = new WebSocket(wsUrl)
17
+ ws.on('open', function open () {
18
+ logger.info('连接到wormhole服务器' + wsUrl)
19
+ // 发送心跳
20
+ heartbeat = setInterval(() => {
21
+ ws.send(JSON.stringify({ type: 'heartbeat', date: new Date() }))
22
+ }, 30000) // 每30秒发送一次心跳
23
+ })
24
+ ws.on('message', msg => {
25
+ let data = ''
26
+ try {
27
+ data = JSON.parse(msg.toString())
28
+ } catch (error) {
29
+ logger.warn(`收到非法消息${data}`)
30
+ }
31
+ const echo = data.echo
32
+ switch (data.type) {
33
+ case 'msg': {
34
+ const { post, token, WormholeClient } = config.Server.HttpRender
35
+ const parsedUrl = new URL(WormholeClient)
36
+ const { hostname, port } = parsedUrl
37
+ const ishttps = WormholeClient.includes('wss://')
38
+ const host = `${ishttps ? 'https' : 'http'}://${hostname}${port ? `:${port}` : ''}/web/${data.date}`
39
+ logger.mark(`web渲染器已连接,地址:${host}`)
40
+ /** 注册渲染器 */
41
+ const rd = new HttpRenderer(host, post, token)
42
+ index = render.app({ id: 'puppeteer', type: 'image', render: rd.render.bind(rd) })
43
+ break
44
+ }
45
+ case 'web':
46
+ if (data.path) {
47
+ const filePath = data.path
48
+ const query = data.query
49
+ if (query.html) {
50
+ ws.send(JSON.stringify({ type: 'web', command: 'redirect', path: filePath, target: query.html.startsWith('/') ? query.html.slice(1) : query.html, echo }))
51
+ return
52
+ }
53
+ const list = ['.css', '.html', '.ttf', '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.ico', '.woff', '.woff2']
54
+ if (!list.some(ext => path.extname(filePath).endsWith(ext))) {
55
+ logger.warn(`拦截非资源文件${filePath}`)
56
+ ws.send(JSON.stringify({ type: 'web', state: 'error', error: '非资源文件', echo }))
57
+ return
58
+ }
59
+ /** 判断一下是否为html 如果是需要特殊处理 */
60
+ if (path.extname(filePath) === '.html') {
61
+ let html = filePath
62
+ /** 获取文件路径 对路径进行处理,去掉../、./ */
63
+ html = `./${html.replace(/\\/g, '/').replace(/(\.\/|\.\.\/)/g, '')}`
64
+ /** 判断是否为html文件且路径存在 */
65
+ if (!fs.existsSync(html)) {
66
+ return ws.send(JSON.stringify({ type: 'web', state: 'error', error: '文件不存在', echo }))
67
+ }
68
+ let content = fs.readFileSync(html, 'utf-8')
69
+ /** 处理所有绝对路径、相对路径 */
70
+ content = content.replace(new RegExp(`(${process.cwd()}|${process.cwd().replace(/\\/g, '/')})`, 'g'), '')
71
+ // 保存到本地
72
+ // filePath = './1.html'
73
+ // fs.writeFileSync(filePath, content, 'utf-8')
74
+ return ws.send(JSON.stringify({
75
+ type: 'web',
76
+ path: data.path,
77
+ command: 'resource',
78
+ data: Buffer.from(content),
79
+ state: 'complete',
80
+ part: 0,
81
+ echo,
82
+ }))
83
+ }
84
+ logger.info(`获取网页文件数据:${filePath}`)
85
+ // 获取文件
86
+ const stream = fs.createReadStream(filePath, { highWaterMark: chunkSize })
87
+ let part = 0
88
+ stream.on('data', chunk => {
89
+ part++
90
+ const message = {
91
+ type: 'web',
92
+ path: data.path,
93
+ command: 'resource',
94
+ data: chunk,
95
+ state: 'part',
96
+ part,
97
+ echo,
98
+ }
99
+ ws.send(JSON.stringify(message))
100
+ })
101
+ stream.on('end', () => {
102
+ part++
103
+ // 如果是最后一片段,则更新状态为 'complete'
104
+ if (stream.readableEnded) {
105
+ ws.send(JSON.stringify({
106
+ type: 'web',
107
+ path: data.path,
108
+ command: 'resource',
109
+ data: '',
110
+ state: 'complete',
111
+ part,
112
+ echo,
113
+ }))
114
+ }
115
+ })
116
+ stream.on('error', err => {
117
+ ws.send(JSON.stringify({ type: 'web', state: 'error', error: err.message, echo }))
118
+ })
119
+ } else {
120
+ ws.send(JSON.stringify({ type: 'web', state: 'error', error: '错误的文件路径', echo }))
121
+ }
122
+ break
123
+ default:
124
+ logger.warn(`未知消息类型${JSON.stringify(data)}`)
125
+ break
126
+ }
127
+ })
128
+ ws.on('close', function close () {
129
+ /** 卸载渲染器 */
130
+ index && render.unapp(index)
131
+ index = 0
132
+ if (heartbeat) {
133
+ clearInterval(heartbeat)
134
+ heartbeat = null
135
+ }
136
+ logger.warn('连接关闭,10秒后尝试重新连接')
137
+ if (!reConnect) {
138
+ reConnect = setTimeout(connect, 10000)
139
+ }
140
+ })
141
+ ws.on('error', function error () {
142
+ /** 卸载渲染器 */
143
+ index && render.unapp(index)
144
+ index = 0
145
+ if (heartbeat) {
146
+ clearInterval(heartbeat)
147
+ heartbeat = null
148
+ }
149
+ logger.warn('连接错误,10秒后尝试重新连接')
150
+ if (!reConnect) {
151
+ reConnect = setTimeout(connect, 10000)
152
+ }
153
+ })
154
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs'
3
+ import path from 'path'
4
+ import { karinDir } from '../core/dir.js'
5
+ const pathList = ['./plugins', './config/config']
6
+ for (const dir of pathList) { mkdir(dir) }
7
+ function mkdir (dirname) {
8
+ if (fs.existsSync(dirname)) { return true }
9
+ /** 递归自调用 */
10
+ if (mkdir(path.dirname(dirname))) { fs.mkdirSync(dirname) }
11
+ return true
12
+ }
13
+ // 删除这两个文件夹
14
+ const delList = ['./config/defSet', './config/view']
15
+ for (const dir of delList) {
16
+ if (fs.existsSync(dir)) { fs.rmdirSync(dir, { recursive: true }) }
17
+ // 复制整个文件夹过去
18
+ fs.copyFileSync(`${karinDir}${dir.replace('.', '')}`, dir)
19
+ }
20
+ // 判断是否为第一次使用
21
+ if (!fs.existsSync('./index.js') && !fs.existsSync('./plugins')) {
22
+ // 创建一个index.js文件 以供初次开箱使用,写入默认配置
23
+ fs.writeFileSync('./index.js', `import { Bot } from 'node-karin'
24
+ console.log(Bot.name + '开始初始化~')
25
+ `)
26
+ }
27
+ // 修改package.json为esm模块
28
+ const pack = JSON.parse(fs.readFileSync('./package.json', 'utf-8'))
29
+ pack.type = 'module'
30
+ fs.writeFileSync('./package.json', JSON.stringify(pack, null, 2))
31
+ console.log('初始化完成~,请通过 node index 启动程序~')