filename to messageoptions + group query + media query

This commit is contained in:
Adhiraj
2020-07-12 13:35:21 +05:30
parent 9061f347af
commit 9f508184eb
9 changed files with 68 additions and 20 deletions

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ yarn.lock
browser-messages.json
package-lock.json
package-lock.json
decoded-ws.json

View File

@@ -212,6 +212,7 @@ To note:
mimetype: Mimetype.pdf, /* (for media messages) specify the type of media (optional for all media types except documents),
import {Mimetype} from '@adiwajshing/baileys'
*/
filename: 'somefile.pdf' // (for media messages) file name for the media
}
```

View File

@@ -144,7 +144,7 @@ export default class WhatsAppWebBase extends WAConnection {
},
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}`)
@@ -158,7 +158,6 @@ export default class WhatsAppWebBase extends WAConnection {
*/
loadEntireConversation(jid: string, onMessage: (m: WAMessage) => void, chunkSize = 25, mostRecentFirst = true) {
let offsetID = null
const loadMessage = async () => {
const json = await this.loadConversation(jid, chunkSize, offsetID, mostRecentFirst)
// 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 */
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
* @param title like, the title of the group

View File

@@ -70,7 +70,8 @@ export interface MessageOptions {
caption?: string
thumbnail?: string
mimetype?: Mimetype
validateID?: boolean
validateID?: boolean,
filename?: string
}
export interface MessageStatusUpdate {
from: string

View File

@@ -106,6 +106,21 @@ export default class WhatsAppWebMessages extends WhatsAppWebBase {
]
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
* @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,
fileSha256: fileSha256.toString('base64'),
fileLength: buffer.length,
fileName: options.filename || 'file',
gifPlayback: isGIF || null,
}
return message
return message as WAMessageContent
}
/** Generic send message function */
async sendGenericMessage(id: string, message: WAMessageContent, options: MessageOptions) {

View File

@@ -5,6 +5,7 @@ import * as assert from 'assert'
import { decodeMediaMessage, validateJIDForSending } from './Utils'
import { promiseTimeout, createTimeout } from '../WAConnection/Utils'
import { WAMessageContent, WAMessage } from '../WAConnection/Constants'
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
@@ -171,6 +172,7 @@ WAClientTest('Groups', (client) => {
})
it('should leave the group', async () => {
await client.groupLeave(gid)
await client.groupCreatorAndParticipants (gid)
})
it('should archive the group', async () => {
await client.archiveChat(gid)

View File

@@ -109,7 +109,6 @@ export async function decodeMediaMessage(message: WAMessageContent, filename: st
fs.writeFileSync(filename + '.jpeg', message[type].jpegThumbnail)
return { filename: filename + '.jpeg' }
}
const messageContent = message[type] as
| proto.VideoMessage
| proto.ImageMessage
@@ -117,9 +116,13 @@ export async function decodeMediaMessage(message: WAMessageContent, filename: st
| proto.DocumentMessage
// 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()
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) => {
// get the keys to decrypt the message
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)) {
return aesDecryptWithIV(file, mediaKeys.cipherKey, mediaKeys.iv) // decrypt media
} else {
throw new Error('')
throw new Error()
}
}
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`) }
}
}
throw new Error('HMAC sign does not match for: ' + buffer.toString('utf-8'))
throw new Error('HMAC sign does not match for ' + buffer.length)
}

View File

@@ -5,7 +5,11 @@ import Decoder from '../Binary/Decoder'
interface BrowserMessagesInfo {
encKey: 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 json: BrowserMessagesInfo = JSON.parse (file)
@@ -13,6 +17,14 @@ const json: BrowserMessagesInfo = JSON.parse (file)
const encKey = Buffer.from (json.encKey, '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 => {
try {
return decryptWA (buffer, macKey, encKey, new Decoder())
@@ -21,19 +33,16 @@ const decrypt = buffer => {
}
}
json.messages.forEach ((str, i) => {
const buffer = Buffer.from (str, 'base64')
console.log ('parsing ' + wsMessages.length + ' messages')
const list = wsMessages.map ((item, i) => {
const buffer = Buffer.from (item.data, 'base64')
try {
const [tag, json, binaryTags] = decrypt (buffer)
console.log (
`
${i}.
messageTag: ${tag}
output: ${JSON.stringify(json)}
binaryTags: ${binaryTags}
`
)
return {tag, json: JSON.stringify(json), binaryTags}
} catch (error) {
console.error (`received error in decoding ${i}: ${error}`)
return null
}
})
})
const str = JSON.stringify (list, null, '\t')
fs.writeFileSync ('decoded-ws.json', str)

View File

@@ -78,6 +78,8 @@ export interface WAChat {
}
export enum WAMetric {
liveLocation = 3,
queryMedia = 4,
queryMessages = 7,
group = 10,
message = 16,
queryLiveLocation = 33,