mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
lint: stricter linting rules
This commit is contained in:
16
package.json
16
package.json
@@ -52,10 +52,18 @@
|
||||
"sharp": "^0.29.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@adiwajshing/keyed-db": { "optional": true },
|
||||
"jimp": { "optional": true },
|
||||
"qrcode-terminal": { "optional": true },
|
||||
"sharp": { "optional": true }
|
||||
"@adiwajshing/keyed-db": {
|
||||
"optional": true
|
||||
},
|
||||
"jimp": {
|
||||
"optional": true
|
||||
},
|
||||
"qrcode-terminal": {
|
||||
"optional": true
|
||||
},
|
||||
"sharp": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"lib/*",
|
||||
|
||||
@@ -10,7 +10,7 @@ export const DEF_CALLBACK_PREFIX = 'CB:'
|
||||
export const DEF_TAG_PREFIX = 'TAG:'
|
||||
export const PHONE_CONNECTION_CB = 'CB:Pong'
|
||||
|
||||
export const WA_DEFAULT_EPHEMERAL = 7*24*60*60
|
||||
export const WA_DEFAULT_EPHEMERAL = 7 * 24 * 60 * 60
|
||||
|
||||
export const NOISE_MODE = 'Noise_XX_25519_AESGCM_SHA256\0\0\0\0'
|
||||
export const NOISE_WA_HEADER = new Uint8Array([87, 65, 5, 2]) // last is "DICT_VERSION"
|
||||
|
||||
@@ -49,7 +49,7 @@ const makeBusinessSocket = (config: LegacySocketConfig) => {
|
||||
mapProductCreate(product)
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
return mapProduct(result.data.product)
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ const makeBusinessSocket = (config: LegacySocketConfig) => {
|
||||
{
|
||||
product_id: productId,
|
||||
...mapProductCreate(
|
||||
{ ...update, originCountryCode: undefined },
|
||||
{ ...update, originCountryCode: undefined },
|
||||
false
|
||||
)
|
||||
}
|
||||
@@ -89,7 +89,7 @@ const makeBusinessSocket = (config: LegacySocketConfig) => {
|
||||
|
||||
return mapProduct(result.data.product)
|
||||
}
|
||||
|
||||
|
||||
// maps product create to send to WA
|
||||
const mapProductCreate = (product: ProductCreate, mapCompliance = true) => {
|
||||
const result: any = {
|
||||
@@ -107,10 +107,10 @@ const makeBusinessSocket = (config: LegacySocketConfig) => {
|
||||
}
|
||||
if(mapCompliance) {
|
||||
Object.assign(result, {
|
||||
compliance_category: product.originCountryCode
|
||||
? undefined :
|
||||
compliance_category: product.originCountryCode
|
||||
? undefined :
|
||||
'COUNTRY_ORIGIN_EXEMPT',
|
||||
compliance_info: product.originCountryCode
|
||||
compliance_info: product.originCountryCode
|
||||
? { country_code_origin: product.originCountryCode }
|
||||
: undefined
|
||||
})
|
||||
|
||||
@@ -6,12 +6,12 @@ import makeAuthSocket from './auth'
|
||||
const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
const { logger } = config
|
||||
const sock = makeAuthSocket(config)
|
||||
const {
|
||||
ev,
|
||||
const {
|
||||
ev,
|
||||
ws: socketEvents,
|
||||
currentEpoch,
|
||||
setQuery,
|
||||
query,
|
||||
query,
|
||||
sendNode,
|
||||
state
|
||||
} = sock
|
||||
@@ -29,9 +29,9 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
)
|
||||
|
||||
const profilePictureUrl = async(jid: string, timeoutMs?: number) => {
|
||||
const response = await query({
|
||||
json: ['query', 'ProfilePicThumb', jid],
|
||||
expect200: false,
|
||||
const response = await query({
|
||||
json: ['query', 'ProfilePicThumb', jid],
|
||||
expect200: false,
|
||||
requiresPhoneConnection: false,
|
||||
timeoutMs
|
||||
})
|
||||
@@ -71,11 +71,11 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
case 'unstar':
|
||||
const starred = updateType === 'star'
|
||||
const updates: WAMessageUpdate[] = (node.content as BinaryNode[]).map(
|
||||
({ attrs }) => ({
|
||||
({ attrs }) => ({
|
||||
key: {
|
||||
remoteJid: jid,
|
||||
id: attrs.index,
|
||||
fromMe: attrs.owner === 'true'
|
||||
remoteJid: jid,
|
||||
id: attrs.index,
|
||||
fromMe: attrs.owner === 'true'
|
||||
},
|
||||
update: { starred }
|
||||
})
|
||||
@@ -93,13 +93,13 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
default:
|
||||
logger.warn({ node }, 'received unrecognized chat update')
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const applyingPresenceUpdate = (update: BinaryNode['attrs']): BaileysEventMap<any>['presence.update'] => {
|
||||
const id = jidNormalizedUser(update.id)
|
||||
const participant = jidNormalizedUser(update.participant || update.id)
|
||||
|
||||
|
||||
const presence: PresenceData = {
|
||||
lastSeen: update.t ? +update.t : undefined,
|
||||
lastKnownPresence: update.type as WAPresence
|
||||
@@ -110,21 +110,21 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
const chatRead = async(fromMessage: WAMessageKey, count: number) => {
|
||||
await setQuery (
|
||||
[
|
||||
{
|
||||
{
|
||||
tag: 'read',
|
||||
attrs: {
|
||||
jid: fromMessage.remoteJid,
|
||||
count: count.toString(),
|
||||
index: fromMessage.id,
|
||||
attrs: {
|
||||
jid: fromMessage.remoteJid,
|
||||
count: count.toString(),
|
||||
index: fromMessage.id,
|
||||
owner: fromMessage.fromMe ? 'true' : 'false'
|
||||
}
|
||||
}
|
||||
],
|
||||
],
|
||||
[ WAMetric.read, WAFlag.ignore ]
|
||||
)
|
||||
if(config.emitOwnEvents) {
|
||||
ev.emit('chats.update', [{ id: fromMessage.remoteJid, unreadCount: count < 0 ? -1 : 0 }])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ev.on('connection.update', async({ connection }) => {
|
||||
@@ -143,7 +143,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
binaryTag: [ WAMetric.queryStatus, WAFlag.ignore ]
|
||||
}),
|
||||
sendNode({
|
||||
json: { tag: 'query', attrs: { type: 'quick_reply', epoch: '1' } },
|
||||
json: { tag: 'query', attrs: { type: 'quick_reply', epoch: '1' } },
|
||||
binaryTag: [ WAMetric.queryQuickReply, WAFlag.ignore ]
|
||||
}),
|
||||
sendNode({
|
||||
@@ -152,21 +152,21 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
}),
|
||||
sendNode({
|
||||
json: { tag: 'query', attrs: { type: 'emoji', epoch: '1' } },
|
||||
binaryTag: [ WAMetric.queryEmoji, WAFlag.ignore ]
|
||||
binaryTag: [ WAMetric.queryEmoji, WAFlag.ignore ]
|
||||
}),
|
||||
sendNode({
|
||||
json: {
|
||||
tag: 'action',
|
||||
json: {
|
||||
tag: 'action',
|
||||
attrs: { type: 'set', epoch: '1' },
|
||||
content: [
|
||||
{ tag: 'presence', attrs: { type: 'available' } }
|
||||
]
|
||||
},
|
||||
},
|
||||
binaryTag: [ WAMetric.presence, WAFlag.available ]
|
||||
})
|
||||
])
|
||||
chatsDebounceTimeout.start()
|
||||
|
||||
|
||||
logger.debug('sent init queries')
|
||||
} catch(error) {
|
||||
logger.error(`error in sending init queries: ${error}`)
|
||||
@@ -246,16 +246,16 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
|
||||
ev.emit('chats.update', [update])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
socketEvents.on('CB:Cmd,type:picture', async json => {
|
||||
json = json[1]
|
||||
const id = jidNormalizedUser(json.jid)
|
||||
const imgUrl = await profilePictureUrl(id).catch(() => '')
|
||||
|
||||
|
||||
ev.emit('contacts.update', [ { id, imgUrl } ])
|
||||
})
|
||||
|
||||
|
||||
// chat archive, pin etc.
|
||||
socketEvents.on('CB:action,,chat', ({ content }: BinaryNode) => {
|
||||
if(Array.isArray(content)) {
|
||||
@@ -269,7 +269,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
const user = json.content[0].attrs
|
||||
if(user.id) {
|
||||
user.id = jidNormalizedUser(user.id)
|
||||
|
||||
|
||||
//ev.emit('contacts.upsert', [user])
|
||||
} else {
|
||||
logger.warn({ json }, 'recv unknown action')
|
||||
@@ -303,7 +303,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
* Modify a given chat (archive, pin etc.)
|
||||
* @param jid the ID of the person/group you are modifiying
|
||||
*/
|
||||
chatModify: async(modification: ChatModification, jid: string, chatInfo: Pick<Chat, 'mute' | 'pin'>, timestampNow?: number) => {
|
||||
chatModify: async(modification: ChatModification, jid: string, chatInfo: Pick<Chat, 'mute' | 'pin'>, timestampNow?: number) => {
|
||||
const chatAttrs: BinaryNode['attrs'] = { jid: jid }
|
||||
let data: BinaryNode[] | undefined = undefined
|
||||
|
||||
@@ -327,10 +327,10 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
}
|
||||
} else if('clear' in modification) {
|
||||
chatAttrs.type = 'clear'
|
||||
chatAttrs.modify_tag = Math.round(Math.random()*1000000).toString()
|
||||
chatAttrs.modify_tag = Math.round(Math.random() * 1000000).toString()
|
||||
if(modification.clear !== 'all') {
|
||||
data = modification.clear.messages.map(({ id, fromMe }) => (
|
||||
{
|
||||
{
|
||||
tag: 'item',
|
||||
attrs: { owner: (!!fromMe).toString(), index: id }
|
||||
}
|
||||
@@ -339,20 +339,20 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
} else if('star' in modification) {
|
||||
chatAttrs.type = modification.star.star ? 'star' : 'unstar'
|
||||
data = modification.star.messages.map(({ id, fromMe }) => (
|
||||
{
|
||||
{
|
||||
tag: 'item',
|
||||
attrs: { owner: (!!fromMe).toString(), index: id }
|
||||
}
|
||||
))
|
||||
} else if('markRead' in modification) {
|
||||
const indexKey = modification.lastMessages[modification.lastMessages.length-1].key
|
||||
const indexKey = modification.lastMessages[modification.lastMessages.length - 1].key
|
||||
return chatRead(indexKey, modification.markRead ? 0 : -1)
|
||||
} else if('delete' in modification) {
|
||||
chatAttrs.type = 'delete'
|
||||
}
|
||||
|
||||
if('lastMessages' in modification) {
|
||||
const indexKey = modification.lastMessages[modification.lastMessages.length-1].key
|
||||
const indexKey = modification.lastMessages[modification.lastMessages.length - 1].key
|
||||
if(indexKey) {
|
||||
chatAttrs.index = indexKey.id
|
||||
chatAttrs.owner = indexKey.fromMe ? 'true' : 'false'
|
||||
@@ -368,20 +368,20 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
|
||||
return response
|
||||
},
|
||||
/**
|
||||
/**
|
||||
* Query whether a given number is registered on WhatsApp
|
||||
* @param str phone number/jid you want to check for
|
||||
* @returns undefined if the number doesn't exists, otherwise the correctly formatted jid
|
||||
*/
|
||||
onWhatsApp: async(str: string) => {
|
||||
const { status, jid, biz } = await query({
|
||||
json: ['query', 'exist', str],
|
||||
json: ['query', 'exist', str],
|
||||
requiresPhoneConnection: false
|
||||
})
|
||||
if(status === 200) {
|
||||
return {
|
||||
exists: true,
|
||||
jid: jidNormalizedUser(jid),
|
||||
return {
|
||||
exists: true,
|
||||
jid: jidNormalizedUser(jid),
|
||||
isBusiness: biz as boolean
|
||||
}
|
||||
}
|
||||
@@ -394,20 +394,20 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
sendPresenceUpdate: (type: WAPresence, jid: string | undefined) => (
|
||||
sendNode({
|
||||
binaryTag: [WAMetric.presence, WAFlag[type]], // weird stuff WA does
|
||||
json: {
|
||||
json: {
|
||||
tag: 'action',
|
||||
attrs: { epoch: currentEpoch().toString(), type: 'set' },
|
||||
content: [
|
||||
{
|
||||
tag: 'presence',
|
||||
{
|
||||
tag: 'presence',
|
||||
attrs: { type: type, to: jid }
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
),
|
||||
/**
|
||||
* Request updates on the presence of a user
|
||||
/**
|
||||
* Request updates on the presence of a user
|
||||
* this returns nothing, you'll receive updates in chats.update event
|
||||
* */
|
||||
presenceSubscribe: async(jid: string) => (
|
||||
@@ -421,7 +421,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
setStatus: async(status: string) => {
|
||||
const response = await setQuery(
|
||||
[
|
||||
{
|
||||
{
|
||||
tag: 'status',
|
||||
attrs: {},
|
||||
content: Buffer.from (status, 'utf-8')
|
||||
@@ -444,7 +444,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
updateProfileName: async(name: string) => {
|
||||
const response = (await setQuery(
|
||||
[
|
||||
{
|
||||
{
|
||||
tag: 'profile',
|
||||
attrs: { name }
|
||||
}
|
||||
@@ -463,14 +463,14 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
},
|
||||
/**
|
||||
* Update the profile picture
|
||||
* @param jid
|
||||
* @param img
|
||||
* @param jid
|
||||
* @param img
|
||||
*/
|
||||
async updateProfilePicture(jid: string, img: Buffer) {
|
||||
jid = jidNormalizedUser (jid)
|
||||
const data = { img: Buffer.from([]), preview: Buffer.from([]) } //await generateProfilePicture(img) TODO
|
||||
const tag = this.generateMessageTag ()
|
||||
const query: BinaryNode = {
|
||||
const query: BinaryNode = {
|
||||
tag: 'picture',
|
||||
attrs: { jid: jid, id: tag, type: 'set' },
|
||||
content: [
|
||||
@@ -481,7 +481,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
|
||||
const user = state.legacy?.user
|
||||
const { eurl } = await this.setQuery ([query], [WAMetric.picture, 136], tag) as { eurl: string, status: number }
|
||||
|
||||
|
||||
if(config.emitOwnEvents) {
|
||||
if(jid === user.id) {
|
||||
user.imgUrl = eurl
|
||||
@@ -502,7 +502,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
* @param type type of operation
|
||||
*/
|
||||
blockUser: async(jid: string, type: 'add' | 'remove' = 'add') => {
|
||||
const json = {
|
||||
const json = {
|
||||
tag: 'block',
|
||||
attrs: { type },
|
||||
content: [ { tag: 'user', attrs: { jid } } ]
|
||||
@@ -522,12 +522,12 @@ const makeChatsSocket = (config: LegacySocketConfig) => {
|
||||
const {
|
||||
profiles: [{
|
||||
profile,
|
||||
wid
|
||||
wid
|
||||
}]
|
||||
} = await query({
|
||||
json: [
|
||||
'query', 'businessProfile',
|
||||
[ { 'wid': jid.replace('@s.whatsapp.net', '@c.us') } ],
|
||||
'query', 'businessProfile',
|
||||
[ { 'wid': jid.replace('@s.whatsapp.net', '@c.us') } ],
|
||||
84
|
||||
],
|
||||
expect200: true,
|
||||
|
||||
@@ -6,8 +6,8 @@ import makeMessagesSocket from './messages'
|
||||
const makeGroupsSocket = (config: LegacySocketConfig) => {
|
||||
const { logger } = config
|
||||
const sock = makeMessagesSocket(config)
|
||||
const {
|
||||
ev,
|
||||
const {
|
||||
ev,
|
||||
ws: socketEvents,
|
||||
query,
|
||||
generateMessageTag,
|
||||
@@ -29,10 +29,10 @@ const makeGroupsSocket = (config: LegacySocketConfig) => {
|
||||
jid: jid,
|
||||
subject: subject,
|
||||
},
|
||||
content: participants ?
|
||||
content: participants ?
|
||||
participants.map(jid => (
|
||||
{ tag: 'participant', attrs: { jid } }
|
||||
)) :
|
||||
)) :
|
||||
additionalNodes
|
||||
}
|
||||
], [WAMetric.group, 136], tag)
|
||||
@@ -42,9 +42,9 @@ const makeGroupsSocket = (config: LegacySocketConfig) => {
|
||||
/** Get the metadata of the group from WA */
|
||||
const groupMetadataFull = async(jid: string) => {
|
||||
const metadata = await query({
|
||||
json: ['query', 'GroupMetadata', jid],
|
||||
json: ['query', 'GroupMetadata', jid],
|
||||
expect200: true
|
||||
})
|
||||
})
|
||||
|
||||
const meta: GroupMetadata = {
|
||||
id: metadata.id,
|
||||
@@ -69,10 +69,10 @@ const makeGroupsSocket = (config: LegacySocketConfig) => {
|
||||
const groupMetadataMinimal = async(jid: string) => {
|
||||
const { attrs, content }:BinaryNode = await query({
|
||||
json: {
|
||||
tag: 'query',
|
||||
tag: 'query',
|
||||
attrs: { type: 'group', jid: jid, epoch: currentEpoch().toString() }
|
||||
},
|
||||
binaryTag: [WAMetric.group, WAFlag.ignore],
|
||||
binaryTag: [WAMetric.group, WAFlag.ignore],
|
||||
expect200: true
|
||||
})
|
||||
const participants: GroupParticipant[] = []
|
||||
@@ -102,7 +102,7 @@ const makeGroupsSocket = (config: LegacySocketConfig) => {
|
||||
}
|
||||
return meta
|
||||
}
|
||||
|
||||
|
||||
socketEvents.on('CB:Chat,cmd:action', (json: BinaryNode) => {
|
||||
/*const data = json[1].data
|
||||
if (data) {
|
||||
@@ -138,7 +138,7 @@ const makeGroupsSocket = (config: LegacySocketConfig) => {
|
||||
result = await groupMetadataFull(jid)
|
||||
}
|
||||
|
||||
return result
|
||||
return result
|
||||
},
|
||||
/**
|
||||
* Create a group
|
||||
@@ -219,16 +219,16 @@ const makeGroupsSocket = (config: LegacySocketConfig) => {
|
||||
return jids
|
||||
},
|
||||
/** Query broadcast list info */
|
||||
getBroadcastListInfo: async(jid: string) => {
|
||||
getBroadcastListInfo: async(jid: string) => {
|
||||
interface WABroadcastListInfo {
|
||||
status: number
|
||||
name: string
|
||||
recipients?: {id: string}[]
|
||||
}
|
||||
|
||||
const result = await query({
|
||||
json: ['query', 'contact', jid],
|
||||
expect200: true,
|
||||
|
||||
const result = await query({
|
||||
json: ['query', 'contact', jid],
|
||||
expect200: true,
|
||||
requiresPhoneConnection: true
|
||||
}) as WABroadcastListInfo
|
||||
|
||||
@@ -245,8 +245,8 @@ const makeGroupsSocket = (config: LegacySocketConfig) => {
|
||||
},
|
||||
groupInviteCode: async(jid: string) => {
|
||||
const response = await sock.query({
|
||||
json: ['query', 'inviteCode', jid],
|
||||
expect200: true,
|
||||
json: ['query', 'inviteCode', jid],
|
||||
expect200: true,
|
||||
requiresPhoneConnection: false
|
||||
})
|
||||
return response.code as string
|
||||
|
||||
@@ -15,8 +15,8 @@ const STATUS_MAP = {
|
||||
const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
const { logger } = config
|
||||
const sock = makeChatsSocket(config)
|
||||
const {
|
||||
ev,
|
||||
const {
|
||||
ev,
|
||||
ws: socketEvents,
|
||||
query,
|
||||
generateMessageTag,
|
||||
@@ -28,10 +28,10 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
let mediaConn: Promise<MediaConnInfo>
|
||||
const refreshMediaConn = async(forceGet = false) => {
|
||||
const media = await mediaConn
|
||||
if(!media || forceGet || (new Date().getTime()-media.fetchDate.getTime()) > media.ttl*1000) {
|
||||
if(!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) {
|
||||
mediaConn = (async() => {
|
||||
const { media_conn } = await query({
|
||||
json: ['query', 'mediaConn'],
|
||||
json: ['query', 'mediaConn'],
|
||||
requiresPhoneConnection: false,
|
||||
expect200: true
|
||||
})
|
||||
@@ -40,12 +40,12 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
})()
|
||||
}
|
||||
|
||||
return mediaConn
|
||||
return mediaConn
|
||||
}
|
||||
|
||||
const fetchMessagesFromWA = async(
|
||||
jid: string,
|
||||
count: number,
|
||||
jid: string,
|
||||
count: number,
|
||||
cursor?: WAMessageCursor
|
||||
) => {
|
||||
let key: WAMessageKey
|
||||
@@ -66,8 +66,8 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
owner: key?.fromMe === false ? 'false' : 'true',
|
||||
}
|
||||
},
|
||||
binaryTag: [WAMetric.queryMessages, WAFlag.ignore],
|
||||
expect200: false,
|
||||
binaryTag: [WAMetric.queryMessages, WAFlag.ignore],
|
||||
expect200: false,
|
||||
requiresPhoneConnection: true
|
||||
})
|
||||
if(Array.isArray(content)) {
|
||||
@@ -78,27 +78,27 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
}
|
||||
|
||||
const updateMediaMessage = async(message: WAMessage) => {
|
||||
const content = message.message?.audioMessage || message.message?.videoMessage || message.message?.imageMessage || message.message?.stickerMessage || message.message?.documentMessage
|
||||
const content = message.message?.audioMessage || message.message?.videoMessage || message.message?.imageMessage || message.message?.stickerMessage || message.message?.documentMessage
|
||||
if(!content) {
|
||||
throw new Boom(
|
||||
`given message ${message.key.id} is not a media message`,
|
||||
`given message ${message.key.id} is not a media message`,
|
||||
{ statusCode: 400, data: message }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const response: BinaryNode = await query ({
|
||||
json: {
|
||||
tag: 'query',
|
||||
attrs: {
|
||||
type: 'media',
|
||||
index: message.key.id,
|
||||
owner: message.key.fromMe ? 'true' : 'false',
|
||||
jid: message.key.remoteJid,
|
||||
type: 'media',
|
||||
index: message.key.id,
|
||||
owner: message.key.fromMe ? 'true' : 'false',
|
||||
jid: message.key.remoteJid,
|
||||
epoch: currentEpoch().toString()
|
||||
}
|
||||
},
|
||||
binaryTag: [WAMetric.queryMedia, WAFlag.ignore],
|
||||
expect200: true,
|
||||
},
|
||||
binaryTag: [WAMetric.queryMedia, WAFlag.ignore],
|
||||
expect200: true,
|
||||
requiresPhoneConnection: true
|
||||
})
|
||||
const attrs = response.attrs
|
||||
@@ -112,14 +112,14 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
const onMessage = (message: WAMessage, type: MessageUpdateType) => {
|
||||
const jid = message.key.remoteJid!
|
||||
// store chat updates in this
|
||||
const chatUpdate: Partial<Chat> = {
|
||||
const chatUpdate: Partial<Chat> = {
|
||||
id: jid,
|
||||
}
|
||||
|
||||
const emitGroupUpdate = (update: Partial<GroupMetadata>) => {
|
||||
ev.emit('groups.update', [ { id: jid, ...update } ])
|
||||
}
|
||||
|
||||
|
||||
if(message.message) {
|
||||
chatUpdate.conversationTimestamp = +toNumber(message.messageTimestamp)
|
||||
// add to count if the message isn't from me & there exists a message
|
||||
@@ -128,7 +128,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
const participant = jidNormalizedUser(message.participant || jid)
|
||||
|
||||
ev.emit(
|
||||
'presence.update',
|
||||
'presence.update',
|
||||
{
|
||||
id: jid,
|
||||
presences: { [participant]: { lastKnownPresence: 'available' } }
|
||||
@@ -144,11 +144,11 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
case proto.ProtocolMessage.ProtocolMessageType.REVOKE:
|
||||
const key = protocolMessage.key
|
||||
const messageStubType = WAMessageStubType.REVOKE
|
||||
ev.emit('messages.update', [
|
||||
{
|
||||
ev.emit('messages.update', [
|
||||
{
|
||||
// the key of the deleted message is updated
|
||||
update: { message: null, key: message.key, messageStubType },
|
||||
key
|
||||
update: { message: null, key: message.key, messageStubType },
|
||||
key
|
||||
}
|
||||
])
|
||||
return
|
||||
@@ -166,7 +166,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
}
|
||||
}
|
||||
|
||||
// check if the message is an action
|
||||
// check if the message is an action
|
||||
if(message.messageStubType) {
|
||||
const { user } = state.legacy!
|
||||
//let actor = jidNormalizedUser (message.participant)
|
||||
@@ -234,14 +234,14 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
const response: BinaryNode = await query({
|
||||
json: {
|
||||
tag: 'query',
|
||||
attrs: {
|
||||
type: 'url',
|
||||
url: text,
|
||||
epoch: currentEpoch().toString()
|
||||
attrs: {
|
||||
type: 'url',
|
||||
url: text,
|
||||
epoch: currentEpoch().toString()
|
||||
}
|
||||
},
|
||||
binaryTag: [26, WAFlag.ignore],
|
||||
expect200: true,
|
||||
},
|
||||
binaryTag: [26, WAFlag.ignore],
|
||||
expect200: true,
|
||||
requiresPhoneConnection: false
|
||||
})
|
||||
const urlInfo = { ...response.attrs } as any as WAUrlInfo
|
||||
@@ -258,9 +258,9 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
tag: 'action',
|
||||
attrs: { epoch: currentEpoch().toString(), type: 'relay' },
|
||||
content: [
|
||||
{
|
||||
tag: 'message',
|
||||
attrs: {},
|
||||
{
|
||||
tag: 'message',
|
||||
attrs: {},
|
||||
content: proto.WebMessageInfo.encode(message).finish()
|
||||
}
|
||||
]
|
||||
@@ -272,9 +272,9 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
|
||||
message.status = WAMessageStatus.PENDING
|
||||
const promise = query({
|
||||
json,
|
||||
binaryTag: [WAMetric.message, flag],
|
||||
tag: mID,
|
||||
json,
|
||||
binaryTag: [WAMetric.message, flag],
|
||||
tag: mID,
|
||||
expect200: true,
|
||||
requiresPhoneConnection: true
|
||||
})
|
||||
@@ -308,7 +308,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
socketEvents.on('CB:action,add:last', json => messagesUpdate(json, true))
|
||||
socketEvents.on('CB:action,add:unread', json => messagesUpdate(json, false))
|
||||
socketEvents.on('CB:action,add:before', json => messagesUpdate(json, false))
|
||||
|
||||
|
||||
// new messages
|
||||
socketEvents.on('CB:action,add:relay,message', (node: BinaryNode) => {
|
||||
const msgs = getBinaryNodeMessages(node)
|
||||
@@ -316,7 +316,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
onMessage(msg, 'notify')
|
||||
}
|
||||
})
|
||||
// If a message has been updated
|
||||
// If a message has been updated
|
||||
// usually called when a video message gets its upload url, or live locations or ciphertext message gets fixed
|
||||
socketEvents.on ('CB:action,add:update,message', (node: BinaryNode) => {
|
||||
const msgs = getBinaryNodeMessages(node)
|
||||
@@ -369,7 +369,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
return
|
||||
}
|
||||
|
||||
const keyPartial = {
|
||||
const keyPartial = {
|
||||
remoteJid: jidNormalizedUser(attributes.to),
|
||||
fromMe: areJidsSameUser(attributes.from, state.legacy?.user?.id || ''),
|
||||
}
|
||||
@@ -413,13 +413,13 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
json: {
|
||||
tag: 'query',
|
||||
attrs: {
|
||||
type: 'message_info',
|
||||
index: messageID,
|
||||
jid: jid,
|
||||
type: 'message_info',
|
||||
index: messageID,
|
||||
jid: jid,
|
||||
epoch: currentEpoch().toString()
|
||||
}
|
||||
},
|
||||
binaryTag: [WAMetric.queryRead, WAFlag.ignore],
|
||||
},
|
||||
binaryTag: [WAMetric.queryRead, WAFlag.ignore],
|
||||
expect200: true,
|
||||
requiresPhoneConnection: true
|
||||
})
|
||||
@@ -427,7 +427,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
if(Array.isArray(content)) {
|
||||
for(const { tag, content: innerData } of content) {
|
||||
const [{ attrs }] = (innerData as BinaryNode[])
|
||||
|
||||
|
||||
const jid = jidNormalizedUser(attrs.jid)
|
||||
const recp = info[jid] || { userJid: jid }
|
||||
const date = +attrs.t
|
||||
@@ -465,14 +465,14 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
|
||||
return stream
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const result = await downloadMediaMessage()
|
||||
return result
|
||||
} catch(error) {
|
||||
if(error.message.includes('404')) { // media needs to be updated
|
||||
logger.info (`updating media of message: ${message.key.id}`)
|
||||
|
||||
|
||||
await updateMediaMessage(message)
|
||||
|
||||
const result = await downloadMediaMessage()
|
||||
@@ -508,13 +508,13 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
page: page.toString(),
|
||||
jid: inJid
|
||||
}
|
||||
},
|
||||
binaryTag: [24, WAFlag.ignore],
|
||||
},
|
||||
binaryTag: [24, WAFlag.ignore],
|
||||
expect200: true
|
||||
}) // encrypt and send off
|
||||
|
||||
return {
|
||||
last: node.attrs?.last === 'true',
|
||||
return {
|
||||
last: node.attrs?.last === 'true',
|
||||
messages: getBinaryNodeMessages(node)
|
||||
}
|
||||
},
|
||||
@@ -531,7 +531,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
isJidGroup(jid)
|
||||
) {
|
||||
const { disappearingMessagesInChat } = content
|
||||
const value = typeof disappearingMessagesInChat === 'boolean' ?
|
||||
const value = typeof disappearingMessagesInChat === 'boolean' ?
|
||||
(disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0) :
|
||||
disappearingMessagesInChat
|
||||
const tag = generateMessageTag(true)
|
||||
@@ -539,8 +539,8 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
{
|
||||
tag: 'group',
|
||||
attrs: { id: tag, jid, type: 'prop', author: userJid },
|
||||
content: [
|
||||
{ tag: 'ephemeral', attrs: { value: value.toString() } }
|
||||
content: [
|
||||
{ tag: 'ephemeral', attrs: { value: value.toString() } }
|
||||
]
|
||||
}
|
||||
])
|
||||
@@ -557,7 +557,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => {
|
||||
...options,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
await relayMessage(msg, { waitForAck: options.waitForAck })
|
||||
return msg
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@ import { BinaryNode, encodeBinaryNodeLegacy } from '../WABinary'
|
||||
* - query phone connection
|
||||
*/
|
||||
export const makeSocket = ({
|
||||
waWebSocketUrl,
|
||||
connectTimeoutMs,
|
||||
phoneResponseTimeMs,
|
||||
logger,
|
||||
agent,
|
||||
waWebSocketUrl,
|
||||
connectTimeoutMs,
|
||||
phoneResponseTimeMs,
|
||||
logger,
|
||||
agent,
|
||||
keepAliveIntervalMs,
|
||||
expectResponseTimeout,
|
||||
expectResponseTimeout,
|
||||
}: LegacySocketConfig) => {
|
||||
// for generating tags
|
||||
const referenceDateSeconds = unixTimestampSeconds(new Date())
|
||||
@@ -53,7 +53,7 @@ export const makeSocket = ({
|
||||
const sendPromise = promisify(ws.send)
|
||||
/** generate message tag and increment epoch */
|
||||
const generateMessageTag = (longTag: boolean = false) => {
|
||||
const tag = `${longTag ? referenceDateSeconds : (referenceDateSeconds%1000)}.--${epoch}`
|
||||
const tag = `${longTag ? referenceDateSeconds : (referenceDateSeconds % 1000)}.--${epoch}`
|
||||
epoch += 1 // increment message count, it makes the 'epoch' field when sending binary messages
|
||||
return tag
|
||||
}
|
||||
@@ -66,7 +66,7 @@ export const makeSocket = ({
|
||||
return sendPromise.call(ws, data) as Promise<void>
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Send a message to the WA servers
|
||||
* @returns the tag attached in the message
|
||||
* */
|
||||
@@ -121,7 +121,7 @@ export const makeSocket = ({
|
||||
|
||||
if(ws.readyState !== ws.CLOSED && ws.readyState !== ws.CLOSING) {
|
||||
try {
|
||||
ws.close()
|
||||
ws.close()
|
||||
} catch{ }
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ export const makeSocket = ({
|
||||
return
|
||||
}
|
||||
//if (this.shouldLogMessages) this.messageLog.push ({ tag: messageTag, json: JSON.stringify(json), fromMe: false })
|
||||
|
||||
|
||||
if(logger.level === 'trace') {
|
||||
logger.trace({ tag: messageTag, fromMe: false, json }, 'communication')
|
||||
}
|
||||
@@ -213,7 +213,7 @@ export const makeSocket = ({
|
||||
|
||||
phoneConnectionChanged(false)
|
||||
}, phoneResponseTimeMs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const clearPhoneCheckInterval = () => {
|
||||
@@ -254,12 +254,12 @@ export const makeSocket = ({
|
||||
}
|
||||
|
||||
cancelToken = () => onErr(new Boom('Cancelled', { statusCode: 500 }))
|
||||
|
||||
|
||||
if(requiresPhoneConnection) {
|
||||
startPhoneCheckInterval()
|
||||
cancelPhoneChecker = exitQueryIfResponseNotExpected(tag, onErr)
|
||||
}
|
||||
|
||||
|
||||
ws.on(`TAG:${tag}`, onRecv)
|
||||
ws.on('ws-close', onErr) // if the socket closes, you'll never receive the message
|
||||
},
|
||||
@@ -268,13 +268,13 @@ export const makeSocket = ({
|
||||
} finally {
|
||||
requiresPhoneConnection && clearPhoneCheckInterval()
|
||||
cancelPhoneChecker && cancelPhoneChecker()
|
||||
|
||||
|
||||
ws.off(`TAG:${tag}`, onRecv)
|
||||
ws.off('ws-close', onErr) // if the socket closes, you'll never receive the message
|
||||
}
|
||||
})(),
|
||||
cancelToken: () => {
|
||||
cancelToken()
|
||||
cancelToken()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,7 +300,7 @@ export const makeSocket = ({
|
||||
// throw back the error
|
||||
throw error
|
||||
}
|
||||
|
||||
|
||||
const response = await promise
|
||||
const responseStatusCode = +(response.status ? response.status : 200) // default status
|
||||
// read here: http://getstatuscode.com/599
|
||||
@@ -308,10 +308,10 @@ export const makeSocket = ({
|
||||
end(new Boom('WA server overloaded', { statusCode: 599, data: { query: json, response } }))
|
||||
}
|
||||
|
||||
if(expect200 && Math.floor(responseStatusCode/100) !== 2) {
|
||||
if(expect200 && Math.floor(responseStatusCode / 100) !== 2) {
|
||||
const message = STATUS_CODES[responseStatusCode] || 'unknown'
|
||||
throw new Boom(
|
||||
`Unexpected status in '${Array.isArray(json) ? json[0] : (json?.tag || 'query')}': ${message}(${responseStatusCode})`,
|
||||
`Unexpected status in '${Array.isArray(json) ? json[0] : (json?.tag || 'query')}': ${message}(${responseStatusCode})`,
|
||||
{ data: { query: json, response }, statusCode: response.status }
|
||||
)
|
||||
}
|
||||
@@ -330,7 +330,7 @@ export const makeSocket = ({
|
||||
check if it's been a suspicious amount of time since the server responded with our last seen
|
||||
it could be that the network is down
|
||||
*/
|
||||
if(diff > keepAliveIntervalMs+5000) {
|
||||
if(diff > keepAliveIntervalMs + 5000) {
|
||||
end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost }))
|
||||
} else if(ws.readyState === ws.OPEN) {
|
||||
sendRawMessage('?,,') // if its all good, send a keep alive request
|
||||
@@ -394,7 +394,7 @@ export const makeSocket = ({
|
||||
}
|
||||
|
||||
end(new Boom(
|
||||
`Connection terminated by server: "${kind || 'unknown'}"`,
|
||||
`Connection terminated by server: "${kind || 'unknown'}"`,
|
||||
{ statusCode: reason }
|
||||
))
|
||||
})
|
||||
@@ -417,12 +417,12 @@ export const makeSocket = ({
|
||||
content: nodes
|
||||
}
|
||||
|
||||
return query({
|
||||
json,
|
||||
binaryTag,
|
||||
tag,
|
||||
expect200: true,
|
||||
requiresPhoneConnection: true
|
||||
return query({
|
||||
json,
|
||||
binaryTag,
|
||||
tag,
|
||||
expect200: true,
|
||||
requiresPhoneConnection: true
|
||||
}) as Promise<{ status: number }>
|
||||
},
|
||||
currentEpoch: () => epoch,
|
||||
|
||||
@@ -11,7 +11,7 @@ const MAX_SYNC_ATTEMPTS = 5
|
||||
export const makeChatsSocket = (config: SocketConfig) => {
|
||||
const { logger } = config
|
||||
const sock = makeMessagesSocket(config)
|
||||
const {
|
||||
const {
|
||||
ev,
|
||||
ws,
|
||||
authState,
|
||||
@@ -95,7 +95,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
|
||||
const fetchStatus = async(jid: string) => {
|
||||
const [result] = await interactiveQuery(
|
||||
[{ tag: 'user', attrs: { jid } }],
|
||||
[{ tag: 'user', attrs: { jid } }],
|
||||
{ tag: 'status', attrs: { } }
|
||||
)
|
||||
if(result) {
|
||||
@@ -130,8 +130,8 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
const result = await query({
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
xmlns: 'blocklist',
|
||||
to: S_WHATSAPP_NET,
|
||||
xmlns: 'blocklist',
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'get'
|
||||
}
|
||||
})
|
||||
@@ -223,7 +223,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
attrs: {
|
||||
type: 'account_sync',
|
||||
timestamp: fromTimestamp.toString(),
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
@@ -238,7 +238,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
await authState.keys.transaction(
|
||||
async() => {
|
||||
const collectionsToHandle = new Set<string>(collections)
|
||||
// in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
|
||||
// in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
|
||||
const attemptsMap = { } as { [T in WAPatchName]: number | undefined }
|
||||
// keep executing till all collections are done
|
||||
// sometimes a single patch request will not return all the patches (God knows why)
|
||||
@@ -265,9 +265,9 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
|
||||
nodes.push({
|
||||
tag: 'collection',
|
||||
attrs: {
|
||||
name,
|
||||
version: state.version.toString(),
|
||||
attrs: {
|
||||
name,
|
||||
version: state.version.toString(),
|
||||
// return snapshot if being synced from scratch
|
||||
return_snapshot: (!state.version).toString()
|
||||
}
|
||||
@@ -289,7 +289,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
const decoded = await extractSyncdPatches(result) // extract from binary node
|
||||
for(const key in decoded) {
|
||||
const name = key as WAPatchName
|
||||
@@ -298,7 +298,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
if(snapshot) {
|
||||
const { state: newState, mutations } = await decodeSyncdSnapshot(name, snapshot, getAppStateSyncKey, initialVersionMap[name])
|
||||
states[name] = newState
|
||||
|
||||
|
||||
logger.info(`restored state of ${name} from snapshot to v${newState.version} with ${mutations.length} mutations`)
|
||||
|
||||
await authState.keys.set({ 'app-state-sync-version': { [name]: newState } })
|
||||
@@ -309,14 +309,14 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
// only process if there are syncd patches
|
||||
if(patches.length) {
|
||||
const { newMutations, state: newState } = await decodePatches(name, patches, states[name], getAppStateSyncKey, initialVersionMap[name])
|
||||
|
||||
|
||||
await authState.keys.set({ 'app-state-sync-version': { [name]: newState } })
|
||||
|
||||
|
||||
logger.info(`synced ${name} to v${newState.version}`)
|
||||
if(newMutations.length) {
|
||||
logger.trace({ newMutations, name }, 'recv new mutations')
|
||||
}
|
||||
|
||||
|
||||
appStateChunk.totalMutations.push(...newMutations)
|
||||
}
|
||||
|
||||
@@ -431,17 +431,17 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
}
|
||||
|
||||
const resyncMainAppState = async() => {
|
||||
|
||||
|
||||
logger.debug('resyncing main app state')
|
||||
|
||||
|
||||
await (
|
||||
mutationMutex.mutex(
|
||||
() => resyncAppState([
|
||||
'critical_block',
|
||||
'critical_unblock_low',
|
||||
'regular_high',
|
||||
'regular_low',
|
||||
'regular'
|
||||
() => resyncAppState([
|
||||
'critical_block',
|
||||
'critical_unblock_low',
|
||||
'regular_high',
|
||||
'regular_low',
|
||||
'regular'
|
||||
])
|
||||
)
|
||||
.catch(err => (
|
||||
@@ -458,7 +458,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
for(const { syncAction: { value: action }, index: [_, id, msgId, fromMe] } of actions) {
|
||||
const update: Partial<Chat> = { id }
|
||||
if(action?.muteAction) {
|
||||
update.mute = action.muteAction?.muted ?
|
||||
update.mute = action.muteAction?.muted ?
|
||||
toNumber(action.muteAction!.muteEndTimestamp!) :
|
||||
undefined
|
||||
} else if(action?.archiveChatAction) {
|
||||
@@ -546,7 +546,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
tag: 'collection',
|
||||
attrs: {
|
||||
name,
|
||||
version: (state.version-1).toString(),
|
||||
version: (state.version - 1).toString(),
|
||||
return_snapshot: 'false'
|
||||
},
|
||||
content: [
|
||||
@@ -562,9 +562,9 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
]
|
||||
}
|
||||
await query(node)
|
||||
|
||||
|
||||
await authState.keys.set({ 'app-state-sync-version': { [name]: state } })
|
||||
|
||||
|
||||
if(config.emitOwnEvents) {
|
||||
const result = await decodePatches(name, [{ ...patch, version: { version: state.version }, }], initial, getAppStateSyncKey)
|
||||
processSyncActions(result.newMutations)
|
||||
@@ -589,7 +589,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
})
|
||||
|
||||
const propsNode = getBinaryNodeChild(abtNode, 'props')
|
||||
|
||||
|
||||
let props: { [_: string]: string } = { }
|
||||
if(propsNode) {
|
||||
props = reduceBinaryNodeToDictionary(propsNode, 'prop')
|
||||
@@ -616,7 +616,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
})
|
||||
|
||||
const propsNode = getBinaryNodeChild(resultNode, 'props')
|
||||
|
||||
|
||||
let props: { [_: string]: string } = { }
|
||||
if(propsNode) {
|
||||
props = reduceBinaryNodeToDictionary(propsNode, 'prop')
|
||||
@@ -651,7 +651,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
}
|
||||
|
||||
lastAccountSyncTimestamp = +attrs.timestamp
|
||||
ev.emit('creds.update', { lastAccountSyncTimestamp })
|
||||
ev.emit('creds.update', { lastAccountSyncTimestamp })
|
||||
break
|
||||
default:
|
||||
logger.info({ node }, 'received unknown sync')
|
||||
@@ -683,7 +683,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
})
|
||||
|
||||
return {
|
||||
...sock,
|
||||
...sock,
|
||||
appPatch,
|
||||
sendPresenceUpdate,
|
||||
presenceSubscribe,
|
||||
|
||||
@@ -21,15 +21,15 @@ export const makeGroupsSocket = (config: SocketConfig) => {
|
||||
|
||||
const groupMetadata = async(jid: string) => {
|
||||
const result = await groupQuery(
|
||||
jid,
|
||||
'get',
|
||||
jid,
|
||||
'get',
|
||||
[ { tag: 'query', attrs: { request: 'interactive' } } ]
|
||||
)
|
||||
return extractGroupMetadata(result)
|
||||
}
|
||||
|
||||
return {
|
||||
...sock,
|
||||
...sock,
|
||||
groupMetadata,
|
||||
groupCreate: async(subject: string, participants: string[]) => {
|
||||
const key = generateMessageID()
|
||||
@@ -86,7 +86,7 @@ export const makeGroupsSocket = (config: SocketConfig) => {
|
||||
action: ParticipantAction
|
||||
) => {
|
||||
const result = await groupQuery(
|
||||
jid,
|
||||
jid,
|
||||
'set',
|
||||
participants.map(
|
||||
jid => ({
|
||||
@@ -135,7 +135,7 @@ export const makeGroupsSocket = (config: SocketConfig) => {
|
||||
return result.attrs.jid
|
||||
},
|
||||
groupToggleEphemeral: async(jid: string, ephemeralExpiration: number) => {
|
||||
const content: BinaryNode = ephemeralExpiration ?
|
||||
const content: BinaryNode = ephemeralExpiration ?
|
||||
{ tag: 'ephemeral', attrs: { ephemeral: ephemeralExpiration.toString() } } :
|
||||
{ tag: 'not_ephemeral', attrs: { } }
|
||||
await groupQuery(jid, 'set', [content])
|
||||
|
||||
@@ -19,14 +19,14 @@ const getStatusFromReceiptType = (type: string | undefined) => {
|
||||
if(typeof type === 'undefined') {
|
||||
return proto.WebMessageInfo.WebMessageInfoStatus.DELIVERY_ACK
|
||||
}
|
||||
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
const { logger } = config
|
||||
const sock = makeChatsSocket(config)
|
||||
const {
|
||||
const {
|
||||
ev,
|
||||
authState,
|
||||
ws,
|
||||
@@ -74,11 +74,11 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
return
|
||||
}
|
||||
|
||||
msgRetryMap[msgId] = retryCount+1
|
||||
msgRetryMap[msgId] = retryCount + 1
|
||||
|
||||
const isGroup = !!node.attrs.participant
|
||||
const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds
|
||||
|
||||
|
||||
const deviceIdentity = proto.ADVSignedDeviceIdentity.encode(account).finish()
|
||||
await assertingPreKeys(1, async preKeys => {
|
||||
const [keyId] = Object.keys(preKeys)
|
||||
@@ -93,14 +93,14 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
to: isGroup ? node.attrs.from : jidEncode(decFrom!.user, 's.whatsapp.net', decFrom!.device, 0)
|
||||
},
|
||||
content: [
|
||||
{
|
||||
tag: 'retry',
|
||||
attrs: {
|
||||
count: retryCount.toString(),
|
||||
{
|
||||
tag: 'retry',
|
||||
attrs: {
|
||||
count: retryCount.toString(),
|
||||
id: node.attrs.id,
|
||||
t: node.attrs.t,
|
||||
v: '1'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
tag: 'registration',
|
||||
@@ -145,10 +145,10 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
switch (protocolMsg.type) {
|
||||
case proto.ProtocolMessage.ProtocolMessageType.HISTORY_SYNC_NOTIFICATION:
|
||||
const histNotification = protocolMsg!.historySyncNotification
|
||||
|
||||
|
||||
logger.info({ histNotification, id: message.key.id }, 'got history notification')
|
||||
const { chats, contacts, messages, isLatest } = await downloadAndProcessHistorySyncNotification(histNotification, historyCache)
|
||||
|
||||
|
||||
const meJid = authState.creds.me!.id
|
||||
await sendNode({
|
||||
tag: 'receipt',
|
||||
@@ -178,15 +178,15 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
let newAppStateSyncKeyId = ''
|
||||
for(const { keyData, keyId } of keys) {
|
||||
const strKeyId = Buffer.from(keyId.keyId!).toString('base64')
|
||||
|
||||
|
||||
logger.info({ strKeyId }, 'injecting new app state sync key')
|
||||
await authState.keys.set({ 'app-state-sync-key': { [strKeyId]: keyData } })
|
||||
|
||||
|
||||
newAppStateSyncKeyId = strKeyId
|
||||
}
|
||||
|
||||
|
||||
ev.emit('creds.update', { myAppStateKeyId: newAppStateSyncKeyId })
|
||||
|
||||
|
||||
resyncMainAppState()
|
||||
} else {
|
||||
[
|
||||
@@ -197,12 +197,12 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
break
|
||||
case proto.ProtocolMessage.ProtocolMessageType.REVOKE:
|
||||
ev.emit('messages.update', [
|
||||
{
|
||||
{
|
||||
key: {
|
||||
...message.key,
|
||||
id: protocolMsg.key!.id
|
||||
},
|
||||
update: { message: null, messageStubType: WAMessageStubType.REVOKE, key: message.key }
|
||||
},
|
||||
update: { message: null, messageStubType: WAMessageStubType.REVOKE, key: message.key }
|
||||
}
|
||||
])
|
||||
break
|
||||
@@ -299,7 +299,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
|
||||
const participants = getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid)
|
||||
if(
|
||||
participants.length === 1 &&
|
||||
participants.length === 1 &&
|
||||
// if recv. "remove" message and sender removed themselves
|
||||
// mark as left
|
||||
areJidsSameUser(participants[0], node.attrs.participant) &&
|
||||
@@ -324,7 +324,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
result.messageStubType = WAMessageStubType.GROUP_CHANGE_RESTRICT
|
||||
result.messageStubParameters = [ (child.tag === 'locked') ? 'on' : 'off' ]
|
||||
break
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
switch (child.tag) {
|
||||
@@ -354,7 +354,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
// message failed to decrypt
|
||||
if(msg.messageStubType === proto.WebMessageInfo.WebMessageInfoStubType.CIPHERTEXT) {
|
||||
logger.error(
|
||||
{ msgId: msg.key.id, params: msg.messageStubParameters },
|
||||
{ msgId: msg.key.id, params: msg.messageStubParameters },
|
||||
'failure in decrypting message'
|
||||
)
|
||||
retryMutex.mutex(
|
||||
@@ -366,7 +366,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
await sendReceipt(msg.key.remoteJid!, msg.key.participant, [msg.key.id!], undefined)
|
||||
logger.debug({ msg: msg.key }, 'sent delivery receipt')
|
||||
}
|
||||
|
||||
|
||||
msg.key.remoteJid = jidNormalizedUser(msg.key.remoteJid!)
|
||||
ev.emit('messages.upsert', { messages: [msg], type: stanza.attrs.offline ? 'append' : 'notify' })
|
||||
}
|
||||
@@ -470,7 +470,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
)
|
||||
} else {
|
||||
ev.emit(
|
||||
'messages.update',
|
||||
'messages.update',
|
||||
ids.map(id => ({
|
||||
key: { ...key, id },
|
||||
update: { status }
|
||||
@@ -521,7 +521,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
...(msg.key || {})
|
||||
}
|
||||
msg.messageTimestamp = +node.attrs.t
|
||||
|
||||
|
||||
const fullMsg = proto.WebMessageInfo.fromObject(msg)
|
||||
ev.emit('messages.upsert', { messages: [fullMsg], type: 'append' })
|
||||
}
|
||||
@@ -549,7 +549,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
'p-' + chat.id!,
|
||||
() => processMessage(msg, chat)
|
||||
)
|
||||
|
||||
|
||||
if(!!msg.message && !msg.message!.protocolMessage) {
|
||||
chat.conversationTimestamp = toNumber(msg.messageTimestamp)
|
||||
if(!msg.key.fromMe) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { makeGroupsSocket } from './groups'
|
||||
export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
const { logger } = config
|
||||
const sock = makeGroupsSocket(config)
|
||||
const {
|
||||
const {
|
||||
ev,
|
||||
authState,
|
||||
query,
|
||||
@@ -31,8 +31,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
const { content } = await query({
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
xmlns: 'privacy',
|
||||
to: S_WHATSAPP_NET,
|
||||
xmlns: 'privacy',
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'get'
|
||||
},
|
||||
content: [
|
||||
@@ -48,7 +48,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
let mediaConn: Promise<MediaConnInfo>
|
||||
const refreshMediaConn = async(forceGet = false) => {
|
||||
const media = await mediaConn
|
||||
if(!media || forceGet || (new Date().getTime()-media.fetchDate.getTime()) > media.ttl*1000) {
|
||||
if(!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) {
|
||||
mediaConn = (async() => {
|
||||
const result = await query({
|
||||
tag: 'iq',
|
||||
@@ -77,7 +77,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* generic send receipt function
|
||||
* generic send receipt function
|
||||
* used for receipts of phone call, read, delivery etc.
|
||||
* */
|
||||
const sendReceipt = async(jid: string, participant: string | undefined, messageIds: string[], type: 'read' | 'read-self' | undefined) => {
|
||||
@@ -124,7 +124,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
|
||||
const getUSyncDevices = async(jids: string[], ignoreZeroDevices: boolean) => {
|
||||
const deviceResults: JidWithDevice[] = []
|
||||
|
||||
|
||||
const users: BinaryNode[] = []
|
||||
jids = Array.from(new Set(jids))
|
||||
for(let jid of jids) {
|
||||
@@ -162,8 +162,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
tag: 'query',
|
||||
attrs: { },
|
||||
content: [
|
||||
{
|
||||
tag: 'devices',
|
||||
{
|
||||
tag: 'devices',
|
||||
attrs: { version: '2' }
|
||||
}
|
||||
]
|
||||
@@ -194,7 +194,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
const assertSessions = async(jids: string[], force: boolean) => {
|
||||
let jidsRequiringFetch: string[] = []
|
||||
if(force) {
|
||||
jidsRequiringFetch = jids
|
||||
jidsRequiringFetch = jids
|
||||
} else {
|
||||
const addrs = jids.map(jid => jidToSignalProtocolAddress(jid).toString())
|
||||
const sessions = await authState.keys.get('session', addrs)
|
||||
@@ -240,11 +240,11 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
|
||||
if(authState.keys.isInTransaction()) {
|
||||
await authState.keys.prefetch(
|
||||
'session',
|
||||
'session',
|
||||
jids.map(jid => jidToSignalProtocolAddress(jid).toString())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const nodes = await Promise.all(
|
||||
jids.map(
|
||||
async jid => {
|
||||
@@ -266,8 +266,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
}
|
||||
|
||||
const relayMessage = async(
|
||||
jid: string,
|
||||
message: proto.IMessage,
|
||||
jid: string,
|
||||
message: proto.IMessage,
|
||||
{ messageId: msgId, participant, additionalAttributes, cachedGroupMetadata }: MessageRelayOptions
|
||||
) => {
|
||||
const meId = authState.creds.me!.id
|
||||
@@ -293,10 +293,10 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
async() => {
|
||||
if(isGroup) {
|
||||
const { ciphertext, senderKeyDistributionMessageKey } = await encryptSenderKeyMsgSignalProto(destinationJid, encodedMsg, meId, authState)
|
||||
|
||||
|
||||
const [groupData, senderKeyMap] = await Promise.all([
|
||||
(async() => {
|
||||
let groupData = cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
|
||||
let groupData = cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
|
||||
if(!groupData) {
|
||||
groupData = await groupMetadata(jid)
|
||||
}
|
||||
@@ -308,13 +308,13 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
return result[jid] || { }
|
||||
})()
|
||||
])
|
||||
|
||||
|
||||
if(!participant) {
|
||||
const participantsList = groupData.participants.map(p => p.id)
|
||||
const additionalDevices = await getUSyncDevices(participantsList, false)
|
||||
devices.push(...additionalDevices)
|
||||
}
|
||||
|
||||
|
||||
const senderKeyJids: string[] = []
|
||||
// ensure a connection is established with every device
|
||||
for(const { user, device } of devices) {
|
||||
@@ -330,44 +330,44 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
// if there are, we re-send the senderkey
|
||||
if(senderKeyJids.length) {
|
||||
logger.debug({ senderKeyJids }, 'sending new sender key')
|
||||
|
||||
|
||||
const encSenderKeyMsg = encodeWAMessage({
|
||||
senderKeyDistributionMessage: {
|
||||
axolotlSenderKeyDistributionMessage: senderKeyDistributionMessageKey,
|
||||
groupId: destinationJid
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
participants.push(
|
||||
...(await createParticipantNodes(senderKeyJids, encSenderKeyMsg))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
binaryNodeContent.push({
|
||||
tag: 'enc',
|
||||
attrs: { v: '2', type: 'skmsg' },
|
||||
content: ciphertext
|
||||
})
|
||||
|
||||
|
||||
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } })
|
||||
} else {
|
||||
const { user: meUser } = jidDecode(meId)
|
||||
|
||||
|
||||
const encodedMeMsg = encodeWAMessage({
|
||||
deviceSentMessage: {
|
||||
destinationJid,
|
||||
message
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
if(!participant) {
|
||||
devices.push({ user })
|
||||
devices.push({ user: meUser })
|
||||
|
||||
|
||||
const additionalDevices = await getUSyncDevices([ meId, jid ], true)
|
||||
devices.push(...additionalDevices)
|
||||
}
|
||||
|
||||
|
||||
const meJids: string[] = []
|
||||
const otherJids: string[] = []
|
||||
for(const { user, device } of devices) {
|
||||
@@ -379,7 +379,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
otherJids.push(jid)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const [meNodes, otherNodes] = await Promise.all([
|
||||
createParticipantNodes(meJids, encodedMeMsg),
|
||||
createParticipantNodes(otherJids, encodedMsg)
|
||||
@@ -387,7 +387,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
participants.push(...meNodes)
|
||||
participants.push(...otherNodes)
|
||||
}
|
||||
|
||||
|
||||
if(participants.length) {
|
||||
binaryNodeContent.push({
|
||||
tag: 'participants',
|
||||
@@ -395,7 +395,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
content: participants
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const stanza: BinaryNode = {
|
||||
tag: 'message',
|
||||
attrs: {
|
||||
@@ -406,32 +406,32 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
},
|
||||
content: binaryNodeContent
|
||||
}
|
||||
|
||||
|
||||
const shouldHaveIdentity = !!participants.find(
|
||||
participant => (participant.content! as BinaryNode[]).find(n => n.attrs.type === 'pkmsg')
|
||||
)
|
||||
|
||||
|
||||
if(shouldHaveIdentity) {
|
||||
(stanza.content as BinaryNode[]).push({
|
||||
tag: 'device-identity',
|
||||
attrs: { },
|
||||
content: proto.ADVSignedDeviceIdentity.encode(authState.creds.account).finish()
|
||||
})
|
||||
|
||||
|
||||
logger.debug({ jid }, 'adding device identity')
|
||||
}
|
||||
|
||||
|
||||
logger.debug({ msgId }, `sending message to ${participants.length} devices`)
|
||||
|
||||
|
||||
await sendNode(stanza)
|
||||
}
|
||||
)
|
||||
|
||||
return msgId
|
||||
}
|
||||
}
|
||||
|
||||
const waUploadToServer = getWAUploadToServer(config, refreshMediaConn)
|
||||
|
||||
|
||||
return {
|
||||
...sock,
|
||||
assertSessions,
|
||||
@@ -454,7 +454,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
isJidGroup(jid)
|
||||
) {
|
||||
const { disappearingMessagesInChat } = content
|
||||
const value = typeof disappearingMessagesInChat === 'boolean' ?
|
||||
const value = typeof disappearingMessagesInChat === 'boolean' ?
|
||||
(disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0) :
|
||||
disappearingMessagesInChat
|
||||
await groupToggleEphemeral(jid, value)
|
||||
|
||||
@@ -16,10 +16,10 @@ import { assertNodeErrorFree, BinaryNode, encodeBinaryNode, getBinaryNodeChild,
|
||||
* - query phone connection
|
||||
*/
|
||||
export const makeSocket = ({
|
||||
waWebSocketUrl,
|
||||
connectTimeoutMs,
|
||||
logger,
|
||||
agent,
|
||||
waWebSocketUrl,
|
||||
connectTimeoutMs,
|
||||
logger,
|
||||
agent,
|
||||
keepAliveIntervalMs,
|
||||
version,
|
||||
browser,
|
||||
@@ -58,7 +58,7 @@ export const makeSocket = ({
|
||||
}
|
||||
|
||||
const { creds } = authState
|
||||
|
||||
|
||||
let lastDateRecv: Date
|
||||
let epoch = 0
|
||||
let keepAliveReq: NodeJS.Timeout
|
||||
@@ -129,7 +129,7 @@ export const makeSocket = ({
|
||||
onErr = err => {
|
||||
reject(err || new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed }))
|
||||
}
|
||||
|
||||
|
||||
ws.on(`TAG:${msgId}`, onRecv)
|
||||
ws.on('close', onErr) // if the socket closes, you'll never receive the message
|
||||
ws.off('error', onErr)
|
||||
@@ -203,10 +203,10 @@ export const makeSocket = ({
|
||||
/** get some pre-keys and do something with them */
|
||||
const assertingPreKeys = async(range: number, execute: (keys: { [_: number]: any }) => Promise<void>) => {
|
||||
const { newPreKeys, lastPreKeyId, preKeysRange } = generateOrGetPreKeys(authState.creds, range)
|
||||
|
||||
|
||||
const update: Partial<AuthenticationCreds> = {
|
||||
nextPreKeyId: Math.max(lastPreKeyId+1, creds.nextPreKeyId),
|
||||
firstUnuploadedPreKeyId: Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId+1)
|
||||
nextPreKeyId: Math.max(lastPreKeyId + 1, creds.nextPreKeyId),
|
||||
firstUnuploadedPreKeyId: Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId + 1)
|
||||
}
|
||||
if(!creds.serverHasPreKeys) {
|
||||
update.serverHasPreKeys = true
|
||||
@@ -255,7 +255,7 @@ export const makeSocket = ({
|
||||
if(logger.level === 'trace') {
|
||||
logger.trace({ msgId, fromMe: false, frame }, 'communication')
|
||||
}
|
||||
|
||||
|
||||
let anyTriggered = false
|
||||
/* Check if this is a response to a message we sent */
|
||||
anyTriggered = ws.emit(`${DEF_TAG_PREFIX}${msgId}`, frame)
|
||||
@@ -263,7 +263,7 @@ export const makeSocket = ({
|
||||
const l0 = frame.tag
|
||||
const l1 = frame.attrs || { }
|
||||
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : ''
|
||||
|
||||
|
||||
Object.keys(l1).forEach(key => {
|
||||
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) || anyTriggered
|
||||
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) || anyTriggered
|
||||
@@ -272,7 +272,7 @@ export const makeSocket = ({
|
||||
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered
|
||||
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered
|
||||
anyTriggered = ws.emit('frame', frame) || anyTriggered
|
||||
|
||||
|
||||
if(!anyTriggered && logger.level === 'debug') {
|
||||
logger.debug({ unhandled: true, msgId, fromMe: false, frame }, 'communication recv')
|
||||
}
|
||||
@@ -293,16 +293,16 @@ export const makeSocket = ({
|
||||
|
||||
if(ws.readyState !== ws.CLOSED && ws.readyState !== ws.CLOSING) {
|
||||
try {
|
||||
ws.close()
|
||||
ws.close()
|
||||
} catch{ }
|
||||
}
|
||||
|
||||
ev.emit('connection.update', {
|
||||
connection: 'close',
|
||||
ev.emit('connection.update', {
|
||||
connection: 'close',
|
||||
lastDisconnect: {
|
||||
error,
|
||||
date: new Date()
|
||||
}
|
||||
}
|
||||
})
|
||||
ev.removeAllListeners('connection.update')
|
||||
}
|
||||
@@ -343,7 +343,7 @@ export const makeSocket = ({
|
||||
check if it's been a suspicious amount of time since the server responded with our last seen
|
||||
it could be that the network is down
|
||||
*/
|
||||
if(diff > keepAliveIntervalMs+5000) {
|
||||
if(diff > keepAliveIntervalMs + 5000) {
|
||||
end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost }))
|
||||
} else if(ws.readyState === ws.OPEN) {
|
||||
// if its all good, send a keep alive request
|
||||
@@ -422,8 +422,8 @@ export const makeSocket = ({
|
||||
})
|
||||
// QR gen
|
||||
ws.on('CB:iq,type:set,pair-device', async(stanza: BinaryNode) => {
|
||||
const iq: BinaryNode = {
|
||||
tag: 'iq',
|
||||
const iq: BinaryNode = {
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'result',
|
||||
@@ -446,7 +446,7 @@ export const makeSocket = ({
|
||||
}
|
||||
|
||||
const qr = [ref, noiseKeyB64, identityKeyB64, advB64].join(',')
|
||||
|
||||
|
||||
ev.emit('connection.update', { qr })
|
||||
|
||||
qrTimer = setTimeout(genPairQR, qrMs)
|
||||
@@ -498,10 +498,10 @@ export const makeSocket = ({
|
||||
|
||||
ev.emit('connection.update', { connection: 'open' })
|
||||
})
|
||||
|
||||
|
||||
ws.on('CB:ib,,offline', (node: BinaryNode) => {
|
||||
const child = getBinaryNodeChild(node, 'offline')
|
||||
const offlineCount = +child.attrs.count
|
||||
const offlineCount = +child.attrs.count
|
||||
|
||||
logger.info(`got ${offlineCount} offline messages/notifications`)
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ type LegacyWASocket = ReturnType<typeof makeLegacySocket>
|
||||
type AnyWASocket = ReturnType<typeof makeMDSocket>
|
||||
|
||||
export const waChatKey = (pin: boolean) => ({
|
||||
key: (c: Chat) => (pin ? (c.pin ? '1' : '0') : '') + (c.archive ? '0' : '1') + (c.conversationTimestamp ? c.conversationTimestamp.toString(16).padStart(8, '0') :'') + c.id,
|
||||
key: (c: Chat) => (pin ? (c.pin ? '1' : '0') : '') + (c.archive ? '0' : '1') + (c.conversationTimestamp ? c.conversationTimestamp.toString(16).padStart(8, '0') : '') + c.id,
|
||||
compare: (k1: string, k2: string) => k2.localeCompare (k1)
|
||||
})
|
||||
|
||||
@@ -33,7 +33,7 @@ export default (
|
||||
logger = logger || DEFAULT_CONNECTION_CONFIG.logger.child({ stream: 'in-mem-store' })
|
||||
chatKey = chatKey || waChatKey(true)
|
||||
const KeyedDB = require('@adiwajshing/keyed-db').default as new (...args: any[]) => KeyedDB<Chat, string>
|
||||
|
||||
|
||||
const chats = new KeyedDB(chatKey, c => c.id)
|
||||
const messages: { [_: string]: ReturnType<typeof makeMessagesDictionary> } = { }
|
||||
const contacts: { [_: string]: Contact } = { }
|
||||
@@ -54,7 +54,7 @@ export default (
|
||||
for(const contact of newContacts) {
|
||||
oldContacts.delete(contact.id)
|
||||
contacts[contact.id] = Object.assign(
|
||||
contacts[contact.id] || {},
|
||||
contacts[contact.id] || {},
|
||||
contact
|
||||
)
|
||||
}
|
||||
@@ -63,7 +63,7 @@ export default (
|
||||
}
|
||||
|
||||
/**
|
||||
* binds to a BaileysEventEmitter.
|
||||
* binds to a BaileysEventEmitter.
|
||||
* It listens to all events and constructs a state that you can query accurate data from.
|
||||
* Eg. can use the store to fetch chats, contacts, messages etc.
|
||||
* @param ev typically the event emitter from the socket connection
|
||||
@@ -76,7 +76,7 @@ export default (
|
||||
if(isLatest) {
|
||||
chats.clear()
|
||||
}
|
||||
|
||||
|
||||
const chatsAdded = chats.insertIfAbsent(...newChats).length
|
||||
logger.debug({ chatsAdded }, 'synced chats')
|
||||
})
|
||||
@@ -150,12 +150,12 @@ export default (
|
||||
|
||||
if(type === 'notify') {
|
||||
if(!chats.get(jid)) {
|
||||
ev.emit('chats.upsert', [
|
||||
{
|
||||
id: jid,
|
||||
conversationTimestamp: toNumber(msg.messageTimestamp),
|
||||
unreadCount: 1
|
||||
}
|
||||
ev.emit('chats.upsert', [
|
||||
{
|
||||
id: jid,
|
||||
conversationTimestamp: toNumber(msg.messageTimestamp),
|
||||
unreadCount: 1
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -268,7 +268,7 @@ export default (
|
||||
const mode = !cursor || 'before' in cursor ? 'before' : 'after'
|
||||
const cursorKey = !!cursor ? ('before' in cursor ? cursor.before : cursor.after) : undefined
|
||||
const cursorValue = cursorKey ? list.get(cursorKey.id) : undefined
|
||||
|
||||
|
||||
let messages: WAMessage[]
|
||||
if(list && mode === 'before' && (!cursorKey || cursorValue)) {
|
||||
if(cursorValue) {
|
||||
@@ -286,7 +286,7 @@ export default (
|
||||
const cursor = { before: fMessage?.key || cursorKey }
|
||||
const extra = await retrieve (diff, cursor)
|
||||
// add to DB
|
||||
for(let i = extra.length-1; i >= 0;i--) {
|
||||
for(let i = extra.length - 1; i >= 0;i--) {
|
||||
list.upsert(extra[i], 'prepend')
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ function makeOrderedDictionary<T>(idGetter: (item: T) => string) {
|
||||
dict[id] = item
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const remove = (item: T) => {
|
||||
const id = idGetter(item)
|
||||
const idx = array.findIndex(i => idGetter(i) === id)
|
||||
@@ -62,7 +62,7 @@ function makeOrderedDictionary<T>(idGetter: (item: T) => string) {
|
||||
clear: () => {
|
||||
array.splice(0, array.length)
|
||||
Object.keys(dict).forEach(key => {
|
||||
delete dict[key]
|
||||
delete dict[key]
|
||||
})
|
||||
},
|
||||
filter: (contain: (item: T) => boolean) => {
|
||||
|
||||
@@ -39,7 +39,7 @@ describe('Media Download Tests', () => {
|
||||
it('should download a full encrypted media correctly', async() => {
|
||||
for(const { type, message, plaintext } of TEST_VECTORS) {
|
||||
const readPipe = await downloadContentFromMessage(message, type)
|
||||
|
||||
|
||||
let buffer = Buffer.alloc(0)
|
||||
for await (const read of readPipe) {
|
||||
buffer = Buffer.concat([ buffer, read ])
|
||||
@@ -53,13 +53,13 @@ describe('Media Download Tests', () => {
|
||||
for(const { type, message, plaintext } of TEST_VECTORS) {
|
||||
// check all edge cases
|
||||
const ranges = [
|
||||
{ startByte: 51, endByte: plaintext.length-100 }, // random numbers
|
||||
{ startByte: 51, endByte: plaintext.length - 100 }, // random numbers
|
||||
{ startByte: 1024, endByte: 2038 }, // larger random multiples of 16
|
||||
{ startByte: 1, endByte: plaintext.length-1 } // borders
|
||||
{ startByte: 1, endByte: plaintext.length - 1 } // borders
|
||||
]
|
||||
for(const range of ranges) {
|
||||
const readPipe = await downloadContentFromMessage(message, type, range)
|
||||
|
||||
|
||||
let buffer = Buffer.alloc(0)
|
||||
for await (const read of readPipe) {
|
||||
buffer = Buffer.concat([ buffer, read ])
|
||||
|
||||
@@ -13,10 +13,10 @@ export type SignalIdentity = {
|
||||
identifierKey: Uint8Array
|
||||
}
|
||||
|
||||
export type LTHashState = {
|
||||
export type LTHashState = {
|
||||
version: number
|
||||
hash: Buffer
|
||||
indexValueMap: {
|
||||
indexValueMap: {
|
||||
[indexMacBase64: string]: { valueMac: Uint8Array | Buffer }
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ export type SignalCreds = {
|
||||
export type AuthenticationCreds = SignalCreds & {
|
||||
readonly noiseKey: KeyPair
|
||||
readonly advSecretKey: string
|
||||
|
||||
|
||||
me?: Contact
|
||||
account?: proto.IADVSignedDeviceIdentity
|
||||
signalIdentities?: SignalIdentity[]
|
||||
|
||||
@@ -10,7 +10,7 @@ export interface PresenceData {
|
||||
lastSeen?: number
|
||||
}
|
||||
|
||||
export type ChatMutation = {
|
||||
export type ChatMutation = {
|
||||
syncAction: proto.ISyncActionData
|
||||
index: string[]
|
||||
}
|
||||
@@ -32,14 +32,14 @@ export type Chat = Omit<proto.IConversation, 'messages'> & {
|
||||
pin?: number | null
|
||||
archive?: boolean
|
||||
}
|
||||
/**
|
||||
/**
|
||||
* the last messages in a chat, sorted reverse-chronologically
|
||||
* for MD modifications, the last message in the array must be the last message recv in the chat
|
||||
* */
|
||||
export type LastMessageList = Pick<proto.IWebMessageInfo, 'key' | 'messageTimestamp'>[]
|
||||
|
||||
export type ChatModification =
|
||||
{
|
||||
export type ChatModification =
|
||||
{
|
||||
archive: boolean
|
||||
lastMessages: LastMessageList
|
||||
} |
|
||||
@@ -54,11 +54,11 @@ export type ChatModification =
|
||||
clear: 'all' | { messages: {id: string, fromMe?: boolean}[] }
|
||||
} |
|
||||
{
|
||||
star: {
|
||||
star: {
|
||||
messages: { id: string, fromMe?: boolean }[],
|
||||
star: boolean
|
||||
}
|
||||
} |
|
||||
} |
|
||||
{
|
||||
markRead: boolean
|
||||
lastMessages: LastMessageList
|
||||
|
||||
@@ -24,15 +24,15 @@ export type BaileysEventMap<T> = {
|
||||
/** delete chats with given ID */
|
||||
'chats.delete': string[]
|
||||
/** presence of contact in a chat updated */
|
||||
'presence.update': { id: string, presences: { [participant: string]: PresenceData } }
|
||||
'presence.update': { id: string, presences: { [participant: string]: PresenceData } }
|
||||
|
||||
'contacts.upsert': Contact[]
|
||||
'contacts.update': Partial<Contact>[]
|
||||
|
||||
'contacts.update': Partial<Contact>[]
|
||||
|
||||
'messages.delete': { keys: WAMessageKey[] } | { jid: string, all: true }
|
||||
'messages.update': WAMessageUpdate[]
|
||||
/**
|
||||
* add/update the given messages. If they were received while the connection was online,
|
||||
/**
|
||||
* add/update the given messages. If they were received while the connection was online,
|
||||
* the update will have type: "notify"
|
||||
* */
|
||||
'messages.upsert': { messages: WAMessage[], type: MessageUpdateType }
|
||||
|
||||
@@ -25,7 +25,7 @@ export type MessageType = keyof proto.Message
|
||||
export type DownloadableMessage = { mediaKey?: Uint8Array, directPath?: string, url?: string }
|
||||
|
||||
export type MediaConnInfo = {
|
||||
auth: string
|
||||
auth: string
|
||||
ttl: number
|
||||
hosts: { hostname: string, maxContentLengthBytes: number }[]
|
||||
fetchDate: Date
|
||||
@@ -77,7 +77,7 @@ export type AnyMediaMessageContent = (
|
||||
image: WAMediaUpload
|
||||
caption?: string
|
||||
jpegThumbnail?: string
|
||||
} & Mentionable & Buttonable & Templatable & WithDimensions) |
|
||||
} & Mentionable & Buttonable & Templatable & WithDimensions) |
|
||||
({
|
||||
video: WAMediaUpload
|
||||
caption?: string
|
||||
@@ -95,22 +95,22 @@ export type AnyMediaMessageContent = (
|
||||
document: WAMediaUpload
|
||||
mimetype: string
|
||||
fileName?: string
|
||||
} & Buttonable & Templatable)) &
|
||||
} & Buttonable & Templatable)) &
|
||||
{ mimetype?: string }
|
||||
|
||||
export type AnyRegularMessageContent = (
|
||||
({
|
||||
text: string
|
||||
}
|
||||
& Mentionable & Buttonable & Templatable & Listable) |
|
||||
AnyMediaMessageContent |
|
||||
}
|
||||
& Mentionable & Buttonable & Templatable & Listable) |
|
||||
AnyMediaMessageContent |
|
||||
{
|
||||
contacts: {
|
||||
displayName?: string
|
||||
contacts: proto.IContactMessage[]
|
||||
}
|
||||
} |
|
||||
{
|
||||
} |
|
||||
{
|
||||
location: WALocationMessage
|
||||
}
|
||||
) & ViewOnce
|
||||
@@ -120,7 +120,7 @@ export type AnyMessageContent = AnyRegularMessageContent | {
|
||||
force?: boolean
|
||||
} | {
|
||||
delete: WAMessageKey
|
||||
} | {
|
||||
} | {
|
||||
disappearingMessagesInChat: boolean | number
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
export type CatalogResult = {
|
||||
data: {
|
||||
paging: { cursors: { before: string, after: string } },
|
||||
data: any[]
|
||||
data: {
|
||||
paging: { cursors: { before: string, after: string } },
|
||||
data: any[]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export type CommonSocketConfig<T> = {
|
||||
/** provide an auth state object to maintain the auth state */
|
||||
auth?: T
|
||||
/** the WS url to connect to WA */
|
||||
waWebSocketUrl: string | URL
|
||||
waWebSocketUrl: string | URL
|
||||
/** Fails the connection if the socket times out in this interval */
|
||||
connectTimeoutMs: number
|
||||
/** Default timeout for queries, undefined for no timeout */
|
||||
|
||||
@@ -21,5 +21,5 @@ export type ConnectionState = {
|
||||
phoneConnected: boolean
|
||||
user?: Contact
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -19,8 +19,8 @@ export type SocketConfig = CommonSocketConfig<AuthenticationState> & {
|
||||
userDevicesCache?: NodeCache
|
||||
/** map to store the retry counts for failed messages */
|
||||
msgRetryCounterMap?: { [msgId: string]: number }
|
||||
/**
|
||||
* fetch a message from your store
|
||||
/**
|
||||
* fetch a message from your store
|
||||
* implement this so that messages failed to send (solves the "this message can take a while" issue) can be retried
|
||||
* */
|
||||
getMessage: (key: proto.IMessageKey) => Promise<proto.IMessage | undefined>
|
||||
@@ -53,15 +53,15 @@ export type WABusinessHoursConfig = {
|
||||
export type WABusinessProfile = {
|
||||
description: string
|
||||
email: string
|
||||
business_hours: {
|
||||
business_hours: {
|
||||
timezone?: string
|
||||
config?: WABusinessHoursConfig[]
|
||||
config?: WABusinessHoursConfig[]
|
||||
business_config?: WABusinessHoursConfig[]
|
||||
}
|
||||
website: string[]
|
||||
categories: {
|
||||
id: string
|
||||
localized_display_name: string
|
||||
localized_display_name: string
|
||||
}[]
|
||||
wid?: string
|
||||
}
|
||||
|
||||
@@ -128,10 +128,10 @@ export const useSingleFileAuthState = (filename: string, logger?: Logger): { sta
|
||||
JSON.stringify({ creds, keys }, BufferJSON.replacer, 2)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
if(existsSync(filename)) {
|
||||
const result = JSON.parse(
|
||||
readFileSync(filename, { encoding: 'utf-8' }),
|
||||
readFileSync(filename, { encoding: 'utf-8' }),
|
||||
BufferJSON.reviver
|
||||
)
|
||||
creds = result.creds
|
||||
@@ -141,8 +141,8 @@ export const useSingleFileAuthState = (filename: string, logger?: Logger): { sta
|
||||
keys = { }
|
||||
}
|
||||
|
||||
return {
|
||||
state: {
|
||||
return {
|
||||
state: {
|
||||
creds,
|
||||
keys: {
|
||||
get: (type, ids) => {
|
||||
@@ -172,7 +172,7 @@ export const useSingleFileAuthState = (filename: string, logger?: Logger): { sta
|
||||
saveState()
|
||||
}
|
||||
}
|
||||
},
|
||||
saveState
|
||||
},
|
||||
saveState
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildren } from '../WABina
|
||||
import { aesDecrypt, aesEncrypt, hkdf, hmacSign } from './crypto'
|
||||
import { toNumber } from './generics'
|
||||
import { LT_HASH_ANTI_TAMPERING } from './lt-hash'
|
||||
import { downloadContentFromMessage, } from './messages-media'
|
||||
import { downloadContentFromMessage, } from './messages-media'
|
||||
|
||||
type FetchAppStateSyncKey = (keyId: string) => Promise<proto.IAppStateSyncKeyData> | proto.IAppStateSyncKeyData
|
||||
|
||||
@@ -39,7 +39,7 @@ const generateMac = (operation: proto.SyncdMutation.SyncdMutationSyncdOperation,
|
||||
const keyData = getKeyData()
|
||||
|
||||
const last = Buffer.alloc(8) // 8 bytes
|
||||
last.set([ keyData.length ], last.length-1)
|
||||
last.set([ keyData.length ], last.length - 1)
|
||||
|
||||
const total = Buffer.concat([ keyData, data, last ])
|
||||
const hmac = hmacSign(total, key, 'sha512')
|
||||
@@ -180,7 +180,7 @@ export const encodeSyncdPatch = async(
|
||||
}
|
||||
|
||||
export const decodeSyncdMutations = async(
|
||||
msgMutations: (proto.ISyncdMutation | proto.ISyncdRecord)[],
|
||||
msgMutations: (proto.ISyncdMutation | proto.ISyncdRecord)[],
|
||||
initialState: LTHashState,
|
||||
getAppStateSyncKey: FetchAppStateSyncKey,
|
||||
validateMacs: boolean
|
||||
@@ -214,7 +214,7 @@ export const decodeSyncdMutations = async(
|
||||
// otherwise, if it's only a record -- it'll be a SET mutation
|
||||
const operation = 'operation' in msgMutation ? msgMutation.operation : proto.SyncdMutation.SyncdMutationSyncdOperation.SET
|
||||
const record = ('record' in msgMutation && !!msgMutation.record) ? msgMutation.record : msgMutation as proto.ISyncdRecord
|
||||
|
||||
|
||||
const key = await getKey(record.keyId!.id!)
|
||||
const content = Buffer.from(record.value!.blob!)
|
||||
const encContent = content.slice(0, -32)
|
||||
@@ -236,12 +236,12 @@ export const decodeSyncdMutations = async(
|
||||
}
|
||||
}
|
||||
|
||||
const indexStr = Buffer.from(syncAction.index).toString()
|
||||
const indexStr = Buffer.from(syncAction.index).toString()
|
||||
mutations.push({
|
||||
syncAction,
|
||||
index: JSON.parse(indexStr),
|
||||
})
|
||||
ltGenerator.mix({
|
||||
ltGenerator.mix({
|
||||
indexMac: record.index!.blob!,
|
||||
valueMac: ogValueMac,
|
||||
operation: operation
|
||||
@@ -252,7 +252,7 @@ export const decodeSyncdMutations = async(
|
||||
}
|
||||
|
||||
export const decodeSyncdPatch = async(
|
||||
msg: proto.ISyncdPatch,
|
||||
msg: proto.ISyncdPatch,
|
||||
name: WAPatchName,
|
||||
initialState: LTHashState,
|
||||
getAppStateSyncKey: FetchAppStateSyncKey,
|
||||
@@ -263,11 +263,11 @@ export const decodeSyncdPatch = async(
|
||||
const mainKeyObj = await getAppStateSyncKey(base64Key)
|
||||
const mainKey = mutationKeys(mainKeyObj.keyData!)
|
||||
const mutationmacs = msg.mutations!.map(mutation => mutation.record!.value!.blob!.slice(-32))
|
||||
|
||||
|
||||
const patchMac = generatePatchMac(msg.snapshotMac, mutationmacs, toNumber(msg.version!.version), name, mainKey.patchMacKey)
|
||||
if(Buffer.compare(patchMac, msg.patchMac) !== 0) {
|
||||
throw new Boom('Invalid patch mac')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await decodeSyncdMutations(msg!.mutations!, initialState, getAppStateSyncKey, validateMacs)
|
||||
@@ -277,7 +277,7 @@ export const decodeSyncdPatch = async(
|
||||
export const extractSyncdPatches = async(result: BinaryNode) => {
|
||||
const syncNode = getBinaryNodeChild(result, 'sync')
|
||||
const collectionNodes = getBinaryNodeChildren(syncNode, 'collection')
|
||||
|
||||
|
||||
const final = { } as { [T in WAPatchName]: { patches: proto.ISyncdPatch[], hasMorePatches: boolean, snapshot?: proto.ISyncdSnapshot } }
|
||||
await Promise.all(
|
||||
collectionNodes.map(
|
||||
@@ -291,7 +291,7 @@ export const extractSyncdPatches = async(result: BinaryNode) => {
|
||||
const name = collectionNode.attrs.name as WAPatchName
|
||||
|
||||
const hasMorePatches = collectionNode.attrs.has_more_patches === 'true'
|
||||
|
||||
|
||||
let snapshot: proto.ISyncdSnapshot | undefined = undefined
|
||||
if(snapshotNode && !!snapshotNode.content) {
|
||||
if(!Buffer.isBuffer(snapshotNode)) {
|
||||
@@ -313,13 +313,13 @@ export const extractSyncdPatches = async(result: BinaryNode) => {
|
||||
|
||||
const syncd = proto.SyncdPatch.decode(content! as Uint8Array)
|
||||
if(!syncd.version) {
|
||||
syncd.version = { version: +collectionNode.attrs.version+1 }
|
||||
syncd.version = { version: +collectionNode.attrs.version + 1 }
|
||||
}
|
||||
|
||||
syncds.push(syncd)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final[name] = { patches: syncds, hasMorePatches, snapshot }
|
||||
}
|
||||
)
|
||||
@@ -354,7 +354,7 @@ export const decodeSyncdSnapshot = async(
|
||||
) => {
|
||||
const newState = newLTHashState()
|
||||
newState.version = toNumber(snapshot.version!.version!)
|
||||
|
||||
|
||||
const { hash, indexValueMap, mutations } = await decodeSyncdMutations(snapshot.records!, newState, getAppStateSyncKey, validateMacs)
|
||||
newState.hash = hash
|
||||
newState.indexValueMap = indexValueMap
|
||||
@@ -408,7 +408,7 @@ export const decodePatches = async(
|
||||
}
|
||||
|
||||
const patchVersion = toNumber(version.version!)
|
||||
|
||||
|
||||
newState.version = patchVersion
|
||||
|
||||
const decodeResult = await decodeSyncdPatch(syncd, name, newState, getAppStateSyncKey, validateMacs)
|
||||
@@ -418,7 +418,7 @@ export const decodePatches = async(
|
||||
if(typeof minimumVersionNumber === 'undefined' || patchVersion > minimumVersionNumber) {
|
||||
successfulMutations.push(...decodeResult.mutations)
|
||||
}
|
||||
|
||||
|
||||
if(validateMacs) {
|
||||
const base64Key = Buffer.from(keyId!.id!).toString('base64')
|
||||
const keyEnc = await getAppStateSyncKey(base64Key)
|
||||
@@ -450,7 +450,7 @@ export const chatModificationToAppPatch = (
|
||||
throw new Boom('Expected last message to be not from me', { statusCode: 400 })
|
||||
}
|
||||
|
||||
const lastMsg = lastMessages[lastMessages.length-1]
|
||||
const lastMsg = lastMessages[lastMessages.length - 1]
|
||||
if(lastMsg.key.fromMe) {
|
||||
throw new Boom('Expected last message in array to be not from me', { statusCode: 400 })
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export const signedKeyPair = (keyPair: KeyPair, keyId: number) => {
|
||||
pubKey.set(signKeys.public, 1)
|
||||
|
||||
const signature = Curve.sign(keyPair.private, pubKey)
|
||||
|
||||
|
||||
return { keyPair: signKeys, signature, keyId }
|
||||
}
|
||||
|
||||
@@ -78,10 +78,10 @@ export function hkdf(buffer: Uint8Array, expandedLength: number, { info, salt }:
|
||||
let prev = Buffer.from([])
|
||||
const buffers = []
|
||||
const num_blocks = Math.ceil(expandedLength / hashLength)
|
||||
|
||||
|
||||
const infoBuff = Buffer.from(info || [])
|
||||
|
||||
for(var i=0; i<num_blocks; i++) {
|
||||
for(var i = 0; i < num_blocks; i++) {
|
||||
const hmac = createHmac(hashAlg, prk)
|
||||
// XXX is there a more optimal way to build up buffers?
|
||||
const input = Buffer.concat([
|
||||
|
||||
@@ -62,7 +62,7 @@ export const decodeMessageStanza = (stanza: BinaryNode, auth: AuthenticationStat
|
||||
|
||||
const fromMe = isMe(stanza.attrs.participant || stanza.attrs.from)
|
||||
const pushname = stanza.attrs.notify
|
||||
|
||||
|
||||
const key: WAMessageKey = {
|
||||
remoteJid: chatId,
|
||||
fromMe,
|
||||
@@ -83,21 +83,21 @@ export const decodeMessageStanza = (stanza: BinaryNode, auth: AuthenticationStat
|
||||
return {
|
||||
fullMessage,
|
||||
decryptionTask: (async() => {
|
||||
let decryptables = 0
|
||||
let decryptables = 0
|
||||
if(Array.isArray(stanza.content)) {
|
||||
for(const { tag, attrs, content } of stanza.content) {
|
||||
if(tag !== 'enc') {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
if(!(content instanceof Uint8Array)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
decryptables += 1
|
||||
|
||||
|
||||
let msgBuffer: Buffer
|
||||
|
||||
|
||||
try {
|
||||
const e2eType = attrs.type
|
||||
switch (e2eType) {
|
||||
@@ -110,13 +110,13 @@ export const decodeMessageStanza = (stanza: BinaryNode, auth: AuthenticationStat
|
||||
msgBuffer = await decryptSignalProto(user, e2eType, content as Buffer, auth)
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
let msg: proto.IMessage = proto.Message.decode(unpadRandomMax16(msgBuffer))
|
||||
msg = msg.deviceSentMessage?.message || msg
|
||||
if(msg.senderKeyDistributionMessage) {
|
||||
await processSenderKeyMessage(author, msg.senderKeyDistributionMessage, auth)
|
||||
}
|
||||
|
||||
|
||||
if(fullMessage.message) {
|
||||
Object.assign(fullMessage.message, msg)
|
||||
} else {
|
||||
|
||||
@@ -38,7 +38,7 @@ export const BufferJSON = {
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const writeRandomPadMax16 = (e: Binary) => {
|
||||
@@ -47,7 +47,7 @@ export const writeRandomPadMax16 = (e: Binary) => {
|
||||
e.writeUint8(t)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var t = randomBytes(1)
|
||||
r(e, 1 + (15 & t[0]))
|
||||
return e
|
||||
@@ -58,12 +58,12 @@ export const unpadRandomMax16 = (e: Uint8Array | Buffer) => {
|
||||
if(0 === t.length) {
|
||||
throw new Error('unpadPkcs7 given empty bytes')
|
||||
}
|
||||
|
||||
|
||||
var r = t[t.length - 1]
|
||||
if(r > t.length) {
|
||||
throw new Error(`unpad given ${t.length} bytes, but pad is ${r}`)
|
||||
}
|
||||
|
||||
|
||||
return new Uint8Array(t.buffer, t.byteOffset, t.length - r)
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ export const encodeInt = (e: number, t: number) => {
|
||||
return a
|
||||
}
|
||||
|
||||
export const encodeBigEndian = (e: number, t=4) => {
|
||||
export const encodeBigEndian = (e: number, t = 4) => {
|
||||
let r = e
|
||||
const a = new Uint8Array(t)
|
||||
for(let i = t - 1; i >= 0; i--) {
|
||||
@@ -121,7 +121,7 @@ export function shallowChanges <T>(old: T, current: T, { lookForDeletedKeys }: {
|
||||
}
|
||||
|
||||
/** unix timestamp of a date in seconds */
|
||||
export const unixTimestampSeconds = (date: Date = new Date()) => Math.floor(date.getTime()/1000)
|
||||
export const unixTimestampSeconds = (date: Date = new Date()) => Math.floor(date.getTime() / 1000)
|
||||
|
||||
export type DebouncedTimeout = ReturnType<typeof debouncedTimeout>
|
||||
|
||||
@@ -175,7 +175,7 @@ export async function promiseTimeout<T>(ms: number, promise: (resolve: (v?: T)=>
|
||||
|
||||
const stack = new Error().stack
|
||||
// Create a promise that rejects in <ms> milliseconds
|
||||
const { delay, cancel } = delayCancellable (ms)
|
||||
const { delay, cancel } = delayCancellable (ms)
|
||||
const p = new Promise ((resolve, reject) => {
|
||||
delay
|
||||
.then(() => reject(
|
||||
@@ -186,8 +186,8 @@ export async function promiseTimeout<T>(ms: number, promise: (resolve: (v?: T)=>
|
||||
}
|
||||
})
|
||||
))
|
||||
.catch (err => reject(err))
|
||||
|
||||
.catch (err => reject(err))
|
||||
|
||||
promise (resolve, reject)
|
||||
})
|
||||
.finally (cancel)
|
||||
@@ -202,7 +202,7 @@ export const bindWaitForConnectionUpdate = (ev: CommonBaileysEventEmitter<any>)
|
||||
let listener: (item: Partial<ConnectionState>) => void
|
||||
await (
|
||||
promiseTimeout(
|
||||
timeoutMs,
|
||||
timeoutMs,
|
||||
(resolve, reject) => {
|
||||
listener = (update) => {
|
||||
if(check(update)) {
|
||||
@@ -236,7 +236,7 @@ export const printQRIfNecessaryListener = (ev: CommonBaileysEventEmitter<any>, l
|
||||
|
||||
/**
|
||||
* utility that fetches latest baileys version from the master branch.
|
||||
* Use to ensure your WA connection is always on the latest version
|
||||
* Use to ensure your WA connection is always on the latest version
|
||||
*/
|
||||
export const fetchLatestBaileysVersion = async() => {
|
||||
const URL = 'https://raw.githubusercontent.com/adiwajshing/Baileys/master/src/Defaults/baileys-version.json'
|
||||
|
||||
@@ -10,21 +10,21 @@ export const newLegacyAuthCreds = () => ({
|
||||
}) as LegacyAuthenticationCreds
|
||||
|
||||
export const decodeWAMessage = (
|
||||
message: Buffer | string,
|
||||
auth: { macKey: Buffer, encKey: Buffer },
|
||||
fromMe: boolean=false
|
||||
message: Buffer | string,
|
||||
auth: { macKey: Buffer, encKey: Buffer },
|
||||
fromMe: boolean = false
|
||||
) => {
|
||||
let commaIndex = message.indexOf(',') // all whatsapp messages have a tag and a comma, followed by the actual message
|
||||
if(commaIndex < 0) {
|
||||
throw new Boom('invalid message', { data: message })
|
||||
} // if there was no comma, then this message must be not be valid
|
||||
|
||||
if(message[commaIndex+1] === ',') {
|
||||
|
||||
if(message[commaIndex + 1] === ',') {
|
||||
commaIndex += 1
|
||||
}
|
||||
|
||||
let data = message.slice(commaIndex+1, message.length)
|
||||
|
||||
let data = message.slice(commaIndex + 1, message.length)
|
||||
|
||||
// get the message tag.
|
||||
// If a query was done, the server will respond with the same message tag we sent the query with
|
||||
const messageTag: string = message.slice(0, commaIndex).toString()
|
||||
@@ -43,7 +43,7 @@ export const decodeWAMessage = (
|
||||
throw new Boom('recieved encrypted buffer when auth creds unavailable', { data: message, statusCode: DisconnectReason.badSession })
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
If the data recieved was not a JSON, then it must be an encrypted message.
|
||||
Such a message can only be decrypted if we're connected successfully to the servers & have encryption keys
|
||||
*/
|
||||
@@ -51,11 +51,11 @@ export const decodeWAMessage = (
|
||||
tags = [data[0], data[1]]
|
||||
data = data.slice(2, data.length)
|
||||
}
|
||||
|
||||
|
||||
const checksum = data.slice(0, 32) // the first 32 bytes of the buffer are the HMAC sign of the message
|
||||
data = data.slice(32, data.length) // the actual message
|
||||
const computedChecksum = hmacSign(data, macKey) // compute the sign of the message we recieved using our macKey
|
||||
|
||||
|
||||
if(checksum.equals(computedChecksum)) {
|
||||
// the checksum the server sent, must match the one we computed for the message to be valid
|
||||
const decrypted = aesDecrypt(data, encKey) // decrypt using AES
|
||||
@@ -73,7 +73,7 @@ export const decodeWAMessage = (
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [messageTag, json, tags] as const
|
||||
@@ -85,7 +85,7 @@ export const decodeWAMessage = (
|
||||
* @param json
|
||||
*/
|
||||
export const validateNewConnection = (
|
||||
json: { [_: string]: any },
|
||||
json: { [_: string]: any },
|
||||
auth: LegacyAuthenticationCreds,
|
||||
curveKeys: CurveKeyPair
|
||||
) => {
|
||||
@@ -164,7 +164,7 @@ export const useSingleFileLegacyAuthState = (file: string) => {
|
||||
|
||||
if(existsSync(file)) {
|
||||
state = JSON.parse(
|
||||
readFileSync(file, { encoding: 'utf-8' }),
|
||||
readFileSync(file, { encoding: 'utf-8' }),
|
||||
BufferJSON.reviver
|
||||
)
|
||||
if(typeof state.encKey === 'string') {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { hkdf } from './crypto'
|
||||
|
||||
/**
|
||||
* LT Hash is a summation based hash algorithm that maintains the integrity of a piece of data
|
||||
* over a series of mutations. You can add/remove mutations and it'll return a hash equal to
|
||||
* over a series of mutations. You can add/remove mutations and it'll return a hash equal to
|
||||
* if the same series of mutations was made sequentially.
|
||||
*/
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ export const hkdfInfoKey = (type: MediaType) => {
|
||||
if(type === 'md-app-state') {
|
||||
str = 'App State'
|
||||
}
|
||||
|
||||
|
||||
const hkdfInfo = str[0].toUpperCase() + str.slice(1)
|
||||
return `WhatsApp ${hkdfInfo} Keys`
|
||||
}
|
||||
@@ -145,7 +145,7 @@ export const generateProfilePicture = async(mediaUpload: WAMediaUpload) => {
|
||||
.resize(640, 640, RESIZE_BILINEAR)
|
||||
.getBufferAsync(MIME_JPEG)
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
img: await img,
|
||||
}
|
||||
@@ -207,8 +207,8 @@ export const getStream = async(item: WAMediaUpload) => {
|
||||
|
||||
/** generates a thumbnail for a given media, if required */
|
||||
export async function generateThumbnail(
|
||||
file: string,
|
||||
mediaType: 'video' | 'image',
|
||||
file: string,
|
||||
mediaType: 'video' | 'image',
|
||||
options: {
|
||||
logger?: Logger
|
||||
}
|
||||
@@ -229,7 +229,7 @@ export async function generateThumbnail(
|
||||
options.logger?.debug('could not generate video thumb: ' + err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return thumbnail
|
||||
}
|
||||
|
||||
@@ -238,9 +238,9 @@ export const getHttpStream = async(url: string | URL, options: AxiosRequestConfi
|
||||
const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' })
|
||||
return fetched.data as Readable
|
||||
}
|
||||
|
||||
|
||||
export const encryptedStream = async(
|
||||
media: WAMediaUpload,
|
||||
media: WAMediaUpload,
|
||||
mediaType: MediaType,
|
||||
saveOriginalFileIfRequired = true,
|
||||
logger?: Logger
|
||||
@@ -266,7 +266,7 @@ export const encryptedStream = async(
|
||||
writeStream = createWriteStream(bodyPath)
|
||||
didSaveToTmpPath = true
|
||||
}
|
||||
|
||||
|
||||
let fileLength = 0
|
||||
const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv)
|
||||
let hmac = Crypto.createHmac('sha256', macKey).update(iv)
|
||||
@@ -278,7 +278,7 @@ export const encryptedStream = async(
|
||||
hmac = hmac.update(buff)
|
||||
encWriteStream.push(buff)
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
for await (const data of stream) {
|
||||
fileLength += data.length
|
||||
@@ -293,21 +293,21 @@ export const encryptedStream = async(
|
||||
}
|
||||
|
||||
onChunk(aes.final())
|
||||
|
||||
|
||||
const mac = hmac.digest().slice(0, 10)
|
||||
sha256Enc = sha256Enc.update(mac)
|
||||
|
||||
|
||||
const fileSha256 = sha256Plain.digest()
|
||||
const fileEncSha256 = sha256Enc.digest()
|
||||
|
||||
|
||||
encWriteStream.push(mac)
|
||||
encWriteStream.push(null)
|
||||
|
||||
|
||||
writeStream && writeStream.end()
|
||||
stream.destroy()
|
||||
|
||||
logger?.debug('encrypted data successfully')
|
||||
|
||||
|
||||
return {
|
||||
mediaKey,
|
||||
encWriteStream,
|
||||
@@ -356,14 +356,14 @@ export const downloadContentFromMessage = async(
|
||||
if(startByte) {
|
||||
const chunk = toSmallestChunkSize(startByte || 0)
|
||||
if(chunk) {
|
||||
startChunk = chunk-AES_CHUNK_SIZE
|
||||
startChunk = chunk - AES_CHUNK_SIZE
|
||||
bytesFetched = chunk
|
||||
|
||||
firstBlockIsIV = true
|
||||
}
|
||||
}
|
||||
|
||||
const endChunk = endByte ? toSmallestChunkSize(endByte || 0)+AES_CHUNK_SIZE : undefined
|
||||
const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined
|
||||
|
||||
const headers: { [_: string]: string } = {
|
||||
Origin: DEFAULT_ORIGIN,
|
||||
@@ -377,7 +377,7 @@ export const downloadContentFromMessage = async(
|
||||
|
||||
// download the message
|
||||
const fetched = await getHttpStream(
|
||||
downloadUrl,
|
||||
downloadUrl,
|
||||
{
|
||||
headers,
|
||||
maxBodyLength: Infinity,
|
||||
@@ -392,11 +392,11 @@ export const downloadContentFromMessage = async(
|
||||
|
||||
const pushBytes = (bytes: Buffer, push: (bytes: Buffer) => void) => {
|
||||
if(startByte || endByte) {
|
||||
const start = bytesFetched >= startByte ? undefined : Math.max(startByte-bytesFetched, 0)
|
||||
const end = bytesFetched+bytes.length < endByte ? undefined : Math.max(endByte-bytesFetched, 0)
|
||||
|
||||
const start = bytesFetched >= startByte ? undefined : Math.max(startByte - bytesFetched, 0)
|
||||
const end = bytesFetched + bytes.length < endByte ? undefined : Math.max(endByte - bytesFetched, 0)
|
||||
|
||||
push(bytes.slice(start, end))
|
||||
|
||||
|
||||
bytesFetched += bytes.length
|
||||
} else {
|
||||
push(bytes)
|
||||
@@ -406,7 +406,7 @@ export const downloadContentFromMessage = async(
|
||||
const output = new Transform({
|
||||
transform(chunk, _, callback) {
|
||||
let data = Buffer.concat([remainingBytes, chunk])
|
||||
|
||||
|
||||
const decryptLength = toSmallestChunkSize(data.length)
|
||||
remainingBytes = data.slice(decryptLength)
|
||||
data = data.slice(0, decryptLength)
|
||||
@@ -424,7 +424,7 @@ export const downloadContentFromMessage = async(
|
||||
if(endByte) {
|
||||
aes.setAutoPadding(false)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -432,7 +432,7 @@ export const downloadContentFromMessage = async(
|
||||
callback()
|
||||
} catch(error) {
|
||||
callback(error)
|
||||
}
|
||||
}
|
||||
},
|
||||
final(callback) {
|
||||
try {
|
||||
@@ -451,14 +451,14 @@ export const downloadContentFromMessage = async(
|
||||
* @param message the media message you want to decode
|
||||
*/
|
||||
export async function decryptMediaMessageBuffer(message: WAMessageContent): Promise<Readable> {
|
||||
/*
|
||||
/*
|
||||
One can infer media type from the key in the message
|
||||
it is usually written as [mediaType]Message. Eg. imageMessage, audioMessage etc.
|
||||
*/
|
||||
const type = Object.keys(message)[0] as MessageType
|
||||
if(
|
||||
!type ||
|
||||
type === 'conversation' ||
|
||||
type === 'conversation' ||
|
||||
type === 'extendedTextMessage'
|
||||
) {
|
||||
throw new Boom(`no media message for "${type}"`, { statusCode: 400 })
|
||||
@@ -492,8 +492,8 @@ export function extensionForMediaMessage(message: WAMessageContent) {
|
||||
const type = Object.keys(message)[0] as MessageType
|
||||
let extension: string
|
||||
if(
|
||||
type === 'locationMessage' ||
|
||||
type === 'liveLocationMessage' ||
|
||||
type === 'locationMessage' ||
|
||||
type === 'liveLocationMessage' ||
|
||||
type === 'productMessage'
|
||||
) {
|
||||
extension = '.jpeg'
|
||||
@@ -539,8 +539,8 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger }: C
|
||||
const body = await axios.post(
|
||||
url,
|
||||
reqBody,
|
||||
{
|
||||
headers: {
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Origin': DEFAULT_ORIGIN
|
||||
},
|
||||
@@ -552,7 +552,7 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger }: C
|
||||
}
|
||||
)
|
||||
result = body.data
|
||||
|
||||
|
||||
if(result?.url || result?.directPath) {
|
||||
urls = {
|
||||
mediaUrl: result.url,
|
||||
@@ -568,7 +568,7 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger }: C
|
||||
result = error.response?.data
|
||||
}
|
||||
|
||||
const isLast = hostname === hosts[uploadInfo.hosts.length-1]?.hostname
|
||||
const isLast = hostname === hosts[uploadInfo.hosts.length - 1]?.hostname
|
||||
logger.warn({ trace: error.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,22 @@ import { Boom } from '@hapi/boom'
|
||||
import { promises as fs } from 'fs'
|
||||
import { proto } from '../../WAProto'
|
||||
import { MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults'
|
||||
import {
|
||||
AnyMediaMessageContent,
|
||||
AnyMessageContent,
|
||||
MediaGenerationOptions,
|
||||
MediaType,
|
||||
MessageContentGenerationOptions,
|
||||
MessageGenerationOptions,
|
||||
import {
|
||||
AnyMediaMessageContent,
|
||||
AnyMessageContent,
|
||||
MediaGenerationOptions,
|
||||
MediaType,
|
||||
MessageContentGenerationOptions,
|
||||
MessageGenerationOptions,
|
||||
MessageGenerationOptionsFromContent,
|
||||
MessageType,
|
||||
MessageType,
|
||||
MessageUserReceipt,
|
||||
WAMediaUpload,
|
||||
WAMessage,
|
||||
WAMessageContent,
|
||||
WAMediaUpload,
|
||||
WAMessage,
|
||||
WAMessageContent,
|
||||
WAMessageStatus,
|
||||
WAProto,
|
||||
WATextMessage
|
||||
WAProto,
|
||||
WATextMessage
|
||||
} from '../Types'
|
||||
import { generateMessageID, unixTimestampSeconds } from './generics'
|
||||
import { encryptedStream, generateThumbnail, getAudioDuration } from './messages-media'
|
||||
@@ -54,7 +54,7 @@ const MessageTypeProto = {
|
||||
const ButtonType = proto.ButtonsMessage.ButtonsMessageHeaderType
|
||||
|
||||
export const prepareWAMessageMedia = async(
|
||||
message: AnyMediaMessageContent,
|
||||
message: AnyMediaMessageContent,
|
||||
options: MediaGenerationOptions
|
||||
) => {
|
||||
const logger = options.logger
|
||||
@@ -66,15 +66,15 @@ export const prepareWAMessageMedia = async(
|
||||
}
|
||||
}
|
||||
|
||||
const uploadData: MediaUploadData = {
|
||||
const uploadData: MediaUploadData = {
|
||||
...message,
|
||||
media: message[mediaType]
|
||||
}
|
||||
delete uploadData[mediaType]
|
||||
// check if cacheable + generate cache key
|
||||
const cacheableKey = typeof uploadData.media === 'object' &&
|
||||
('url' in uploadData.media) &&
|
||||
!!uploadData.media.url &&
|
||||
const cacheableKey = typeof uploadData.media === 'object' &&
|
||||
('url' in uploadData.media) &&
|
||||
!!uploadData.media.url &&
|
||||
!!options.mediaCache && (
|
||||
// generate the key
|
||||
mediaType + ':' + uploadData.media.url!.toString()
|
||||
@@ -93,7 +93,7 @@ export const prepareWAMessageMedia = async(
|
||||
const mediaBuff: Buffer = options.mediaCache!.get(cacheableKey)
|
||||
if(mediaBuff) {
|
||||
logger?.debug({ cacheableKey }, 'got media cache hit')
|
||||
|
||||
|
||||
const obj = WAProto.Message.decode(mediaBuff)
|
||||
const key = `${mediaType}Message`
|
||||
|
||||
@@ -105,7 +105,7 @@ export const prepareWAMessageMedia = async(
|
||||
}
|
||||
|
||||
const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined'
|
||||
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') &&
|
||||
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') &&
|
||||
(typeof uploadData['jpegThumbnail'] === 'undefined')
|
||||
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation
|
||||
const {
|
||||
@@ -118,7 +118,7 @@ export const prepareWAMessageMedia = async(
|
||||
didSaveToTmpPath
|
||||
} = await encryptedStream(uploadData.media, mediaType, requiresOriginalForSomeProcessing)
|
||||
// url safe Base64 encode the SHA256 hash of the body
|
||||
const fileEncSha256B64 = encodeURIComponent(
|
||||
const fileEncSha256B64 = encodeURIComponent(
|
||||
fileEncSha256.toString('base64')
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
@@ -239,7 +239,7 @@ export const generateForwardMessageContent = (
|
||||
}
|
||||
|
||||
export const generateWAMessageContent = async(
|
||||
message: AnyMessageContent,
|
||||
message: AnyMessageContent,
|
||||
options: MessageContentGenerationOptions
|
||||
) => {
|
||||
let m: WAMessageContent = {}
|
||||
@@ -256,7 +256,7 @@ export const generateWAMessageContent = async(
|
||||
extContent.previewType = 0
|
||||
} catch(error) { // ignore if fails
|
||||
options.logger?.warn({ trace: error.stack }, 'url generation failed')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.extendedTextMessage = extContent
|
||||
@@ -265,7 +265,7 @@ export const generateWAMessageContent = async(
|
||||
if(!contactLen) {
|
||||
throw new Boom('require atleast 1 contact', { statusCode: 400 })
|
||||
}
|
||||
|
||||
|
||||
if(contactLen === 1) {
|
||||
m.contactMessage = WAProto.ContactMessage.fromObject(message.contacts.contacts[0])
|
||||
} else {
|
||||
@@ -284,7 +284,7 @@ export const generateWAMessageContent = async(
|
||||
message.force
|
||||
)
|
||||
} else if('disappearingMessagesInChat' in message) {
|
||||
const exp = typeof message.disappearingMessagesInChat === 'boolean' ?
|
||||
const exp = typeof message.disappearingMessagesInChat === 'boolean' ?
|
||||
(message.disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0) :
|
||||
message.disappearingMessagesInChat
|
||||
m = prepareDisappearingMessageSettingContent(exp)
|
||||
@@ -309,7 +309,7 @@ export const generateWAMessageContent = async(
|
||||
|
||||
const type = Object.keys(m)[0].replace('Message', '').toUpperCase()
|
||||
buttonsMessage.headerType = ButtonType[type]
|
||||
|
||||
|
||||
Object.assign(buttonsMessage, m)
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ export const generateWAMessageContent = async(
|
||||
hydratedButtons: message.templateButtons
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if('text' in message) {
|
||||
templateMessage.hydratedTemplate.hydratedContentText = message.text
|
||||
} else {
|
||||
@@ -332,7 +332,7 @@ export const generateWAMessageContent = async(
|
||||
if('caption' in message) {
|
||||
templateMessage.hydratedTemplate.hydratedContentText = message.caption
|
||||
}
|
||||
|
||||
|
||||
Object.assign(templateMessage.hydratedTemplate, m)
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ export const generateWAMessageContent = async(
|
||||
|
||||
m = { templateMessage }
|
||||
}
|
||||
|
||||
|
||||
if('sections' in message && !!message.sections) {
|
||||
const listMessage: proto.IListMessage = {
|
||||
sections: message.sections,
|
||||
@@ -370,8 +370,8 @@ export const generateWAMessageContent = async(
|
||||
}
|
||||
|
||||
export const generateWAMessageFromContent = (
|
||||
jid: string,
|
||||
message: WAMessageContent,
|
||||
jid: string,
|
||||
message: WAMessageContent,
|
||||
options: MessageGenerationOptionsFromContent
|
||||
) => {
|
||||
if(!options.timestamp) {
|
||||
@@ -389,7 +389,7 @@ export const generateWAMessageFromContent = (
|
||||
message[key].contextInfo.participant = participant
|
||||
message[key].contextInfo.stanzaId = quoted.key.id
|
||||
message[key].contextInfo.quotedMessage = quoted.message
|
||||
|
||||
|
||||
// if a participant is quoted, then it must be a group
|
||||
// hence, remoteJid of group must also be entered
|
||||
if(quoted.key.participant || quoted.participant) {
|
||||
@@ -403,7 +403,7 @@ export const generateWAMessageFromContent = (
|
||||
// and it's not a protocol message -- delete, toggle disappear message
|
||||
key !== 'protocolMessage' &&
|
||||
// already not converted to disappearing message
|
||||
key !== 'ephemeralMessage'
|
||||
key !== 'ephemeralMessage'
|
||||
) {
|
||||
message[key].contextInfo = {
|
||||
...(message[key].contextInfo || {}),
|
||||
@@ -478,10 +478,10 @@ export const extractMessageContent = (content: WAMessageContent | undefined | nu
|
||||
return { conversation: 'contentText' in msg ? msg.contentText : ('hydratedContentText' in msg ? msg.hydratedContentText : '') }
|
||||
}
|
||||
}
|
||||
|
||||
content = content?.ephemeralMessage?.message ||
|
||||
|
||||
content = content?.ephemeralMessage?.message ||
|
||||
content?.viewOnceMessage?.message ||
|
||||
content ||
|
||||
content ||
|
||||
undefined
|
||||
|
||||
if(content?.buttonsMessage) {
|
||||
|
||||
@@ -30,7 +30,7 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
const result = Buffer.concat([cipher.update(plaintext), cipher.final(), cipher.getAuthTag()])
|
||||
|
||||
writeCounter += 1
|
||||
|
||||
|
||||
authenticate(result)
|
||||
return result
|
||||
}
|
||||
@@ -42,8 +42,8 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
const cipher = createDecipheriv('aes-256-gcm', decKey, iv)
|
||||
// decrypt additional adata
|
||||
const tagLength = 128 >> 3
|
||||
const enc = ciphertext.slice(0, ciphertext.length-tagLength)
|
||||
const tag = ciphertext.slice(ciphertext.length-tagLength)
|
||||
const enc = ciphertext.slice(0, ciphertext.length - tagLength)
|
||||
const tag = ciphertext.slice(ciphertext.length - tagLength)
|
||||
// set additional data
|
||||
cipher.setAAD(hash)
|
||||
cipher.setAuthTag(tag)
|
||||
@@ -55,7 +55,7 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
} else {
|
||||
writeCounter += 1
|
||||
}
|
||||
|
||||
|
||||
authenticate(ciphertext)
|
||||
return result
|
||||
}
|
||||
@@ -83,7 +83,7 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
writeCounter = 0
|
||||
isFinished = true
|
||||
}
|
||||
|
||||
|
||||
const data = Binary.build(NOISE_MODE).readBuffer()
|
||||
let hash = Buffer.from(data.byteLength === 32 ? data : sha256(Buffer.from(data)))
|
||||
let salt = hash
|
||||
@@ -109,19 +109,19 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
processHandshake: ({ serverHello }: proto.HandshakeMessage, noiseKey: KeyPair) => {
|
||||
authenticate(serverHello!.ephemeral!)
|
||||
mixIntoKey(Curve.sharedKey(privateKey, serverHello.ephemeral!))
|
||||
|
||||
|
||||
const decStaticContent = decrypt(serverHello!.static!)
|
||||
mixIntoKey(Curve.sharedKey(privateKey, decStaticContent))
|
||||
|
||||
|
||||
const certDecoded = decrypt(serverHello!.payload!)
|
||||
const { details: certDetails, signature: certSignature } = proto.NoiseCertificate.decode(certDecoded)
|
||||
|
||||
|
||||
const { key: certKey } = proto.NoiseCertificateDetails.decode(certDetails)
|
||||
|
||||
|
||||
if(Buffer.compare(decStaticContent, certKey) !== 0) {
|
||||
throw new Boom('certification match failed', { statusCode: 400 })
|
||||
}
|
||||
|
||||
|
||||
const keyEnc = encrypt(noiseKey.public)
|
||||
mixIntoKey(Curve.sharedKey(noiseKey.private, serverHello!.ephemeral!))
|
||||
|
||||
@@ -135,16 +135,16 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
const introSize = sentIntro ? 0 : NOISE_WA_HEADER.length
|
||||
|
||||
outBinary.ensureAdditionalCapacity(introSize + 3 + data.byteLength)
|
||||
|
||||
|
||||
if(!sentIntro) {
|
||||
outBinary.writeByteArray(NOISE_WA_HEADER)
|
||||
sentIntro = true
|
||||
}
|
||||
|
||||
|
||||
outBinary.writeUint8(data.byteLength >> 16)
|
||||
outBinary.writeUint16(65535 & data.byteLength)
|
||||
outBinary.write(data)
|
||||
|
||||
|
||||
const bytes = outBinary.readByteArray()
|
||||
return bytes as Uint8Array
|
||||
},
|
||||
@@ -175,5 +175,5 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
|
||||
inBinary.peek(peekSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export const createSignalIdentity = (
|
||||
wid: string,
|
||||
accountSignatureKey: Uint8Array
|
||||
): SignalIdentity => {
|
||||
return {
|
||||
return {
|
||||
identifier: { name: wid, deviceId: 0 },
|
||||
identifierKey: generateSignalPubKey(accountSignatureKey)
|
||||
}
|
||||
@@ -145,7 +145,7 @@ export const decryptGroupSignalProto = (group: string, user: string, msg: Buffer
|
||||
|
||||
export const processSenderKeyMessage = async(
|
||||
authorJid: string,
|
||||
item: proto.ISenderKeyDistributionMessage,
|
||||
item: proto.ISenderKeyDistributionMessage,
|
||||
auth: SignalAuthState
|
||||
) => {
|
||||
const builder = new GroupSessionBuilder(signalStorage(auth))
|
||||
@@ -171,7 +171,7 @@ export const decryptSignalProto = async(user: string, type: 'pkmsg' | 'msg', msg
|
||||
break
|
||||
case 'msg':
|
||||
result = await session.decryptWhisperMessage(msg)
|
||||
break
|
||||
break
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -231,7 +231,7 @@ export const parseAndInjectE2ESessions = async(node: BinaryNode, auth: SignalAut
|
||||
const identity = getBinaryNodeChildBuffer(node, 'identity')
|
||||
const jid = node.attrs.jid
|
||||
const registrationId = getBinaryNodeChildUInt(node, 'registration', 4)
|
||||
|
||||
|
||||
const device = {
|
||||
registrationId,
|
||||
identityKey: generateSignalPubKey(identity),
|
||||
|
||||
@@ -130,8 +130,8 @@ export const configureSuccessfulPairing = (
|
||||
id: msgId,
|
||||
},
|
||||
content: [
|
||||
{
|
||||
tag: 'pair-device-sign',
|
||||
{
|
||||
tag: 'pair-device-sign',
|
||||
attrs: { },
|
||||
content: [
|
||||
{ tag: 'device-identity', attrs: { 'key-index': `${keyIndex}` }, content: accountEnc }
|
||||
@@ -141,9 +141,9 @@ export const configureSuccessfulPairing = (
|
||||
}
|
||||
|
||||
const authUpdate: Partial<AuthenticationCreds> = {
|
||||
account,
|
||||
me: { id: jid, verifiedName },
|
||||
signalIdentities: [...(signalIdentities || []), identity]
|
||||
account,
|
||||
me: { id: jid, verifiedName },
|
||||
signalIdentities: [...(signalIdentities || []), identity]
|
||||
}
|
||||
return {
|
||||
creds: authUpdate,
|
||||
|
||||
@@ -14,7 +14,7 @@ export const isLegacyBinaryNode = (buffer: Buffer) => {
|
||||
}
|
||||
|
||||
function decode(buffer: Buffer, indexRef: { index: number }): BinaryNode {
|
||||
|
||||
|
||||
const checkEOS = (length: number) => {
|
||||
if(indexRef.index + length > buffer.length) {
|
||||
throw new Error('end of stream')
|
||||
@@ -201,7 +201,7 @@ function decode(buffer: Buffer, indexRef: { index: number }): BinaryNode {
|
||||
throw new Error('invalid node')
|
||||
}
|
||||
// read the attributes in
|
||||
|
||||
|
||||
const attributesLength = (listSize - 1) >> 1
|
||||
for(let i = 0; i < attributesLength; i++) {
|
||||
const key = readString(readByte())
|
||||
@@ -243,10 +243,10 @@ function decode(buffer: Buffer, indexRef: { index: number }): BinaryNode {
|
||||
}
|
||||
|
||||
const encode = ({ tag, attrs, content }: BinaryNode, buffer: number[] = []) => {
|
||||
|
||||
|
||||
const pushByte = (value: number) => buffer.push(value & 0xff)
|
||||
|
||||
const pushInt = (value: number, n: number, littleEndian=false) => {
|
||||
const pushInt = (value: number, n: number, littleEndian = false) => {
|
||||
for(let i = 0; i < n; i++) {
|
||||
const curShift = littleEndian ? i : n - 1 - i
|
||||
buffer.push((value >> (curShift * 8)) & 0xff)
|
||||
@@ -263,7 +263,7 @@ const encode = ({ tag, attrs, content }: BinaryNode, buffer: number[] = []) => {
|
||||
if(length >= 4294967296) {
|
||||
throw new Error('string too large to encode: ' + length)
|
||||
}
|
||||
|
||||
|
||||
if(length >= 1 << 20) {
|
||||
pushByte(Tags.BINARY_32)
|
||||
pushInt(length, 4) // 32 bit integer
|
||||
@@ -294,7 +294,7 @@ const encode = ({ tag, attrs, content }: BinaryNode, buffer: number[] = []) => {
|
||||
if(token === 'c.us') {
|
||||
token = 's.whatsapp.net'
|
||||
}
|
||||
|
||||
|
||||
const tokenIndex = SingleByteTokens.indexOf(token)
|
||||
if(!i && token === 's.whatsapp.net') {
|
||||
writeToken(tokenIndex)
|
||||
@@ -341,14 +341,14 @@ const encode = ({ tag, attrs, content }: BinaryNode, buffer: number[] = []) => {
|
||||
typeof attrs[k] !== 'undefined' && attrs[k] !== null
|
||||
))
|
||||
|
||||
writeListStart(2*validAttributes.length + 1 + (typeof content !== 'undefined' && content !== null ? 1 : 0))
|
||||
writeListStart(2 * validAttributes.length + 1 + (typeof content !== 'undefined' && content !== null ? 1 : 0))
|
||||
writeString(tag)
|
||||
|
||||
validAttributes.forEach((key) => {
|
||||
if(typeof attrs[key] === 'string') {
|
||||
writeString(key)
|
||||
writeString(attrs[key])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if(typeof content === 'string') {
|
||||
@@ -364,7 +364,7 @@ const encode = ({ tag, attrs, content }: BinaryNode, buffer: number[] = []) => {
|
||||
}
|
||||
}
|
||||
} else if(typeof content === 'undefined' || content === null) {
|
||||
|
||||
|
||||
} else {
|
||||
throw new Error(`invalid children for header "${tag}": ${content} (${typeof content})`)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export const jidDecode = (jid: string) => {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const server = jid.slice(sepIdx+1)
|
||||
const server = jid.slice(sepIdx + 1)
|
||||
const userCombined = jid.slice(0, sepIdx)
|
||||
|
||||
const [userAgent, device] = userCombined.split(':')
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* the binary node WA uses internally for communication
|
||||
*
|
||||
* this is manipulated soley as an object and it does not have any functions.
|
||||
* This is done for easy serialization, to prevent running into issues with prototypes &
|
||||
/**
|
||||
* the binary node WA uses internally for communication
|
||||
*
|
||||
* this is manipulated soley as an object and it does not have any functions.
|
||||
* This is done for easy serialization, to prevent running into issues with prototypes &
|
||||
* to maintain functional code structure
|
||||
* */
|
||||
export type BinaryNode = {
|
||||
|
||||
46
yarn.lock
46
yarn.lock
@@ -4,7 +4,7 @@
|
||||
|
||||
"@adiwajshing/eslint-config@git+https://github.com/adiwajshing/eslint-config":
|
||||
version "1.0.0"
|
||||
resolved "git+https://github.com/adiwajshing/eslint-config#db16c7427bd6dcf8fba20e0aaa526724e46c83aa"
|
||||
resolved "git+https://github.com/adiwajshing/eslint-config#ee2b90dba10bc161d85745be59217efa10bc1eb3"
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin" "^4.33.0"
|
||||
"@typescript-eslint/parser" "^4.33.0"
|
||||
@@ -1800,14 +1800,14 @@ data-urls@^2.0.0:
|
||||
whatwg-mimetype "^2.3.0"
|
||||
whatwg-url "^8.0.0"
|
||||
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1:
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
|
||||
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^4.0.1:
|
||||
debug@^4.0.1, debug@^4.3.1:
|
||||
version "4.3.3"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
|
||||
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
|
||||
@@ -2033,21 +2033,21 @@ escodegen@^2.0.0:
|
||||
source-map "~0.6.1"
|
||||
|
||||
eslint-plugin-react@^7.26.1:
|
||||
version "7.28.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz#8f3ff450677571a659ce76efc6d80b6a525adbdf"
|
||||
integrity sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==
|
||||
version "7.29.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.2.tgz#2d4da69d30d0a736efd30890dc6826f3e91f3f7c"
|
||||
integrity sha512-ypEBTKOy5liFQXZWMchJ3LN0JX1uPI6n7MN7OPHKacqXAxq5gYC30TdO7wqGYQyxD1OrzpobdHC3hDmlRWDg9w==
|
||||
dependencies:
|
||||
array-includes "^3.1.4"
|
||||
array.prototype.flatmap "^1.2.5"
|
||||
doctrine "^2.1.0"
|
||||
estraverse "^5.3.0"
|
||||
jsx-ast-utils "^2.4.1 || ^3.0.0"
|
||||
minimatch "^3.0.4"
|
||||
minimatch "^3.1.2"
|
||||
object.entries "^1.1.5"
|
||||
object.fromentries "^2.0.5"
|
||||
object.hasown "^1.1.0"
|
||||
object.values "^1.1.5"
|
||||
prop-types "^15.7.2"
|
||||
prop-types "^15.8.1"
|
||||
resolve "^2.0.0-next.3"
|
||||
semver "^6.3.0"
|
||||
string.prototype.matchall "^4.0.6"
|
||||
@@ -2180,16 +2180,11 @@ estraverse@^4.1.1:
|
||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
|
||||
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
|
||||
|
||||
estraverse@^5.1.0, estraverse@^5.3.0:
|
||||
estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
|
||||
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
|
||||
|
||||
estraverse@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
|
||||
integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
|
||||
|
||||
esutils@^2.0.2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||
@@ -2680,9 +2675,9 @@ is-ci@^3.0.0:
|
||||
ci-info "^3.1.1"
|
||||
|
||||
is-core-module@^2.2.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491"
|
||||
integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
|
||||
integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
@@ -3544,10 +3539,10 @@ min-document@^2.19.0:
|
||||
dependencies:
|
||||
dom-walk "^0.1.0"
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
minimatch@^3.0.4, minimatch@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
@@ -3873,11 +3868,16 @@ phin@^2.9.1:
|
||||
resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c"
|
||||
integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.3:
|
||||
picomatch@^2.0.4:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
|
||||
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
|
||||
|
||||
picomatch@^2.2.3:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
pino-abstract-transport@v0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz#4b54348d8f73713bfd14e3dc44228739aa13d9c0"
|
||||
@@ -4010,7 +4010,7 @@ prompts@^2.0.1:
|
||||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.5"
|
||||
|
||||
prop-types@^15.7.2:
|
||||
prop-types@^15.8.1:
|
||||
version "15.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||
|
||||
Reference in New Issue
Block a user