@kyyinfinite/lumina 1.0.2 → 1.0.5
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/package.json
CHANGED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import crypto from 'node:crypto'
|
|
2
|
+
import { ConnectionError } from '../errors.js'
|
|
3
|
+
|
|
4
|
+
const KNOWN_PACKAGES = [
|
|
5
|
+
'@kyyinfinite/baileys',
|
|
6
|
+
'@whiskeysockets/baileys',
|
|
7
|
+
'baileys',
|
|
8
|
+
'@adiwajshing/baileys',
|
|
9
|
+
'@brunocgc/baileys',
|
|
10
|
+
'@open-wa/baileys',
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
const REQUIRED_FNS = [
|
|
14
|
+
'prepareWAMessageMedia',
|
|
15
|
+
'generateWAMessageFromContent',
|
|
16
|
+
'generatePollMessage',
|
|
17
|
+
'generateReactionMessage',
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
function extractFns(mod) {
|
|
21
|
+
const missing = REQUIRED_FNS.filter((fn) => typeof mod[fn] !== 'function')
|
|
22
|
+
if (missing.length > 0) return null
|
|
23
|
+
return {
|
|
24
|
+
prepareWAMessageMedia: mod.prepareWAMessageMedia,
|
|
25
|
+
generateWAMessageFromContent: mod.generateWAMessageFromContent,
|
|
26
|
+
generatePollMessage: mod.generatePollMessage,
|
|
27
|
+
generateReactionMessage: mod.generateReactionMessage,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function makeFallbackFns(socket) {
|
|
32
|
+
const generateId = () => crypto.randomBytes(8).toString('hex').toUpperCase()
|
|
33
|
+
|
|
34
|
+
const generateWAMessageFromContent = (jid, content, opts = {}) => ({
|
|
35
|
+
key: {
|
|
36
|
+
remoteJid: jid,
|
|
37
|
+
fromMe: true,
|
|
38
|
+
id: opts.messageId ?? generateId(),
|
|
39
|
+
participant: opts.participant,
|
|
40
|
+
},
|
|
41
|
+
message: content,
|
|
42
|
+
messageTimestamp: Math.floor(Date.now() / 1000),
|
|
43
|
+
status: 1,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const generatePollMessage = async (jid, opts) => {
|
|
47
|
+
const msg = await socket.sendMessage(jid, {
|
|
48
|
+
poll: {
|
|
49
|
+
name: opts.name,
|
|
50
|
+
values: opts.values,
|
|
51
|
+
selectableCount: opts.selectableCount ?? 1,
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
return msg
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const generateReactionMessage = (jid, opts) =>
|
|
58
|
+
generateWAMessageFromContent(jid, {
|
|
59
|
+
reactionMessage: { key: opts.key, text: opts.text },
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const prepareWAMessageMedia = async (media, opts = {}) => {
|
|
63
|
+
if (!socket.waUploadToServer) {
|
|
64
|
+
throw new ConnectionError(
|
|
65
|
+
'Cannot upload media: no baileys package found and socket.waUploadToServer is unavailable',
|
|
66
|
+
{ code: 'LUMINA_ADAPTER_NO_UPLOAD' },
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
const [type, source] = Object.entries(media)[0]
|
|
70
|
+
const uploaded = await socket.waUploadToServer(
|
|
71
|
+
Buffer.isBuffer(source) ? source : source,
|
|
72
|
+
{ mediaType: type, jid: opts.jid },
|
|
73
|
+
)
|
|
74
|
+
return { [type]: { url: uploaded.url, ...uploaded } }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { generateWAMessageFromContent, generatePollMessage, generateReactionMessage, prepareWAMessageMedia }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export class BaileysAdapter {
|
|
81
|
+
#fns = null
|
|
82
|
+
#source = 'unknown'
|
|
83
|
+
|
|
84
|
+
static async resolve(socket, opts = {}) {
|
|
85
|
+
const adapter = new BaileysAdapter()
|
|
86
|
+
|
|
87
|
+
if (opts.baileys && typeof opts.baileys === 'object') {
|
|
88
|
+
const fns = extractFns(opts.baileys)
|
|
89
|
+
if (fns) {
|
|
90
|
+
adapter.#fns = fns
|
|
91
|
+
adapter.#source = 'injected-module'
|
|
92
|
+
return adapter
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (typeof opts.baileysPackage === 'string') {
|
|
97
|
+
try {
|
|
98
|
+
const mod = await import(opts.baileysPackage)
|
|
99
|
+
const fns = extractFns(mod)
|
|
100
|
+
if (fns) {
|
|
101
|
+
adapter.#fns = fns
|
|
102
|
+
adapter.#source = opts.baileysPackage
|
|
103
|
+
return adapter
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
throw new ConnectionError(
|
|
107
|
+
`baileysPackage "${opts.baileysPackage}" could not be imported or is missing required functions`,
|
|
108
|
+
{ code: 'LUMINA_ADAPTER_PACKAGE_NOT_FOUND' },
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for (const pkg of KNOWN_PACKAGES) {
|
|
114
|
+
try {
|
|
115
|
+
const mod = await import(pkg)
|
|
116
|
+
const fns = extractFns(mod)
|
|
117
|
+
if (fns) {
|
|
118
|
+
adapter.#fns = fns
|
|
119
|
+
adapter.#source = pkg
|
|
120
|
+
return adapter
|
|
121
|
+
}
|
|
122
|
+
} catch {}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
adapter.#fns = makeFallbackFns(socket)
|
|
126
|
+
adapter.#source = 'socket-fallback'
|
|
127
|
+
return adapter
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
get source() {
|
|
131
|
+
return this.#source
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
get prepareWAMessageMedia() { return this.#fns.prepareWAMessageMedia }
|
|
135
|
+
get generateWAMessageFromContent() { return this.#fns.generateWAMessageFromContent }
|
|
136
|
+
get generatePollMessage() { return this.#fns.generatePollMessage }
|
|
137
|
+
get generateReactionMessage() { return this.#fns.generateReactionMessage }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export default BaileysAdapter
|
package/src/client/connection.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { ConnectionError } from '../errors.js'
|
|
2
|
+
import { BaileysAdapter } from './baileys-adapter.js'
|
|
2
3
|
|
|
3
4
|
export class Connection {
|
|
5
|
+
#socket = null
|
|
6
|
+
#adapter = null
|
|
7
|
+
|
|
4
8
|
constructor(socket, opts = {}) {
|
|
5
9
|
if (!socket) throw new ConnectionError('socket is required', { code: 'LUMINA_CONNECTION_NO_SOCKET' })
|
|
6
|
-
this.#socket = socket
|
|
7
|
-
this.uploadJid = opts.uploadJid
|
|
8
|
-
this.logger = opts.logger
|
|
9
10
|
|
|
10
11
|
const required = ['relayMessage', 'ev']
|
|
11
12
|
for (const m of required) {
|
|
@@ -15,34 +16,46 @@ export class Connection {
|
|
|
15
16
|
})
|
|
16
17
|
}
|
|
17
18
|
}
|
|
19
|
+
|
|
20
|
+
this.#socket = socket
|
|
21
|
+
this.uploadJid = opts.uploadJid
|
|
22
|
+
this.logger = opts.logger
|
|
23
|
+
this.#adapter = BaileysAdapter.resolve(socket, opts)
|
|
18
24
|
}
|
|
19
25
|
|
|
20
|
-
#
|
|
26
|
+
async #fn() {
|
|
27
|
+
if (this.#adapter instanceof Promise) this.#adapter = await this.#adapter
|
|
28
|
+
return this.#adapter
|
|
29
|
+
}
|
|
21
30
|
|
|
22
31
|
async uploadMedia(media, opts = {}) {
|
|
23
|
-
const
|
|
32
|
+
const adapter = await this.#fn()
|
|
24
33
|
const jid = opts.jid ?? this.uploadJid
|
|
25
34
|
if (!jid) {
|
|
26
35
|
throw new ConnectionError('uploadJid must be set (via constructor or per-call)', {
|
|
27
36
|
code: 'LUMINA_CONNECTION_NO_UPLOAD_JID',
|
|
28
37
|
})
|
|
29
38
|
}
|
|
30
|
-
return prepareWAMessageMedia(media, {
|
|
39
|
+
return adapter.prepareWAMessageMedia(media, {
|
|
40
|
+
upload: this.#socket.waUploadToServer,
|
|
41
|
+
jid,
|
|
42
|
+
...opts,
|
|
43
|
+
})
|
|
31
44
|
}
|
|
32
45
|
|
|
33
46
|
async generateMessage(jid, content, opts = {}) {
|
|
34
|
-
const
|
|
35
|
-
return generateWAMessageFromContent(jid, content, opts)
|
|
47
|
+
const adapter = await this.#fn()
|
|
48
|
+
return adapter.generateWAMessageFromContent(jid, content, opts)
|
|
36
49
|
}
|
|
37
50
|
|
|
38
51
|
async generatePoll(jid, opts) {
|
|
39
|
-
const
|
|
40
|
-
return generatePollMessage(jid, opts)
|
|
52
|
+
const adapter = await this.#fn()
|
|
53
|
+
return adapter.generatePollMessage(jid, opts)
|
|
41
54
|
}
|
|
42
55
|
|
|
43
56
|
async generateReaction(jid, opts) {
|
|
44
|
-
const
|
|
45
|
-
return generateReactionMessage(jid, opts)
|
|
57
|
+
const adapter = await this.#fn()
|
|
58
|
+
return adapter.generateReactionMessage(jid, opts)
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
async relayMessage(jid, message, opts = {}) {
|
|
@@ -65,8 +78,11 @@ export class Connection {
|
|
|
65
78
|
this.#socket.ev.off(event, handler)
|
|
66
79
|
}
|
|
67
80
|
|
|
68
|
-
get raw() {
|
|
69
|
-
|
|
81
|
+
get raw() { return this.#socket }
|
|
82
|
+
|
|
83
|
+
async adapterSource() {
|
|
84
|
+
const adapter = await this.#fn()
|
|
85
|
+
return adapter.source
|
|
70
86
|
}
|
|
71
87
|
}
|
|
72
88
|
|
package/src/media/video.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
*
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
|
|
5
1
|
import { PassThrough, Readable } from 'node:stream'
|
|
6
|
-
|
|
7
2
|
import { MediaError } from '../errors.js'
|
|
8
3
|
import { resize } from './image.js'
|
|
9
4
|
|
|
@@ -16,7 +11,7 @@ async function loadFfmpeg() {
|
|
|
16
11
|
return (await import('fluent-ffmpeg')).default
|
|
17
12
|
} catch (err) {
|
|
18
13
|
throw new MediaError(
|
|
19
|
-
|
|
14
|
+
'fluent-ffmpeg is not installed. Run: npm i fluent-ffmpeg',
|
|
20
15
|
{ code: 'LUMINA_MEDIA_FFMPEG_MISSING', cause: err },
|
|
21
16
|
)
|
|
22
17
|
}
|
|
@@ -25,8 +20,6 @@ async function loadFfmpeg() {
|
|
|
25
20
|
return ffmpegPromise
|
|
26
21
|
}
|
|
27
22
|
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
23
|
export function getMp4Duration(buffer, opts = {}) {
|
|
31
24
|
const { silent = true } = opts
|
|
32
25
|
|
|
@@ -36,16 +29,12 @@ export function getMp4Duration(buffer, opts = {}) {
|
|
|
36
29
|
}
|
|
37
30
|
|
|
38
31
|
try {
|
|
39
|
-
if (!Buffer.isBuffer(buffer) || buffer.length < 8)
|
|
40
|
-
return fail('invalid buffer: too short')
|
|
41
|
-
}
|
|
32
|
+
if (!Buffer.isBuffer(buffer) || buffer.length < 8) return fail('invalid buffer: too short')
|
|
42
33
|
|
|
43
34
|
let offset = 0
|
|
44
35
|
while (offset < buffer.length - 8) {
|
|
45
36
|
const size = buffer.readUInt32BE(offset)
|
|
46
|
-
if (size < 8 || offset + size > buffer.length)
|
|
47
|
-
return fail('invalid atom size')
|
|
48
|
-
}
|
|
37
|
+
if (size < 8 || offset + size > buffer.length) return fail('invalid atom size')
|
|
49
38
|
|
|
50
39
|
const type = buffer.toString('ascii', offset + 4, offset + 8)
|
|
51
40
|
if (type === 'moov') {
|
|
@@ -53,9 +42,7 @@ export function getMp4Duration(buffer, opts = {}) {
|
|
|
53
42
|
const moovEnd = offset + size
|
|
54
43
|
while (moovOffset < moovEnd - 8) {
|
|
55
44
|
const childSize = buffer.readUInt32BE(moovOffset)
|
|
56
|
-
if (childSize < 8 || moovOffset + childSize > moovEnd)
|
|
57
|
-
return fail('invalid child atom size')
|
|
58
|
-
}
|
|
45
|
+
if (childSize < 8 || moovOffset + childSize > moovEnd) return fail('invalid child atom size')
|
|
59
46
|
const childType = buffer.toString('ascii', moovOffset + 4, moovOffset + 8)
|
|
60
47
|
if (childType === 'mvhd') {
|
|
61
48
|
const version = buffer.readUInt8(moovOffset + 8)
|
|
@@ -87,8 +74,6 @@ export function getMp4Duration(buffer, opts = {}) {
|
|
|
87
74
|
}
|
|
88
75
|
}
|
|
89
76
|
|
|
90
|
-
|
|
91
|
-
*
|
|
92
77
|
export async function extractThumbnail(videoBuffer, opts = {}) {
|
|
93
78
|
const {
|
|
94
79
|
time,
|
|
@@ -105,10 +90,14 @@ export async function extractThumbnail(videoBuffer, opts = {}) {
|
|
|
105
90
|
return new Promise((resolve, reject) => {
|
|
106
91
|
const fail = (err) => {
|
|
107
92
|
if (silent) return resolve(result === 'base64' ? '' : Buffer.alloc(0))
|
|
108
|
-
reject(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
93
|
+
reject(
|
|
94
|
+
err instanceof MediaError
|
|
95
|
+
? err
|
|
96
|
+
: new MediaError(err?.message ?? String(err), {
|
|
97
|
+
code: 'LUMINA_MEDIA_FFMPEG_FAILED',
|
|
98
|
+
cause: err,
|
|
99
|
+
}),
|
|
100
|
+
)
|
|
112
101
|
}
|
|
113
102
|
|
|
114
103
|
if (!Buffer.isBuffer(videoBuffer) || videoBuffer.length === 0) {
|
|
@@ -130,9 +119,7 @@ export async function extractThumbnail(videoBuffer, opts = {}) {
|
|
|
130
119
|
if (frame.length === 0) {
|
|
131
120
|
return fail(new MediaError('ffmpeg produced empty output', { code: 'LUMINA_MEDIA_FFMPEG_EMPTY' }))
|
|
132
121
|
}
|
|
133
|
-
if (resizeOutput) {
|
|
134
|
-
frame = await resize(frame, { width, height, fit: 'cover', format })
|
|
135
|
-
}
|
|
122
|
+
if (resizeOutput) frame = await resize(frame, { width, height, fit: 'cover', format })
|
|
136
123
|
resolve(result === 'base64' ? frame.toString('base64') : frame)
|
|
137
124
|
} catch (err) {
|
|
138
125
|
fail(err)
|
|
@@ -143,10 +130,12 @@ export async function extractThumbnail(videoBuffer, opts = {}) {
|
|
|
143
130
|
try {
|
|
144
131
|
ffmpeg(input)
|
|
145
132
|
.outputOptions([`-ss ${seekTime}`, '-vframes 1', '-vcodec png', '-f image2pipe'])
|
|
146
|
-
.on('error', (err) =>
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
133
|
+
.on('error', (err) =>
|
|
134
|
+
fail(new MediaError(`ffmpeg error: ${err.message}`, {
|
|
135
|
+
code: 'LUMINA_MEDIA_FFMPEG_FAILED',
|
|
136
|
+
cause: err,
|
|
137
|
+
})),
|
|
138
|
+
)
|
|
150
139
|
.pipe(output, { end: true })
|
|
151
140
|
} catch (err) {
|
|
152
141
|
fail(err)
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
*
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
1
|
|
|
5
2
|
import { HighlightType, HighlightLabel } from '../proto/enums.js'
|
|
6
3
|
import { KEYWORDS, SLASH_COMMENT_LANGS, HASH_COMMENT_LANGS, BLOCK_COMMENT_LANGS } from './code-tokenizer-keywords.js'
|
|
@@ -17,7 +14,6 @@ function identifierChar(lang) {
|
|
|
17
14
|
}
|
|
18
15
|
}
|
|
19
16
|
|
|
20
|
-
*
|
|
21
17
|
export function tokenizeCode(code, lang = 'javascript') {
|
|
22
18
|
if (typeof code !== 'string' || code.length === 0) {
|
|
23
19
|
return { codeBlock: [], unifiedBlocks: [] }
|
package/src/proto/updater.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
*
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
1
|
|
|
6
2
|
import { promises as fs } from 'node:fs'
|
|
7
3
|
import path from 'node:path'
|
|
@@ -47,7 +43,6 @@ const RE_CONST_REQUIRE =
|
|
|
47
43
|
const RE_DESTRUCT_REQUIRE =
|
|
48
44
|
/(?:const|let|var)\s*\{([^}]+)\}\s*=\s*require\(\s*(['"])([^'"]+)\2\s*\)/g
|
|
49
45
|
|
|
50
|
-
*
|
|
51
46
|
export function transformToESM(src) {
|
|
52
47
|
let out = src
|
|
53
48
|
out = out.replace(RE_CONST_REQUIRE, (_, name, _q, mod) => `import ${name} from '${mod}'`)
|
|
@@ -67,7 +62,6 @@ export function transformToESM(src) {
|
|
|
67
62
|
return out
|
|
68
63
|
}
|
|
69
64
|
|
|
70
|
-
*
|
|
71
65
|
export function applyKnownFixes(filename, content) {
|
|
72
66
|
const applied = []
|
|
73
67
|
let next = content
|
|
@@ -100,7 +94,6 @@ export class ProtoUpdater {
|
|
|
100
94
|
}
|
|
101
95
|
}
|
|
102
96
|
|
|
103
|
-
*
|
|
104
97
|
async #listJsFiles(dir) {
|
|
105
98
|
const entries = await fs.readdir(dir, { withFileTypes: true })
|
|
106
99
|
const out = []
|
|
@@ -112,7 +105,6 @@ export class ProtoUpdater {
|
|
|
112
105
|
return out
|
|
113
106
|
}
|
|
114
107
|
|
|
115
|
-
*
|
|
116
108
|
async backup() {
|
|
117
109
|
await fs.mkdir(this.backupDir, { recursive: true })
|
|
118
110
|
const timestamp = Date.now()
|
|
@@ -127,7 +119,6 @@ export class ProtoUpdater {
|
|
|
127
119
|
return record
|
|
128
120
|
}
|
|
129
121
|
|
|
130
|
-
*
|
|
131
122
|
async #hashTree(dir) {
|
|
132
123
|
const files = (await this.#listJsFiles(dir)).sort()
|
|
133
124
|
const hashes = await Promise.all(
|
|
@@ -139,7 +130,6 @@ export class ProtoUpdater {
|
|
|
139
130
|
return sha256(hashes.join('\n'))
|
|
140
131
|
}
|
|
141
132
|
|
|
142
|
-
*
|
|
143
133
|
async restore(id) {
|
|
144
134
|
const record = this.history.find((h) => h.id === id)
|
|
145
135
|
if (!record) {
|
|
@@ -160,7 +150,6 @@ export class ProtoUpdater {
|
|
|
160
150
|
await this.restore(last.id)
|
|
161
151
|
}
|
|
162
152
|
|
|
163
|
-
*
|
|
164
153
|
async validate() {
|
|
165
154
|
const files = await this.#listJsFiles(this.protoPath)
|
|
166
155
|
const errors = []
|
|
@@ -176,7 +165,6 @@ export class ProtoUpdater {
|
|
|
176
165
|
return { ok: errors.length === 0, errors }
|
|
177
166
|
}
|
|
178
167
|
|
|
179
|
-
*
|
|
180
168
|
async update(opts = {}) {
|
|
181
169
|
const autoRollback = opts.autoRollback ?? true
|
|
182
170
|
const dryRun = opts.dryRun ?? false
|