mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
Fetch stories + broadcast list info
This commit is contained in:
@@ -199,7 +199,7 @@ export namespace WA {
|
|||||||
'recent',
|
'recent',
|
||||||
]
|
]
|
||||||
export const Message = Coding.WebMessageInfo
|
export const Message = Coding.WebMessageInfo
|
||||||
export type NodeAttributes = Record<string, string> | string | null
|
export type NodeAttributes = { [key: string]: string } | string | null
|
||||||
export type NodeData = Array<Node> | any | null
|
export type NodeData = Array<Node> | any | null
|
||||||
export type Node = [string, NodeAttributes, NodeData]
|
export type Node = [string, NodeAttributes, NodeData]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import WAConnection from '../WAConnection/WAConnection'
|
import WAConnection from '../WAConnection/WAConnection'
|
||||||
import { MessageStatus, MessageStatusUpdate, PresenceUpdate, Presence, ChatModification } from './Constants'
|
import { MessageStatus, MessageStatusUpdate, PresenceUpdate, Presence, ChatModification, WABroadcastListInfo } from './Constants'
|
||||||
import {
|
import {
|
||||||
WAMessage,
|
WAMessage,
|
||||||
WANode,
|
WANode,
|
||||||
@@ -95,17 +95,33 @@ export default class WhatsAppWebBase extends WAConnection {
|
|||||||
const response = await this.queryExpecting200(['query', 'ProfilePicThumb', jid || this.userMetaData.id])
|
const response = await this.queryExpecting200(['query', 'ProfilePicThumb', jid || this.userMetaData.id])
|
||||||
return response.eurl as string
|
return response.eurl as string
|
||||||
}
|
}
|
||||||
|
/** Query broadcast list info */
|
||||||
|
async getBroadcastListInfo(jid: string) { return this.queryExpecting200(['query', 'contact', jid]) as Promise<WABroadcastListInfo> }
|
||||||
/** Get your contacts */
|
/** Get your contacts */
|
||||||
async getContacts() {
|
async getContacts() {
|
||||||
const json = ['query', { epoch: this.msgCount.toString(), type: 'contacts' }, null]
|
const json = ['query', { epoch: this.msgCount.toString(), type: 'contacts' }, null]
|
||||||
const response = await this.query(json, [WAMetric.group, WAFlag.ignore]) // this has to be an encrypted query
|
const response = await this.query(json, [6, WAFlag.ignore]) // this has to be an encrypted query
|
||||||
console.log(response)
|
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
/** Get the stories of your contacts */
|
||||||
|
async getStories() {
|
||||||
|
const json = ['query', { epoch: this.msgCount.toString(), type: 'status' }, null]
|
||||||
|
const response = await this.queryExpecting200(json, [30, WAFlag.ignore]) as WANode
|
||||||
|
if (Array.isArray(response[2])) {
|
||||||
|
return response[2].map (row => (
|
||||||
|
{
|
||||||
|
unread: row[1]?.unread,
|
||||||
|
count: row[1]?.count,
|
||||||
|
messages: Array.isArray(row[2]) ? row[2].map (m => m[2]) : []
|
||||||
|
} as {unread: number, count: number, messages: WAMessage[]}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
/** Fetch your chats */
|
/** Fetch your chats */
|
||||||
getChats() {
|
async getChats() {
|
||||||
const json = ['query', { epoch: this.msgCount.toString(), type: 'chat' }, null]
|
const json = ['query', { epoch: this.msgCount.toString(), type: 'chat' }, null]
|
||||||
return this.query(json, [WAMetric.group, WAFlag.ignore]) // this has to be an encrypted query
|
return this.query(json, [5, WAFlag.ignore]) // this has to be an encrypted query
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Check if your phone is connected
|
* Check if your phone is connected
|
||||||
@@ -189,86 +205,4 @@ export default class WhatsAppWebBase extends WAConnection {
|
|||||||
const json = ['action', {epoch: this.msgCount.toString(), type: 'set'}, nodes]
|
const json = ['action', {epoch: this.msgCount.toString(), type: 'set'}, nodes]
|
||||||
return this.queryExpecting200(json, [WAMetric.group, WAFlag.ignore]) as Promise<{status: number}>
|
return this.queryExpecting200(json, [WAMetric.group, WAFlag.ignore]) as Promise<{status: number}>
|
||||||
}
|
}
|
||||||
/** Generic function for group queries */
|
|
||||||
async groupQuery(type: string, jid?: string, subject?: string, participants?: string[]) {
|
|
||||||
const json: WANode = [
|
|
||||||
'group',
|
|
||||||
{
|
|
||||||
author: this.userMetaData.id,
|
|
||||||
id: generateMessageTag(),
|
|
||||||
type: type,
|
|
||||||
jid: jid,
|
|
||||||
subject: subject,
|
|
||||||
},
|
|
||||||
participants ? participants.map((str) => ['participant', { jid: str }, null]) : [],
|
|
||||||
]
|
|
||||||
const q = ['action', { type: 'set', epoch: this.msgCount.toString() }, [json]]
|
|
||||||
return this.queryExpecting200(q, [WAMetric.group, WAFlag.ignore])
|
|
||||||
}
|
|
||||||
/** 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 query = ['query', {type: 'group', jid: jid, epoch: this.msgCount.toString()}, null]
|
|
||||||
const response = await this.queryExpecting200(query, [WAMetric.group, WAFlag.ignore])
|
|
||||||
const json = response[2][0]
|
|
||||||
const creatorDesc = json[1]
|
|
||||||
const participants = json[2] ? json[2].filter (item => item[0] === 'participant') : []
|
|
||||||
const description = json[2] ? json[2].find (item => item[0] === 'description') : null
|
|
||||||
return {
|
|
||||||
id: jid,
|
|
||||||
owner: creatorDesc?.creator,
|
|
||||||
creator: creatorDesc?.creator,
|
|
||||||
creation: parseInt(creatorDesc?.create),
|
|
||||||
subject: null,
|
|
||||||
desc: description ? description[2].toString('utf-8') : null,
|
|
||||||
participants: participants.map (item => ({ id: item[1].jid, isAdmin: item[1].type==='admin' }))
|
|
||||||
} as WAGroupMetadata
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Create a group
|
|
||||||
* @param title like, the title of the group
|
|
||||||
* @param participants people to include in the group
|
|
||||||
*/
|
|
||||||
groupCreate = (title: string, participants: string[]) =>
|
|
||||||
this.groupQuery('create', null, title, participants) as Promise<WAGroupCreateResponse>
|
|
||||||
/**
|
|
||||||
* Leave a group
|
|
||||||
* @param jid the ID of the group
|
|
||||||
*/
|
|
||||||
groupLeave = (jid: string) => this.groupQuery('leave', jid) as Promise<{ status: number }>
|
|
||||||
/**
|
|
||||||
* Update the subject of the group
|
|
||||||
* @param {string} jid the ID of the group
|
|
||||||
* @param {string} title the new title of the group
|
|
||||||
*/
|
|
||||||
groupUpdateSubject = (jid: string, title: string) =>
|
|
||||||
this.groupQuery('subject', jid, title) as Promise<{ status: number }>
|
|
||||||
/**
|
|
||||||
* Add somebody to the group
|
|
||||||
* @param jid the ID of the group
|
|
||||||
* @param participants the people to add
|
|
||||||
*/
|
|
||||||
groupAdd = (jid: string, participants: string[]) =>
|
|
||||||
this.groupQuery('add', jid, null, participants) as Promise<WAGroupModification>
|
|
||||||
/**
|
|
||||||
* Remove somebody from the group
|
|
||||||
* @param jid the ID of the group
|
|
||||||
* @param participants the people to remove
|
|
||||||
*/
|
|
||||||
groupRemove = (jid: string, participants: string[]) =>
|
|
||||||
this.groupQuery('remove', jid, null, participants) as Promise<WAGroupModification>
|
|
||||||
/**
|
|
||||||
* Make someone admin on the group
|
|
||||||
* @param jid the ID of the group
|
|
||||||
* @param participants the people to make admin
|
|
||||||
*/
|
|
||||||
groupMakeAdmin = (jid: string, participants: string[]) =>
|
|
||||||
this.groupQuery('promote', jid, null, participants) as Promise<WAGroupModification>
|
|
||||||
/** Get the invite link of the given group */
|
|
||||||
async groupInviteCode(jid: string) {
|
|
||||||
const json = ['query', 'inviteCode', jid]
|
|
||||||
const response = await this.queryExpecting200(json)
|
|
||||||
return response.code as string
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,11 @@ export interface MessageOptions {
|
|||||||
validateID?: boolean,
|
validateID?: boolean,
|
||||||
filename?: string
|
filename?: string
|
||||||
}
|
}
|
||||||
|
export interface WABroadcastListInfo {
|
||||||
|
status: number
|
||||||
|
name: string
|
||||||
|
recipients?: {id: string}[]
|
||||||
|
}
|
||||||
export interface MessageInfo {
|
export interface MessageInfo {
|
||||||
reads: {jid: string, t: string}[]
|
reads: {jid: string, t: string}[]
|
||||||
deliveries: {jid: string, t: string}[]
|
deliveries: {jid: string, t: string}[]
|
||||||
|
|||||||
88
src/WAClient/Groups.ts
Normal file
88
src/WAClient/Groups.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import WhatsAppWebBase from './Base'
|
||||||
|
import { WAMessage, WAMetric, WAFlag, WANode, WAGroupMetadata, WAGroupCreateResponse, WAGroupModification } from '../WAConnection/Constants'
|
||||||
|
import { generateMessageTag } from '../WAConnection/Utils'
|
||||||
|
|
||||||
|
export default class WhatsAppWebGroups extends WhatsAppWebBase {
|
||||||
|
/** Generic function for group queries */
|
||||||
|
async groupQuery(type: string, jid?: string, subject?: string, participants?: string[]) {
|
||||||
|
const json: WANode = [
|
||||||
|
'group',
|
||||||
|
{
|
||||||
|
author: this.userMetaData.id,
|
||||||
|
id: generateMessageTag(),
|
||||||
|
type: type,
|
||||||
|
jid: jid,
|
||||||
|
subject: subject,
|
||||||
|
},
|
||||||
|
participants ? participants.map((str) => ['participant', { jid: str }, null]) : [],
|
||||||
|
]
|
||||||
|
const q = ['action', { type: 'set', epoch: this.msgCount.toString() }, [json]]
|
||||||
|
return this.queryExpecting200(q, [WAMetric.group, WAFlag.ignore])
|
||||||
|
}
|
||||||
|
/** 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 query = ['query', {type: 'group', jid: jid, epoch: this.msgCount.toString()}, null]
|
||||||
|
const response = await this.queryExpecting200(query, [WAMetric.group, WAFlag.ignore])
|
||||||
|
const json = response[2][0]
|
||||||
|
const creatorDesc = json[1]
|
||||||
|
const participants = json[2] ? json[2].filter (item => item[0] === 'participant') : []
|
||||||
|
const description = json[2] ? json[2].find (item => item[0] === 'description') : null
|
||||||
|
return {
|
||||||
|
id: jid,
|
||||||
|
owner: creatorDesc?.creator,
|
||||||
|
creator: creatorDesc?.creator,
|
||||||
|
creation: parseInt(creatorDesc?.create),
|
||||||
|
subject: null,
|
||||||
|
desc: description ? description[2].toString('utf-8') : null,
|
||||||
|
participants: participants.map (item => ({ id: item[1].jid, isAdmin: item[1].type==='admin' }))
|
||||||
|
} as WAGroupMetadata
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create a group
|
||||||
|
* @param title like, the title of the group
|
||||||
|
* @param participants people to include in the group
|
||||||
|
*/
|
||||||
|
groupCreate = (title: string, participants: string[]) =>
|
||||||
|
this.groupQuery('create', null, title, participants) as Promise<WAGroupCreateResponse>
|
||||||
|
/**
|
||||||
|
* Leave a group
|
||||||
|
* @param jid the ID of the group
|
||||||
|
*/
|
||||||
|
groupLeave = (jid: string) => this.groupQuery('leave', jid) as Promise<{ status: number }>
|
||||||
|
/**
|
||||||
|
* Update the subject of the group
|
||||||
|
* @param {string} jid the ID of the group
|
||||||
|
* @param {string} title the new title of the group
|
||||||
|
*/
|
||||||
|
groupUpdateSubject = (jid: string, title: string) =>
|
||||||
|
this.groupQuery('subject', jid, title) as Promise<{ status: number }>
|
||||||
|
/**
|
||||||
|
* Add somebody to the group
|
||||||
|
* @param jid the ID of the group
|
||||||
|
* @param participants the people to add
|
||||||
|
*/
|
||||||
|
groupAdd = (jid: string, participants: string[]) =>
|
||||||
|
this.groupQuery('add', jid, null, participants) as Promise<WAGroupModification>
|
||||||
|
/**
|
||||||
|
* Remove somebody from the group
|
||||||
|
* @param jid the ID of the group
|
||||||
|
* @param participants the people to remove
|
||||||
|
*/
|
||||||
|
groupRemove = (jid: string, participants: string[]) =>
|
||||||
|
this.groupQuery('remove', jid, null, participants) as Promise<WAGroupModification>
|
||||||
|
/**
|
||||||
|
* Make someone admin on the group
|
||||||
|
* @param jid the ID of the group
|
||||||
|
* @param participants the people to make admin
|
||||||
|
*/
|
||||||
|
groupMakeAdmin = (jid: string, participants: string[]) =>
|
||||||
|
this.groupQuery('promote', jid, null, participants) as Promise<WAGroupModification>
|
||||||
|
/** Get the invite link of the given group */
|
||||||
|
async groupInviteCode(jid: string) {
|
||||||
|
const json = ['query', 'inviteCode', jid]
|
||||||
|
const response = await this.queryExpecting200(json)
|
||||||
|
return response.code as string
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import WhatsAppWebBase from './Base'
|
import WhatsAppWebGroups from './Groups'
|
||||||
import fetch from 'node-fetch'
|
import fetch from 'node-fetch'
|
||||||
import {
|
import {
|
||||||
MessageOptions,
|
MessageOptions,
|
||||||
@@ -18,7 +18,7 @@ import { WAMessageContent, WAMetric, WAFlag, WANode, WAMessage } from '../WAConn
|
|||||||
import { validateJIDForSending, generateThumbnail, getMediaKeys } from './Utils'
|
import { validateJIDForSending, generateThumbnail, getMediaKeys } from './Utils'
|
||||||
import { proto } from '../../WAMessage/WAMessage'
|
import { proto } from '../../WAMessage/WAMessage'
|
||||||
|
|
||||||
export default class WhatsAppWebMessages extends WhatsAppWebBase {
|
export default class WhatsAppWebMessages extends WhatsAppWebGroups {
|
||||||
/** Get the message info, who has read it, who its been delivered to */
|
/** Get the message info, who has read it, who its been delivered to */
|
||||||
async messageInfo (jid: string, messageID: string) {
|
async messageInfo (jid: string, messageID: string) {
|
||||||
const query = ['query', {type: 'message_info', index: messageID, jid: jid, epoch: this.msgCount.toString()}, null]
|
const query = ['query', {type: 'message_info', index: messageID, jid: jid, epoch: this.msgCount.toString()}, null]
|
||||||
@@ -295,4 +295,43 @@ export default class WhatsAppWebMessages extends WhatsAppWebBase {
|
|||||||
const response = await this.queryExpecting200(json, [WAMetric.message, WAFlag.ignore], null, messageJSON.key.id)
|
const response = await this.queryExpecting200(json, [WAMetric.message, WAFlag.ignore], null, messageJSON.key.id)
|
||||||
return { status: response.status as number, messageID: messageJSON.key.id } as WASendMessageResponse
|
return { status: response.status as number, messageID: messageJSON.key.id } as WASendMessageResponse
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Load the entire friggin conversation with a group or person
|
||||||
|
* @param onMessage callback for every message retreived
|
||||||
|
* @param [chunkSize] the number of messages to load in a single request
|
||||||
|
* @param [mostRecentFirst] retreive the most recent message first or retreive from the converation start
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
let lastMessage
|
||||||
|
if (mostRecentFirst) {
|
||||||
|
for (let i = json.length - 1; i >= 0; i--) {
|
||||||
|
onMessage(json[i])
|
||||||
|
lastMessage = json[i]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < json.length; i++) {
|
||||||
|
onMessage(json[i])
|
||||||
|
lastMessage = json[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if there are still more messages
|
||||||
|
if (json.length >= chunkSize) {
|
||||||
|
offsetID = lastMessage.key // get the last message
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// send query after 200 ms
|
||||||
|
setTimeout(() => loadMessage().then(resolve).catch(reject), 200)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loadMessage() as Promise<void>
|
||||||
|
}
|
||||||
|
/** Generic function for action, set queries */
|
||||||
|
async setQuery (nodes: WANode[]) {
|
||||||
|
const json = ['action', {epoch: this.msgCount.toString(), type: 'set'}, nodes]
|
||||||
|
return this.queryExpecting200(json, [WAMetric.group, WAFlag.ignore]) as Promise<{status: number}>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,13 +70,14 @@ WAClientTest('Messages', (client) => {
|
|||||||
await client.clearMessage (messages[0].key)
|
await client.clearMessage (messages[0].key)
|
||||||
})
|
})
|
||||||
it ('should load convo', async () => {
|
it ('should load convo', async () => {
|
||||||
const [chats] = await client.receiveChatsAndContacts ()
|
/*const [chats] = await client.receiveChatsAndContacts ()
|
||||||
for (var i in chats) {
|
for (var i in chats) {
|
||||||
if (chats[i].jid.includes('@g.us')) {
|
if (chats[i].jid.includes('@g.us')) {
|
||||||
console.log (chats[i].jid)
|
console.log (chats[i].jid)
|
||||||
const data = await client.groupCreatorAndParticipants (chats[i].jid)
|
const data = await client.groupCreatorAndParticipants (chats[i].jid)
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -116,6 +117,9 @@ WAClientTest('Misc', (client) => {
|
|||||||
assert.ok(response.status)
|
assert.ok(response.status)
|
||||||
assert.strictEqual(typeof response.status, 'string')
|
assert.strictEqual(typeof response.status, 'string')
|
||||||
})
|
})
|
||||||
|
it('should return the stories', async () => {
|
||||||
|
await client.getStories()
|
||||||
|
})
|
||||||
it('should return the profile picture', async () => {
|
it('should return the profile picture', async () => {
|
||||||
const response = await client.getProfilePicture(testJid)
|
const response = await client.getProfilePicture(testJid)
|
||||||
assert.ok(response)
|
assert.ok(response)
|
||||||
|
|||||||
@@ -77,11 +77,11 @@ export function errorOnNon200Status(p: Promise<any>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function decryptWA (message: any, macKey: Buffer, encKey: Buffer, decoder: Decoder, fromMe: boolean=false): [string, Object, [number, number]?] {
|
export function decryptWA (message: any, macKey: Buffer, encKey: Buffer, decoder: Decoder, fromMe: boolean=false): [string, Object, [number, number]?] {
|
||||||
const commaIndex = message.indexOf(',') // all whatsapp messages have a tag and a comma, followed by the actual message
|
let commaIndex = message.indexOf(',') // all whatsapp messages have a tag and a comma, followed by the actual message
|
||||||
if (commaIndex < 0) {
|
|
||||||
// if there was no comma, then this message must be not be valid
|
if (commaIndex < 0) throw Error ('invalid message: ' + message) // if there was no comma, then this message must be not be valid
|
||||||
throw Error ('invalid message: ' + message)
|
|
||||||
}
|
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.
|
// get the message tag.
|
||||||
// If a query was done, the server will respond with the same message tag we sent the query with
|
// If a query was done, the server will respond with the same message tag we sent the query with
|
||||||
|
|||||||
Reference in New Issue
Block a user