Added support for sending + toggling disappearing messages

This commit is contained in:
Adhiraj Singh
2020-12-12 00:21:11 +05:30
parent a778f999a9
commit eb7b8506a9
6 changed files with 167 additions and 11 deletions

View File

@@ -254,8 +254,7 @@ To note:
``` ts
const info: MessageOptions = {
quoted: quotedMessage, // the message you want to quote
contextInfo: { forwardingScore: 2, isForwarded: true }, // some random context info
// (can show a forwarded message with this too)
contextInfo: { forwardingScore: 2, isForwarded: true }, // some random context info (can show a forwarded message with this too)
timestamp: Date(), // optional, if you want to manually set the timestamp of the message
caption: "hello there!", // (for media messages) the caption to send with the media (cannot be sent with stickers though)
thumbnail: "23GD#4/==", /* (for location & media messages) has to be a base 64 encoded JPEG if you want to send a custom thumb,
@@ -263,13 +262,16 @@ To note:
Do not enter this field if you want to automatically generate a thumb
*/
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
/* will send audio messages as voice notes, if set to true */
ptt: true,
// will detect links & generate a link preview automatically (default true)
detectLinks: true
detectLinks: true,
/** Should it send as a disappearing messages.
* By default 'chat' -- which follows the setting of the chat */
sendEphemeral: 'chat'
}
```
## Forwarding Messages
@@ -359,6 +361,22 @@ await conn.modifyChat (jid, ChatModification.delete) // will delete the chat (ca
**Note:** to unmute or unpin a chat, one must pass the timestamp of the pinning or muting. This is returned by the pin & mute functions. This is also available in the `WAChat` objects of the respective chats, as a `mute` or `pin` property.
## Disappearing Messages
``` ts
const jid = '1234@s.whatsapp.net' // can also be a group
// turn on disappearing messages
await conn.toggleDisappearingMessages(
jid,
WA_DEFAULT_EPHEMERAL // this is 1 week in seconds -- how long you want messages to appear for
)
// will automatically send as a disappearing message
await conn.sendMessage(jid, 'Hello poof!', MessageType.text)
// turn off disappearing messages
await conn.toggleDisappearingMessages(jid, 0)
```
## Misc
- To load chats in a paginated manner

View File

@@ -1,6 +1,6 @@
{
"name": "@adiwajshing/baileys",
"version": "3.3.2",
"version": "3.4.0",
"description": "WhatsApp Web API",
"homepage": "https://github.com/adiwajshing/Baileys",
"main": "lib/WAConnection/WAConnection.js",

View File

@@ -1,8 +1,8 @@
import { Presence, ChatModification, delay, newMessagesDB } from '../WAConnection/WAConnection'
import { Presence, ChatModification, delay, newMessagesDB, WA_DEFAULT_EPHEMERAL, MessageType } from '../WAConnection/WAConnection'
import { promises as fs } from 'fs'
import * as assert from 'assert'
import fetch from 'node-fetch'
import { WAConnectionTest, testJid, assertChatDBIntegrity } from './Common'
import { WAConnectionTest, testJid, assertChatDBIntegrity, sendAndRetreiveMessage } from './Common'
WAConnectionTest('Misc', conn => {
@@ -197,4 +197,65 @@ WAConnectionTest('Misc', conn => {
await task
})
it('should toggle disappearing messages', async () => {
let chat = conn.chats.get(testJid)
if (!chat) {
// wait for chats
await new Promise(resolve => (
conn.once('chats-received', () => resolve())
))
chat = conn.chats.get(testJid)
}
const waitForChatUpdate = (ephemeralOn: boolean) => (
new Promise(resolve => (
conn.on('chat-update', ({ jid, ephemeral }) => {
if (jid === testJid && typeof ephemeral !== 'undefined') {
assert.strictEqual(!!(+ephemeral), ephemeralOn)
assert.strictEqual(!!(+chat.ephemeral), ephemeralOn)
resolve()
conn.removeAllListeners('chat-update')
}
})
))
)
const toggleDisappearingMessages = async (on: boolean) => {
const update = waitForChatUpdate(on)
await conn.toggleDisappearingMessages(testJid, on ? WA_DEFAULT_EPHEMERAL : 0)
await update
}
if (!chat.eph_setting_ts) {
await toggleDisappearingMessages(true)
}
await delay(1000)
let msg = await sendAndRetreiveMessage(
conn,
'This will go poof 😱',
MessageType.text
)
assert.ok(msg.message?.ephemeralMessage)
const contextInfo = msg.message?.ephemeralMessage?.message?.extendedTextMessage?.contextInfo
assert.strictEqual(contextInfo.expiration, chat.ephemeral)
assert.strictEqual(+contextInfo.ephemeralSettingTimestamp, +chat.eph_setting_ts)
// test message deletion
await conn.deleteMessage(testJid, msg.key)
await delay(1000)
await toggleDisappearingMessages(false)
await delay(1000)
msg = await sendAndRetreiveMessage(
conn,
'This will not go poof 😔',
MessageType.text
)
assert.ok(msg.message.extendedTextMessage)
})
})

View File

@@ -438,6 +438,23 @@ export class WAConnection extends Base {
update && Object.assign(chatUpdate, update)
}
}
const ephemeralProtocolMsg = message.message?.ephemeralMessage?.message?.protocolMessage
if (
ephemeralProtocolMsg &&
ephemeralProtocolMsg.type === WAMessageProto.ProtocolMessage.ProtocolMessageType.EPHEMERAL_SETTING
) {
chatUpdate.eph_setting_ts = message.messageTimestamp.toString()
chatUpdate.ephemeral = ephemeralProtocolMsg.ephemeralExpiration.toString()
if (ephemeralProtocolMsg.ephemeralExpiration) {
chat.eph_setting_ts = chatUpdate.eph_setting_ts
chat.ephemeral = chatUpdate.ephemeral
} else {
delete chat.eph_setting_ts
delete chat.ephemeral
}
}
const messages = chat.messages
const protocolMessage = message.message?.protocolMessage
@@ -471,7 +488,7 @@ export class WAConnection extends Base {
messages.delete (messages.all()[0]) // delete oldest messages
}
// only update if it's an actual message
if (message.message) {
if (message.message && !ephemeralProtocolMsg) {
this.chatUpdateTime (chat, +toNumber(message.messageTimestamp))
chatUpdate.t = chat.t
}
@@ -515,7 +532,7 @@ export class WAConnection extends Base {
const announce = message.messageStubParameters[0] === 'on' ? 'true' : 'false'
emitGroupUpdate({ announce })
break
case WA_MESSAGE_STUB_TYPE.GROUP_CHANGE_ANNOUNCE:
case WA_MESSAGE_STUB_TYPE.GROUP_CHANGE_RESTRICT:
const restrict = message.messageStubParameters[0] === 'on' ? 'true' : 'false'
emitGroupUpdate({ restrict })
break

View File

@@ -9,7 +9,7 @@ import {
WALocationMessage,
WAContactMessage,
WATextMessage,
WAMessageContent, WAMetric, WAFlag, WAMessage, BaileysError, WA_MESSAGE_STATUS_TYPE, WAMessageProto, MediaConnInfo, MessageTypeProto, URL_REGEX, WAUrlInfo
WAMessageContent, WAMetric, WAFlag, WAMessage, BaileysError, WA_MESSAGE_STATUS_TYPE, WAMessageProto, MediaConnInfo, MessageTypeProto, URL_REGEX, WAUrlInfo, WA_DEFAULT_EPHEMERAL
} from './Constants'
import { generateMessageID, sha256, hmacSign, aesEncrypWithIV, randomBytes, generateThumbnail, getMediaKeys, decodeMediaMessageBuffer, extensionForMediaMessage, whatsappID, unixTimestampSeconds, getAudioDuration } from './Utils'
import { Mutex } from './Mutex'
@@ -47,6 +47,22 @@ export class WAConnection extends Base {
const preparedMessage = this.prepareMessageFromContent(id, content, options)
return preparedMessage
}
/**
* Toggles disappearing messages for the given chat
*
* @param jid the chat to toggle
* @param ephemeralExpiration 0 to disable, enter any positive number to enable disappearing messages for the specified duration;
* For the default see WA_DEFAULT_EPHEMERAL
*/
async toggleDisappearingMessages(jid: string, ephemeralExpiration?: number, opts: { waitForAck: boolean } = { waitForAck: true }) {
const message = this.prepareMessageFromContent(
jid,
this.prepareDisappearingMessageSettingContent(ephemeralExpiration),
{}
)
await this.relayWAMessage(message, opts)
return message
}
/** Prepares the message content */
async prepareMessageContent (message: string | WATextMessage | WALocationMessage | WAContactMessage | Buffer, type: MessageType, options: MessageOptions) {
let m: WAMessageContent = {}
@@ -85,6 +101,20 @@ export class WAConnection extends Base {
}
return WAMessageProto.Message.fromObject (m)
}
prepareDisappearingMessageSettingContent(ephemeralExpiration?: number) {
ephemeralExpiration = ephemeralExpiration || 0
const content: WAMessageContent = {
ephemeralMessage: {
message: {
protocolMessage: {
type: WAMessageProto.ProtocolMessage.ProtocolMessageType.EPHEMERAL_SETTING,
ephemeralExpiration
}
}
}
}
return WAMessageProto.Message.fromObject(content)
}
/** Prepare a media message for sending */
async prepareMessageMedia(buffer: Buffer, mediaType: MessageType, options: MessageOptions = {}) {
await this.waitForConnection ()
@@ -175,7 +205,7 @@ export class WAConnection extends Base {
/** prepares a WAMessage for sending from the given content & options */
prepareMessageFromContent(id: string, message: WAMessageContent, options: MessageOptions) {
if (!options.timestamp) options.timestamp = new Date() // set timestamp to now
if (typeof options.sendEphemeral === 'undefined') options.sendEphemeral = 'chat'
// prevent an annoying bug (WA doesn't accept sending messages with '@c.us')
id = whatsappID (id)
@@ -202,6 +232,28 @@ export class WAConnection extends Base {
if (options?.thumbnail) {
message[key].jpegThumbnail = Buffer.from(options.thumbnail, 'base64')
}
const chat = this.chats.get(id)
if (
// if we want to send a disappearing message
((options?.sendEphemeral === 'chat' && chat?.ephemeral) ||
options?.sendEphemeral === true) &&
// and it's not a protocol message -- delete, toggle disappear message
key !== 'protocolMessage' &&
// already not converted to disappearing message
key !== 'ephemeralMessage'
) {
message[key].contextInfo = {
...(message[key].contextInfo || {}),
expiration: chat?.ephemeral || WA_DEFAULT_EPHEMERAL,
ephemeralSettingTimestamp: chat?.eph_setting_ts
}
message = {
ephemeralMessage: {
message
}
}
}
message = WAMessageProto.Message.fromObject (message)
const messageJSON = {

View File

@@ -7,6 +7,7 @@ export const WS_URL = 'wss://web.whatsapp.com/ws'
export const DEFAULT_ORIGIN = 'https://web.whatsapp.com'
export const KEEP_ALIVE_INTERVAL_MS = 20*1000
export const WA_DEFAULT_EPHEMERAL = 7*24*60*60
// export the WAMessage Prototypes
export { proto as WAMessageProto }
@@ -224,6 +225,10 @@ export interface WAChat {
spam: 'false' | 'true'
modify_tag: string
name?: string
/** when ephemeral messages were toggled on */
eph_setting_ts?: string
/** how long each message lasts for */
ephemeral?: string
// Baileys added properties
messages: KeyedDB<WAMessage, string>
@@ -367,6 +372,9 @@ export interface MessageOptions {
forceNewMediaOptions?: boolean
/** Wait for the message to be sent to the server (default true) */
waitForAck?: boolean
/** Should it send as a disappearing messages.
* By default 'chat' -- which follows the setting of the chat */
sendEphemeral?: 'chat' | boolean
}
export interface WABroadcastListInfo {
status: number