group promote/demote event fix + archive fix + group events rework

This commit is contained in:
Adhiraj Singh
2020-11-12 13:46:10 +05:30
parent c4548f756b
commit eaf56f6d71
7 changed files with 147 additions and 109 deletions

View File

@@ -184,7 +184,7 @@ on (event: 'open', listener: (result: WAOpenResult) => void): this
on (event: 'connecting', listener: () => void): this
/** when the connection has closed */
on (event: 'close', listener: (err: {reason?: DisconnectReason | string, isReconnecting: boolean}) => void): this
/** when the socket has closed */
/** when the socket is closed */
on (event: 'ws-close', listener: (err: {reason?: DisconnectReason | string}) => void): this
/** when WA updates the credentials */
on (event: 'credentials-updated', listener: (auth: AuthenticationCredentials) => void): this
@@ -207,17 +207,11 @@ on (event: 'message-update', listener: (message: WAMessage) => void): this
/** when a message's status is updated (deleted, delivered, read, sent etc.) */
on (event: 'message-status-update', listener: (message: WAMessageStatusUpdate) => void): this
/** when participants are added to a group */
on (event: 'group-participants-add', listener: (update: {jid: string, participants: string[], actor?: string}) => void): this
/** when participants are removed or leave from a group */
on (event: 'group-participants-remove', listener: (update: {jid: string, participants: string[], actor?: string}) => void): this
/** when participants are promoted in a group */
on (event: 'group-participants-promote', listener: (update: {jid: string, participants: string[], actor?: string}) => void): this
/** when participants are demoted in a group */
on (event: 'group-participants-demote', listener: (update: {jid: string, participants: string[], actor?: string}) => void): this
/** when the group settings is updated */
on (event: 'group-settings-update', listener: (update: {jid: string, restrict?: string, announce?: string, actor?: string}) => void): this
/** when the group description is updated */
on (event: 'group-description-update', listener: (update: {jid: string, description?: string, actor?: string}) => void): this
on (event: 'group-participants-update', listener: (update: {jid: string, participants: string[], actor?: string, action: WAParticipantAction}) => void): this
/** when the group is updated */
on (event: 'group-update', listener: (update: Partial<WAGroupMetadata> & {jid: string, actor?: string}) => void): this
/** when WA sends back a pong */
on (event: 'received-pong', listener: () => void): this
```
## Sending Messages

View File

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

View File

@@ -1,4 +1,4 @@
import { MessageType, GroupSettingChange, delay, ChatModification } from '../WAConnection/WAConnection'
import { MessageType, GroupSettingChange, delay, ChatModification, whatsappID } from '../WAConnection/WAConnection'
import * as assert from 'assert'
import { WAConnectionTest, testJid } from './Common'
@@ -29,7 +29,7 @@ WAConnectionTest('Groups', (conn) => {
const newDesc = 'Wow this was set from Baileys'
const waitForEvent = new Promise (resolve => {
conn.on ('group-description-update', ({jid, actor}) => {
conn.once ('group-update', ({jid, actor}) => {
if (jid === gid) {
assert.ok (actor, conn.user.jid)
resolve ()
@@ -39,8 +39,6 @@ WAConnectionTest('Groups', (conn) => {
await conn.groupUpdateDescription (gid, newDesc)
await waitForEvent
conn.removeAllListeners ('group-description-update')
const metadata = await conn.groupMetadata(gid)
assert.strictEqual(metadata.desc, newDesc)
})
@@ -61,7 +59,7 @@ WAConnectionTest('Groups', (conn) => {
it('should update the subject', async () => {
const subject = 'Baileyz ' + Math.floor(Math.random()*5)
const waitForEvent = new Promise (resolve => {
conn.on ('chat-update', ({jid, name}) => {
conn.once ('chat-update', ({jid, name}) => {
if (jid === gid) {
assert.equal (name, subject)
resolve ()
@@ -70,14 +68,14 @@ WAConnectionTest('Groups', (conn) => {
})
await conn.groupUpdateSubject(gid, subject)
await waitForEvent
conn.removeAllListeners ('chat-update')
const metadata = await conn.groupMetadata(gid)
assert.strictEqual(metadata.subject, subject)
})
it('should update the group settings', async () => {
const waitForEvent = new Promise (resolve => {
conn.on ('group-settings-update', ({jid, announce}) => {
conn.once ('group-update', ({jid, announce}) => {
if (jid === gid) {
assert.equal (announce, 'true')
resolve ()
@@ -92,25 +90,42 @@ WAConnectionTest('Groups', (conn) => {
await delay (2000)
await conn.groupSettingChange (gid, GroupSettingChange.settingsChange, true)
})
it('should remove someone from a group', async () => {
it('should promote someone', async () => {
const waitForEvent = new Promise (resolve => {
conn.on ('group-participants-remove', ({jid, participants}) => {
conn.once ('group-participants-update', ({ jid, action }) => {
if (jid === gid) {
assert.equal (participants[0], testJid)
assert.strictEqual (action, 'promote')
resolve ()
}
})
})
await conn.groupMakeAdmin(gid, [ testJid ])
await waitForEvent
})
it('should remove someone from a group', async () => {
const metadata = await conn.groupMetadata (gid)
if (metadata.participants.find(({id}) => id === testJid)) {
if (metadata.participants.find(({id}) => whatsappID(id) === testJid)) {
const waitForEvent = new Promise (resolve => {
conn.once ('group-participants-update', ({jid, participants, action}) => {
if (jid === gid) {
assert.strictEqual (participants[0], testJid)
assert.strictEqual (action, 'remove')
resolve ()
}
})
})
await conn.groupRemove(gid, [testJid])
await waitForEvent
}
conn.removeAllListeners ('group-participants-remove')
} else console.log(`could not find testJid`)
})
it('should leave the group', async () => {
const waitForEvent = new Promise (resolve => {
conn.on ('chat-update', ({jid, read_only}) => {
conn.once ('chat-update', ({jid, read_only}) => {
if (jid === gid) {
assert.equal (read_only, 'true')
resolve ()
@@ -119,13 +134,12 @@ WAConnectionTest('Groups', (conn) => {
})
await conn.groupLeave(gid)
await waitForEvent
conn.removeAllListeners ('chat-update')
await conn.groupMetadataMinimal (gid)
})
it('should archive the group', async () => {
const waitForEvent = new Promise (resolve => {
conn.on ('chat-update', ({jid, archive}) => {
conn.once ('chat-update', ({jid, archive}) => {
if (jid === gid) {
assert.equal (archive, 'true')
resolve ()
@@ -134,11 +148,10 @@ WAConnectionTest('Groups', (conn) => {
})
await conn.modifyChat(gid, ChatModification.archive)
await waitForEvent
conn.removeAllListeners ('chat-update')
})
it('should delete the group', async () => {
const waitForEvent = new Promise (resolve => {
conn.on ('chat-update', (chat) => {
conn.once ('chat-update', (chat) => {
if (chat.jid === gid) {
assert.equal (chat['delete'], 'true')
resolve ()
@@ -147,6 +160,5 @@ WAConnectionTest('Groups', (conn) => {
})
await conn.deleteChat(gid)
await waitForEvent
conn.removeAllListeners ('chat-update')
})
})

View File

@@ -1,6 +1,6 @@
import * as QR from 'qrcode-terminal'
import { WAConnection as Base } from './3.Connect'
import { WAMessageStatusUpdate, WAMessage, WAContact, WAChat, WAMessageProto, WA_MESSAGE_STUB_TYPE, WA_MESSAGE_STATUS_TYPE, PresenceUpdate, BaileysEvent, DisconnectReason, WANode, WAOpenResult, Presence, AuthenticationCredentials } from './Constants'
import { WAMessageStatusUpdate, WAMessage, WAContact, WAChat, WAMessageProto, WA_MESSAGE_STUB_TYPE, WA_MESSAGE_STATUS_TYPE, PresenceUpdate, BaileysEvent, DisconnectReason, WANode, WAOpenResult, Presence, AuthenticationCredentials, WAParticipantAction, WAGroupMetadata } from './Constants'
import { whatsappID, unixTimestampSeconds, isGroupID, GET_MESSAGE_ID, WA_MESSAGE_ID, waMessageKey } from './Utils'
import KeyedDB from '@adiwajshing/keyed-db'
import { Mutex } from './Mutex'
@@ -21,6 +21,23 @@ export class WAConnection extends Base {
}
this.chatAddMessageAppropriate (message)
})
this.on('CB:Chat,cmd:action', json => {
const data = json[1].data
if (data) {
const emitGroupParticipantsUpdate = (action: WAParticipantAction) => this.emit(
'group-participants-update',
{ participants: data[2].participants.map(whatsappID), actor: data[1], jid: json[1].id, action }
)
switch (data[0]) {
case "promote":
emitGroupParticipantsUpdate('promote')
break
case "demote":
emitGroupParticipantsUpdate('demote')
break
}
}
})
// presence updates
this.on('CB:Presence', json => {
const update = json[1] as PresenceUpdate
@@ -269,11 +286,14 @@ export class WAConnection extends Base {
const jid = chat.jid
let actor = whatsappID (message.participant)
let participants: string[]
const emitParticipantsUpdate = (action: WAParticipantAction) => this.emit ('group-participants-update', { jid, actor, participants, action })
const emitGroupUpdate = (update: Partial<WAGroupMetadata>) => this.emit ('group-update', { jid, actor, ...update })
switch (message.messageStubType) {
case WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_LEAVE:
case WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_REMOVE:
participants = message.messageStubParameters.map (whatsappID)
this.emit ('group-participants-remove', { jid, actor, participants})
emitParticipantsUpdate('remove')
// mark the chat read only if you left the group
if (participants.includes(this.user.jid)) {
chat.read_only = 'true'
@@ -288,24 +308,34 @@ export class WAConnection extends Base {
delete chat.read_only
this.emit ('chat-update', { jid, read_only: 'false' })
}
this.emit ('group-participants-add', { jid, participants, actor })
emitParticipantsUpdate('add')
break
case WA_MESSAGE_STUB_TYPE.GROUP_CHANGE_ANNOUNCE:
const announce = message.messageStubParameters[0] === 'on' ? 'true' : 'false'
this.emit ('group-settings-update', { jid, announce, actor })
emitGroupUpdate({ announce })
break
case WA_MESSAGE_STUB_TYPE.GROUP_CHANGE_ANNOUNCE:
const restrict = message.messageStubParameters[0] === 'on' ? 'true' : 'false'
this.emit ('group-settings-update', { jid, restrict, actor })
emitGroupUpdate({ restrict })
break
case WA_MESSAGE_STUB_TYPE.GROUP_CHANGE_DESCRIPTION:
this.emit ('group-description-update', { jid, actor })
const desc = message.messageStubParameters[0]
emitGroupUpdate({ desc })
break
case WA_MESSAGE_STUB_TYPE.GROUP_CHANGE_SUBJECT:
case WA_MESSAGE_STUB_TYPE.GROUP_CREATE:
chat.name = message.messageStubParameters[0]
this.emit ('chat-update', { jid, name: chat.name })
break
case WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_PROMOTE:
case WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_DEMOTE:
participants = message.messageStubParameters.map (whatsappID)
emitParticipantsUpdate(
WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_PROMOTE ?
'promote' :
'demote'
)
break
}
}
}
@@ -357,17 +387,9 @@ export class WAConnection extends Base {
/** when a message's status is updated (deleted, delivered, read, sent etc.) */
on (event: 'message-status-update', listener: (message: WAMessageStatusUpdate) => void): this
/** when participants are added to a group */
on (event: 'group-participants-add', listener: (update: {jid: string, participants: string[], actor?: string}) => void): this
/** when participants are removed or leave from a group */
on (event: 'group-participants-remove', listener: (update: {jid: string, participants: string[], actor?: string}) => void): this
/** when participants are promoted in a group */
on (event: 'group-participants-promote', listener: (update: {jid: string, participants: string[], actor?: string}) => void): this
/** when participants are demoted in a group */
on (event: 'group-participants-demote', listener: (update: {jid: string, participants: string[], actor?: string}) => void): this
/** when the group settings is updated */
on (event: 'group-settings-update', listener: (update: {jid: string, restrict?: string, announce?: string, actor?: string}) => void): this
/** when the group description is updated */
on (event: 'group-description-update', listener: (update: {jid: string, description?: string, actor?: string}) => void): this
on (event: 'group-participants-update', listener: (update: {jid: string, participants: string[], actor?: string, action: WAParticipantAction}) => void): this
/** when the group is updated */
on (event: 'group-update', listener: (update: Partial<WAGroupMetadata> & {jid: string, actor?: string}) => void): this
/** when WA sends back a pong */
on (event: 'received-pong', listener: () => void): this

View File

@@ -1,12 +1,12 @@
import {WAConnection as Base} from './4.Events'
import { Presence, WABroadcastListInfo, WAProfilePictureChange, ChatModification, WALoadChatOptions } from './Constants'
import { Presence, WABroadcastListInfo, WAProfilePictureChange, WALoadChatOptions } from './Constants'
import {
WAMessage,
WANode,
WAMetric,
WAFlag,
} from '../WAConnection/Constants'
import { generateProfilePicture, whatsappID, unixTimestampSeconds } from './Utils'
import { generateProfilePicture, whatsappID } from './Utils'
import { Mutex } from './Mutex'
// All user related functions -- get profile picture, set status etc.
@@ -88,7 +88,7 @@ export class WAConnection extends Base {
async getBroadcastListInfo(jid: string) { return this.query({json: ['query', 'contact', jid], expect200: true }) as Promise<WABroadcastListInfo> }
/** Delete the chat of a given ID */
async deleteChat (jid: string) {
const response = await this.setQuery ([ ['chat', {type: 'delete', jid: jid}, null] ], [12, WAFlag.ignore]) as {status: number}
const response = await this.setQuery ([ ['chat', {type: 'delete', jid: jid}, null] ], [12, WAFlag.ignore])
const chat = this.chats.get (jid)
if (chat) {
this.chats.delete (chat)
@@ -147,51 +147,4 @@ export class WAConnection extends Base {
}
return response
}
/**
* Modify a given chat (archive, pin etc.)
* @param jid the ID of the person/group you are modifiying
* @param durationMs only for muting, how long to mute the chat for
*/
@Mutex ((jid, type) => jid+type)
async modifyChat (jid: string, type: ChatModification, durationMs?: number) {
jid = whatsappID (jid)
const chat = this.assertChatGet (jid)
let chatAttrs: Record<string, string> = {jid: jid}
if (type === ChatModification.mute && !durationMs) {
throw new Error('duration must be set to the timestamp of the time of pinning/unpinning of the chat')
}
durationMs = durationMs || 0
switch (type) {
case ChatModification.pin:
case ChatModification.mute:
const strStamp = (unixTimestampSeconds() + Math.floor(durationMs/1000)).toString()
chatAttrs.type = type
chatAttrs[type] = strStamp
break
case ChatModification.unpin:
case ChatModification.unmute:
chatAttrs.type = type.replace ('un', '') // replace 'unpin' with 'pin'
chatAttrs.previous = chat[type.replace ('un', '')]
break
default:
chatAttrs.type = type
break
}
const response = await this.setQuery ([['chat', chatAttrs, null]])
if (chat) {
if (type.includes('un')) {
type = type.replace ('un', '') as ChatModification
delete chat[type.replace('un','')]
this.emit ('chat-update', { jid, [type]: false })
} else {
chat[type] = chatAttrs[type] || 'true'
this.emit ('chat-update', { jid, [type]: chat[type] })
}
}
return response
}
}

View File

@@ -1,6 +1,6 @@
import {WAConnection as Base} from './6.MessagesSend'
import { MessageType, WAMessageKey, MessageInfo, WAMessageContent, WAMetric, WAFlag, WANode, WAMessage, WAMessageProto } from './Constants'
import { whatsappID, delay, toNumber, unixTimestampSeconds, GET_MESSAGE_ID, WA_MESSAGE_ID } from './Utils'
import { MessageType, WAMessageKey, MessageInfo, WAMessageContent, WAMetric, WAFlag, WANode, WAMessage, WAMessageProto, ChatModification, BaileysError } from './Constants'
import { whatsappID, delay, toNumber, unixTimestampSeconds, GET_MESSAGE_ID, WA_MESSAGE_ID, isGroupID } from './Utils'
import { Mutex } from './Mutex'
export class WAConnection extends Base {
@@ -325,4 +325,64 @@ export class WAConnection extends Base {
await this.relayWAMessage (waMessage)
return waMessage
}
/**
* Modify a given chat (archive, pin etc.)
* @param jid the ID of the person/group you are modifiying
* @param durationMs only for muting, how long to mute the chat for
*/
@Mutex ((jid, type) => jid+type)
async modifyChat (jid: string, type: ChatModification, durationMs?: number) {
jid = whatsappID (jid)
const chat = this.assertChatGet (jid)
let chatAttrs: Record<string, string> = {jid: jid}
if (type === ChatModification.mute && !durationMs) {
throw new BaileysError(
'duration must be set to the timestamp of the time of pinning/unpinning of the chat',
{ status: 400 }
)
}
durationMs = durationMs || 0
switch (type) {
case ChatModification.pin:
case ChatModification.mute:
const strStamp = (unixTimestampSeconds() + Math.floor(durationMs/1000)).toString()
chatAttrs.type = type
chatAttrs[type] = strStamp
break
case ChatModification.unpin:
case ChatModification.unmute:
chatAttrs.type = type.replace ('un', '') // replace 'unpin' with 'pin'
chatAttrs.previous = chat[type.replace ('un', '')]
break
default:
chatAttrs.type = type
const msg = (await this.loadMessages(jid, 1)).messages[0]
if (msg) {
chatAttrs.index = msg.key.id
chatAttrs.owner = msg.key.fromMe.toString()
}
if (isGroupID(jid)) {
chatAttrs.participant = this.user?.jid
}
break
}
const response = await this.setQuery ([['chat', chatAttrs, null]], [ WAMetric.chat, WAFlag.ignore ])
if (chat) {
if (type.includes('un')) {
type = type.replace ('un', '') as ChatModification
delete chat[type.replace('un','')]
this.emit ('chat-update', { jid, [type]: false })
} else {
chat[type] = chatAttrs[type] || 'true'
this.emit ('chat-update', { jid, [type]: chat[type] })
}
}
return response
}
}

View File

@@ -162,9 +162,9 @@ export interface WAGroupMetadata {
descOwner?: string
descId?: string
/** is set when the group only allows admins to change group settings */
restrict?: 'true'
restrict?: 'true' | 'false'
/** is set when the group only allows admins to write messages */
announce?: 'true'
announce?: 'true' | 'false'
participants: [{ id: string; isAdmin: boolean; isSuperAdmin: boolean }]
}
export interface WAGroupModification {
@@ -413,6 +413,7 @@ export interface WASendMessageResponse {
messageID: string
message: WAMessage
}
export type WAParticipantAction = 'add' | 'remove' | 'promote' | 'demote'
export type BaileysEvent =
'open' |
'connecting' |
@@ -427,12 +428,8 @@ export type BaileysEvent =
'message-new' |
'message-update' |
'message-status-update' |
'group-participants-add' |
'group-participants-remove' |
'group-participants-promote' |
'group-participants-demote' |
'group-settings-update' |
'group-description-update' |
'group-participants-update' |
'group-update' |
'received-pong' |
'credentials-updated' |
'connection-validated'