mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
filename to messageoptions + group query + media query
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,3 +11,4 @@ yarn.lock
|
|||||||
browser-messages.json
|
browser-messages.json
|
||||||
package-lock.json
|
package-lock.json
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
decoded-ws.json
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ To note:
|
|||||||
mimetype: Mimetype.pdf, /* (for media messages) specify the type of media (optional for all media types except documents),
|
mimetype: Mimetype.pdf, /* (for media messages) specify the type of media (optional for all media types except documents),
|
||||||
import {Mimetype} from '@adiwajshing/baileys'
|
import {Mimetype} from '@adiwajshing/baileys'
|
||||||
*/
|
*/
|
||||||
|
filename: 'somefile.pdf' // (for media messages) file name for the media
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export default class WhatsAppWebBase extends WAConnection {
|
|||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
]
|
]
|
||||||
const response = await this.query(json, [WAMetric.group, WAFlag.ignore])
|
const response = await this.query(json, [WAMetric.queryMessages, WAFlag.ignore])
|
||||||
|
|
||||||
if (response.status) throw new Error(`error in query, got status: ${response.status}`)
|
if (response.status) throw new Error(`error in query, got status: ${response.status}`)
|
||||||
|
|
||||||
@@ -158,7 +158,6 @@ export default class WhatsAppWebBase extends WAConnection {
|
|||||||
*/
|
*/
|
||||||
loadEntireConversation(jid: string, onMessage: (m: WAMessage) => void, chunkSize = 25, mostRecentFirst = true) {
|
loadEntireConversation(jid: string, onMessage: (m: WAMessage) => void, chunkSize = 25, mostRecentFirst = true) {
|
||||||
let offsetID = null
|
let offsetID = null
|
||||||
|
|
||||||
const loadMessage = async () => {
|
const loadMessage = async () => {
|
||||||
const json = await this.loadConversation(jid, chunkSize, offsetID, mostRecentFirst)
|
const json = await this.loadConversation(jid, chunkSize, offsetID, mostRecentFirst)
|
||||||
// callback with most recent message first (descending order of date)
|
// callback with most recent message first (descending order of date)
|
||||||
@@ -208,6 +207,20 @@ export default class WhatsAppWebBase extends WAConnection {
|
|||||||
}
|
}
|
||||||
/** Get the metadata of the group */
|
/** Get the metadata of the group */
|
||||||
groupMetadata = (jid: string) => this.queryExpecting200(['query', 'GroupMetadata', jid]) as Promise<WAGroupMetadata>
|
groupMetadata = (jid: string) => this.queryExpecting200(['query', 'GroupMetadata', jid]) as Promise<WAGroupMetadata>
|
||||||
|
/** Get the metadata (works after you've left the group also) */
|
||||||
|
groupCreatorAndParticipants = async (jid: string) => {
|
||||||
|
const response = await this.queryExpecting200(['query', {type: 'group', jid: jid, epoch: '5'}, null], [WAMetric.group, WAFlag.ignore])
|
||||||
|
if (!response[2] || !response[2][1]) throw new Error ('Data missing in ' + JSON.stringify(response))
|
||||||
|
const json = response[2]
|
||||||
|
return {
|
||||||
|
id: jid,
|
||||||
|
owner: json[1].creator,
|
||||||
|
creator: json[1].creator,
|
||||||
|
creation: parseInt(json[1].create),
|
||||||
|
subject: null,
|
||||||
|
participants: json[2] ? json[2].map (item => ({ id: item[1].jid, isAdmin: item[1].type==='admin' })) : []
|
||||||
|
} as WAGroupMetadata
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Create a group
|
* Create a group
|
||||||
* @param title like, the title of the group
|
* @param title like, the title of the group
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ export interface MessageOptions {
|
|||||||
caption?: string
|
caption?: string
|
||||||
thumbnail?: string
|
thumbnail?: string
|
||||||
mimetype?: Mimetype
|
mimetype?: Mimetype
|
||||||
validateID?: boolean
|
validateID?: boolean,
|
||||||
|
filename?: string
|
||||||
}
|
}
|
||||||
export interface MessageStatusUpdate {
|
export interface MessageStatusUpdate {
|
||||||
from: string
|
from: string
|
||||||
|
|||||||
@@ -106,6 +106,21 @@ export default class WhatsAppWebMessages extends WhatsAppWebBase {
|
|||||||
]
|
]
|
||||||
return this.setQuery ([attrs])
|
return this.setQuery ([attrs])
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Fetches the latest url & media key for the given message.
|
||||||
|
* You may need to call this when the message is old & the content is deleted off of the WA servers
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
|
async updateMediaMessage (message: WAMessage) {
|
||||||
|
const content = message.message?.audioMessage || message.message?.videoMessage || message.message?.imageMessage || message.message?.stickerMessage || message.message?.documentMessage
|
||||||
|
if (!content) throw new Error (`given message ${message.key.id} is not a media message`)
|
||||||
|
|
||||||
|
const query = ['query',{type: 'media', index: message.key.id, owner: message.key.fromMe ? 'true' : 'false', jid: message.key.remoteJid, epoch: this.msgCount.toString()},null]
|
||||||
|
const response = await this.query (query, [WAMetric.queryMedia, WAFlag.ignore])
|
||||||
|
if (response[1].code !== 200) throw new Error ('unexpected status ' + response[1].code)
|
||||||
|
|
||||||
|
Object.keys (response[1]).forEach (key => content[key] = response[1][key]) // update message
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Delete a message in a chat for everyone
|
* Delete a message in a chat for everyone
|
||||||
* @param id the person or group where you're trying to delete the message
|
* @param id the person or group where you're trying to delete the message
|
||||||
@@ -207,9 +222,10 @@ export default class WhatsAppWebMessages extends WhatsAppWebBase {
|
|||||||
fileEncSha256: fileEncSha256B64,
|
fileEncSha256: fileEncSha256B64,
|
||||||
fileSha256: fileSha256.toString('base64'),
|
fileSha256: fileSha256.toString('base64'),
|
||||||
fileLength: buffer.length,
|
fileLength: buffer.length,
|
||||||
|
fileName: options.filename || 'file',
|
||||||
gifPlayback: isGIF || null,
|
gifPlayback: isGIF || null,
|
||||||
}
|
}
|
||||||
return message
|
return message as WAMessageContent
|
||||||
}
|
}
|
||||||
/** Generic send message function */
|
/** Generic send message function */
|
||||||
async sendGenericMessage(id: string, message: WAMessageContent, options: MessageOptions) {
|
async sendGenericMessage(id: string, message: WAMessageContent, options: MessageOptions) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import * as assert from 'assert'
|
|||||||
|
|
||||||
import { decodeMediaMessage, validateJIDForSending } from './Utils'
|
import { decodeMediaMessage, validateJIDForSending } from './Utils'
|
||||||
import { promiseTimeout, createTimeout } from '../WAConnection/Utils'
|
import { promiseTimeout, createTimeout } from '../WAConnection/Utils'
|
||||||
|
import { WAMessageContent, WAMessage } from '../WAConnection/Constants'
|
||||||
|
|
||||||
require ('dotenv').config () // dotenv to load test jid
|
require ('dotenv').config () // dotenv to load test jid
|
||||||
const testJid = process.env.TEST_JID || '1234@s.whatsapp.net' // set TEST_JID=xyz@s.whatsapp.net in a .env file in the root directory
|
const testJid = process.env.TEST_JID || '1234@s.whatsapp.net' // set TEST_JID=xyz@s.whatsapp.net in a .env file in the root directory
|
||||||
@@ -171,6 +172,7 @@ WAClientTest('Groups', (client) => {
|
|||||||
})
|
})
|
||||||
it('should leave the group', async () => {
|
it('should leave the group', async () => {
|
||||||
await client.groupLeave(gid)
|
await client.groupLeave(gid)
|
||||||
|
await client.groupCreatorAndParticipants (gid)
|
||||||
})
|
})
|
||||||
it('should archive the group', async () => {
|
it('should archive the group', async () => {
|
||||||
await client.archiveChat(gid)
|
await client.archiveChat(gid)
|
||||||
|
|||||||
@@ -109,7 +109,6 @@ export async function decodeMediaMessage(message: WAMessageContent, filename: st
|
|||||||
fs.writeFileSync(filename + '.jpeg', message[type].jpegThumbnail)
|
fs.writeFileSync(filename + '.jpeg', message[type].jpegThumbnail)
|
||||||
return { filename: filename + '.jpeg' }
|
return { filename: filename + '.jpeg' }
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageContent = message[type] as
|
const messageContent = message[type] as
|
||||||
| proto.VideoMessage
|
| proto.VideoMessage
|
||||||
| proto.ImageMessage
|
| proto.ImageMessage
|
||||||
@@ -117,9 +116,13 @@ export async function decodeMediaMessage(message: WAMessageContent, filename: st
|
|||||||
| proto.DocumentMessage
|
| proto.DocumentMessage
|
||||||
|
|
||||||
// download the message
|
// download the message
|
||||||
const fetched = await fetch(messageContent.url, {})
|
const fetched = await fetch(messageContent.url, { headers: { Origin: 'https://web.whatsapp.com' } })
|
||||||
const buffer = await fetched.buffer()
|
const buffer = await fetched.buffer()
|
||||||
|
|
||||||
|
if (buffer.length === 0) {
|
||||||
|
throw new Error ('Empty buffer returned. File has possibly been deleted from WA servers. Run `client.updateMediaMessage()` to refresh the url')
|
||||||
|
}
|
||||||
|
|
||||||
const decryptedMedia = (type: MessageType) => {
|
const decryptedMedia = (type: MessageType) => {
|
||||||
// get the keys to decrypt the message
|
// get the keys to decrypt the message
|
||||||
const mediaKeys = getMediaKeys(messageContent.mediaKey, type) //getMediaKeys(Buffer.from(messageContent.mediaKey, 'base64'), type)
|
const mediaKeys = getMediaKeys(messageContent.mediaKey, type) //getMediaKeys(Buffer.from(messageContent.mediaKey, 'base64'), type)
|
||||||
@@ -134,7 +137,7 @@ export async function decodeMediaMessage(message: WAMessageContent, filename: st
|
|||||||
if (sign.equals(mac)) {
|
if (sign.equals(mac)) {
|
||||||
return aesDecryptWithIV(file, mediaKeys.cipherKey, mediaKeys.iv) // decrypt media
|
return aesDecryptWithIV(file, mediaKeys.cipherKey, mediaKeys.iv) // decrypt media
|
||||||
} else {
|
} else {
|
||||||
throw new Error('')
|
throw new Error()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const allTypes = [type, ...Object.keys(HKDFInfoKeys)]
|
const allTypes = [type, ...Object.keys(HKDFInfoKeys)]
|
||||||
@@ -151,5 +154,5 @@ export async function decodeMediaMessage(message: WAMessageContent, filename: st
|
|||||||
if (i === 0) { console.log (`decryption of ${type} media with original HKDF key failed`) }
|
if (i === 0) { console.log (`decryption of ${type} media with original HKDF key failed`) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error('HMAC sign does not match for: ' + buffer.toString('utf-8'))
|
throw new Error('HMAC sign does not match for ' + buffer.length)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ import Decoder from '../Binary/Decoder'
|
|||||||
interface BrowserMessagesInfo {
|
interface BrowserMessagesInfo {
|
||||||
encKey: string,
|
encKey: string,
|
||||||
macKey: string,
|
macKey: string,
|
||||||
messages: string[]
|
harFilePath: string
|
||||||
|
}
|
||||||
|
interface WSMessage {
|
||||||
|
type: 'send' | 'receive',
|
||||||
|
data: string
|
||||||
}
|
}
|
||||||
const file = fs.readFileSync ('./browser-messages.json', {encoding: 'utf-8'})
|
const file = fs.readFileSync ('./browser-messages.json', {encoding: 'utf-8'})
|
||||||
const json: BrowserMessagesInfo = JSON.parse (file)
|
const json: BrowserMessagesInfo = JSON.parse (file)
|
||||||
@@ -13,6 +17,14 @@ const json: BrowserMessagesInfo = JSON.parse (file)
|
|||||||
const encKey = Buffer.from (json.encKey, 'base64')
|
const encKey = Buffer.from (json.encKey, 'base64')
|
||||||
const macKey = Buffer.from (json.macKey, 'base64')
|
const macKey = Buffer.from (json.macKey, 'base64')
|
||||||
|
|
||||||
|
const harFile = JSON.parse ( fs.readFileSync( json.harFilePath , {encoding: 'utf-8'}))
|
||||||
|
const entries = harFile['log']['entries']
|
||||||
|
let wsMessages: WSMessage[] = []
|
||||||
|
entries.forEach ((e, i) => {
|
||||||
|
if ('_webSocketMessages' in e) {
|
||||||
|
wsMessages.push (...e['_webSocketMessages'])
|
||||||
|
}
|
||||||
|
})
|
||||||
const decrypt = buffer => {
|
const decrypt = buffer => {
|
||||||
try {
|
try {
|
||||||
return decryptWA (buffer, macKey, encKey, new Decoder())
|
return decryptWA (buffer, macKey, encKey, new Decoder())
|
||||||
@@ -21,19 +33,16 @@ const decrypt = buffer => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
json.messages.forEach ((str, i) => {
|
console.log ('parsing ' + wsMessages.length + ' messages')
|
||||||
const buffer = Buffer.from (str, 'base64')
|
const list = wsMessages.map ((item, i) => {
|
||||||
|
const buffer = Buffer.from (item.data, 'base64')
|
||||||
try {
|
try {
|
||||||
const [tag, json, binaryTags] = decrypt (buffer)
|
const [tag, json, binaryTags] = decrypt (buffer)
|
||||||
console.log (
|
return {tag, json: JSON.stringify(json), binaryTags}
|
||||||
`
|
|
||||||
${i}.
|
|
||||||
messageTag: ${tag}
|
|
||||||
output: ${JSON.stringify(json)}
|
|
||||||
binaryTags: ${binaryTags}
|
|
||||||
`
|
|
||||||
)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error (`received error in decoding ${i}: ${error}`)
|
console.error (`received error in decoding ${i}: ${error}`)
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const str = JSON.stringify (list, null, '\t')
|
||||||
|
fs.writeFileSync ('decoded-ws.json', str)
|
||||||
@@ -78,6 +78,8 @@ export interface WAChat {
|
|||||||
}
|
}
|
||||||
export enum WAMetric {
|
export enum WAMetric {
|
||||||
liveLocation = 3,
|
liveLocation = 3,
|
||||||
|
queryMedia = 4,
|
||||||
|
queryMessages = 7,
|
||||||
group = 10,
|
group = 10,
|
||||||
message = 16,
|
message = 16,
|
||||||
queryLiveLocation = 33,
|
queryLiveLocation = 33,
|
||||||
|
|||||||
Reference in New Issue
Block a user