Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix incoming LID and sending LID #408

Merged
merged 26 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/Socket/messages-recv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,8 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {

const handleReceipt = async(node: BinaryNode) => {
const { attrs, content } = node
const isNodeFromMe = areJidsSameUser(attrs.participant || attrs.from, authState.creds.me?.id)
const isLid = attrs.from.includes('lid')
const isNodeFromMe = areJidsSameUser(attrs.participant || attrs.from, isLid ? authState.creds.me?.lid : authState.creds.me?.id)
const remoteJid = !isNodeFromMe || isJidGroup(attrs.from) ? attrs.from : attrs.recipient
const fromMe = !attrs.recipient || (attrs.type === 'retry' && isNodeFromMe)

Expand Down Expand Up @@ -655,9 +656,17 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
const { fullMessage: msg, category, author, decrypt } = decryptMessageNode(
node,
authState.creds.me!.id,
authState.creds.me!.lid || '',
signalRepository,
logger,
)

if(msg.message?.protocolMessage?.type === proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER) {
if(node.attrs.sender_pn) {
ev.emit('chats.phoneNumberShare', { lid: node.attrs.from, jid: node.attrs.sender_pn })
}
}

if(shouldIgnoreJid(msg.key.remoteJid!)) {
logger.debug({ key: msg.key }, 'ignored message')
await sendMessageAck(node)
Expand Down
9 changes: 5 additions & 4 deletions src/Socket/messages-send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,12 +312,13 @@ export const makeMessagesSocket = (config: SocketConfig) => {
const statusJid = 'status@broadcast'
const isGroup = server === 'g.us'
const isStatus = jid === statusJid
const isLid = server === 'lid'

msgId = msgId || generateMessageID()
useUserDevicesCache = useUserDevicesCache !== false

const participants: BinaryNode[] = []
const destinationJid = (!isStatus) ? jidEncode(user, isGroup ? 'g.us' : 's.whatsapp.net') : statusJid
const destinationJid = (!isStatus) ? jidEncode(user, isLid ? 'lid' : isGroup ? 'g.us' : 's.whatsapp.net') : statusJid
const binaryNodeContent: BinaryNode[] = []
const devices: JidWithDevice[] = []

Expand Down Expand Up @@ -377,7 +378,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
devices.push(...additionalDevices)
}

const patched = await patchMessageBeforeSending(message, devices.map(d => jidEncode(d.user, 's.whatsapp.net', d.device)))
const patched = await patchMessageBeforeSending(message, devices.map(d => jidEncode(d.user, isLid ? 'lid' : 's.whatsapp.net', d.device)))
const bytes = encodeWAMessage(patched)

const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage(
Expand All @@ -391,7 +392,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
const senderKeyJids: string[] = []
// ensure a connection is established with every device
for(const { user, device } of devices) {
const jid = jidEncode(user, 's.whatsapp.net', device)
const jid = jidEncode(user, isLid ? 'lid' : 's.whatsapp.net', device)
if(!senderKeyMap[jid] || !!participant) {
senderKeyJids.push(jid)
// store that this person has had the sender keys sent to them
Expand Down Expand Up @@ -444,8 +445,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
const meJids: string[] = []
const otherJids: string[] = []
for(const { user, device } of devices) {
const jid = jidEncode(user, 's.whatsapp.net', device)
const isMe = user === meUser
const jid = jidEncode(isMe && isLid ? authState.creds?.me?.lid!.split(':')[0] || user : user, isLid ? 'lid' : 's.whatsapp.net', device)
if(isMe) {
meJids.push(jid)
} else {
Expand Down
4 changes: 3 additions & 1 deletion src/Socket/socket.ts
PurpShell marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -623,13 +623,15 @@ export const makeSocket = (config: SocketConfig) => {
}
})
// login complete
ws.on('CB:success', async() => {
ws.on('CB:success', async(node: BinaryNode) => {
await uploadPreKeysToServerIfRequired()
await sendPassiveIq('active')

logger.info('opened connection to WA')
clearTimeout(qrTimer) // will never happen in all likelyhood -- but just in case WA sends success on first try

ev.emit('creds.update', { me: { id: authState.creds.me!.id, lid: node.attrs.lid } })

ev.emit('connection.update', { connection: 'open' })
})

Expand Down
1 change: 1 addition & 0 deletions src/Types/Contact.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add lidJid as well in contacts.upsert here

            ev.emit('contacts.upsert', [{ id, name: action.contactAction!.fullName!, lidJid: action.contactAction!.lidJid! }])

Copy link
Collaborator Author

@PurpShell PurpShell Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need, official whatsapp web launches a new conversation. it is up to you to change contact info or replace the chat id in your database.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface Contact {
id: string
lid?: string
/** name of the contact, you have saved on your WA */
name?: string
/** name of the contact, the contact has set on their own on WA */
Expand Down
2 changes: 2 additions & 0 deletions src/Types/Events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type BaileysEventMap = {
'chats.upsert': Chat[]
/** update the given chats */
'chats.update': ChatUpdate[]
'chats.phoneNumberShare': {lid: string, jid: string}
/** delete chats with given ID */
'chats.delete': string[]
/** presence of contact in a chat updated */
Expand Down Expand Up @@ -54,6 +55,7 @@ export type BaileysEventMap = {

'blocklist.set': { blocklist: string[] }
'blocklist.update': { blocklist: string[], type: 'add' | 'remove' }

/** Receive an update on a call, including when the call was received, rejected, accepted */
'call': WACallEvent[]
'labels.edit': Label
Expand Down
10 changes: 9 additions & 1 deletion src/Types/Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ export type PollMessageOptions = {
messageSecret?: Uint8Array
}

type SharePhoneNumber = {
sharePhoneNumber: boolean
}

type RequestPhoneNumber = {
requestPhoneNumber: boolean
}

export type MediaType = keyof typeof MEDIA_HKDF_KEY_MAPPING
export type AnyMediaMessageContent = (
({
Expand Down Expand Up @@ -169,7 +177,7 @@ export type AnyRegularMessageContent = (
businessOwnerJid?: string
body?: string
footer?: string
}
} | SharePhoneNumber | RequestPhoneNumber
) & ViewOnce

export type AnyMessageContent = AnyRegularMessageContent | {
Expand Down
26 changes: 21 additions & 5 deletions src/Utils/decode-wa-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Boom } from '@hapi/boom'
import { Logger } from 'pino'
import { proto } from '../../WAProto'
import { SignalRepository, WAMessageKey } from '../Types'
import { areJidsSameUser, BinaryNode, isJidBroadcast, isJidGroup, isJidStatusBroadcast, isJidUser } from '../WABinary'
import { areJidsSameUser, BinaryNode, isJidBroadcast, isJidGroup, isJidStatusBroadcast, isJidUser, isLidUser } from '../WABinary'
import { unpadRandomMax16 } from './generics'

const NO_MESSAGE_FOUND_ERROR_TEXT = 'Message absent from node'
Expand All @@ -15,7 +15,8 @@ type MessageType = 'chat' | 'peer_broadcast' | 'other_broadcast' | 'group' | 'di
*/
export function decodeMessageNode(
stanza: BinaryNode,
meId: string
meId: string,
meLid: string
) {
let msgType: MessageType
let chatId: string
Expand All @@ -27,6 +28,7 @@ export function decodeMessageNode(
const recipient: string | undefined = stanza.attrs.recipient

const isMe = (jid: string) => areJidsSameUser(jid, meId)
const isMeLid = (jid: string) => areJidsSameUser(jid, meLid)

if(isJidUser(from)) {
if(recipient) {
Expand All @@ -39,6 +41,19 @@ export function decodeMessageNode(
chatId = from
}

msgType = 'chat'
author = from
} else if(isLidUser(from)) {
PurpShell marked this conversation as resolved.
Show resolved Hide resolved
if(recipient) {
if(!isMeLid(from)) {
throw new Boom('receipient present, but msg not from me', { data: stanza })
}

chatId = recipient
} else {
chatId = from
}

msgType = 'chat'
author = from
} else if(isJidGroup(from)) {
Expand Down Expand Up @@ -67,7 +82,7 @@ export function decodeMessageNode(
throw new Boom('Unknown message type', { data: stanza })
}

const fromMe = isMe(stanza.attrs.participant || stanza.attrs.from)
const fromMe = (isLidUser(from) ? isMeLid : isMe)(stanza.attrs.participant || stanza.attrs.from)
const pushname = stanza.attrs.notify

const key: WAMessageKey = {
Expand Down Expand Up @@ -98,10 +113,11 @@ export function decodeMessageNode(
export const decryptMessageNode = (
stanza: BinaryNode,
meId: string,
meLid: string,
repository: SignalRepository,
logger: Logger
) => {
const { fullMessage, author, sender } = decodeMessageNode(stanza, meId)
const { fullMessage, author, sender } = decodeMessageNode(stanza, meId, meLid)
return {
fullMessage,
category: stanza.attrs.category,
Expand Down Expand Up @@ -183,4 +199,4 @@ export const decryptMessageNode = (
}
}
}
}
}
6 changes: 6 additions & 0 deletions src/Utils/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,12 @@ export const generateWAMessageContent = async(
selectableOptionsCount: message.poll.selectableCount,
options: message.poll.values.map(optionName => ({ optionName })),
}
} else if('sharePhoneNumber' in message) {
m.protocolMessage = {
type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
}
} else if('requestPhoneNumber' in message) {
m.requestPhoneNumberMessage = {}
} else {
m = await prepareWAMessageMedia(
message,
Expand Down
4 changes: 2 additions & 2 deletions src/WABinary/decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export const decodeDecompressedBinaryNode = (
const device = readByte()
const user = readString(readByte())

return jidEncode(user, 's.whatsapp.net', device, agent)
return jidEncode(user, agent === 0 ? 's.whatsapp.net' : 'lid', device)
}

const readString = (tag: number): string => {
Expand Down Expand Up @@ -262,4 +262,4 @@ export const decodeDecompressedBinaryNode = (
export const decodeBinaryNode = (buff: Buffer): BinaryNode => {
const decompBuff = decompressingIfRequired(buff)
return decodeDecompressedBinaryNode(decompBuff, constants)
}
}
8 changes: 4 additions & 4 deletions src/WABinary/encode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ export const encodeBinaryNode = (
pushBytes(bytes)
}

const writeJid = ({ agent, device, user, server }: FullJid) => {
if(typeof agent !== 'undefined' || typeof device !== 'undefined') {
const writeJid = ({ domainType, device, user, server }: FullJid) => {
if(typeof device !== 'undefined') {
pushByte(TAGS.AD_JID)
pushByte(agent || 0)
pushByte(domainType || 0)
pushByte(device || 0)
writeString(user)
} else {
Expand Down Expand Up @@ -233,4 +233,4 @@ export const encodeBinaryNode = (
}

return Buffer.from(buffer)
}
}
12 changes: 7 additions & 5 deletions src/WABinary/jid-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const SERVER_JID = '[email protected]'
export const PSA_WID = '[email protected]'
export const STORIES_JID = 'status@broadcast'

export type JidServer = 'c.us' | 'g.us' | 'broadcast' | 's.whatsapp.net' | 'call'
export type JidServer = 'c.us' | 'g.us' | 'broadcast' | 's.whatsapp.net' | 'call' | 'lid'

export type JidWithDevice = {
user: string
Expand All @@ -13,7 +13,7 @@ export type JidWithDevice = {

export type FullJid = JidWithDevice & {
server: JidServer | string
agent?: number
domainType?: number
}

export const jidEncode = (user: string | number | null, server: JidServer, device?: number, agent?: number) => {
Expand All @@ -30,12 +30,12 @@ export const jidDecode = (jid: string | undefined): FullJid | undefined => {
const userCombined = jid!.slice(0, sepIdx)

const [userAgent, device] = userCombined.split(':')
const [user, agent] = userAgent.split('_')
const user = userAgent.split('_')[0]

return {
server,
user,
agent: agent ? +agent : undefined,
domainType: server === 'lid' ? 1 : 0,
device: device ? +device : undefined
}
}
Expand All @@ -46,6 +46,8 @@ export const areJidsSameUser = (jid1: string | undefined, jid2: string | undefin
)
/** is the jid a user */
export const isJidUser = (jid: string | undefined) => (jid?.endsWith('@s.whatsapp.net'))
/** is the jid a group */
export const isLidUser = (jid: string | undefined) => (jid?.endsWith('@lid'))
/** is the jid a broadcast */
export const isJidBroadcast = (jid: string | undefined) => (jid?.endsWith('@broadcast'))
/** is the jid a group */
Expand All @@ -61,4 +63,4 @@ export const jidNormalizedUser = (jid: string | undefined) => {

const { user, server } = result
return jidEncode(user, server === 'c.us' ? 's.whatsapp.net' : server as JidServer)
}
}
Loading