lid, wip: Support LIDs in Baileys (#1747)
* lid-mapping: get missing lid from usync * lid-mapping, jid-utils: change to isPnUser and store multiple mappings * process-message: parse protocolMsg mapping, and store from new msgs * types: lid-mapping event, addressing enum, alt, contact, group types * validate, decode: use lid for identity, better logic * lid: final commit * linting * linting * linting * linting * misc: fix testing and also remove version json * lint: IDE fucking up lint * lid-mapping: fix build error on NPM * message-retry: fix proto import
This commit is contained in:
+23
-4
@@ -2,12 +2,31 @@
|
||||
import type { Config } from 'jest'
|
||||
|
||||
const config: Config = {
|
||||
roots: ['<rootDir>/src'],
|
||||
testMatch: ['**.test.ts'],
|
||||
preset: 'ts-jest/presets/default-esm',
|
||||
testEnvironment: 'node',
|
||||
roots: ['<rootDir>/src', '<rootDir>/WAProto'],
|
||||
testMatch: ['<rootDir>/src/**/*.test.ts'],
|
||||
extensionsToTreatAsEsm: ['.ts'],
|
||||
transform: {
|
||||
'^.+\\.(ts|tsx)$': ['ts-jest', { useESM: true }]
|
||||
moduleNameMapper: {
|
||||
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.tsx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
useESM: true,
|
||||
tsconfig: {
|
||||
module: 'esnext',
|
||||
verbatimModuleSyntax: false,
|
||||
allowImportingTsExtensions: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
'^.+\\.js$': ['ts-jest', { useESM: true }],
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!(protobufjs|long|@protobufjs|@types/long)/)',
|
||||
],
|
||||
}
|
||||
|
||||
export default config
|
||||
+3
-3
@@ -57,12 +57,12 @@
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^20.9.0",
|
||||
"@types/ws": "^8.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.32.0",
|
||||
"@typescript-eslint/parser": "^8.32.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8",
|
||||
"@typescript-eslint/parser": "^8",
|
||||
"@whiskeysockets/eslint-config": "^1.0.0",
|
||||
"conventional-changelog-cli": "^2.2.2",
|
||||
"esbuild-register": "^3.6.0",
|
||||
"eslint": "^9.31.0",
|
||||
"eslint": "^9",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-prettier": "^5.4.0",
|
||||
"jest": "^30.0.5",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"version": [2, 3000, 1023223821]
|
||||
}
|
||||
@@ -3,9 +3,8 @@ import { makeLibSignalRepository } from '../Signal/libsignal'
|
||||
import type { AuthenticationState, SocketConfig, WAVersion } from '../Types'
|
||||
import { Browsers } from '../Utils'
|
||||
import logger from '../Utils/logger'
|
||||
import defaultVersion from './baileys-version.json' with { type: 'json' }
|
||||
|
||||
const { version } = defaultVersion
|
||||
const version = [2, 3000, 1023223821]
|
||||
|
||||
export const UNAUTHORIZED_CODES = [401, 403, 419]
|
||||
|
||||
|
||||
+12
-2
@@ -12,8 +12,18 @@ import { SenderKeyRecord } from './Group/sender-key-record'
|
||||
import { GroupCipher, GroupSessionBuilder, SenderKeyDistributionMessage } from './Group'
|
||||
import { LIDMappingStore } from './lid-mapping'
|
||||
|
||||
export function makeLibSignalRepository(auth: SignalAuthState): SignalRepository {
|
||||
const lidMapping = new LIDMappingStore(auth.keys as SignalKeyStoreWithTransaction)
|
||||
export function makeLibSignalRepository(
|
||||
auth: SignalAuthState,
|
||||
onWhatsAppFunc?: (...jids: string[]) => Promise<
|
||||
| {
|
||||
jid: string
|
||||
exists: boolean
|
||||
lid: string
|
||||
}[]
|
||||
| undefined
|
||||
>
|
||||
): SignalRepository {
|
||||
const lidMapping = new LIDMappingStore(auth.keys as SignalKeyStoreWithTransaction, onWhatsAppFunc)
|
||||
const storage = signalStorage(auth, lidMapping)
|
||||
|
||||
const parsedKeys = auth.keys as SignalKeyStoreWithTransaction
|
||||
|
||||
+46
-10
@@ -1,22 +1,51 @@
|
||||
import type { SignalKeyStoreWithTransaction } from '../Types'
|
||||
import logger from '../Utils/logger'
|
||||
import { isJidUser, isLidUser, jidDecode } from '../WABinary'
|
||||
import { isLidUser, isPnUser, jidDecode } from '../WABinary'
|
||||
|
||||
//TODO: Caching
|
||||
export class LIDMappingStore {
|
||||
private readonly keys: SignalKeyStoreWithTransaction
|
||||
private onWhatsAppFunc?: (...jids: string[]) => Promise<
|
||||
| {
|
||||
jid: string
|
||||
exists: boolean
|
||||
lid: string
|
||||
}[]
|
||||
| undefined
|
||||
>
|
||||
|
||||
constructor(keys: SignalKeyStoreWithTransaction) {
|
||||
constructor(
|
||||
keys: SignalKeyStoreWithTransaction,
|
||||
onWhatsAppFunc?: (...jids: string[]) => Promise<
|
||||
| {
|
||||
jid: string
|
||||
exists: boolean
|
||||
lid: string
|
||||
}[]
|
||||
| undefined
|
||||
>
|
||||
) {
|
||||
this.keys = keys
|
||||
this.onWhatsAppFunc = onWhatsAppFunc // needed to get LID from PN if not found
|
||||
}
|
||||
|
||||
/**
|
||||
* Store LID-PN mapping - USER LEVEL
|
||||
*/
|
||||
async storeLIDPNMapping(lid: string, pn: string): Promise<void> {
|
||||
return this.storeLIDPNMappings([{ lid, pn }])
|
||||
}
|
||||
|
||||
/**
|
||||
* Store LID-PN mapping - USER LEVEL
|
||||
*/
|
||||
async storeLIDPNMappings(pairs: { lid: string; pn: string }[]): Promise<void> {
|
||||
// Validate inputs
|
||||
if (!((isLidUser(lid) && isJidUser(pn)) || (isJidUser(lid) && isLidUser(pn)))) {
|
||||
const pairMap: { [_: string]: string } = {}
|
||||
for (const { lid, pn } of pairs) {
|
||||
if (!((isLidUser(lid) && isPnUser(pn)) || (isPnUser(lid) && isLidUser(pn)))) {
|
||||
logger.warn(`Invalid LID-PN mapping: ${lid}, ${pn}`)
|
||||
return
|
||||
continue
|
||||
}
|
||||
|
||||
const [lidJid, pnJid] = isLidUser(lid) ? [lid, pn] : [pn, lid]
|
||||
@@ -28,26 +57,28 @@ export class LIDMappingStore {
|
||||
|
||||
const pnUser = pnDecoded.user
|
||||
const lidUser = lidDecoded.user
|
||||
pairMap[pnUser] = lidUser
|
||||
}
|
||||
|
||||
logger.trace(`Storing USER LID mapping: PN ${pnUser} → LID ${lidUser}`)
|
||||
logger.trace({ pairMap }, `Storing ${Object.keys(pairMap).length} pn mappings`)
|
||||
|
||||
await this.keys.transaction(async () => {
|
||||
for (const [pnUser, lidUser] of Object.entries(pairMap)) {
|
||||
await this.keys.set({
|
||||
'lid-mapping': {
|
||||
[pnUser]: lidUser, // "554396160286" -> "102765716062358"
|
||||
[`${lidUser}_reverse`]: pnUser // "102765716062358_reverse" -> "554396160286"
|
||||
}
|
||||
})
|
||||
}
|
||||
}, 'lid-mapping')
|
||||
|
||||
logger.trace(`USER LID mapping stored: PN ${pnUser} → LID ${lidUser}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LID for PN - Returns device-specific LID based on user mapping
|
||||
*/
|
||||
async getLIDForPN(pn: string): Promise<string | null> {
|
||||
if (!isJidUser(pn)) return null
|
||||
if (!isPnUser(pn)) return null
|
||||
|
||||
const decoded = jidDecode(pn)
|
||||
if (!decoded) return null
|
||||
@@ -55,12 +86,17 @@ export class LIDMappingStore {
|
||||
// Look up user-level mapping (whatsmeow approach)
|
||||
const pnUser = decoded.user
|
||||
const stored = await this.keys.get('lid-mapping', [pnUser])
|
||||
const lidUser = stored[pnUser]
|
||||
let lidUser = stored[pnUser]
|
||||
|
||||
if (!lidUser) {
|
||||
logger.trace(`No LID mapping found for PN user ${pnUser}`)
|
||||
logger.trace(`No LID mapping found for PN user ${pnUser}; getting from USync`)
|
||||
const { exists, lid } = (await this.onWhatsAppFunc?.(pn))?.[0]!
|
||||
if (exists) {
|
||||
lidUser = jidDecode(lid)?.user
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof lidUser !== 'string') return null
|
||||
|
||||
|
||||
+4
-19
@@ -53,7 +53,7 @@ import {
|
||||
S_WHATSAPP_NET
|
||||
} from '../WABinary'
|
||||
import { USyncQuery, USyncUser } from '../WAUSync'
|
||||
import { makeUSyncSocket } from './usync'
|
||||
import { makeSocket } from './socket.js'
|
||||
const MAX_SYNC_ATTEMPTS = 2
|
||||
|
||||
export const makeChatsSocket = (config: SocketConfig) => {
|
||||
@@ -65,8 +65,8 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
shouldIgnoreJid,
|
||||
shouldSyncHistoryMessage
|
||||
} = config
|
||||
const sock = makeUSyncSocket(config)
|
||||
const { ev, ws, authState, generateMessageTag, sendNode, query, onUnexpectedError } = sock
|
||||
const sock = makeSocket(config)
|
||||
const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError } = sock
|
||||
|
||||
let privacySettings: { [_: string]: string } | undefined
|
||||
|
||||
@@ -221,21 +221,6 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
return botList
|
||||
}
|
||||
|
||||
const onWhatsApp = async (...jids: string[]) => {
|
||||
const usyncQuery = new USyncQuery().withContactProtocol().withLIDProtocol()
|
||||
|
||||
for (const jid of jids) {
|
||||
const phone = `+${jid.replace('+', '').split('@')[0]?.split(':')[0]}`
|
||||
usyncQuery.withUser(new USyncUser().withPhone(phone))
|
||||
}
|
||||
|
||||
const results = await sock.executeUSyncQuery(usyncQuery)
|
||||
|
||||
if (results) {
|
||||
return results.list.filter(a => !!a.contact).map(({ contact, id, lid }) => ({ jid: id, exists: contact, lid }))
|
||||
}
|
||||
}
|
||||
|
||||
const fetchStatus = async (...jids: string[]) => {
|
||||
const usyncQuery = new USyncQuery().withStatusProtocol()
|
||||
|
||||
@@ -1092,6 +1077,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
}
|
||||
})(),
|
||||
processMessage(msg, {
|
||||
signalRepository,
|
||||
shouldProcessHistoryMsg,
|
||||
placeholderResendCache,
|
||||
ev,
|
||||
@@ -1195,7 +1181,6 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
sendPresenceUpdate,
|
||||
presenceSubscribe,
|
||||
profilePictureUrl,
|
||||
onWhatsApp,
|
||||
fetchBlocklist,
|
||||
fetchStatus,
|
||||
fetchDisappearingDuration,
|
||||
|
||||
+11
-10
@@ -1,14 +1,14 @@
|
||||
import { proto } from '../../WAProto/index.js'
|
||||
import type { GroupMetadata, GroupParticipant, ParticipantAction, SocketConfig, WAMessageKey } from '../Types'
|
||||
import { WAMessageStubType } from '../Types'
|
||||
import { WAMessageAddressingMode, WAMessageStubType } from '../Types'
|
||||
import { generateMessageIDV2, unixTimestampSeconds } from '../Utils'
|
||||
import {
|
||||
type BinaryNode,
|
||||
getBinaryNodeChild,
|
||||
getBinaryNodeChildren,
|
||||
getBinaryNodeChildString,
|
||||
isJidUser,
|
||||
isLidUser,
|
||||
isPnUser,
|
||||
jidEncode,
|
||||
jidNormalizedUser
|
||||
} from '../WABinary'
|
||||
@@ -305,12 +305,12 @@ export const extractGroupMetadata = (result: BinaryNode) => {
|
||||
let desc: string | undefined
|
||||
let descId: string | undefined
|
||||
let descOwner: string | undefined
|
||||
let descOwnerJid: string | undefined
|
||||
let descOwnerPn: string | undefined
|
||||
let descTime: number | undefined
|
||||
if (descChild) {
|
||||
desc = getBinaryNodeChildString(descChild, 'body')
|
||||
descOwner = descChild.attrs.participant ? jidNormalizedUser(descChild.attrs.participant) : undefined
|
||||
descOwnerJid = descChild.attrs.participant_pn ? jidNormalizedUser(descChild.attrs.participant_pn) : undefined
|
||||
descOwnerPn = descChild.attrs.participant_pn ? jidNormalizedUser(descChild.attrs.participant_pn) : undefined
|
||||
descTime = +descChild.attrs.t!
|
||||
descId = descChild.attrs.id
|
||||
}
|
||||
@@ -320,20 +320,21 @@ export const extractGroupMetadata = (result: BinaryNode) => {
|
||||
const memberAddMode = getBinaryNodeChildString(group, 'member_add_mode') === 'all_member_add'
|
||||
const metadata: GroupMetadata = {
|
||||
id: groupId!,
|
||||
addressingMode: group.attrs.addressing_mode as 'pn' | 'lid',
|
||||
notify: group.attrs.notify,
|
||||
addressingMode: group.attrs.addressing_mode === 'lid' ? WAMessageAddressingMode.LID : WAMessageAddressingMode.PN,
|
||||
subject: group.attrs.subject!,
|
||||
subjectOwner: group.attrs.s_o,
|
||||
subjectOwnerJid: group.attrs.s_o_pn,
|
||||
subjectOwnerPn: group.attrs.s_o_pn,
|
||||
subjectTime: +group.attrs.s_t!,
|
||||
size: group.attrs.size ? +group.attrs.size : getBinaryNodeChildren(group, 'participant').length,
|
||||
creation: +group.attrs.creation!,
|
||||
owner: group.attrs.creator ? jidNormalizedUser(group.attrs.creator) : undefined,
|
||||
ownerJid: group.attrs.creator_pn ? jidNormalizedUser(group.attrs.creator_pn) : undefined,
|
||||
ownerPn: group.attrs.creator_pn ? jidNormalizedUser(group.attrs.creator_pn) : undefined,
|
||||
owner_country_code: group.attrs.creator_country_code,
|
||||
desc,
|
||||
descId,
|
||||
descOwner,
|
||||
descOwnerJid,
|
||||
descOwnerPn,
|
||||
descTime,
|
||||
linkedParent: getBinaryNodeChild(group, 'linked_parent')?.attrs.jid || undefined,
|
||||
restrict: !!getBinaryNodeChild(group, 'locked'),
|
||||
@@ -345,8 +346,8 @@ export const extractGroupMetadata = (result: BinaryNode) => {
|
||||
participants: getBinaryNodeChildren(group, 'participant').map(({ attrs }) => {
|
||||
return {
|
||||
id: attrs.jid!,
|
||||
jid: isJidUser(attrs.jid) ? attrs.jid : jidNormalizedUser(attrs.phone_number),
|
||||
lid: isLidUser(attrs.jid) ? attrs.jid : attrs.lid,
|
||||
phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phoneNumber) ? attrs.phoneNumber : undefined,
|
||||
lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
|
||||
admin: (attrs.type || null) as GroupParticipant['admin']
|
||||
}
|
||||
}),
|
||||
|
||||
+31
-46
@@ -50,7 +50,6 @@ import {
|
||||
getBinaryNodeChildString,
|
||||
isJidGroup,
|
||||
isJidStatusBroadcast,
|
||||
isJidUser,
|
||||
isLidUser,
|
||||
jidDecode,
|
||||
jidNormalizedUser,
|
||||
@@ -549,6 +548,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
|
||||
const handleGroupNotification = (participant: string, child: BinaryNode, msg: Partial<proto.IWebMessageInfo>) => {
|
||||
const participantJid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || participant
|
||||
// TODO: Add participant LID
|
||||
switch (child?.tag) {
|
||||
case 'create':
|
||||
const metadata = extractGroupMetadata(child)
|
||||
@@ -697,11 +697,16 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
break
|
||||
case 'devices':
|
||||
const devices = getBinaryNodeChildren(child, 'device')
|
||||
if (areJidsSameUser(child!.attrs.jid, authState.creds.me!.id)) {
|
||||
const deviceJids = devices.map(d => d.attrs.jid)
|
||||
logger.info({ deviceJids }, 'got my own devices')
|
||||
if (
|
||||
areJidsSameUser(child!.attrs.jid, authState.creds.me!.id) ||
|
||||
areJidsSameUser(child!.attrs.lid, authState.creds.me!.lid)
|
||||
) {
|
||||
const deviceData = devices.map(d => ({ id: d.attrs.jid, lid: d.attrs.lid }))
|
||||
logger.info({ deviceData }, 'my own devices changed')
|
||||
}
|
||||
|
||||
//TODO: drop a new event, add hashes
|
||||
|
||||
break
|
||||
case 'server_sync':
|
||||
const update = getBinaryNodeChild(node, 'collection')
|
||||
@@ -1034,7 +1039,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
// correctly set who is asking for the retry
|
||||
key.participant = key.participant || attrs.from
|
||||
const retryNode = getBinaryNodeChild(node, 'retry')
|
||||
if (ids[0] && key.participant && willSendMessageAgain(ids[0], key.participant!)) {
|
||||
if (ids[0] && key.participant && willSendMessageAgain(ids[0], key.participant)) {
|
||||
if (key.fromMe) {
|
||||
try {
|
||||
updateSendMessageAgainCount(ids[0], key.participant)
|
||||
@@ -1142,7 +1147,25 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
msg.message?.protocolMessage?.type === proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER &&
|
||||
node.attrs.sender_pn
|
||||
) {
|
||||
ev.emit('chats.phoneNumberShare', { lid: node.attrs.from!, jid: node.attrs.sender_pn })
|
||||
const lid = jidNormalizedUser(node.attrs.from),
|
||||
pn = jidNormalizedUser(node.attrs.sender_pn)
|
||||
ev.emit('lid-mapping.update', { lid, pn })
|
||||
await signalRepository.storeLIDPNMapping(lid, pn)
|
||||
}
|
||||
|
||||
const alt = msg.key.participantAlt || msg.key.remoteJidAlt
|
||||
// store new mappings we didn't have before
|
||||
if (!!alt) {
|
||||
const altServer = jidDecode(alt)?.server
|
||||
const lidMapping = signalRepository.getLIDMappingStore()
|
||||
if (altServer === 'lid') {
|
||||
if (!(await lidMapping.getPNForLID(alt))) {
|
||||
await lidMapping.storeLIDPNMapping(alt, msg.key.participant || msg.key.remoteJid!)
|
||||
}
|
||||
} else {
|
||||
if (!(await lidMapping.getLIDForPN(alt))) {
|
||||
await lidMapping.storeLIDPNMapping(msg.key.participant || msg.key.remoteJid!, alt)
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.key?.remoteJid && msg.key?.id && messageRetryManager) {
|
||||
@@ -1154,44 +1177,6 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
},
|
||||
'Added message to recent cache for retry receipts'
|
||||
)
|
||||
|
||||
if (msg.message?.protocolMessage?.lidMigrationMappingSyncMessage?.encodedMappingPayload) {
|
||||
try {
|
||||
const payload = msg.message.protocolMessage.lidMigrationMappingSyncMessage.encodedMappingPayload
|
||||
const decoded = proto.LIDMigrationMappingSyncPayload.decode(payload)
|
||||
|
||||
logger.debug(
|
||||
{
|
||||
mappingCount: decoded.pnToLidMappings?.length || 0,
|
||||
timestamp: decoded.chatDbMigrationTimestamp
|
||||
},
|
||||
'Received LID migration sync message from server'
|
||||
)
|
||||
|
||||
const lidMapping = signalRepository.getLIDMappingStore()
|
||||
if (decoded.pnToLidMappings && decoded.pnToLidMappings.length > 0) {
|
||||
for (const mapping of decoded.pnToLidMappings) {
|
||||
const pn = `${mapping.pn}@s.whatsapp.net`
|
||||
// Use latestLid if available, otherwise assignedLid (proper LID refresh)
|
||||
const lidValue = mapping.latestLid || mapping.assignedLid
|
||||
const lid = `${lidValue}@lid`
|
||||
|
||||
await lidMapping.storeLIDPNMapping(lid, pn)
|
||||
logger.debug(
|
||||
{
|
||||
pn,
|
||||
lid,
|
||||
assignedLid: mapping.assignedLid,
|
||||
latestLid: mapping.latestLid,
|
||||
usedLatest: !!mapping.latestLid
|
||||
},
|
||||
'Stored server-provided PN-LID mapping'
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error({ error }, 'Failed to process LID migration sync message')
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -1263,8 +1248,8 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
// message was sent by us from a different device
|
||||
type = 'sender'
|
||||
// need to specially handle this case
|
||||
if (isJidUser(msg.key.remoteJid!)) {
|
||||
participant = author
|
||||
if (isLidUser(msg.key.remoteJid!) || isLidUser(msg.key.remoteJidAlt)) {
|
||||
participant = author // TODO: investigate sending receipts to LIDs and not PNs
|
||||
}
|
||||
} else if (!sendActiveReceipts) {
|
||||
type = 'inactive'
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
SocketConfig,
|
||||
WAMessageKey
|
||||
} from '../Types'
|
||||
import { WAMessageAddressingMode } from '../Types'
|
||||
import {
|
||||
aggregateMessageKeysNotFromMe,
|
||||
assertMediaContent,
|
||||
@@ -40,16 +41,15 @@ import {
|
||||
getBinaryNodeChild,
|
||||
getBinaryNodeChildren,
|
||||
isJidGroup,
|
||||
isJidUser,
|
||||
isPnUser,
|
||||
jidDecode,
|
||||
jidEncode,
|
||||
jidNormalizedUser,
|
||||
type JidWithDevice,
|
||||
S_WHATSAPP_NET
|
||||
S_WHATSAPP_NET,
|
||||
transferDevice
|
||||
} from '../WABinary'
|
||||
import { USyncQuery, USyncUser } from '../WAUSync'
|
||||
import { makeGroupsSocket } from './groups'
|
||||
import type { NewsletterSocket } from './newsletter'
|
||||
import { makeNewsletterSocket } from './newsletter'
|
||||
|
||||
export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
@@ -63,7 +63,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
enableRecentMessageCache,
|
||||
maxMsgRetryCount
|
||||
} = config
|
||||
const sock: NewsletterSocket = makeNewsletterSocket(makeGroupsSocket(config))
|
||||
const sock = makeNewsletterSocket(config)
|
||||
const {
|
||||
ev,
|
||||
authState,
|
||||
@@ -147,7 +147,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
node.attrs.t = unixTimestampSeconds().toString()
|
||||
}
|
||||
|
||||
if (type === 'sender' && isJidUser(jid)) {
|
||||
if (type === 'sender' && isPnUser(jid)) {
|
||||
node.attrs.recipient = jid
|
||||
node.attrs.to = participant!
|
||||
} else {
|
||||
@@ -445,11 +445,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
if (!jid.includes('@s.whatsapp.net')) return
|
||||
|
||||
try {
|
||||
const jidDecoded = jidDecode(jid)
|
||||
const deviceId = jidDecoded?.device || 0
|
||||
const lidDecoded = jidDecode(lidForPN)
|
||||
const lidWithDevice = jidEncode(lidDecoded?.user!, 'lid', deviceId)
|
||||
|
||||
const lidWithDevice = transferDevice(jid, lidForPN)
|
||||
await signalRepository.migrateSession(jid, lidWithDevice)
|
||||
logger.debug({ fromJid: jid, toJid: lidWithDevice }, 'Migrated device session to LID')
|
||||
|
||||
@@ -904,7 +900,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
}
|
||||
|
||||
if (!isStatus) {
|
||||
const groupAddressingMode = groupData?.addressingMode || (isLid ? 'lid' : 'pn')
|
||||
const groupAddressingMode =
|
||||
groupData?.addressingMode || (isLid ? WAMessageAddressingMode.LID : WAMessageAddressingMode.PN)
|
||||
additionalAttributes = {
|
||||
...additionalAttributes,
|
||||
addressing_mode: groupAddressingMode
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { NewsletterCreateResponse, WAMediaUpload } from '../Types'
|
||||
import type { NewsletterCreateResponse, SocketConfig, WAMediaUpload } from '../Types'
|
||||
import type { NewsletterMetadata, NewsletterUpdate } from '../Types'
|
||||
import { QueryIds, XWAPaths } from '../Types'
|
||||
import { generateProfilePicture } from '../Utils/messages-media'
|
||||
import { getBinaryNodeChild } from '../WABinary'
|
||||
import type { GroupsSocket } from './groups'
|
||||
import { makeGroupsSocket } from './groups'
|
||||
import { executeWMexQuery as genericExecuteWMexQuery } from './mex'
|
||||
|
||||
const parseNewsletterCreateResponse = (response: NewsletterCreateResponse): NewsletterMetadata => {
|
||||
@@ -41,7 +41,8 @@ const parseNewsletterMetadata = (result: unknown): NewsletterMetadata | null =>
|
||||
return null
|
||||
}
|
||||
|
||||
export const makeNewsletterSocket = (sock: GroupsSocket) => {
|
||||
export const makeNewsletterSocket = (config: SocketConfig) => {
|
||||
const sock = makeGroupsSocket(config)
|
||||
const { query, generateMessageTag } = sock
|
||||
|
||||
const executeWMexQuery = <T>(variables: Record<string, unknown>, queryId: string, dataPath: string): Promise<T> => {
|
||||
|
||||
+170
-93
@@ -43,6 +43,7 @@ import {
|
||||
jidEncode,
|
||||
S_WHATSAPP_NET
|
||||
} from '../WABinary'
|
||||
import { USyncQuery, USyncUser } from '../WAUSync/'
|
||||
import { WebSocketClient } from './Client'
|
||||
|
||||
/**
|
||||
@@ -67,6 +68,9 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
makeSignalRepository
|
||||
} = config
|
||||
|
||||
const uqTagId = generateMdTagPrefix()
|
||||
const generateMessageTag = () => `${uqTagId}${epoch++}`
|
||||
|
||||
if (printQRInTerminal) {
|
||||
console.warn(
|
||||
'⚠️ The printQRInTerminal option has been deprecated. You will no longer receive QR codes in the terminal automatically. Please listen to the connection.update event yourself and handle the QR your way. You can remove this message by removing this opttion. This message will be removed in a future version.'
|
||||
@@ -83,98 +87,6 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'))
|
||||
}
|
||||
|
||||
const ws = new WebSocketClient(url, config)
|
||||
|
||||
ws.connect()
|
||||
|
||||
const ev = makeEventBuffer(logger)
|
||||
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
|
||||
const ephemeralKeyPair = Curve.generateKeyPair()
|
||||
/** WA noise protocol wrapper */
|
||||
const noise = makeNoiseHandler({
|
||||
keyPair: ephemeralKeyPair,
|
||||
NOISE_HEADER: NOISE_WA_HEADER,
|
||||
logger,
|
||||
routingInfo: authState?.creds?.routingInfo
|
||||
})
|
||||
|
||||
const { creds } = authState
|
||||
// add transaction capability
|
||||
const keys = addTransactionCapability(authState.keys, logger, transactionOpts)
|
||||
const signalRepository = makeSignalRepository({ creds, keys })
|
||||
|
||||
let lastDateRecv: Date
|
||||
let epoch = 1
|
||||
let keepAliveReq: NodeJS.Timeout
|
||||
let qrTimer: NodeJS.Timeout
|
||||
let closed = false
|
||||
|
||||
const uqTagId = generateMdTagPrefix()
|
||||
const generateMessageTag = () => `${uqTagId}${epoch++}`
|
||||
|
||||
const sendPromise = promisify(ws.send)
|
||||
/** send a raw buffer */
|
||||
const sendRawMessage = async (data: Uint8Array | Buffer) => {
|
||||
if (!ws.isOpen) {
|
||||
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })
|
||||
}
|
||||
|
||||
const bytes = noise.encodeFrame(data)
|
||||
await promiseTimeout<void>(connectTimeoutMs, async (resolve, reject) => {
|
||||
try {
|
||||
await sendPromise.call(ws, bytes)
|
||||
resolve()
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** send a binary node */
|
||||
const sendNode = (frame: BinaryNode) => {
|
||||
if (logger.level === 'trace') {
|
||||
logger.trace({ xml: binaryNodeToString(frame), msg: 'xml send' })
|
||||
}
|
||||
|
||||
const buff = encodeBinaryNode(frame)
|
||||
return sendRawMessage(buff)
|
||||
}
|
||||
|
||||
/** log & process any unexpected errors */
|
||||
const onUnexpectedError = (err: Error | Boom, msg: string) => {
|
||||
logger.error({ err }, `unexpected error in '${msg}'`)
|
||||
}
|
||||
|
||||
/** await the next incoming message */
|
||||
const awaitNextMessage = async <T>(sendMsg?: Uint8Array) => {
|
||||
if (!ws.isOpen) {
|
||||
throw new Boom('Connection Closed', {
|
||||
statusCode: DisconnectReason.connectionClosed
|
||||
})
|
||||
}
|
||||
|
||||
let onOpen: (data: T) => void
|
||||
let onClose: (err: Error) => void
|
||||
|
||||
const result = promiseTimeout<T>(connectTimeoutMs, (resolve, reject) => {
|
||||
onOpen = resolve
|
||||
onClose = mapWebSocketError(reject)
|
||||
ws.on('frame', onOpen)
|
||||
ws.on('close', onClose)
|
||||
ws.on('error', onClose)
|
||||
}).finally(() => {
|
||||
ws.off('frame', onOpen)
|
||||
ws.off('close', onClose)
|
||||
ws.off('error', onClose)
|
||||
})
|
||||
|
||||
if (sendMsg) {
|
||||
sendRawMessage(sendMsg).catch(onClose!)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a message with a certain tag to be received
|
||||
* @param msgId the message tag to await
|
||||
@@ -244,6 +156,169 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
return result
|
||||
}
|
||||
|
||||
const executeUSyncQuery = async (usyncQuery: USyncQuery) => {
|
||||
if (usyncQuery.protocols.length === 0) {
|
||||
throw new Boom('USyncQuery must have at least one protocol')
|
||||
}
|
||||
|
||||
// todo: validate users, throw WARNING on no valid users
|
||||
// variable below has only validated users
|
||||
const validUsers = usyncQuery.users
|
||||
|
||||
const userNodes = validUsers.map(user => {
|
||||
return {
|
||||
tag: 'user',
|
||||
attrs: {
|
||||
jid: !user.phone ? user.id : undefined
|
||||
},
|
||||
content: usyncQuery.protocols.map(a => a.getUserElement(user)).filter(a => a !== null)
|
||||
} as BinaryNode
|
||||
})
|
||||
|
||||
const listNode: BinaryNode = {
|
||||
tag: 'list',
|
||||
attrs: {},
|
||||
content: userNodes
|
||||
}
|
||||
|
||||
const queryNode: BinaryNode = {
|
||||
tag: 'query',
|
||||
attrs: {},
|
||||
content: usyncQuery.protocols.map(a => a.getQueryElement())
|
||||
}
|
||||
const iq = {
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'get',
|
||||
xmlns: 'usync'
|
||||
},
|
||||
content: [
|
||||
{
|
||||
tag: 'usync',
|
||||
attrs: {
|
||||
context: usyncQuery.context,
|
||||
mode: usyncQuery.mode,
|
||||
sid: generateMessageTag(),
|
||||
last: 'true',
|
||||
index: '0'
|
||||
},
|
||||
content: [queryNode, listNode]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const result = await query(iq)
|
||||
|
||||
return usyncQuery.parseUSyncQueryResult(result)
|
||||
}
|
||||
|
||||
const onWhatsApp = async (...jids: string[]) => {
|
||||
const usyncQuery = new USyncQuery().withContactProtocol().withLIDProtocol()
|
||||
|
||||
for (const jid of jids) {
|
||||
const phone = `+${jid.replace('+', '').split('@')[0]?.split(':')[0]}`
|
||||
usyncQuery.withUser(new USyncUser().withPhone(phone))
|
||||
}
|
||||
|
||||
const results = await executeUSyncQuery(usyncQuery)
|
||||
|
||||
if (results) {
|
||||
return results.list
|
||||
.filter(a => !!a.contact)
|
||||
.map(({ contact, id, lid }) => ({ jid: id, exists: contact as boolean, lid: lid as string }))
|
||||
}
|
||||
}
|
||||
|
||||
const ws = new WebSocketClient(url, config)
|
||||
|
||||
ws.connect()
|
||||
|
||||
const ev = makeEventBuffer(logger)
|
||||
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
|
||||
const ephemeralKeyPair = Curve.generateKeyPair()
|
||||
/** WA noise protocol wrapper */
|
||||
const noise = makeNoiseHandler({
|
||||
keyPair: ephemeralKeyPair,
|
||||
NOISE_HEADER: NOISE_WA_HEADER,
|
||||
logger,
|
||||
routingInfo: authState?.creds?.routingInfo
|
||||
})
|
||||
|
||||
const { creds } = authState
|
||||
// add transaction capability
|
||||
const keys = addTransactionCapability(authState.keys, logger, transactionOpts)
|
||||
const signalRepository = makeSignalRepository({ creds, keys }, onWhatsApp)
|
||||
|
||||
let lastDateRecv: Date
|
||||
let epoch = 1
|
||||
let keepAliveReq: NodeJS.Timeout
|
||||
let qrTimer: NodeJS.Timeout
|
||||
let closed = false
|
||||
|
||||
const sendPromise = promisify(ws.send)
|
||||
/** send a raw buffer */
|
||||
const sendRawMessage = async (data: Uint8Array | Buffer) => {
|
||||
if (!ws.isOpen) {
|
||||
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })
|
||||
}
|
||||
|
||||
const bytes = noise.encodeFrame(data)
|
||||
await promiseTimeout<void>(connectTimeoutMs, async (resolve, reject) => {
|
||||
try {
|
||||
await sendPromise.call(ws, bytes)
|
||||
resolve()
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** send a binary node */
|
||||
const sendNode = (frame: BinaryNode) => {
|
||||
if (logger.level === 'trace') {
|
||||
logger.trace({ xml: binaryNodeToString(frame), msg: 'xml send' })
|
||||
}
|
||||
|
||||
const buff = encodeBinaryNode(frame)
|
||||
return sendRawMessage(buff)
|
||||
}
|
||||
|
||||
/** log & process any unexpected errors */
|
||||
const onUnexpectedError = (err: Error | Boom, msg: string) => {
|
||||
logger.error({ err }, `unexpected error in '${msg}'`)
|
||||
}
|
||||
|
||||
/** await the next incoming message */
|
||||
const awaitNextMessage = async <T>(sendMsg?: Uint8Array) => {
|
||||
if (!ws.isOpen) {
|
||||
throw new Boom('Connection Closed', {
|
||||
statusCode: DisconnectReason.connectionClosed
|
||||
})
|
||||
}
|
||||
|
||||
let onOpen: (data: T) => void
|
||||
let onClose: (err: Error) => void
|
||||
|
||||
const result = promiseTimeout<T>(connectTimeoutMs, (resolve, reject) => {
|
||||
onOpen = resolve
|
||||
onClose = mapWebSocketError(reject)
|
||||
ws.on('frame', onOpen)
|
||||
ws.on('close', onClose)
|
||||
ws.on('error', onClose)
|
||||
}).finally(() => {
|
||||
ws.off('frame', onOpen)
|
||||
ws.off('close', onClose)
|
||||
ws.off('error', onClose)
|
||||
})
|
||||
|
||||
if (sendMsg) {
|
||||
sendRawMessage(sendMsg).catch(onClose!)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/** connection handshake */
|
||||
const validateConnection = async () => {
|
||||
let helloMsg: proto.IHandshakeMessage = {
|
||||
@@ -878,7 +953,9 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
requestPairingCode,
|
||||
/** Waits for the connection to WA to reach a state */
|
||||
waitForConnectionUpdate: bindWaitForConnectionUpdate(ev),
|
||||
sendWAMBuffer
|
||||
sendWAMBuffer,
|
||||
executeUSyncQuery,
|
||||
onWhatsApp
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import { Boom } from '@hapi/boom'
|
||||
import type { SocketConfig } from '../Types'
|
||||
import { type BinaryNode, S_WHATSAPP_NET } from '../WABinary'
|
||||
import { USyncQuery } from '../WAUSync'
|
||||
import { makeSocket } from './socket'
|
||||
|
||||
export const makeUSyncSocket = (config: SocketConfig) => {
|
||||
const sock = makeSocket(config)
|
||||
|
||||
const { generateMessageTag, query } = sock
|
||||
|
||||
const executeUSyncQuery = async (usyncQuery: USyncQuery) => {
|
||||
if (usyncQuery.protocols.length === 0) {
|
||||
throw new Boom('USyncQuery must have at least one protocol')
|
||||
}
|
||||
|
||||
// todo: validate users, throw WARNING on no valid users
|
||||
// variable below has only validated users
|
||||
const validUsers = usyncQuery.users
|
||||
|
||||
const userNodes = validUsers.map(user => {
|
||||
return {
|
||||
tag: 'user',
|
||||
attrs: {
|
||||
jid: !user.phone ? user.id : undefined
|
||||
},
|
||||
content: usyncQuery.protocols.map(a => a.getUserElement(user)).filter(a => a !== null)
|
||||
} as BinaryNode
|
||||
})
|
||||
|
||||
const listNode: BinaryNode = {
|
||||
tag: 'list',
|
||||
attrs: {},
|
||||
content: userNodes
|
||||
}
|
||||
|
||||
const queryNode: BinaryNode = {
|
||||
tag: 'query',
|
||||
attrs: {},
|
||||
content: usyncQuery.protocols.map(a => a.getQueryElement())
|
||||
}
|
||||
const iq = {
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'get',
|
||||
xmlns: 'usync'
|
||||
},
|
||||
content: [
|
||||
{
|
||||
tag: 'usync',
|
||||
attrs: {
|
||||
context: usyncQuery.context,
|
||||
mode: usyncQuery.mode,
|
||||
sid: generateMessageTag(),
|
||||
last: 'true',
|
||||
index: '0'
|
||||
},
|
||||
content: [queryNode, listNode]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const result = await query(iq)
|
||||
|
||||
return usyncQuery.parseUSyncQueryResult(result)
|
||||
}
|
||||
|
||||
return {
|
||||
...sock,
|
||||
executeUSyncQuery
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
export interface Contact {
|
||||
/** ID either in lid or jid format **/
|
||||
/** ID either in lid or jid format (preferred) **/
|
||||
id: string
|
||||
/** ID in Lid (anonymous) format (@lid) **/
|
||||
/** ID in LID format (@lid) **/
|
||||
lid?: string
|
||||
/** ID in Phone Number format (@s.whatsapp.net) **/
|
||||
jid?: string
|
||||
/** ID in PN format (@s.whatsapp.net) **/
|
||||
phoneNumber?: string
|
||||
/** name of the contact, you have saved on your WA */
|
||||
name?: string
|
||||
/** name of the contact, the contact has set on their own on WA */
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ export type BaileysEventMap = {
|
||||
'chats.upsert': Chat[]
|
||||
/** update the given chats */
|
||||
'chats.update': ChatUpdate[]
|
||||
'chats.phoneNumberShare': { lid: string; jid: string }
|
||||
'lid-mapping.update': { lid: string; pn: string }
|
||||
/** delete chats with given ID */
|
||||
'chats.delete': string[]
|
||||
/** presence of contact in a chat updated */
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Contact } from './Contact'
|
||||
import type { WAMessageAddressingMode } from './Message'
|
||||
|
||||
export type GroupParticipant = Contact & {
|
||||
isAdmin?: boolean
|
||||
@@ -14,21 +15,22 @@ export type RequestJoinMethod = 'invite_link' | 'linked_group_join' | 'non_admin
|
||||
|
||||
export interface GroupMetadata {
|
||||
id: string
|
||||
notify?: string
|
||||
/** group uses 'lid' or 'pn' to send messages */
|
||||
addressingMode: 'pn' | 'lid'
|
||||
addressingMode?: WAMessageAddressingMode
|
||||
owner: string | undefined
|
||||
ownerJid?: string | undefined
|
||||
ownerPn?: string | undefined
|
||||
owner_country_code?: string | undefined
|
||||
subject: string
|
||||
/** group subject owner */
|
||||
subjectOwner?: string
|
||||
subjectOwnerJid?: string
|
||||
subjectOwnerPn?: string
|
||||
/** group subject modification date */
|
||||
subjectTime?: number
|
||||
creation?: number
|
||||
desc?: string
|
||||
descOwner?: string
|
||||
descOwnerJid?: string
|
||||
descOwnerPn?: string
|
||||
descId?: string
|
||||
descTime?: number
|
||||
/** if this group is part of a community, it returns the jid of the community to which it belongs */
|
||||
|
||||
@@ -14,12 +14,10 @@ export type WAMessageContent = proto.IMessage
|
||||
export type WAContactMessage = proto.Message.IContactMessage
|
||||
export type WAContactsArrayMessage = proto.Message.IContactsArrayMessage
|
||||
export type WAMessageKey = proto.IMessageKey & {
|
||||
senderLid?: string
|
||||
remoteJidAlt?: string
|
||||
participantAlt?: string
|
||||
server_id?: string
|
||||
senderPn?: string
|
||||
participantLid?: string
|
||||
participantPn?: string
|
||||
isViewOnce?: boolean
|
||||
isViewOnce?: boolean // TODO: remove out of the message key, place in WebMessageInfo
|
||||
}
|
||||
export type WATextMessage = proto.Message.IExtendedTextMessage
|
||||
export type WAContextInfo = proto.IContextInfo
|
||||
@@ -39,6 +37,11 @@ export type WAMediaUpload = Buffer | WAMediaPayloadStream | WAMediaPayloadURL
|
||||
/** Set of message types that are supported by the library */
|
||||
export type MessageType = keyof proto.Message
|
||||
|
||||
export enum WAMessageAddressingMode {
|
||||
PN = 'pn',
|
||||
LID = 'lid'
|
||||
}
|
||||
|
||||
export type MessageWithContextInfo =
|
||||
| 'imageMessage'
|
||||
| 'contactMessage'
|
||||
|
||||
+11
-1
@@ -137,5 +137,15 @@ export type SocketConfig = {
|
||||
/** cached group metadata, use to prevent redundant requests to WA & speed up msg sending */
|
||||
cachedGroupMetadata: (jid: string) => Promise<GroupMetadata | undefined>
|
||||
|
||||
makeSignalRepository: (auth: SignalAuthState) => SignalRepository
|
||||
makeSignalRepository: (
|
||||
auth: SignalAuthState,
|
||||
onWhatsAppFunc?: (...jids: string[]) => Promise<
|
||||
| {
|
||||
jid: string
|
||||
exists: boolean
|
||||
lid: string
|
||||
}[]
|
||||
| undefined
|
||||
>
|
||||
) => SignalRepository
|
||||
}
|
||||
|
||||
@@ -19,14 +19,7 @@ import {
|
||||
LabelAssociationType,
|
||||
type MessageLabelAssociation
|
||||
} from '../Types/LabelAssociation'
|
||||
import {
|
||||
type BinaryNode,
|
||||
getBinaryNodeChild,
|
||||
getBinaryNodeChildren,
|
||||
isJidGroup,
|
||||
isJidUser,
|
||||
jidNormalizedUser
|
||||
} from '../WABinary'
|
||||
import { type BinaryNode, getBinaryNodeChild, getBinaryNodeChildren, isJidGroup, jidNormalizedUser } from '../WABinary'
|
||||
import { aesDecrypt, aesEncrypt, hkdf, hmacSign } from './crypto'
|
||||
import { toNumber } from './generics'
|
||||
import type { ILogger } from './logger'
|
||||
@@ -845,7 +838,7 @@ export const processSyncAction = (
|
||||
id: id!,
|
||||
name: action.contactAction.fullName!,
|
||||
lid: action.contactAction.lidJid || undefined,
|
||||
jid: isJidUser(id) ? id : undefined
|
||||
phoneNumber: action.contactAction.pnJid || undefined
|
||||
}
|
||||
])
|
||||
} else if (action?.pushNameSetting) {
|
||||
|
||||
@@ -6,14 +6,12 @@ import {
|
||||
type BinaryNode,
|
||||
isJidBroadcast,
|
||||
isJidGroup,
|
||||
isJidMetaIa,
|
||||
isJidMetaAI,
|
||||
isJidNewsletter,
|
||||
isJidStatusBroadcast,
|
||||
isJidUser,
|
||||
isLidUser,
|
||||
jidDecode,
|
||||
jidEncode,
|
||||
jidNormalizedUser
|
||||
isPnUser,
|
||||
transferDevice
|
||||
} from '../WABinary'
|
||||
import { unpadRandomMax16 } from './generics'
|
||||
import type { ILogger } from './logger'
|
||||
@@ -23,17 +21,7 @@ const getDecryptionJid = async (sender: string, repository: SignalRepository): P
|
||||
return sender
|
||||
}
|
||||
|
||||
const lidMapping = repository.getLIDMappingStore()
|
||||
const normalizedSender = jidNormalizedUser(sender)
|
||||
const lidForPN = await lidMapping.getLIDForPN(normalizedSender)
|
||||
|
||||
if (lidForPN?.includes('@lid')) {
|
||||
const senderDecoded = jidDecode(sender)
|
||||
const deviceId = senderDecoded?.device || 0
|
||||
return jidEncode(jidDecode(lidForPN)!.user, 'lid', deviceId)
|
||||
}
|
||||
|
||||
return sender
|
||||
return (await repository.getLIDMappingStore().getLIDForPN(sender))!
|
||||
}
|
||||
|
||||
const storeMappingFromEnvelope = async (
|
||||
@@ -45,7 +33,7 @@ const storeMappingFromEnvelope = async (
|
||||
): Promise<void> => {
|
||||
const { senderAlt } = extractAddressingContext(stanza)
|
||||
|
||||
if (senderAlt && isLidUser(senderAlt) && isJidUser(sender) && decryptionJid === sender) {
|
||||
if (senderAlt && isLidUser(senderAlt) && isPnUser(sender) && decryptionJid === sender) {
|
||||
try {
|
||||
await repository.storeLIDPNMapping(senderAlt, sender)
|
||||
logger.debug({ sender, senderAlt }, 'Stored LID mapping from envelope')
|
||||
@@ -95,14 +83,23 @@ export const extractAddressingContext = (stanza: BinaryNode) => {
|
||||
let senderAlt: string | undefined
|
||||
let recipientAlt: string | undefined
|
||||
|
||||
const sender = stanza.attrs.participant || stanza.attrs.from
|
||||
|
||||
if (addressingMode === 'lid') {
|
||||
// Message is LID-addressed: sender is LID, extract corresponding PN
|
||||
senderAlt = stanza.attrs.participant_pn || stanza.attrs.sender_pn
|
||||
// without device data
|
||||
senderAlt = stanza.attrs.participant_pn || stanza.attrs.sender_pn || stanza.attrs.peer_recipient_pn
|
||||
recipientAlt = stanza.attrs.recipient_pn
|
||||
// with device data
|
||||
if (sender && senderAlt) senderAlt = transferDevice(sender, senderAlt)
|
||||
} else {
|
||||
// Message is PN-addressed: sender is PN, extract corresponding LID
|
||||
senderAlt = stanza.attrs.participant_lid || stanza.attrs.sender_lid
|
||||
// without device data
|
||||
senderAlt = stanza.attrs.participant_lid || stanza.attrs.sender_lid || stanza.attrs.peer_recipient_lid
|
||||
recipientAlt = stanza.attrs.recipient_lid
|
||||
|
||||
//with device data
|
||||
if (sender && senderAlt) senderAlt = transferDevice(sender, senderAlt)
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -126,11 +123,13 @@ export function decodeMessageNode(stanza: BinaryNode, meId: string, meLid: strin
|
||||
const participant: string | undefined = stanza.attrs.participant
|
||||
const recipient: string | undefined = stanza.attrs.recipient
|
||||
|
||||
const addressingContext = extractAddressingContext(stanza)
|
||||
|
||||
const isMe = (jid: string) => areJidsSameUser(jid, meId)
|
||||
const isMeLid = (jid: string) => areJidsSameUser(jid, meLid)
|
||||
|
||||
if (isJidUser(from) || isLidUser(from)) {
|
||||
if (recipient && !isJidMetaIa(recipient)) {
|
||||
if (isPnUser(from) || isLidUser(from)) {
|
||||
if (recipient && !isJidMetaAI(recipient)) {
|
||||
if (!isMe(from!) && !isMeLid(from!)) {
|
||||
throw new Boom('receipient present, but msg not from me', { data: stanza })
|
||||
}
|
||||
@@ -177,13 +176,11 @@ export function decodeMessageNode(stanza: BinaryNode, meId: string, meLid: strin
|
||||
|
||||
const key: WAMessageKey = {
|
||||
remoteJid: chatId,
|
||||
remoteJidAlt: !isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
|
||||
fromMe,
|
||||
id: msgId,
|
||||
senderLid: stanza?.attrs?.sender_lid,
|
||||
senderPn: stanza?.attrs?.sender_pn || stanza?.attrs?.peer_recipient_pn,
|
||||
participant,
|
||||
participantPn: stanza?.attrs?.participant_pn,
|
||||
participantLid: stanza?.attrs?.participant_lid,
|
||||
participantAlt: isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
|
||||
...(msgType === 'newsletter' && stanza.attrs.server_id ? { server_id: stanza.attrs.server_id } : {})
|
||||
}
|
||||
|
||||
@@ -228,7 +225,7 @@ export const decryptMessageNode = (
|
||||
}
|
||||
|
||||
if (tag === 'unavailable' && attrs.type === 'view_once') {
|
||||
fullMessage.key.isViewOnce = true
|
||||
fullMessage.key.isViewOnce = true // TODO: remove from here and add a STUB TYPE
|
||||
}
|
||||
|
||||
if (tag !== 'enc' && tag !== 'plaintext') {
|
||||
@@ -243,6 +240,12 @@ export const decryptMessageNode = (
|
||||
|
||||
let msgBuffer: Uint8Array
|
||||
|
||||
const user = isPnUser(sender) ? sender : author // TODO: flaky logic
|
||||
const decryptionJid = await getDecryptionJid(user, repository)
|
||||
if (tag !== 'plaintext') {
|
||||
await storeMappingFromEnvelope(stanza, user, decryptionJid, repository, logger)
|
||||
}
|
||||
|
||||
try {
|
||||
const e2eType = tag === 'plaintext' ? 'plaintext' : attrs.type
|
||||
|
||||
@@ -256,16 +259,11 @@ export const decryptMessageNode = (
|
||||
break
|
||||
case 'pkmsg':
|
||||
case 'msg':
|
||||
const user = isJidUser(sender) ? sender : author
|
||||
const decryptionJid = await getDecryptionJid(user, repository)
|
||||
|
||||
msgBuffer = await repository.decryptMessage({
|
||||
jid: decryptionJid,
|
||||
type: e2eType,
|
||||
ciphertext: content
|
||||
})
|
||||
|
||||
await storeMappingFromEnvelope(stanza, user, decryptionJid, repository, logger)
|
||||
break
|
||||
case 'plaintext':
|
||||
msgBuffer = content
|
||||
|
||||
@@ -3,8 +3,7 @@ import axios, { type AxiosRequestConfig } from 'axios'
|
||||
import { createHash, randomBytes } from 'crypto'
|
||||
import { platform, release } from 'os'
|
||||
import { proto } from '../../WAProto/index.js'
|
||||
import version from '../Defaults/baileys-version.json' with { type: 'json' }
|
||||
const baileysVersion = version.version
|
||||
const baileysVersion = [2, 3000, 1023223821]
|
||||
import type {
|
||||
BaileysEventEmitter,
|
||||
BaileysEventMap,
|
||||
|
||||
@@ -4,7 +4,6 @@ import { inflate } from 'zlib'
|
||||
import { proto } from '../../WAProto/index.js'
|
||||
import type { Chat, Contact } from '../Types'
|
||||
import { WAMessageStubType } from '../Types'
|
||||
import { isJidUser } from '../WABinary'
|
||||
import { toNumber } from './generics'
|
||||
import { normalizeMessageContent } from './messages'
|
||||
import { downloadContentFromMessage } from './messages-media'
|
||||
@@ -42,7 +41,7 @@ export const processHistoryMessage = (item: proto.IHistorySync) => {
|
||||
id: chat.id,
|
||||
name: chat.name || undefined,
|
||||
lid: chat.lidJid || undefined,
|
||||
jid: isJidUser(chat.id) ? chat.id : undefined
|
||||
phoneNumber: chat.pnJid || undefined
|
||||
})
|
||||
|
||||
const msgs = chat.messages || []
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LRUCache } from 'lru-cache'
|
||||
import { proto } from '../../WAProto'
|
||||
import type { proto } from '../../WAProto/index.js'
|
||||
import type { ILogger } from './logger'
|
||||
|
||||
/** Number of sent messages to cache in memory for handling retry receipts */
|
||||
|
||||
@@ -9,7 +9,8 @@ import type {
|
||||
ParticipantAction,
|
||||
RequestJoinAction,
|
||||
RequestJoinMethod,
|
||||
SignalKeyStoreWithTransaction
|
||||
SignalKeyStoreWithTransaction,
|
||||
SignalRepository
|
||||
} from '../Types'
|
||||
import { WAMessageStubType } from '../Types'
|
||||
import { getContentType, normalizeMessageContent } from '../Utils/messages'
|
||||
@@ -27,6 +28,7 @@ type ProcessMessageContext = {
|
||||
ev: BaileysEventEmitter
|
||||
logger?: ILogger
|
||||
options: AxiosRequestConfig<{}>
|
||||
signalRepository: SignalRepository
|
||||
}
|
||||
|
||||
const REAL_MSG_STUB_TYPES = new Set([
|
||||
@@ -145,7 +147,16 @@ export function decryptPollVote(
|
||||
|
||||
const processMessage = async (
|
||||
message: proto.IWebMessageInfo,
|
||||
{ shouldProcessHistoryMsg, placeholderResendCache, ev, creds, keyStore, logger, options }: ProcessMessageContext
|
||||
{
|
||||
shouldProcessHistoryMsg,
|
||||
placeholderResendCache,
|
||||
ev,
|
||||
creds,
|
||||
signalRepository,
|
||||
keyStore,
|
||||
logger,
|
||||
options
|
||||
}: ProcessMessageContext
|
||||
) => {
|
||||
const meId = creds.me!.id
|
||||
const { accountSettings } = creds
|
||||
@@ -292,6 +303,19 @@ const processMessage = async (
|
||||
}
|
||||
])
|
||||
break
|
||||
case proto.Message.ProtocolMessage.Type.LID_MIGRATION_MAPPING_SYNC:
|
||||
const lidMappingStore = signalRepository.getLIDMappingStore()
|
||||
const encodedPayload = protocolMsg.lidMigrationMappingSyncMessage?.encodedMappingPayload!
|
||||
const { pnToLidMappings, chatDbMigrationTimestamp } =
|
||||
proto.LIDMigrationMappingSyncPayload.decode(encodedPayload)
|
||||
logger?.debug({ pnToLidMappings, chatDbMigrationTimestamp }, 'got lid mappings and chat db migration timestamp')
|
||||
const pairs = []
|
||||
for (const { pn, latestLid, assignedLid } of pnToLidMappings) {
|
||||
const lid = latestLid || assignedLid
|
||||
pairs.push({ lid: `${lid}@lid`, pn: `${pn}@s.whatsapp.net` })
|
||||
}
|
||||
|
||||
await lidMappingStore.storeLIDPNMappings(pairs)
|
||||
}
|
||||
} else if (content?.reactionMessage) {
|
||||
const reaction: proto.IReaction = {
|
||||
|
||||
@@ -133,6 +133,7 @@ export const configureSuccessfulPairing = (
|
||||
|
||||
const bizName = businessNode?.attrs.name
|
||||
const jid = deviceNode.attrs.jid
|
||||
const lid = deviceNode.attrs.lid
|
||||
|
||||
const { details, hmac, accountType } = proto.ADVSignedDeviceIdentityHMAC.decode(deviceIdentityNode.content as Buffer)
|
||||
const isHostedAccount = accountType !== undefined && accountType === proto.ADVEncryptionType.HOSTED
|
||||
@@ -154,7 +155,7 @@ export const configureSuccessfulPairing = (
|
||||
const deviceMsg = Buffer.concat([devicePrefix, deviceDetails, signedIdentityKey.public, accountSignatureKey])
|
||||
account.deviceSignature = Curve.sign(signedIdentityKey.private, deviceMsg)
|
||||
|
||||
const identity = createSignalIdentity(jid!, accountSignatureKey)
|
||||
const identity = createSignalIdentity(lid!, accountSignatureKey)
|
||||
const accountEnc = encodeSignedDeviceIdentity(account, false)
|
||||
|
||||
const deviceIdentity = proto.ADVDeviceIdentity.decode(account.details)
|
||||
@@ -183,7 +184,7 @@ export const configureSuccessfulPairing = (
|
||||
|
||||
const authUpdate: Partial<AuthenticationCreds> = {
|
||||
account,
|
||||
me: { id: jid!, name: bizName },
|
||||
me: { id: jid!, name: bizName, lid },
|
||||
signalIdentities: [...(signalIdentities || []), identity],
|
||||
platform: platformNode?.attrs.name
|
||||
}
|
||||
|
||||
@@ -44,11 +44,11 @@ export const jidDecode = (jid: string | undefined): FullJid | undefined => {
|
||||
/** is the jid a user */
|
||||
export const areJidsSameUser = (jid1: string | undefined, jid2: string | undefined) =>
|
||||
jidDecode(jid1)?.user === jidDecode(jid2)?.user
|
||||
/** is the jid Meta IA */
|
||||
export const isJidMetaIa = (jid: string | undefined) => jid?.endsWith('@bot')
|
||||
/** is the jid a user */
|
||||
export const isJidUser = (jid: string | undefined) => jid?.endsWith('@s.whatsapp.net')
|
||||
/** is the jid a group */
|
||||
/** is the jid Meta AI */
|
||||
export const isJidMetaAI = (jid: string | undefined) => jid?.endsWith('@bot')
|
||||
/** is the jid a PN user */
|
||||
export const isPnUser = (jid: string | undefined) => jid?.endsWith('@s.whatsapp.net')
|
||||
/** is the jid a LID */
|
||||
export const isLidUser = (jid: string | undefined) => jid?.endsWith('@lid')
|
||||
/** is the jid a broadcast */
|
||||
export const isJidBroadcast = (jid: string | undefined) => jid?.endsWith('@broadcast')
|
||||
@@ -72,3 +72,10 @@ export const jidNormalizedUser = (jid: string | undefined) => {
|
||||
const { user, server } = result
|
||||
return jidEncode(user, server === 'c.us' ? 's.whatsapp.net' : (server as JidServer))
|
||||
}
|
||||
|
||||
export const transferDevice = (fromJid: string, toJid: string) => {
|
||||
const fromDecoded = jidDecode(fromJid)
|
||||
const deviceId = fromDecoded?.device || 0
|
||||
const { server, user } = jidDecode(toJid)!
|
||||
return jidEncode(user, server, deviceId)
|
||||
}
|
||||
|
||||
@@ -622,7 +622,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.7.0":
|
||||
"@eslint-community/eslint-utils@npm:^4.7.0":
|
||||
version: 4.7.0
|
||||
resolution: "@eslint-community/eslint-utils@npm:4.7.0"
|
||||
dependencies:
|
||||
@@ -633,6 +633,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint-community/eslint-utils@npm:^4.8.0":
|
||||
version: 4.8.0
|
||||
resolution: "@eslint-community/eslint-utils@npm:4.8.0"
|
||||
dependencies:
|
||||
eslint-visitor-keys: "npm:^3.4.3"
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
|
||||
checksum: 10c0/33b93d2a4e9d5fe4c11d02d0fc5ed69e12fcb1e7ca031ded0d6adb24e768c36df77288ed79eecc784f9db34219816247db27688dfe869fb7fbf096840a097d7a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.12.1":
|
||||
version: 4.12.1
|
||||
resolution: "@eslint-community/regexpp@npm:4.12.1"
|
||||
@@ -651,19 +662,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint/config-helpers@npm:^0.3.0":
|
||||
version: 0.3.0
|
||||
resolution: "@eslint/config-helpers@npm:0.3.0"
|
||||
checksum: 10c0/013ae7b189eeae8b30cc2ee87bc5c9c091a9cd615579003290eb28bebad5d78806a478e74ba10b3fe08ed66975b52af7d2cd4b4b43990376412b14e5664878c8
|
||||
"@eslint/config-helpers@npm:^0.3.1":
|
||||
version: 0.3.1
|
||||
resolution: "@eslint/config-helpers@npm:0.3.1"
|
||||
checksum: 10c0/f6c5b3a0b76a0d7d84cc93e310c259e6c3e0792ddd0a62c5fc0027796ffae44183432cb74b2c2b1162801ee1b1b34a6beb5d90a151632b4df7349f994146a856
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint/core@npm:^0.15.0, @eslint/core@npm:^0.15.1":
|
||||
version: 0.15.1
|
||||
resolution: "@eslint/core@npm:0.15.1"
|
||||
"@eslint/core@npm:^0.15.2":
|
||||
version: 0.15.2
|
||||
resolution: "@eslint/core@npm:0.15.2"
|
||||
dependencies:
|
||||
"@types/json-schema": "npm:^7.0.15"
|
||||
checksum: 10c0/abaf641940776638b8c15a38d99ce0dac551a8939310ec81b9acd15836a574cf362588eaab03ab11919bc2a0f9648b19ea8dee33bf12675eb5b6fd38bda6f25e
|
||||
checksum: 10c0/c17a6dc4f5a6006ecb60165cc38bcd21fefb4a10c7a2578a0cfe5813bbd442531a87ed741da5adab5eb678e8e693fda2e2b14555b035355537e32bcec367ea17
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -684,7 +695,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint/js@npm:9.31.0, @eslint/js@npm:^9.31.0":
|
||||
"@eslint/js@npm:9.35.0":
|
||||
version: 9.35.0
|
||||
resolution: "@eslint/js@npm:9.35.0"
|
||||
checksum: 10c0/d40fe38724bc76c085c0b753cdf937fa35c0d6807ae76b2632e3f5f66c3040c91adcf1aff2ce70b4f45752e60629fadc415eeec9af3be3c274bae1cac54b9840
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint/js@npm:^9.31.0":
|
||||
version: 9.31.0
|
||||
resolution: "@eslint/js@npm:9.31.0"
|
||||
checksum: 10c0/f9d4c73d0fafe70679a418cbb25ab7ebcc8f1dba6c32456d6f8ba5a137d583ecff233cfe10f61f41d7d4d2220e94cff1f39fc7ed1fa3819d1888dee1cad678ea
|
||||
@@ -698,13 +716,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint/plugin-kit@npm:^0.3.1":
|
||||
version: 0.3.3
|
||||
resolution: "@eslint/plugin-kit@npm:0.3.3"
|
||||
"@eslint/plugin-kit@npm:^0.3.5":
|
||||
version: 0.3.5
|
||||
resolution: "@eslint/plugin-kit@npm:0.3.5"
|
||||
dependencies:
|
||||
"@eslint/core": "npm:^0.15.1"
|
||||
"@eslint/core": "npm:^0.15.2"
|
||||
levn: "npm:^0.4.1"
|
||||
checksum: 10c0/c61888eb8757abc0d25a53c1832f85521c2f347126c475eb32d3596be3505e8619e0ceddee7346d195089a2eb1633b61e6127a5772b8965a85eb9f55b8b1cebe
|
||||
checksum: 10c0/c178c1b58c574200c0fd125af3e4bc775daba7ce434ba6d1eeaf9bcb64b2e9fea75efabffb3ed3ab28858e55a016a5efa95f509994ee4341b341199ca630b89e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2162,7 +2180,28 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:^8.32.0, @typescript-eslint/eslint-plugin@npm:^8.37.0":
|
||||
"@typescript-eslint/eslint-plugin@npm:^8":
|
||||
version: 8.42.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.42.0"
|
||||
dependencies:
|
||||
"@eslint-community/regexpp": "npm:^4.10.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.42.0"
|
||||
"@typescript-eslint/type-utils": "npm:8.42.0"
|
||||
"@typescript-eslint/utils": "npm:8.42.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.42.0"
|
||||
graphemer: "npm:^1.4.0"
|
||||
ignore: "npm:^7.0.0"
|
||||
natural-compare: "npm:^1.4.0"
|
||||
ts-api-utils: "npm:^2.1.0"
|
||||
peerDependencies:
|
||||
"@typescript-eslint/parser": ^8.42.0
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10c0/835fd7497f0e4eaef55dc3d94079acc0ad1dc74735916915f160419b1e7f44d04fbce683b4871148d1af33046bd5ae3fed59103d4c49460776b560c42173bbff
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:^8.37.0":
|
||||
version: 8.37.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.37.0"
|
||||
dependencies:
|
||||
@@ -2183,7 +2222,23 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:^8.32.0, @typescript-eslint/parser@npm:^8.37.0":
|
||||
"@typescript-eslint/parser@npm:^8":
|
||||
version: 8.42.0
|
||||
resolution: "@typescript-eslint/parser@npm:8.42.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": "npm:8.42.0"
|
||||
"@typescript-eslint/types": "npm:8.42.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.42.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.42.0"
|
||||
debug: "npm:^4.3.4"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10c0/f071154bce7f874449236919a7367d977317959fe6d454fe5369ca54dee7d057fe3b8b250c5990ea4205a9c52fd59702da63d1721895c72d745168aa31532112
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:^8.37.0":
|
||||
version: 8.37.0
|
||||
resolution: "@typescript-eslint/parser@npm:8.37.0"
|
||||
dependencies:
|
||||
@@ -2212,6 +2267,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/project-service@npm:8.42.0":
|
||||
version: 8.42.0
|
||||
resolution: "@typescript-eslint/project-service@npm:8.42.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/tsconfig-utils": "npm:^8.42.0"
|
||||
"@typescript-eslint/types": "npm:^8.42.0"
|
||||
debug: "npm:^4.3.4"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10c0/788b0bc52683be376cd768a4fed3202cdaccc86f231ec94a0f6bbb1389fdfd0e14c505f03015cefb73869de63c8089b78a169ed957048a1e5ee1b6250ec19604
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:8.37.0":
|
||||
version: 8.37.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.37.0"
|
||||
@@ -2222,6 +2290,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:8.42.0":
|
||||
version: 8.42.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.42.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.42.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.42.0"
|
||||
checksum: 10c0/caca15f2124909c588ed3e48fe0769ad8baa296a0b229f724ec94f5f746e486e08dd49eeddd66d01f09e2ddaed03f9e18d7b535a44196d413f283e22f929f623
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@npm:8.37.0, @typescript-eslint/tsconfig-utils@npm:^8.37.0":
|
||||
version: 8.37.0
|
||||
resolution: "@typescript-eslint/tsconfig-utils@npm:8.37.0"
|
||||
@@ -2231,6 +2309,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@npm:8.42.0, @typescript-eslint/tsconfig-utils@npm:^8.42.0":
|
||||
version: 8.42.0
|
||||
resolution: "@typescript-eslint/tsconfig-utils@npm:8.42.0"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10c0/03882eeee279fafa2cb4ee3154742417fd29395b3bfe3f867d9d4cb9cb68d1200c885c35b96dd558a1aff8561ac3700cff8ca7680a5cf34e5e0e136a6ee3c30c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:8.37.0":
|
||||
version: 8.37.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.37.0"
|
||||
@@ -2247,6 +2334,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:8.42.0":
|
||||
version: 8.42.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.42.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.42.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.42.0"
|
||||
"@typescript-eslint/utils": "npm:8.42.0"
|
||||
debug: "npm:^4.3.4"
|
||||
ts-api-utils: "npm:^2.1.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10c0/47e5f7276cafd7719d3e2f2e456fa988927e658d15c2c188a692d9c639f9d76f582a6c133cb1bf01eba9027e1022eb6b79b57861a96302460e5e847c2b536afa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:8.37.0, @typescript-eslint/types@npm:^8.37.0":
|
||||
version: 8.37.0
|
||||
resolution: "@typescript-eslint/types@npm:8.37.0"
|
||||
@@ -2254,6 +2357,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:8.42.0, @typescript-eslint/types@npm:^8.42.0":
|
||||
version: 8.42.0
|
||||
resolution: "@typescript-eslint/types@npm:8.42.0"
|
||||
checksum: 10c0/d585dff5005328282cc59f9402e886a3db64727906ad3e68b49d7ef73bc07bef3ed569287ba826ebaa07b69be42a72232a38529951d64c28cebd83db0892cd33
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:8.37.0":
|
||||
version: 8.37.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.37.0"
|
||||
@@ -2274,6 +2384,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:8.42.0":
|
||||
version: 8.42.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.42.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/project-service": "npm:8.42.0"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:8.42.0"
|
||||
"@typescript-eslint/types": "npm:8.42.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.42.0"
|
||||
debug: "npm:^4.3.4"
|
||||
fast-glob: "npm:^3.3.2"
|
||||
is-glob: "npm:^4.0.3"
|
||||
minimatch: "npm:^9.0.4"
|
||||
semver: "npm:^7.6.0"
|
||||
ts-api-utils: "npm:^2.1.0"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10c0/2d3354d780421cfa90f812048984c43cd47aabecef7a5c0f56ad0b91331cb369d1c8366da90bf9a8f6df47df3741f9e16897e998f16270ac55376f519b775c23
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:8.37.0":
|
||||
version: 8.37.0
|
||||
resolution: "@typescript-eslint/utils@npm:8.37.0"
|
||||
@@ -2289,6 +2419,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:8.42.0":
|
||||
version: 8.42.0
|
||||
resolution: "@typescript-eslint/utils@npm:8.42.0"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": "npm:^4.7.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.42.0"
|
||||
"@typescript-eslint/types": "npm:8.42.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.42.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <6.0.0"
|
||||
checksum: 10c0/acf30019023669ddae00c02cabfa74fc12defccd4703e552ab5115edbeceaaf1688c1586873bf66aefeb3f03eb1ed456905403303913c724db38bf030e40a700
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:8.37.0":
|
||||
version: 8.37.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.37.0"
|
||||
@@ -2299,6 +2444,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:8.42.0":
|
||||
version: 8.42.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.42.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.42.0"
|
||||
eslint-visitor-keys: "npm:^4.2.1"
|
||||
checksum: 10c0/22c942f2a100d71c08f952b976446e824ddf227d4ac02b7016e12d4a33804ab06072d570695baed3565d0a08a1d3fa6ff3ccf97a122d63e65780f871d597f1b1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ungap/structured-clone@npm:^1.3.0":
|
||||
version: 1.3.0
|
||||
resolution: "@ungap/structured-clone@npm:1.3.0"
|
||||
@@ -2849,14 +3004,14 @@ __metadata:
|
||||
"@types/jest": "npm:^30.0.0"
|
||||
"@types/node": "npm:^20.9.0"
|
||||
"@types/ws": "npm:^8.0.0"
|
||||
"@typescript-eslint/eslint-plugin": "npm:^8.32.0"
|
||||
"@typescript-eslint/parser": "npm:^8.32.0"
|
||||
"@typescript-eslint/eslint-plugin": "npm:^8"
|
||||
"@typescript-eslint/parser": "npm:^8"
|
||||
"@whiskeysockets/eslint-config": "npm:^1.0.0"
|
||||
async-mutex: "npm:^0.5.0"
|
||||
axios: "npm:^1.6.0"
|
||||
conventional-changelog-cli: "npm:^2.2.2"
|
||||
esbuild-register: "npm:^3.6.0"
|
||||
eslint: "npm:^9.31.0"
|
||||
eslint: "npm:^9"
|
||||
eslint-config-prettier: "npm:^10.1.2"
|
||||
eslint-plugin-prettier: "npm:^5.4.0"
|
||||
jest: "npm:^30.0.5"
|
||||
@@ -4513,18 +4668,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint@npm:^9.31.0":
|
||||
version: 9.31.0
|
||||
resolution: "eslint@npm:9.31.0"
|
||||
"eslint@npm:^9":
|
||||
version: 9.35.0
|
||||
resolution: "eslint@npm:9.35.0"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": "npm:^4.2.0"
|
||||
"@eslint-community/eslint-utils": "npm:^4.8.0"
|
||||
"@eslint-community/regexpp": "npm:^4.12.1"
|
||||
"@eslint/config-array": "npm:^0.21.0"
|
||||
"@eslint/config-helpers": "npm:^0.3.0"
|
||||
"@eslint/core": "npm:^0.15.0"
|
||||
"@eslint/config-helpers": "npm:^0.3.1"
|
||||
"@eslint/core": "npm:^0.15.2"
|
||||
"@eslint/eslintrc": "npm:^3.3.1"
|
||||
"@eslint/js": "npm:9.31.0"
|
||||
"@eslint/plugin-kit": "npm:^0.3.1"
|
||||
"@eslint/js": "npm:9.35.0"
|
||||
"@eslint/plugin-kit": "npm:^0.3.5"
|
||||
"@humanfs/node": "npm:^0.16.6"
|
||||
"@humanwhocodes/module-importer": "npm:^1.0.1"
|
||||
"@humanwhocodes/retry": "npm:^0.4.2"
|
||||
@@ -4559,7 +4714,7 @@ __metadata:
|
||||
optional: true
|
||||
bin:
|
||||
eslint: bin/eslint.js
|
||||
checksum: 10c0/3fd1cd5b38b907ecb3f5e7537ab91204efb38bc1ad0ca6e46fc4112f13b594272ff56e641b41580049bc333fbcb5b1b99ca9a542e8406e7da5e951068cbaec77
|
||||
checksum: 10c0/798c527520ccf62106f8cd210bd1db1f8eb1b0e7a56feb0a8b322bf3a1e6a0bc6dc3a414542c22b1b393d58d5e3cd0252c44c023049de9067b836450503a2f03
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user