mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
chore: format everything
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
export * from './types'
|
||||
export * from './websocket'
|
||||
export * from './websocket'
|
||||
|
||||
@@ -8,12 +8,15 @@ export abstract class AbstractSocketClient extends EventEmitter {
|
||||
abstract get isClosing(): boolean
|
||||
abstract get isConnecting(): boolean
|
||||
|
||||
constructor(public url: URL, public config: SocketConfig) {
|
||||
constructor(
|
||||
public url: URL,
|
||||
public config: SocketConfig
|
||||
) {
|
||||
super()
|
||||
this.setMaxListeners(0)
|
||||
}
|
||||
|
||||
abstract connect(): Promise<void>
|
||||
abstract close(): Promise<void>
|
||||
abstract send(str: Uint8Array | string, cb?: (err?: Error) => void): boolean;
|
||||
}
|
||||
abstract send(str: Uint8Array | string, cb?: (err?: Error) => void): boolean
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { DEFAULT_ORIGIN } from '../../Defaults'
|
||||
import { AbstractSocketClient } from './types'
|
||||
|
||||
export class WebSocketClient extends AbstractSocketClient {
|
||||
|
||||
protected socket: WebSocket | null = null
|
||||
|
||||
get isOpen(): boolean {
|
||||
@@ -20,7 +19,7 @@ export class WebSocketClient extends AbstractSocketClient {
|
||||
}
|
||||
|
||||
async connect(): Promise<void> {
|
||||
if(this.socket) {
|
||||
if (this.socket) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -29,20 +28,20 @@ export class WebSocketClient extends AbstractSocketClient {
|
||||
headers: this.config.options?.headers as {},
|
||||
handshakeTimeout: this.config.connectTimeoutMs,
|
||||
timeout: this.config.connectTimeoutMs,
|
||||
agent: this.config.agent,
|
||||
agent: this.config.agent
|
||||
})
|
||||
|
||||
this.socket.setMaxListeners(0)
|
||||
|
||||
const events = ['close', 'error', 'upgrade', 'message', 'open', 'ping', 'pong', 'unexpected-response']
|
||||
|
||||
for(const event of events) {
|
||||
for (const event of events) {
|
||||
this.socket?.on(event, (...args: any[]) => this.emit(event, ...args))
|
||||
}
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
if(!this.socket) {
|
||||
if (!this.socket) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,43 +1,46 @@
|
||||
import { GetCatalogOptions, ProductCreate, ProductUpdate, SocketConfig } from '../Types'
|
||||
import { parseCatalogNode, parseCollectionsNode, parseOrderDetailsNode, parseProductNode, toProductNode, uploadingNecessaryImagesOfProduct } from '../Utils/business'
|
||||
import {
|
||||
parseCatalogNode,
|
||||
parseCollectionsNode,
|
||||
parseOrderDetailsNode,
|
||||
parseProductNode,
|
||||
toProductNode,
|
||||
uploadingNecessaryImagesOfProduct
|
||||
} from '../Utils/business'
|
||||
import { BinaryNode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary'
|
||||
import { getBinaryNodeChild } from '../WABinary/generic-utils'
|
||||
import { makeMessagesRecvSocket } from './messages-recv'
|
||||
|
||||
export const makeBusinessSocket = (config: SocketConfig) => {
|
||||
const sock = makeMessagesRecvSocket(config)
|
||||
const {
|
||||
authState,
|
||||
query,
|
||||
waUploadToServer
|
||||
} = sock
|
||||
const { authState, query, waUploadToServer } = sock
|
||||
|
||||
const getCatalog = async({ jid, limit, cursor }: GetCatalogOptions) => {
|
||||
const getCatalog = async ({ jid, limit, cursor }: GetCatalogOptions) => {
|
||||
jid = jid || authState.creds.me?.id
|
||||
jid = jidNormalizedUser(jid)
|
||||
|
||||
const queryParamNodes: BinaryNode[] = [
|
||||
{
|
||||
tag: 'limit',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: Buffer.from((limit || 10).toString())
|
||||
},
|
||||
{
|
||||
tag: 'width',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: Buffer.from('100')
|
||||
},
|
||||
{
|
||||
tag: 'height',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: Buffer.from('100')
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
if(cursor) {
|
||||
if (cursor) {
|
||||
queryParamNodes.push({
|
||||
tag: 'after',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: cursor
|
||||
})
|
||||
}
|
||||
@@ -54,7 +57,7 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
||||
tag: 'product_catalog',
|
||||
attrs: {
|
||||
jid,
|
||||
'allow_shop_source': 'true'
|
||||
allow_shop_source: 'true'
|
||||
},
|
||||
content: queryParamNodes
|
||||
}
|
||||
@@ -63,7 +66,7 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
||||
return parseCatalogNode(result)
|
||||
}
|
||||
|
||||
const getCollections = async(jid?: string, limit = 51) => {
|
||||
const getCollections = async (jid?: string, limit = 51) => {
|
||||
jid = jid || authState.creds.me?.id
|
||||
jid = jidNormalizedUser(jid)
|
||||
const result = await query({
|
||||
@@ -72,33 +75,33 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'get',
|
||||
xmlns: 'w:biz:catalog',
|
||||
'smax_id': '35'
|
||||
smax_id: '35'
|
||||
},
|
||||
content: [
|
||||
{
|
||||
tag: 'collections',
|
||||
attrs: {
|
||||
'biz_jid': jid,
|
||||
biz_jid: jid
|
||||
},
|
||||
content: [
|
||||
{
|
||||
tag: 'collection_limit',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: Buffer.from(limit.toString())
|
||||
},
|
||||
{
|
||||
tag: 'item_limit',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: Buffer.from(limit.toString())
|
||||
},
|
||||
{
|
||||
tag: 'width',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: Buffer.from('100')
|
||||
},
|
||||
{
|
||||
tag: 'height',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: Buffer.from('100')
|
||||
}
|
||||
]
|
||||
@@ -109,14 +112,14 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
||||
return parseCollectionsNode(result)
|
||||
}
|
||||
|
||||
const getOrderDetails = async(orderId: string, tokenBase64: string) => {
|
||||
const getOrderDetails = async (orderId: string, tokenBase64: string) => {
|
||||
const result = await query({
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'get',
|
||||
xmlns: 'fb:thrift_iq',
|
||||
'smax_id': '5'
|
||||
smax_id: '5'
|
||||
},
|
||||
content: [
|
||||
{
|
||||
@@ -128,23 +131,23 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
||||
content: [
|
||||
{
|
||||
tag: 'image_dimensions',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: [
|
||||
{
|
||||
tag: 'width',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: Buffer.from('100')
|
||||
},
|
||||
{
|
||||
tag: 'height',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: Buffer.from('100')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
tag: 'token',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: Buffer.from(tokenBase64)
|
||||
}
|
||||
]
|
||||
@@ -155,7 +158,7 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
||||
return parseOrderDetailsNode(result)
|
||||
}
|
||||
|
||||
const productUpdate = async(productId: string, update: ProductUpdate) => {
|
||||
const productUpdate = async (productId: string, update: ProductUpdate) => {
|
||||
update = await uploadingNecessaryImagesOfProduct(update, waUploadToServer)
|
||||
const editNode = toProductNode(productId, update)
|
||||
|
||||
@@ -174,12 +177,12 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
||||
editNode,
|
||||
{
|
||||
tag: 'width',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: '100'
|
||||
},
|
||||
{
|
||||
tag: 'height',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: '100'
|
||||
}
|
||||
]
|
||||
@@ -193,7 +196,7 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
||||
return parseProductNode(productNode!)
|
||||
}
|
||||
|
||||
const productCreate = async(create: ProductCreate) => {
|
||||
const productCreate = async (create: ProductCreate) => {
|
||||
// ensure isHidden is defined
|
||||
create.isHidden = !!create.isHidden
|
||||
create = await uploadingNecessaryImagesOfProduct(create, waUploadToServer)
|
||||
@@ -214,12 +217,12 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
||||
createNode,
|
||||
{
|
||||
tag: 'width',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: '100'
|
||||
},
|
||||
{
|
||||
tag: 'height',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: '100'
|
||||
}
|
||||
]
|
||||
@@ -233,7 +236,7 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
||||
return parseProductNode(productNode!)
|
||||
}
|
||||
|
||||
const productDelete = async(productIds: string[]) => {
|
||||
const productDelete = async (productIds: string[]) => {
|
||||
const result = await query({
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
@@ -245,19 +248,17 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
||||
{
|
||||
tag: 'product_catalog_delete',
|
||||
attrs: { v: '1' },
|
||||
content: productIds.map(
|
||||
id => ({
|
||||
tag: 'product',
|
||||
attrs: { },
|
||||
content: [
|
||||
{
|
||||
tag: 'id',
|
||||
attrs: { },
|
||||
content: Buffer.from(id)
|
||||
}
|
||||
]
|
||||
})
|
||||
)
|
||||
content: productIds.map(id => ({
|
||||
tag: 'product',
|
||||
attrs: {},
|
||||
content: [
|
||||
{
|
||||
tag: 'id',
|
||||
attrs: {},
|
||||
content: Buffer.from(id)
|
||||
}
|
||||
]
|
||||
}))
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,62 +1,70 @@
|
||||
import { proto } from '../../WAProto'
|
||||
import { GroupMetadata, GroupParticipant, ParticipantAction, SocketConfig, WAMessageKey, WAMessageStubType } from '../Types'
|
||||
import {
|
||||
GroupMetadata,
|
||||
GroupParticipant,
|
||||
ParticipantAction,
|
||||
SocketConfig,
|
||||
WAMessageKey,
|
||||
WAMessageStubType
|
||||
} from '../Types'
|
||||
import { generateMessageIDV2, unixTimestampSeconds } from '../Utils'
|
||||
import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString, jidEncode, jidNormalizedUser } from '../WABinary'
|
||||
import {
|
||||
BinaryNode,
|
||||
getBinaryNodeChild,
|
||||
getBinaryNodeChildren,
|
||||
getBinaryNodeChildString,
|
||||
jidEncode,
|
||||
jidNormalizedUser
|
||||
} from '../WABinary'
|
||||
import { makeChatsSocket } from './chats'
|
||||
|
||||
export const makeGroupsSocket = (config: SocketConfig) => {
|
||||
const sock = makeChatsSocket(config)
|
||||
const { authState, ev, query, upsertMessage } = sock
|
||||
|
||||
const groupQuery = async(jid: string, type: 'get' | 'set', content: BinaryNode[]) => (
|
||||
const groupQuery = async (jid: string, type: 'get' | 'set', content: BinaryNode[]) =>
|
||||
query({
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
type,
|
||||
xmlns: 'w:g2',
|
||||
to: jid,
|
||||
to: jid
|
||||
},
|
||||
content
|
||||
})
|
||||
)
|
||||
|
||||
const groupMetadata = async(jid: string) => {
|
||||
const result = await groupQuery(
|
||||
jid,
|
||||
'get',
|
||||
[ { tag: 'query', attrs: { request: 'interactive' } } ]
|
||||
)
|
||||
const groupMetadata = async (jid: string) => {
|
||||
const result = await groupQuery(jid, 'get', [{ tag: 'query', attrs: { request: 'interactive' } }])
|
||||
return extractGroupMetadata(result)
|
||||
}
|
||||
|
||||
|
||||
const groupFetchAllParticipating = async() => {
|
||||
const groupFetchAllParticipating = async () => {
|
||||
const result = await query({
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
to: '@g.us',
|
||||
xmlns: 'w:g2',
|
||||
type: 'get',
|
||||
type: 'get'
|
||||
},
|
||||
content: [
|
||||
{
|
||||
tag: 'participating',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: [
|
||||
{ tag: 'participants', attrs: { } },
|
||||
{ tag: 'description', attrs: { } }
|
||||
{ tag: 'participants', attrs: {} },
|
||||
{ tag: 'description', attrs: {} }
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
const data: { [_: string]: GroupMetadata } = { }
|
||||
const data: { [_: string]: GroupMetadata } = {}
|
||||
const groupsChild = getBinaryNodeChild(result, 'groups')
|
||||
if(groupsChild) {
|
||||
if (groupsChild) {
|
||||
const groups = getBinaryNodeChildren(groupsChild, 'group')
|
||||
for(const groupNode of groups) {
|
||||
for (const groupNode of groups) {
|
||||
const meta = extractGroupMetadata({
|
||||
tag: 'result',
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: [groupNode]
|
||||
})
|
||||
data[meta.id] = meta
|
||||
@@ -68,9 +76,9 @@ export const makeGroupsSocket = (config: SocketConfig) => {
|
||||
return data
|
||||
}
|
||||
|
||||
sock.ws.on('CB:ib,,dirty', async(node: BinaryNode) => {
|
||||
sock.ws.on('CB:ib,,dirty', async (node: BinaryNode) => {
|
||||
const { attrs } = getBinaryNodeChild(node, 'dirty')!
|
||||
if(attrs.type !== 'groups') {
|
||||
if (attrs.type !== 'groups') {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -81,89 +89,69 @@ export const makeGroupsSocket = (config: SocketConfig) => {
|
||||
return {
|
||||
...sock,
|
||||
groupMetadata,
|
||||
groupCreate: async(subject: string, participants: string[]) => {
|
||||
groupCreate: async (subject: string, participants: string[]) => {
|
||||
const key = generateMessageIDV2()
|
||||
const result = await groupQuery(
|
||||
'@g.us',
|
||||
'set',
|
||||
[
|
||||
{
|
||||
tag: 'create',
|
||||
attrs: {
|
||||
subject,
|
||||
key
|
||||
},
|
||||
content: participants.map(jid => ({
|
||||
tag: 'participant',
|
||||
attrs: { jid }
|
||||
}))
|
||||
}
|
||||
]
|
||||
)
|
||||
const result = await groupQuery('@g.us', 'set', [
|
||||
{
|
||||
tag: 'create',
|
||||
attrs: {
|
||||
subject,
|
||||
key
|
||||
},
|
||||
content: participants.map(jid => ({
|
||||
tag: 'participant',
|
||||
attrs: { jid }
|
||||
}))
|
||||
}
|
||||
])
|
||||
return extractGroupMetadata(result)
|
||||
},
|
||||
groupLeave: async(id: string) => {
|
||||
await groupQuery(
|
||||
'@g.us',
|
||||
'set',
|
||||
[
|
||||
{
|
||||
tag: 'leave',
|
||||
attrs: { },
|
||||
content: [
|
||||
{ tag: 'group', attrs: { id } }
|
||||
]
|
||||
}
|
||||
]
|
||||
)
|
||||
groupLeave: async (id: string) => {
|
||||
await groupQuery('@g.us', 'set', [
|
||||
{
|
||||
tag: 'leave',
|
||||
attrs: {},
|
||||
content: [{ tag: 'group', attrs: { id } }]
|
||||
}
|
||||
])
|
||||
},
|
||||
groupUpdateSubject: async(jid: string, subject: string) => {
|
||||
await groupQuery(
|
||||
jid,
|
||||
'set',
|
||||
[
|
||||
{
|
||||
tag: 'subject',
|
||||
attrs: { },
|
||||
content: Buffer.from(subject, 'utf-8')
|
||||
}
|
||||
]
|
||||
)
|
||||
groupUpdateSubject: async (jid: string, subject: string) => {
|
||||
await groupQuery(jid, 'set', [
|
||||
{
|
||||
tag: 'subject',
|
||||
attrs: {},
|
||||
content: Buffer.from(subject, 'utf-8')
|
||||
}
|
||||
])
|
||||
},
|
||||
groupRequestParticipantsList: async(jid: string) => {
|
||||
const result = await groupQuery(
|
||||
jid,
|
||||
'get',
|
||||
[
|
||||
{
|
||||
tag: 'membership_approval_requests',
|
||||
attrs: {}
|
||||
}
|
||||
]
|
||||
)
|
||||
groupRequestParticipantsList: async (jid: string) => {
|
||||
const result = await groupQuery(jid, 'get', [
|
||||
{
|
||||
tag: 'membership_approval_requests',
|
||||
attrs: {}
|
||||
}
|
||||
])
|
||||
const node = getBinaryNodeChild(result, 'membership_approval_requests')
|
||||
const participants = getBinaryNodeChildren(node, 'membership_approval_request')
|
||||
return participants.map(v => v.attrs)
|
||||
},
|
||||
groupRequestParticipantsUpdate: async(jid: string, participants: string[], action: 'approve' | 'reject') => {
|
||||
const result = await groupQuery(
|
||||
jid,
|
||||
'set',
|
||||
[{
|
||||
groupRequestParticipantsUpdate: async (jid: string, participants: string[], action: 'approve' | 'reject') => {
|
||||
const result = await groupQuery(jid, 'set', [
|
||||
{
|
||||
tag: 'membership_requests_action',
|
||||
attrs: {},
|
||||
content: [
|
||||
content: [
|
||||
{
|
||||
tag: action,
|
||||
attrs: { },
|
||||
attrs: {},
|
||||
content: participants.map(jid => ({
|
||||
tag: 'participant',
|
||||
attrs: { jid }
|
||||
}))
|
||||
}
|
||||
]
|
||||
}]
|
||||
)
|
||||
}
|
||||
])
|
||||
const node = getBinaryNodeChild(result, 'membership_requests_action')
|
||||
const nodeAction = getBinaryNodeChild(node, action)
|
||||
const participantsAffected = getBinaryNodeChildren(nodeAction, 'participant')
|
||||
@@ -171,63 +159,49 @@ export const makeGroupsSocket = (config: SocketConfig) => {
|
||||
return { status: p.attrs.error || '200', jid: p.attrs.jid }
|
||||
})
|
||||
},
|
||||
groupParticipantsUpdate: async(
|
||||
jid: string,
|
||||
participants: string[],
|
||||
action: ParticipantAction
|
||||
) => {
|
||||
const result = await groupQuery(
|
||||
jid,
|
||||
'set',
|
||||
[
|
||||
{
|
||||
tag: action,
|
||||
attrs: { },
|
||||
content: participants.map(jid => ({
|
||||
tag: 'participant',
|
||||
attrs: { jid }
|
||||
}))
|
||||
}
|
||||
]
|
||||
)
|
||||
groupParticipantsUpdate: async (jid: string, participants: string[], action: ParticipantAction) => {
|
||||
const result = await groupQuery(jid, 'set', [
|
||||
{
|
||||
tag: action,
|
||||
attrs: {},
|
||||
content: participants.map(jid => ({
|
||||
tag: 'participant',
|
||||
attrs: { jid }
|
||||
}))
|
||||
}
|
||||
])
|
||||
const node = getBinaryNodeChild(result, action)
|
||||
const participantsAffected = getBinaryNodeChildren(node, 'participant')
|
||||
return participantsAffected.map(p => {
|
||||
return { status: p.attrs.error || '200', jid: p.attrs.jid, content: p }
|
||||
})
|
||||
},
|
||||
groupUpdateDescription: async(jid: string, description?: string) => {
|
||||
groupUpdateDescription: async (jid: string, description?: string) => {
|
||||
const metadata = await groupMetadata(jid)
|
||||
const prev = metadata.descId ?? null
|
||||
|
||||
await groupQuery(
|
||||
jid,
|
||||
'set',
|
||||
[
|
||||
{
|
||||
tag: 'description',
|
||||
attrs: {
|
||||
...(description ? { id: generateMessageIDV2() } : { delete: 'true' }),
|
||||
...(prev ? { prev } : {})
|
||||
},
|
||||
content: description ? [
|
||||
{ tag: 'body', attrs: {}, content: Buffer.from(description, 'utf-8') }
|
||||
] : undefined
|
||||
}
|
||||
]
|
||||
)
|
||||
await groupQuery(jid, 'set', [
|
||||
{
|
||||
tag: 'description',
|
||||
attrs: {
|
||||
...(description ? { id: generateMessageIDV2() } : { delete: 'true' }),
|
||||
...(prev ? { prev } : {})
|
||||
},
|
||||
content: description ? [{ tag: 'body', attrs: {}, content: Buffer.from(description, 'utf-8') }] : undefined
|
||||
}
|
||||
])
|
||||
},
|
||||
groupInviteCode: async(jid: string) => {
|
||||
groupInviteCode: async (jid: string) => {
|
||||
const result = await groupQuery(jid, 'get', [{ tag: 'invite', attrs: {} }])
|
||||
const inviteNode = getBinaryNodeChild(result, 'invite')
|
||||
return inviteNode?.attrs.code
|
||||
},
|
||||
groupRevokeInvite: async(jid: string) => {
|
||||
groupRevokeInvite: async (jid: string) => {
|
||||
const result = await groupQuery(jid, 'set', [{ tag: 'invite', attrs: {} }])
|
||||
const inviteNode = getBinaryNodeChild(result, 'invite')
|
||||
return inviteNode?.attrs.code
|
||||
},
|
||||
groupAcceptInvite: async(code: string) => {
|
||||
groupAcceptInvite: async (code: string) => {
|
||||
const results = await groupQuery('@g.us', 'set', [{ tag: 'invite', attrs: { code } }])
|
||||
const result = getBinaryNodeChild(results, 'group')
|
||||
return result?.attrs.jid
|
||||
@@ -239,8 +213,10 @@ export const makeGroupsSocket = (config: SocketConfig) => {
|
||||
* @param invitedJid jid of person you invited
|
||||
* @returns true if successful
|
||||
*/
|
||||
groupRevokeInviteV4: async(groupJid: string, invitedJid: string) => {
|
||||
const result = await groupQuery(groupJid, 'set', [{ tag: 'revoke', attrs: {}, content: [{ tag: 'participant', attrs: { jid: invitedJid } }] }])
|
||||
groupRevokeInviteV4: async (groupJid: string, invitedJid: string) => {
|
||||
const result = await groupQuery(groupJid, 'set', [
|
||||
{ tag: 'revoke', attrs: {}, content: [{ tag: 'participant', attrs: { jid: invitedJid } }] }
|
||||
])
|
||||
return !!result
|
||||
},
|
||||
|
||||
@@ -249,87 +225,90 @@ export const makeGroupsSocket = (config: SocketConfig) => {
|
||||
* @param key the key of the invite message, or optionally only provide the jid of the person who sent the invite
|
||||
* @param inviteMessage the message to accept
|
||||
*/
|
||||
groupAcceptInviteV4: ev.createBufferedFunction(async(key: string | WAMessageKey, inviteMessage: proto.Message.IGroupInviteMessage) => {
|
||||
key = typeof key === 'string' ? { remoteJid: key } : key
|
||||
const results = await groupQuery(inviteMessage.groupJid!, 'set', [{
|
||||
tag: 'accept',
|
||||
attrs: {
|
||||
code: inviteMessage.inviteCode!,
|
||||
expiration: inviteMessage.inviteExpiration!.toString(),
|
||||
admin: key.remoteJid!
|
||||
}
|
||||
}])
|
||||
|
||||
// if we have the full message key
|
||||
// update the invite message to be expired
|
||||
if(key.id) {
|
||||
// create new invite message that is expired
|
||||
inviteMessage = proto.Message.GroupInviteMessage.fromObject(inviteMessage)
|
||||
inviteMessage.inviteExpiration = 0
|
||||
inviteMessage.inviteCode = ''
|
||||
ev.emit('messages.update', [
|
||||
groupAcceptInviteV4: ev.createBufferedFunction(
|
||||
async (key: string | WAMessageKey, inviteMessage: proto.Message.IGroupInviteMessage) => {
|
||||
key = typeof key === 'string' ? { remoteJid: key } : key
|
||||
const results = await groupQuery(inviteMessage.groupJid!, 'set', [
|
||||
{
|
||||
key,
|
||||
update: {
|
||||
message: {
|
||||
groupInviteMessage: inviteMessage
|
||||
}
|
||||
tag: 'accept',
|
||||
attrs: {
|
||||
code: inviteMessage.inviteCode!,
|
||||
expiration: inviteMessage.inviteExpiration!.toString(),
|
||||
admin: key.remoteJid!
|
||||
}
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
// generate the group add message
|
||||
await upsertMessage(
|
||||
{
|
||||
key: {
|
||||
remoteJid: inviteMessage.groupJid,
|
||||
id: generateMessageIDV2(sock.user?.id),
|
||||
fromMe: false,
|
||||
// if we have the full message key
|
||||
// update the invite message to be expired
|
||||
if (key.id) {
|
||||
// create new invite message that is expired
|
||||
inviteMessage = proto.Message.GroupInviteMessage.fromObject(inviteMessage)
|
||||
inviteMessage.inviteExpiration = 0
|
||||
inviteMessage.inviteCode = ''
|
||||
ev.emit('messages.update', [
|
||||
{
|
||||
key,
|
||||
update: {
|
||||
message: {
|
||||
groupInviteMessage: inviteMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
// generate the group add message
|
||||
await upsertMessage(
|
||||
{
|
||||
key: {
|
||||
remoteJid: inviteMessage.groupJid,
|
||||
id: generateMessageIDV2(sock.user?.id),
|
||||
fromMe: false,
|
||||
participant: key.remoteJid
|
||||
},
|
||||
messageStubType: WAMessageStubType.GROUP_PARTICIPANT_ADD,
|
||||
messageStubParameters: [authState.creds.me!.id],
|
||||
participant: key.remoteJid,
|
||||
messageTimestamp: unixTimestampSeconds()
|
||||
},
|
||||
messageStubType: WAMessageStubType.GROUP_PARTICIPANT_ADD,
|
||||
messageStubParameters: [
|
||||
authState.creds.me!.id
|
||||
],
|
||||
participant: key.remoteJid,
|
||||
messageTimestamp: unixTimestampSeconds()
|
||||
},
|
||||
'notify'
|
||||
)
|
||||
'notify'
|
||||
)
|
||||
|
||||
return results.attrs.from
|
||||
}),
|
||||
groupGetInviteInfo: async(code: string) => {
|
||||
return results.attrs.from
|
||||
}
|
||||
),
|
||||
groupGetInviteInfo: async (code: string) => {
|
||||
const results = await groupQuery('@g.us', 'get', [{ tag: 'invite', attrs: { code } }])
|
||||
return extractGroupMetadata(results)
|
||||
},
|
||||
groupToggleEphemeral: async(jid: string, ephemeralExpiration: number) => {
|
||||
const content: BinaryNode = ephemeralExpiration ?
|
||||
{ tag: 'ephemeral', attrs: { expiration: ephemeralExpiration.toString() } } :
|
||||
{ tag: 'not_ephemeral', attrs: { } }
|
||||
groupToggleEphemeral: async (jid: string, ephemeralExpiration: number) => {
|
||||
const content: BinaryNode = ephemeralExpiration
|
||||
? { tag: 'ephemeral', attrs: { expiration: ephemeralExpiration.toString() } }
|
||||
: { tag: 'not_ephemeral', attrs: {} }
|
||||
await groupQuery(jid, 'set', [content])
|
||||
},
|
||||
groupSettingUpdate: async(jid: string, setting: 'announcement' | 'not_announcement' | 'locked' | 'unlocked') => {
|
||||
await groupQuery(jid, 'set', [ { tag: setting, attrs: { } } ])
|
||||
groupSettingUpdate: async (jid: string, setting: 'announcement' | 'not_announcement' | 'locked' | 'unlocked') => {
|
||||
await groupQuery(jid, 'set', [{ tag: setting, attrs: {} }])
|
||||
},
|
||||
groupMemberAddMode: async(jid: string, mode: 'admin_add' | 'all_member_add') => {
|
||||
await groupQuery(jid, 'set', [ { tag: 'member_add_mode', attrs: { }, content: mode } ])
|
||||
groupMemberAddMode: async (jid: string, mode: 'admin_add' | 'all_member_add') => {
|
||||
await groupQuery(jid, 'set', [{ tag: 'member_add_mode', attrs: {}, content: mode }])
|
||||
},
|
||||
groupJoinApprovalMode: async(jid: string, mode: 'on' | 'off') => {
|
||||
await groupQuery(jid, 'set', [ { tag: 'membership_approval_mode', attrs: { }, content: [ { tag: 'group_join', attrs: { state: mode } } ] } ])
|
||||
groupJoinApprovalMode: async (jid: string, mode: 'on' | 'off') => {
|
||||
await groupQuery(jid, 'set', [
|
||||
{ tag: 'membership_approval_mode', attrs: {}, content: [{ tag: 'group_join', attrs: { state: mode } }] }
|
||||
])
|
||||
},
|
||||
groupFetchAllParticipating
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const extractGroupMetadata = (result: BinaryNode) => {
|
||||
const group = getBinaryNodeChild(result, 'group')!
|
||||
const descChild = getBinaryNodeChild(group, 'description')
|
||||
let desc: string | undefined
|
||||
let descId: string | undefined
|
||||
if(descChild) {
|
||||
if (descChild) {
|
||||
desc = getBinaryNodeChildString(descChild, 'body')
|
||||
descId = descChild.attrs.id
|
||||
}
|
||||
@@ -355,14 +334,12 @@ export const extractGroupMetadata = (result: BinaryNode) => {
|
||||
isCommunityAnnounce: !!getBinaryNodeChild(group, 'default_sub_group'),
|
||||
joinApprovalMode: !!getBinaryNodeChild(group, 'membership_approval_mode'),
|
||||
memberAddMode,
|
||||
participants: getBinaryNodeChildren(group, 'participant').map(
|
||||
({ attrs }) => {
|
||||
return {
|
||||
id: attrs.jid,
|
||||
admin: (attrs.type || null) as GroupParticipant['admin'],
|
||||
}
|
||||
participants: getBinaryNodeChildren(group, 'participant').map(({ attrs }) => {
|
||||
return {
|
||||
id: attrs.jid,
|
||||
admin: (attrs.type || null) as GroupParticipant['admin']
|
||||
}
|
||||
),
|
||||
}),
|
||||
ephemeralDuration: eph ? +eph : undefined
|
||||
}
|
||||
return metadata
|
||||
|
||||
@@ -3,11 +3,10 @@ import { UserFacingSocketConfig } from '../Types'
|
||||
import { makeBusinessSocket } from './business'
|
||||
|
||||
// export the last socket layer
|
||||
const makeWASocket = (config: UserFacingSocketConfig) => (
|
||||
const makeWASocket = (config: UserFacingSocketConfig) =>
|
||||
makeBusinessSocket({
|
||||
...DEFAULT_CONNECTION_CONFIG,
|
||||
...config
|
||||
})
|
||||
)
|
||||
|
||||
export default makeWASocket
|
||||
export default makeWASocket
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -28,7 +28,7 @@ import {
|
||||
getPlatformId,
|
||||
makeEventBuffer,
|
||||
makeNoiseHandler,
|
||||
promiseTimeout,
|
||||
promiseTimeout
|
||||
} from '../Utils'
|
||||
import {
|
||||
assertNodeErrorFree,
|
||||
@@ -61,21 +61,22 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
defaultQueryTimeoutMs,
|
||||
transactionOpts,
|
||||
qrTimeout,
|
||||
makeSignalRepository,
|
||||
makeSignalRepository
|
||||
} = config
|
||||
|
||||
if(printQRInTerminal) {
|
||||
console.warn('⚠️ The printQRInTerminal option has been deprecated. You will no longer receive QR codes in the terminal automatically. Please listen to the connection.update event yourself and handle the QR your way. You can remove this message by removing this opttion. This message will be removed in a future version.')
|
||||
if (printQRInTerminal) {
|
||||
console.warn(
|
||||
'⚠️ The printQRInTerminal option has been deprecated. You will no longer receive QR codes in the terminal automatically. Please listen to the connection.update event yourself and handle the QR your way. You can remove this message by removing this opttion. This message will be removed in a future version.'
|
||||
)
|
||||
}
|
||||
|
||||
const url = typeof waWebSocketUrl === 'string' ? new URL(waWebSocketUrl) : waWebSocketUrl
|
||||
|
||||
|
||||
if(config.mobile || url.protocol === 'tcp:') {
|
||||
if (config.mobile || url.protocol === 'tcp:') {
|
||||
throw new Boom('Mobile API is not supported anymore', { statusCode: DisconnectReason.loggedOut })
|
||||
}
|
||||
|
||||
if(url.protocol === 'wss' && authState?.creds?.routingInfo) {
|
||||
if (url.protocol === 'wss' && authState?.creds?.routingInfo) {
|
||||
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'))
|
||||
}
|
||||
|
||||
@@ -110,28 +111,25 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
|
||||
const sendPromise = promisify(ws.send)
|
||||
/** send a raw buffer */
|
||||
const sendRawMessage = async(data: Uint8Array | Buffer) => {
|
||||
if(!ws.isOpen) {
|
||||
const sendRawMessage = async (data: Uint8Array | Buffer) => {
|
||||
if (!ws.isOpen) {
|
||||
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })
|
||||
}
|
||||
|
||||
const bytes = noise.encodeFrame(data)
|
||||
await promiseTimeout<void>(
|
||||
connectTimeoutMs,
|
||||
async(resolve, reject) => {
|
||||
try {
|
||||
await sendPromise.call(ws, bytes)
|
||||
resolve()
|
||||
} catch(error) {
|
||||
reject(error)
|
||||
}
|
||||
await promiseTimeout<void>(connectTimeoutMs, async (resolve, reject) => {
|
||||
try {
|
||||
await sendPromise.call(ws, bytes)
|
||||
resolve()
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/** send a binary node */
|
||||
const sendNode = (frame: BinaryNode) => {
|
||||
if(logger.level === 'trace') {
|
||||
if (logger.level === 'trace') {
|
||||
logger.trace({ xml: binaryNodeToString(frame), msg: 'xml send' })
|
||||
}
|
||||
|
||||
@@ -141,15 +139,12 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
|
||||
/** log & process any unexpected errors */
|
||||
const onUnexpectedError = (err: Error | Boom, msg: string) => {
|
||||
logger.error(
|
||||
{ err },
|
||||
`unexpected error in '${msg}'`
|
||||
)
|
||||
logger.error({ err }, `unexpected error in '${msg}'`)
|
||||
}
|
||||
|
||||
/** await the next incoming message */
|
||||
const awaitNextMessage = async<T>(sendMsg?: Uint8Array) => {
|
||||
if(!ws.isOpen) {
|
||||
const awaitNextMessage = async <T>(sendMsg?: Uint8Array) => {
|
||||
if (!ws.isOpen) {
|
||||
throw new Boom('Connection Closed', {
|
||||
statusCode: DisconnectReason.connectionClosed
|
||||
})
|
||||
@@ -164,14 +159,13 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
ws.on('frame', onOpen)
|
||||
ws.on('close', onClose)
|
||||
ws.on('error', onClose)
|
||||
}).finally(() => {
|
||||
ws.off('frame', onOpen)
|
||||
ws.off('close', onClose)
|
||||
ws.off('error', onClose)
|
||||
})
|
||||
.finally(() => {
|
||||
ws.off('frame', onOpen)
|
||||
ws.off('close', onClose)
|
||||
ws.off('error', onClose)
|
||||
})
|
||||
|
||||
if(sendMsg) {
|
||||
if (sendMsg) {
|
||||
sendRawMessage(sendMsg).catch(onClose!)
|
||||
}
|
||||
|
||||
@@ -183,22 +177,20 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
* @param msgId the message tag to await
|
||||
* @param timeoutMs timeout after which the promise will reject
|
||||
*/
|
||||
const waitForMessage = async<T>(msgId: string, timeoutMs = defaultQueryTimeoutMs) => {
|
||||
const waitForMessage = async <T>(msgId: string, timeoutMs = defaultQueryTimeoutMs) => {
|
||||
let onRecv: (json) => void
|
||||
let onErr: (err) => void
|
||||
try {
|
||||
const result = await promiseTimeout<T>(timeoutMs,
|
||||
(resolve, reject) => {
|
||||
onRecv = resolve
|
||||
onErr = err => {
|
||||
reject(err || new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed }))
|
||||
}
|
||||
const result = await promiseTimeout<T>(timeoutMs, (resolve, reject) => {
|
||||
onRecv = resolve
|
||||
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)
|
||||
},
|
||||
)
|
||||
ws.on(`TAG:${msgId}`, onRecv)
|
||||
ws.on('close', onErr) // if the socket closes, you'll never receive the message
|
||||
ws.off('error', onErr)
|
||||
})
|
||||
|
||||
return result as any
|
||||
} finally {
|
||||
@@ -209,19 +201,16 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
}
|
||||
|
||||
/** send a query, and wait for its response. auto-generates message ID if not provided */
|
||||
const query = async(node: BinaryNode, timeoutMs?: number) => {
|
||||
if(!node.attrs.id) {
|
||||
const query = async (node: BinaryNode, timeoutMs?: number) => {
|
||||
if (!node.attrs.id) {
|
||||
node.attrs.id = generateMessageTag()
|
||||
}
|
||||
|
||||
const msgId = node.attrs.id
|
||||
|
||||
const [result] = await Promise.all([
|
||||
waitForMessage(msgId, timeoutMs),
|
||||
sendNode(node)
|
||||
])
|
||||
const [result] = await Promise.all([waitForMessage(msgId, timeoutMs), sendNode(node)])
|
||||
|
||||
if('tag' in result) {
|
||||
if ('tag' in result) {
|
||||
assertNodeErrorFree(result)
|
||||
}
|
||||
|
||||
@@ -229,7 +218,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
}
|
||||
|
||||
/** connection handshake */
|
||||
const validateConnection = async() => {
|
||||
const validateConnection = async () => {
|
||||
let helloMsg: proto.IHandshakeMessage = {
|
||||
clientHello: { ephemeral: ephemeralKeyPair.public }
|
||||
}
|
||||
@@ -247,7 +236,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
const keyEnc = await noise.processHandshake(handshake, creds.noiseKey)
|
||||
|
||||
let node: proto.IClientPayload
|
||||
if(!creds.me) {
|
||||
if (!creds.me) {
|
||||
node = generateRegistrationNode(creds, config)
|
||||
logger.info({ node }, 'not logged in, attempting registration...')
|
||||
} else {
|
||||
@@ -255,22 +244,20 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
logger.info({ node }, 'logging in...')
|
||||
}
|
||||
|
||||
const payloadEnc = noise.encrypt(
|
||||
proto.ClientPayload.encode(node).finish()
|
||||
)
|
||||
const payloadEnc = noise.encrypt(proto.ClientPayload.encode(node).finish())
|
||||
await sendRawMessage(
|
||||
proto.HandshakeMessage.encode({
|
||||
clientFinish: {
|
||||
static: keyEnc,
|
||||
payload: payloadEnc,
|
||||
},
|
||||
payload: payloadEnc
|
||||
}
|
||||
}).finish()
|
||||
)
|
||||
noise.finishInit()
|
||||
startKeepAliveRequest()
|
||||
}
|
||||
|
||||
const getAvailablePreKeysOnServer = async() => {
|
||||
const getAvailablePreKeysOnServer = async () => {
|
||||
const result = await query({
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
@@ -279,33 +266,29 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
type: 'get',
|
||||
to: S_WHATSAPP_NET
|
||||
},
|
||||
content: [
|
||||
{ tag: 'count', attrs: {} }
|
||||
]
|
||||
content: [{ tag: 'count', attrs: {} }]
|
||||
})
|
||||
const countChild = getBinaryNodeChild(result, 'count')
|
||||
return +countChild!.attrs.value
|
||||
}
|
||||
|
||||
/** generates and uploads a set of pre-keys to the server */
|
||||
const uploadPreKeys = async(count = INITIAL_PREKEY_COUNT) => {
|
||||
await keys.transaction(
|
||||
async() => {
|
||||
logger.info({ count }, 'uploading pre-keys')
|
||||
const { update, node } = await getNextPreKeysNode({ creds, keys }, count)
|
||||
const uploadPreKeys = async (count = INITIAL_PREKEY_COUNT) => {
|
||||
await keys.transaction(async () => {
|
||||
logger.info({ count }, 'uploading pre-keys')
|
||||
const { update, node } = await getNextPreKeysNode({ creds, keys }, count)
|
||||
|
||||
await query(node)
|
||||
ev.emit('creds.update', update)
|
||||
await query(node)
|
||||
ev.emit('creds.update', update)
|
||||
|
||||
logger.info({ count }, 'uploaded pre-keys')
|
||||
}
|
||||
)
|
||||
logger.info({ count }, 'uploaded pre-keys')
|
||||
})
|
||||
}
|
||||
|
||||
const uploadPreKeysToServerIfRequired = async() => {
|
||||
const uploadPreKeysToServerIfRequired = async () => {
|
||||
const preKeyCount = await getAvailablePreKeysOnServer()
|
||||
logger.info(`${preKeyCount} pre-keys found on server`)
|
||||
if(preKeyCount <= MIN_PREKEY_COUNT) {
|
||||
if (preKeyCount <= MIN_PREKEY_COUNT) {
|
||||
await uploadPreKeys()
|
||||
}
|
||||
}
|
||||
@@ -319,10 +302,10 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
|
||||
anyTriggered = ws.emit('frame', frame)
|
||||
// if it's a binary node
|
||||
if(!(frame instanceof Uint8Array)) {
|
||||
if (!(frame instanceof Uint8Array)) {
|
||||
const msgId = frame.attrs.id
|
||||
|
||||
if(logger.level === 'trace') {
|
||||
if (logger.level === 'trace') {
|
||||
logger.trace({ xml: binaryNodeToString(frame), msg: 'recv xml' })
|
||||
}
|
||||
|
||||
@@ -333,7 +316,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
const l1 = frame.attrs || {}
|
||||
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : ''
|
||||
|
||||
for(const key of Object.keys(l1)) {
|
||||
for (const key of Object.keys(l1)) {
|
||||
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
|
||||
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}`, frame) || anyTriggered
|
||||
@@ -342,7 +325,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered
|
||||
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered
|
||||
|
||||
if(!anyTriggered && logger.level === 'debug') {
|
||||
if (!anyTriggered && logger.level === 'debug') {
|
||||
logger.debug({ unhandled: true, msgId, fromMe: false, frame }, 'communication recv')
|
||||
}
|
||||
}
|
||||
@@ -350,16 +333,13 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
}
|
||||
|
||||
const end = (error: Error | undefined) => {
|
||||
if(closed) {
|
||||
if (closed) {
|
||||
logger.trace({ trace: error?.stack }, 'connection already closed')
|
||||
return
|
||||
}
|
||||
|
||||
closed = true
|
||||
logger.info(
|
||||
{ trace: error?.stack },
|
||||
error ? 'connection errored' : 'connection closed'
|
||||
)
|
||||
logger.info({ trace: error?.stack }, error ? 'connection errored' : 'connection closed')
|
||||
|
||||
clearInterval(keepAliveReq)
|
||||
clearTimeout(qrTimer)
|
||||
@@ -369,10 +349,10 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
ws.removeAllListeners('open')
|
||||
ws.removeAllListeners('message')
|
||||
|
||||
if(!ws.isClosed && !ws.isClosing) {
|
||||
if (!ws.isClosed && !ws.isClosing) {
|
||||
try {
|
||||
ws.close()
|
||||
} catch{ }
|
||||
} catch {}
|
||||
}
|
||||
|
||||
ev.emit('connection.update', {
|
||||
@@ -385,12 +365,12 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
ev.removeAllListeners('connection.update')
|
||||
}
|
||||
|
||||
const waitForSocketOpen = async() => {
|
||||
if(ws.isOpen) {
|
||||
const waitForSocketOpen = async () => {
|
||||
if (ws.isOpen) {
|
||||
return
|
||||
}
|
||||
|
||||
if(ws.isClosed || ws.isClosing) {
|
||||
if (ws.isClosed || ws.isClosing) {
|
||||
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })
|
||||
}
|
||||
|
||||
@@ -402,17 +382,16 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
ws.on('open', onOpen)
|
||||
ws.on('close', onClose)
|
||||
ws.on('error', onClose)
|
||||
}).finally(() => {
|
||||
ws.off('open', onOpen)
|
||||
ws.off('close', onClose)
|
||||
ws.off('error', onClose)
|
||||
})
|
||||
.finally(() => {
|
||||
ws.off('open', onOpen)
|
||||
ws.off('close', onClose)
|
||||
ws.off('error', onClose)
|
||||
})
|
||||
}
|
||||
|
||||
const startKeepAliveRequest = () => (
|
||||
keepAliveReq = setInterval(() => {
|
||||
if(!lastDateRecv) {
|
||||
const startKeepAliveRequest = () =>
|
||||
(keepAliveReq = setInterval(() => {
|
||||
if (!lastDateRecv) {
|
||||
lastDateRecv = new Date()
|
||||
}
|
||||
|
||||
@@ -421,49 +400,42 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
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.isOpen) {
|
||||
} else if (ws.isOpen) {
|
||||
// if its all good, send a keep alive request
|
||||
query(
|
||||
{
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
id: generateMessageTag(),
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'get',
|
||||
xmlns: 'w:p',
|
||||
},
|
||||
content: [{ tag: 'ping', attrs: {} }]
|
||||
}
|
||||
)
|
||||
.catch(err => {
|
||||
logger.error({ trace: err.stack }, 'error in sending keep alive')
|
||||
})
|
||||
query({
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
id: generateMessageTag(),
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'get',
|
||||
xmlns: 'w:p'
|
||||
},
|
||||
content: [{ tag: 'ping', attrs: {} }]
|
||||
}).catch(err => {
|
||||
logger.error({ trace: err.stack }, 'error in sending keep alive')
|
||||
})
|
||||
} else {
|
||||
logger.warn('keep alive called when WS not open')
|
||||
}
|
||||
}, keepAliveIntervalMs)
|
||||
)
|
||||
}, keepAliveIntervalMs))
|
||||
/** i have no idea why this exists. pls enlighten me */
|
||||
const sendPassiveIq = (tag: 'passive' | 'active') => (
|
||||
const sendPassiveIq = (tag: 'passive' | 'active') =>
|
||||
query({
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
to: S_WHATSAPP_NET,
|
||||
xmlns: 'passive',
|
||||
type: 'set',
|
||||
type: 'set'
|
||||
},
|
||||
content: [
|
||||
{ tag, attrs: {} }
|
||||
]
|
||||
content: [{ tag, attrs: {} }]
|
||||
})
|
||||
)
|
||||
|
||||
/** logout & invalidate connection */
|
||||
const logout = async(msg?: string) => {
|
||||
const logout = async (msg?: string) => {
|
||||
const jid = authState.creds.me?.id
|
||||
if(jid) {
|
||||
if (jid) {
|
||||
await sendNode({
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
@@ -487,7 +459,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
end(new Boom(msg || 'Intentional Logout', { statusCode: DisconnectReason.loggedOut }))
|
||||
}
|
||||
|
||||
const requestPairingCode = async(phoneNumber: string): Promise<string> => {
|
||||
const requestPairingCode = async (phoneNumber: string): Promise<string> => {
|
||||
authState.creds.pairingCode = bytesToCrockford(randomBytes(5))
|
||||
authState.creds.me = {
|
||||
id: jidEncode(phoneNumber, 's.whatsapp.net'),
|
||||
@@ -572,10 +544,10 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
|
||||
ws.on('message', onMessageReceived)
|
||||
|
||||
ws.on('open', async() => {
|
||||
ws.on('open', async () => {
|
||||
try {
|
||||
await validateConnection()
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'error in validating connection')
|
||||
end(err)
|
||||
}
|
||||
@@ -583,15 +555,17 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
ws.on('error', mapWebSocketError(end))
|
||||
ws.on('close', () => end(new Boom('Connection Terminated', { statusCode: DisconnectReason.connectionClosed })))
|
||||
// the server terminated the connection
|
||||
ws.on('CB:xmlstreamend', () => end(new Boom('Connection Terminated by Server', { statusCode: DisconnectReason.connectionClosed })))
|
||||
ws.on('CB:xmlstreamend', () =>
|
||||
end(new Boom('Connection Terminated by Server', { statusCode: DisconnectReason.connectionClosed }))
|
||||
)
|
||||
// QR gen
|
||||
ws.on('CB:iq,type:set,pair-device', async(stanza: BinaryNode) => {
|
||||
ws.on('CB:iq,type:set,pair-device', async (stanza: BinaryNode) => {
|
||||
const iq: BinaryNode = {
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'result',
|
||||
id: stanza.attrs.id,
|
||||
id: stanza.attrs.id
|
||||
}
|
||||
}
|
||||
await sendNode(iq)
|
||||
@@ -604,12 +578,12 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
|
||||
let qrMs = qrTimeout || 60_000 // time to let a QR live
|
||||
const genPairQR = () => {
|
||||
if(!ws.isOpen) {
|
||||
if (!ws.isOpen) {
|
||||
return
|
||||
}
|
||||
|
||||
const refNode = refNodes.shift()
|
||||
if(!refNode) {
|
||||
if (!refNode) {
|
||||
end(new Boom('QR refs attempts ended', { statusCode: DisconnectReason.timedOut }))
|
||||
return
|
||||
}
|
||||
@@ -627,7 +601,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
})
|
||||
// device paired for the first time
|
||||
// if device pairs successfully, the server asks to restart the connection
|
||||
ws.on('CB:iq,,pair-success', async(stanza: BinaryNode) => {
|
||||
ws.on('CB:iq,,pair-success', async (stanza: BinaryNode) => {
|
||||
logger.debug('pair success recv')
|
||||
try {
|
||||
const { reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds)
|
||||
@@ -641,13 +615,13 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
ev.emit('connection.update', { isNewLogin: true, qr: undefined })
|
||||
|
||||
await sendNode(reply)
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
logger.info({ trace: error.stack }, 'error in pairing')
|
||||
end(error)
|
||||
}
|
||||
})
|
||||
// login complete
|
||||
ws.on('CB:success', async(node: BinaryNode) => {
|
||||
ws.on('CB:success', async (node: BinaryNode) => {
|
||||
await uploadPreKeysToServerIfRequired()
|
||||
await sendPassiveIq('active')
|
||||
|
||||
@@ -677,7 +651,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
})
|
||||
|
||||
ws.on('CB:ib,,offline_preview', (node: BinaryNode) => {
|
||||
logger.info('offline preview received', JSON.stringify(node))
|
||||
logger.info('offline preview received', JSON.stringify(node))
|
||||
sendNode({
|
||||
tag: 'ib',
|
||||
attrs: {},
|
||||
@@ -688,7 +662,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
ws.on('CB:ib,,edge_routing', (node: BinaryNode) => {
|
||||
const edgeRoutingNode = getBinaryNodeChild(node, 'edge_routing')
|
||||
const routingInfo = getBinaryNodeChild(edgeRoutingNode, 'routing_info')
|
||||
if(routingInfo?.content) {
|
||||
if (routingInfo?.content) {
|
||||
authState.creds.routingInfo = Buffer.from(routingInfo?.content as Uint8Array)
|
||||
ev.emit('creds.update', authState.creds)
|
||||
}
|
||||
@@ -696,7 +670,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
|
||||
let didStartBuffer = false
|
||||
process.nextTick(() => {
|
||||
if(creds.me?.id) {
|
||||
if (creds.me?.id) {
|
||||
// start buffering important events
|
||||
// if we're logged in
|
||||
ev.buffer()
|
||||
@@ -712,7 +686,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
const offlineNotifs = +(child?.attrs.count || 0)
|
||||
|
||||
logger.info(`handled ${offlineNotifs} offline messages/notifications`)
|
||||
if(didStartBuffer) {
|
||||
if (didStartBuffer) {
|
||||
ev.flush()
|
||||
logger.trace('flushed events for initial buffer')
|
||||
}
|
||||
@@ -724,21 +698,19 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
ev.on('creds.update', update => {
|
||||
const name = update.me?.name
|
||||
// if name has just been received
|
||||
if(creds.me?.name !== name) {
|
||||
if (creds.me?.name !== name) {
|
||||
logger.debug({ name }, 'updated pushName')
|
||||
sendNode({
|
||||
tag: 'presence',
|
||||
attrs: { name: name! }
|
||||
}).catch(err => {
|
||||
logger.warn({ trace: err.stack }, 'error in sending presence update on name change')
|
||||
})
|
||||
.catch(err => {
|
||||
logger.warn({ trace: err.stack }, 'error in sending presence update on name change')
|
||||
})
|
||||
}
|
||||
|
||||
Object.assign(creds, update)
|
||||
})
|
||||
|
||||
|
||||
return {
|
||||
type: 'md' as 'md',
|
||||
ws,
|
||||
@@ -762,7 +734,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
requestPairingCode,
|
||||
/** Waits for the connection to WA to reach a state */
|
||||
waitForConnectionUpdate: bindWaitForConnectionUpdate(ev),
|
||||
sendWAMBuffer,
|
||||
sendWAMBuffer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,11 +744,6 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
* */
|
||||
function mapWebSocketError(handler: (err: Error) => void) {
|
||||
return (error: Error) => {
|
||||
handler(
|
||||
new Boom(
|
||||
`WebSocket Error (${error?.message})`,
|
||||
{ statusCode: getCodeFromWSError(error), data: error }
|
||||
)
|
||||
)
|
||||
handler(new Boom(`WebSocket Error (${error?.message})`, { statusCode: getCodeFromWSError(error), data: error }))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,10 @@ import { makeSocket } from './socket'
|
||||
export const makeUSyncSocket = (config: SocketConfig) => {
|
||||
const sock = makeSocket(config)
|
||||
|
||||
const {
|
||||
generateMessageTag,
|
||||
query,
|
||||
} = sock
|
||||
const { generateMessageTag, query } = sock
|
||||
|
||||
const executeUSyncQuery = async(usyncQuery: USyncQuery) => {
|
||||
if(usyncQuery.protocols.length === 0) {
|
||||
const executeUSyncQuery = async (usyncQuery: USyncQuery) => {
|
||||
if (usyncQuery.protocols.length === 0) {
|
||||
throw new Boom('USyncQuery must have at least one protocol')
|
||||
}
|
||||
|
||||
@@ -21,15 +18,13 @@ export const makeUSyncSocket = (config: SocketConfig) => {
|
||||
// variable below has only validated users
|
||||
const validUsers = usyncQuery.users
|
||||
|
||||
const userNodes = validUsers.map((user) => {
|
||||
const userNodes = validUsers.map(user => {
|
||||
return {
|
||||
tag: 'user',
|
||||
attrs: {
|
||||
jid: !user.phone ? user.id : undefined,
|
||||
jid: !user.phone ? user.id : undefined
|
||||
},
|
||||
content: usyncQuery.protocols
|
||||
.map((a) => a.getUserElement(user))
|
||||
.filter(a => a !== null)
|
||||
content: usyncQuery.protocols.map(a => a.getUserElement(user)).filter(a => a !== null)
|
||||
} as BinaryNode
|
||||
})
|
||||
|
||||
@@ -42,14 +37,14 @@ export const makeUSyncSocket = (config: SocketConfig) => {
|
||||
const queryNode: BinaryNode = {
|
||||
tag: 'query',
|
||||
attrs: {},
|
||||
content: usyncQuery.protocols.map((a) => a.getQueryElement())
|
||||
content: usyncQuery.protocols.map(a => a.getQueryElement())
|
||||
}
|
||||
const iq = {
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
to: S_WHATSAPP_NET,
|
||||
type: 'get',
|
||||
xmlns: 'usync',
|
||||
xmlns: 'usync'
|
||||
},
|
||||
content: [
|
||||
{
|
||||
@@ -59,14 +54,11 @@ export const makeUSyncSocket = (config: SocketConfig) => {
|
||||
mode: usyncQuery.mode,
|
||||
sid: generateMessageTag(),
|
||||
last: 'true',
|
||||
index: '0',
|
||||
index: '0'
|
||||
},
|
||||
content: [
|
||||
queryNode,
|
||||
listNode
|
||||
]
|
||||
content: [queryNode, listNode]
|
||||
}
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
const result = await query(iq)
|
||||
@@ -76,6 +68,6 @@ export const makeUSyncSocket = (config: SocketConfig) => {
|
||||
|
||||
return {
|
||||
...sock,
|
||||
executeUSyncQuery,
|
||||
executeUSyncQuery
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user