Set Status + Set Group Description + Better Message Tags

This commit is contained in:
Adhiraj
2020-08-02 13:45:27 +05:30
parent d812d52b33
commit c121d17c12
7 changed files with 78 additions and 15 deletions

View File

@@ -74,6 +74,15 @@ export default class WhatsAppWebBase extends WAConnection {
async getStatus (jid?: string) {
return this.query(['query', 'Status', jid || this.userMetaData.id]) as Promise<{ status: string }>
}
async setStatus (status: string) {
return this.setQuery ([
[
'status',
null,
Buffer.from (status, 'utf-8')
]
])
}
/** Get the URL to download the profile picture of a person/group */
async getProfilePicture(jid: string | null) {
const response = await this.queryExpecting200(['query', 'ProfilePicThumb', jid || this.userMetaData.id])
@@ -204,6 +213,7 @@ export default class WhatsAppWebBase extends WAConnection {
/** Generic function for action, set queries */
async setQuery (nodes: WANode[], binaryTags: WATag = [WAMetric.group, WAFlag.ignore], tag?: string) {
const json = ['action', {epoch: this.msgCount.toString(), type: 'set'}, nodes]
return this.queryExpecting200(json, binaryTags, null, tag) as Promise<{status: number}>
const result = await this.queryExpecting200(json, binaryTags, null, tag) as Promise<{status: number}>
return result
}
}

View File

@@ -1,6 +1,7 @@
import WhatsAppWebBase from './Base'
import { WAMessage, WAMetric, WAFlag, WANode, WAGroupMetadata, WAGroupCreateResponse, WAGroupModification } from '../WAConnection/Constants'
import { GroupSettingChange } from './Constants'
import { generateMessageID } from '../WAConnection/Utils'
export default class WhatsAppWebGroups extends WhatsAppWebBase {
/** Generic function for group queries */
@@ -17,7 +18,8 @@ export default class WhatsAppWebGroups extends WhatsAppWebBase {
},
participants ? participants.map(str => ['participant', { jid: str }, null]) : additionalNodes,
]
return this.setQuery ([json], [WAMetric.group, WAFlag.ignore], tag)
const result = await this.setQuery ([json], [WAMetric.group, WAFlag.ignore], tag)
return result
}
/** Get the metadata of the group */
groupMetadata = (jid: string) => this.queryExpecting200(['query', 'GroupMetadata', jid]) as Promise<WAGroupMetadata>
@@ -58,6 +60,20 @@ export default class WhatsAppWebGroups extends WhatsAppWebBase {
*/
groupUpdateSubject = (jid: string, title: string) =>
this.groupQuery('subject', jid, title) as Promise<{ status: number }>
/**
* Update the group description
* @param {string} jid the ID of the group
* @param {string} title the new title of the group
*/
groupUpdateDescription = async (jid: string, description: string) => {
const metadata = await this.groupMetadata (jid)
const node: WANode = [
'description',
{id: generateMessageID(), prev: metadata?.descId},
Buffer.from (description, 'utf-8')
]
return this.groupQuery ('description', jid, null, null, [node])
}
/**
* Add somebody to the group
* @param jid the ID of the group

View File

@@ -254,7 +254,7 @@ export default class WhatsAppWebMessages extends WhatsAppWebGroups {
return this.sendGenericMessage(id, m, options)
}
/** Prepare a media message for sending */
protected async prepareMediaMessage(buffer: Buffer, mediaType: MessageType, options: MessageOptions = {}) {
async prepareMediaMessage(buffer: Buffer, mediaType: MessageType, options: MessageOptions = {}) {
if (mediaType === MessageType.document && !options.mimetype) {
throw new Error('mimetype required to send a document')
}
@@ -278,10 +278,10 @@ export default class WhatsAppWebMessages extends WhatsAppWebGroups {
const fileSha256 = sha256(buffer)
// url safe Base64 encode the SHA256 hash of the body
const fileEncSha256B64 = sha256(body)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/\=+$/, '')
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/\=+$/, '')
await generateThumbnail(buffer, mediaType, options)
// send a query JSON to obtain the url & auth token to upload our media
@@ -311,6 +311,7 @@ export default class WhatsAppWebMessages extends WhatsAppWebGroups {
fileLength: buffer.length,
fileName: options.filename || 'file',
gifPlayback: isGIF || null,
caption: options.caption
}
return message as WAMessageContent
}
@@ -332,13 +333,13 @@ export default class WhatsAppWebMessages extends WhatsAppWebGroups {
message[key].contextInfo.participant = participant
message[key].contextInfo.stanzaId = quoted.key.id
message[key].contextInfo.quotedMessage = quoted.message
// if a participant is quoted, then it must be a group
// hence, remoteJid of group must also be entered
if (quoted.key.participant) {
message[key].contextInfo.remoteJid = quoted.key.remoteJid
}
}
message[key].caption = options?.caption
if (!message[key].jpegThumbnail) message[key].jpegThumbnail = options?.thumbnail
const messageJSON = {

View File

@@ -5,7 +5,8 @@ import * as assert from 'assert'
import fetch from 'node-fetch'
import { decodeMediaMessage, validateJIDForSending } from './Utils'
import { promiseTimeout, createTimeout, Browsers } from '../WAConnection/Utils'
import { promiseTimeout, createTimeout, Browsers, generateMessageTag } from '../WAConnection/Utils'
import { MessageLogLevel } 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
@@ -19,6 +20,8 @@ async function sendAndRetreiveMessage(client: WAClient, content, type: MessageTy
function WAClientTest(name: string, func: (client: WAClient) => void) {
describe(name, () => {
const client = new WAClient()
client.logLevel = MessageLogLevel.info
before(async () => {
const file = './auth_info.json'
await client.connectSlim(file)
@@ -121,9 +124,24 @@ WAClientTest('Misc', (client) => {
})
it('should return the status', async () => {
const response = await client.getStatus(testJid)
assert.ok(response.status)
assert.strictEqual(typeof response.status, 'string')
})
it('should update status', async () => {
const newStatus = 'v cool status'
const response = await client.getStatus()
assert.strictEqual(typeof response.status, 'string')
await createTimeout (1000)
await client.setStatus (newStatus)
const response2 = await client.getStatus()
assert.equal (response2.status, newStatus)
await createTimeout (1000)
await client.setStatus (response.status) // update back
})
it('should return the stories', async () => {
await client.getStories()
})
@@ -195,6 +213,15 @@ WAClientTest('Groups', (client) => {
assert.strictEqual(metadata.id, gid)
assert.strictEqual(metadata.participants.filter((obj) => obj.id.split('@')[0] === testJid.split('@')[0]).length, 1)
})
it('should update the group description', async () => {
const newDesc = 'Wow this was set from Baileys'
await client.groupUpdateDescription (gid, newDesc)
await createTimeout (1000)
const metadata = await client.groupMetadata(gid)
assert.strictEqual(metadata.desc, newDesc)
})
it('should send a message on the group', async () => {
await client.sendMessage(gid, 'hello', MessageType.text)
})

View File

@@ -326,7 +326,7 @@ export default class WAConnectionBase {
}
}
generateMessageTag () {
return `${this.referenceDate.getTime()/1000}.--${this.msgCount}`
return `${Math.round(this.referenceDate.getTime())/1000}.--${this.msgCount}`
}
protected log(text, level: MessageLogLevel) {
if (this.logLevel >= level)

View File

@@ -61,6 +61,7 @@ export interface WAGroupMetadata {
creation: number
desc?: string
descOwner?: string
descId?: string
participants: [{ id: string; isAdmin: boolean; isSuperAdmin: boolean }]
}
export interface WAGroupModification {

View File

@@ -65,15 +65,23 @@ export function randomBytes(length) {
return Crypto.randomBytes(length)
}
export const createTimeout = (timeout) => new Promise(resolve => setTimeout(resolve, timeout))
export function promiseTimeout<T>(ms: number, promise: Promise<T>) {
export async function promiseTimeout<T>(ms: number, promise: Promise<T>) {
if (!ms) return promise
// Create a promise that rejects in <ms> milliseconds
const timeout = createTimeout (ms).then (() => { throw new BaileysError ('Timed out', promise) })
return Promise.race([promise, timeout]) as Promise<T>
let timeoutI
const timeout = new Promise(
(_, reject) => timeoutI = setTimeout(() => reject(new BaileysError ('Timed out', promise)), ms)
)
try {
const content = await Promise.race([promise, timeout])
return content as T
} finally {
clearTimeout (timeoutI)
}
}
// whatsapp requires a message tag for every message, we just use the timestamp as one
export function generateMessageTag(epoch?: number) {
let tag = new Date().getTime().toString()
let tag = Math.round(new Date().getTime()/1000).toString()
if (epoch) tag += '.--' + epoch // attach epoch if provided
return tag
}