mirror of
https://github.com/cheveguerra/whatsapp-web.js.git
synced 2026-04-18 03:29:14 +00:00
Compare commits
9 Commits
fix-button
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f23ab82565 | ||
|
|
f44555713d | ||
|
|
9f15499592 | ||
|
|
ac7df25075 | ||
|
|
812ff0ddfc | ||
|
|
780005a81a | ||
|
|
c44af204a1 | ||
|
|
dc16bbbdac | ||
|
|
4846625119 |
86
example.js
86
example.js
@@ -1,4 +1,4 @@
|
|||||||
const { Client, Location, List, Buttons, LocalAuth} = require('./index');
|
const { Client, Location, List, Buttons, LocalAuth } = require('./index');
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
authStrategy: new LocalAuth(),
|
authStrategy: new LocalAuth(),
|
||||||
@@ -32,12 +32,6 @@ client.on('ready', () => {
|
|||||||
client.on('message', async msg => {
|
client.on('message', async msg => {
|
||||||
console.log('MESSAGE RECEIVED', msg);
|
console.log('MESSAGE RECEIVED', msg);
|
||||||
|
|
||||||
if (msg.selectedButtonId == 'test') {
|
|
||||||
return msg.reply('You clicked the button!');
|
|
||||||
} else if (msg.selectedRowId == 'test') {
|
|
||||||
return msg.reply('You clicked that section');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.body === '!ping reply') {
|
if (msg.body === '!ping reply') {
|
||||||
// Send a new message as a reply to the current one
|
// Send a new message as a reply to the current one
|
||||||
msg.reply('pong');
|
msg.reply('pong');
|
||||||
@@ -195,30 +189,14 @@ client.on('message', async msg => {
|
|||||||
client.interface.openChatWindowAt(quotedMsg.id._serialized);
|
client.interface.openChatWindowAt(quotedMsg.id._serialized);
|
||||||
}
|
}
|
||||||
} else if (msg.body === '!buttons') {
|
} else if (msg.body === '!buttons') {
|
||||||
let button = new Buttons(
|
let button = new Buttons('Button body', [{ body: 'bt1' }, { body: 'bt2' }, { body: 'bt3' }], 'title', 'footer');
|
||||||
'Button body\n\nWant to test buttons some more? Check out https://github.com/wwebjs/buttons-test',
|
|
||||||
[
|
|
||||||
{ body: 'Some text' },
|
|
||||||
{ body: 'Try clicking me (id:test)', id: 'test'},
|
|
||||||
],
|
|
||||||
'title',
|
|
||||||
'footer'
|
|
||||||
);
|
|
||||||
client.sendMessage(msg.from, button);
|
client.sendMessage(msg.from, button);
|
||||||
} else if (msg.body === '!list') {
|
} else if (msg.body === '!list') {
|
||||||
let sections = [
|
let sections = [{ title: 'sectionTitle', rows: [{ title: 'ListItem1', description: 'desc' }, { title: 'ListItem2' }] }];
|
||||||
{
|
let list = new List('List body', 'btnText', sections, 'Title', 'footer');
|
||||||
title: 'Secton title',
|
client.sendMessage(msg.from, list);
|
||||||
rows: [
|
|
||||||
{title:'ListItem1', description: 'desc'},
|
|
||||||
{title: 'Try clicking me (id: test)', id: 'test'}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
let list = new List('List body', 'btnText', sections, 'Custom title', 'custom footer, google.com');
|
|
||||||
await client.sendMessage(msg.from, list);
|
|
||||||
} else if (msg.body === '!reaction') {
|
} else if (msg.body === '!reaction') {
|
||||||
await msg.react('👍');
|
msg.react('👍');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -253,7 +231,7 @@ client.on('message_ack', (msg, ack) => {
|
|||||||
ACK_PLAYED: 4
|
ACK_PLAYED: 4
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if(ack == 3) {
|
if (ack == 3) {
|
||||||
// The message was read
|
// The message was read
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -276,7 +254,7 @@ client.on('group_update', (notification) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
client.on('change_state', state => {
|
client.on('change_state', state => {
|
||||||
console.log('CHANGE STATE', state );
|
console.log('CHANGE STATE', state);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Change to false if you don't want to reject incoming calls
|
// Change to false if you don't want to reject incoming calls
|
||||||
@@ -292,3 +270,51 @@ client.on('disconnected', (reason) => {
|
|||||||
console.log('Client was logged out', reason);
|
console.log('Client was logged out', reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
client.on('contact_changed', async (message, oldId, newId, isContact) => {
|
||||||
|
/** The time the event occurred. */
|
||||||
|
const eventTime = (new Date(message.timestamp * 1000)).toLocaleString();
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`The contact ${oldId.slice(0, -5)}` +
|
||||||
|
`${!isContact ? ' that participates in group ' +
|
||||||
|
`${(await client.getChatById(message.to ?? message.from)).name} ` : ' '}` +
|
||||||
|
`changed their phone number\nat ${eventTime}.\n` +
|
||||||
|
`Their new phone number is ${newId.slice(0, -5)}.\n`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about the {@name message}:
|
||||||
|
*
|
||||||
|
* 1. If a notification was emitted due to a group participant changing their phone number:
|
||||||
|
* {@name message.author} is a participant's id before the change.
|
||||||
|
* {@name message.recipients[0]} is a participant's id after the change (a new one).
|
||||||
|
*
|
||||||
|
* 1.1 If the contact who changed their number WAS in the current user's contact list at the time of the change:
|
||||||
|
* {@name message.to} is a group chat id the event was emitted in.
|
||||||
|
* {@name message.from} is a current user's id that got an notification message in the group.
|
||||||
|
* Also the {@name message.fromMe} is TRUE.
|
||||||
|
*
|
||||||
|
* 1.2 Otherwise:
|
||||||
|
* {@name message.from} is a group chat id the event was emitted in.
|
||||||
|
* {@name message.to} is @type {undefined}.
|
||||||
|
* Also {@name message.fromMe} is FALSE.
|
||||||
|
*
|
||||||
|
* 2. If a notification was emitted due to a contact changing their phone number:
|
||||||
|
* {@name message.templateParams} is an array of two user's ids:
|
||||||
|
* the old (before the change) and a new one, stored in alphabetical order.
|
||||||
|
* {@name message.from} is a current user's id that has a chat with a user,
|
||||||
|
* whos phone number was changed.
|
||||||
|
* {@name message.to} is a user's id (after the change), the current user has a chat with.
|
||||||
|
*/
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('group_admin_changed', (notification) => {
|
||||||
|
if (notification.type === 'promote') {
|
||||||
|
/**
|
||||||
|
* Emitted when a current user is promoted to an admin.
|
||||||
|
* {@link notification.author} is a user who performs the action of promoting/demoting the current user.
|
||||||
|
*/
|
||||||
|
console.log(`You were promoted by ${notification.author}`);
|
||||||
|
} else if (notification.type === 'demote')
|
||||||
|
/** Emitted when a current user is demoted to a regular user. */
|
||||||
|
console.log(`You were demoted by ${notification.author}`);
|
||||||
|
});
|
||||||
|
|||||||
78
index.d.ts
vendored
78
index.d.ts
vendored
@@ -1,8 +1,6 @@
|
|||||||
|
|
||||||
import { EventEmitter } from 'events'
|
import { EventEmitter } from 'events'
|
||||||
import { RequestInit } from 'node-fetch'
|
import { RequestInit } from 'node-fetch'
|
||||||
import { ButtonSpec, FormattedButtonSpec } from './src/structures/Buttons'
|
|
||||||
import { FormattedSectionSpec, SectionSpec } from './src/structures/List'
|
|
||||||
import * as puppeteer from 'puppeteer'
|
import * as puppeteer from 'puppeteer'
|
||||||
|
|
||||||
declare namespace WAWebJS {
|
declare namespace WAWebJS {
|
||||||
@@ -150,6 +148,12 @@ declare namespace WAWebJS {
|
|||||||
/** Unmutes the Chat */
|
/** Unmutes the Chat */
|
||||||
unmuteChat(chatId: string): Promise<void>
|
unmuteChat(chatId: string): Promise<void>
|
||||||
|
|
||||||
|
/** Sets the current user's profile picture */
|
||||||
|
setProfilePicture(media: MessageMedia): Promise<boolean>
|
||||||
|
|
||||||
|
/** Deletes the current user's profile picture */
|
||||||
|
deleteProfilePicture(): Promise<boolean>
|
||||||
|
|
||||||
/** Generic event */
|
/** Generic event */
|
||||||
on(event: string, listener: (...args: any) => void): this
|
on(event: string, listener: (...args: any) => void): this
|
||||||
|
|
||||||
@@ -194,12 +198,30 @@ declare namespace WAWebJS {
|
|||||||
notification: GroupNotification
|
notification: GroupNotification
|
||||||
) => void): this
|
) => void): this
|
||||||
|
|
||||||
|
/** Emitted when a current user is promoted to an admin or demoted to a regular user */
|
||||||
|
on(event: 'group_admin_changed', listener: (
|
||||||
|
/** GroupNotification with more information about the action */
|
||||||
|
notification: GroupNotification
|
||||||
|
) => void): this
|
||||||
|
|
||||||
/** Emitted when group settings are updated, such as subject, description or picture */
|
/** Emitted when group settings are updated, such as subject, description or picture */
|
||||||
on(event: 'group_update', listener: (
|
on(event: 'group_update', listener: (
|
||||||
/** GroupNotification with more information about the action */
|
/** GroupNotification with more information about the action */
|
||||||
notification: GroupNotification
|
notification: GroupNotification
|
||||||
) => void): this
|
) => void): this
|
||||||
|
|
||||||
|
/** Emitted when a contact or a group participant changed their phone number. */
|
||||||
|
on(event: 'contact_changed', listener: (
|
||||||
|
/** Message with more information about the event. */
|
||||||
|
message: Message,
|
||||||
|
/** Old user's id. */
|
||||||
|
oldId : String,
|
||||||
|
/** New user's id. */
|
||||||
|
newId : String,
|
||||||
|
/** Indicates if a contact or a group participant changed their phone number. */
|
||||||
|
isContact : Boolean
|
||||||
|
) => void): this
|
||||||
|
|
||||||
/** Emitted when media has been uploaded for a message sent by the client */
|
/** Emitted when media has been uploaded for a message sent by the client */
|
||||||
on(event: 'media_uploaded', listener: (
|
on(event: 'media_uploaded', listener: (
|
||||||
/** The message with media that was uploaded */
|
/** The message with media that was uploaded */
|
||||||
@@ -219,6 +241,12 @@ declare namespace WAWebJS {
|
|||||||
/** The new ACK value */
|
/** The new ACK value */
|
||||||
ack: MessageAck
|
ack: MessageAck
|
||||||
) => void): this
|
) => void): this
|
||||||
|
|
||||||
|
/** Emitted when a chat unread count changes */
|
||||||
|
on(event: 'unread_count', listener: (
|
||||||
|
/** The chat that was affected */
|
||||||
|
chat: Chat
|
||||||
|
) => void): this
|
||||||
|
|
||||||
/** Emitted when a new message is created, which may include the current user's own messages */
|
/** Emitted when a new message is created, which may include the current user's own messages */
|
||||||
on(event: 'message_create', listener: (
|
on(event: 'message_create', listener: (
|
||||||
@@ -249,6 +277,22 @@ declare namespace WAWebJS {
|
|||||||
reaction: Reaction
|
reaction: Reaction
|
||||||
) => void): this
|
) => void): this
|
||||||
|
|
||||||
|
/** Emitted when a chat is removed */
|
||||||
|
on(event: 'chat_removed', listener: (
|
||||||
|
/** The chat that was removed */
|
||||||
|
chat: Chat
|
||||||
|
) => void): this
|
||||||
|
|
||||||
|
/** Emitted when a chat is archived/unarchived */
|
||||||
|
on(event: 'chat_archived', listener: (
|
||||||
|
/** The chat that was archived/unarchived */
|
||||||
|
chat: Chat,
|
||||||
|
/** State the chat is currently in */
|
||||||
|
currState: boolean,
|
||||||
|
/** State the chat was previously in */
|
||||||
|
prevState: boolean
|
||||||
|
) => void): this
|
||||||
|
|
||||||
/** Emitted when loading screen is appearing */
|
/** Emitted when loading screen is appearing */
|
||||||
on(event: 'loading_screen', listener: (percent: string, message: string) => void): this
|
on(event: 'loading_screen', listener: (percent: string, message: string) => void): this
|
||||||
|
|
||||||
@@ -500,8 +544,10 @@ declare namespace WAWebJS {
|
|||||||
MESSAGE_REVOKED_ME = 'message_revoke_me',
|
MESSAGE_REVOKED_ME = 'message_revoke_me',
|
||||||
MESSAGE_ACK = 'message_ack',
|
MESSAGE_ACK = 'message_ack',
|
||||||
MEDIA_UPLOADED = 'media_uploaded',
|
MEDIA_UPLOADED = 'media_uploaded',
|
||||||
|
CONTACT_CHANGED = 'contact_changed',
|
||||||
GROUP_JOIN = 'group_join',
|
GROUP_JOIN = 'group_join',
|
||||||
GROUP_LEAVE = 'group_leave',
|
GROUP_LEAVE = 'group_leave',
|
||||||
|
GROUP_ADMIN_CHANGED = 'group_admin_changed',
|
||||||
GROUP_UPDATE = 'group_update',
|
GROUP_UPDATE = 'group_update',
|
||||||
QR_RECEIVED = 'qr',
|
QR_RECEIVED = 'qr',
|
||||||
LOADING_SCREEN = 'loading_screen',
|
LOADING_SCREEN = 'loading_screen',
|
||||||
@@ -639,6 +685,7 @@ declare namespace WAWebJS {
|
|||||||
* broadcast: false,
|
* broadcast: false,
|
||||||
* fromMe: false,
|
* fromMe: false,
|
||||||
* hasQuotedMsg: false,
|
* hasQuotedMsg: false,
|
||||||
|
* hasReaction: false,
|
||||||
* location: undefined,
|
* location: undefined,
|
||||||
* mentionedIds: []
|
* mentionedIds: []
|
||||||
* }
|
* }
|
||||||
@@ -668,6 +715,8 @@ declare namespace WAWebJS {
|
|||||||
hasMedia: boolean,
|
hasMedia: boolean,
|
||||||
/** Indicates if the message was sent as a reply to another message */
|
/** Indicates if the message was sent as a reply to another message */
|
||||||
hasQuotedMsg: boolean,
|
hasQuotedMsg: boolean,
|
||||||
|
/** Indicates whether there are reactions to the message */
|
||||||
|
hasReaction: boolean,
|
||||||
/** Indicates the duration of the message in seconds */
|
/** Indicates the duration of the message in seconds */
|
||||||
duration: string,
|
duration: string,
|
||||||
/** ID that represents the message */
|
/** ID that represents the message */
|
||||||
@@ -769,6 +818,10 @@ declare namespace WAWebJS {
|
|||||||
* Gets the payment details associated with a given message
|
* Gets the payment details associated with a given message
|
||||||
*/
|
*/
|
||||||
getPayment: () => Promise<Payment>,
|
getPayment: () => Promise<Payment>,
|
||||||
|
/**
|
||||||
|
* Gets the reactions associated with the given message
|
||||||
|
*/
|
||||||
|
getReactions: () => Promise<ReactionList[]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ID that represents a message */
|
/** ID that represents a message */
|
||||||
@@ -1021,6 +1074,8 @@ declare namespace WAWebJS {
|
|||||||
timestamp: number,
|
timestamp: number,
|
||||||
/** Amount of messages unread */
|
/** Amount of messages unread */
|
||||||
unreadCount: number,
|
unreadCount: number,
|
||||||
|
/** Last message fo chat */
|
||||||
|
lastMessage: Message,
|
||||||
|
|
||||||
/** Archives this chat */
|
/** Archives this chat */
|
||||||
archive: () => Promise<void>,
|
archive: () => Promise<void>,
|
||||||
@@ -1164,6 +1219,10 @@ declare namespace WAWebJS {
|
|||||||
revokeInvite: () => Promise<void>;
|
revokeInvite: () => Promise<void>;
|
||||||
/** Makes the bot leave the group */
|
/** Makes the bot leave the group */
|
||||||
leave: () => Promise<void>;
|
leave: () => Promise<void>;
|
||||||
|
/** Sets the group's picture.*/
|
||||||
|
setPicture: (media: MessageMedia) => Promise<boolean>;
|
||||||
|
/** Deletes the group's picture */
|
||||||
|
deletePicture: () => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1338,21 +1397,21 @@ declare namespace WAWebJS {
|
|||||||
export class List {
|
export class List {
|
||||||
body: string
|
body: string
|
||||||
buttonText: string
|
buttonText: string
|
||||||
sections: Array<FormattedSectionSpec>
|
sections: Array<any>
|
||||||
title?: string | null
|
title?: string | null
|
||||||
footer?: string | null
|
footer?: string | null
|
||||||
|
|
||||||
constructor(body: string, buttonText: string, sections: Array<SectionSpec>, title?: string | null, footer?: string | null)
|
constructor(body: string, buttonText: string, sections: Array<any>, title?: string | null, footer?: string | null)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Message type Buttons */
|
/** Message type Buttons */
|
||||||
export class Buttons {
|
export class Buttons {
|
||||||
body: string | MessageMedia
|
body: string | MessageMedia
|
||||||
buttons: FormattedButtonSpec
|
buttons: Array<{ buttonId: string; buttonText: {displayText: string}; type: number }>
|
||||||
title?: string | null
|
title?: string | null
|
||||||
footer?: string | null
|
footer?: string | null
|
||||||
|
|
||||||
constructor(body: string, buttons: Array<ButtonSpec>, title?: string | null, footer?: string | null)
|
constructor(body: string, buttons: Array<{ id?: string; body: string }>, title?: string | null, footer?: string | null)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Message type Reaction */
|
/** Message type Reaction */
|
||||||
@@ -1367,6 +1426,13 @@ declare namespace WAWebJS {
|
|||||||
senderId: string
|
senderId: string
|
||||||
ack?: number
|
ack?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ReactionList = {
|
||||||
|
id: string,
|
||||||
|
aggregateEmoji: string,
|
||||||
|
hasReactionByMe: boolean,
|
||||||
|
senders: Array<Reaction>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export = WAWebJS
|
export = WAWebJS
|
||||||
|
|||||||
102
src/Client.js
102
src/Client.js
@@ -10,7 +10,7 @@ const { WhatsWebURL, DefaultOptions, Events, WAState } = require('./util/Constan
|
|||||||
const { ExposeStore, LoadUtils } = require('./util/Injected');
|
const { ExposeStore, LoadUtils } = require('./util/Injected');
|
||||||
const ChatFactory = require('./factories/ChatFactory');
|
const ChatFactory = require('./factories/ChatFactory');
|
||||||
const ContactFactory = require('./factories/ContactFactory');
|
const ContactFactory = require('./factories/ContactFactory');
|
||||||
const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List, Reaction } = require('./structures');
|
const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List, Reaction, Chat } = require('./structures');
|
||||||
const LegacySessionAuth = require('./authStrategies/LegacySessionAuth');
|
const LegacySessionAuth = require('./authStrategies/LegacySessionAuth');
|
||||||
const NoAuth = require('./authStrategies/NoAuth');
|
const NoAuth = require('./authStrategies/NoAuth');
|
||||||
|
|
||||||
@@ -45,6 +45,8 @@ const NoAuth = require('./authStrategies/NoAuth');
|
|||||||
* @fires Client#group_update
|
* @fires Client#group_update
|
||||||
* @fires Client#disconnected
|
* @fires Client#disconnected
|
||||||
* @fires Client#change_state
|
* @fires Client#change_state
|
||||||
|
* @fires Client#contact_changed
|
||||||
|
* @fires Client#group_admin_changed
|
||||||
*/
|
*/
|
||||||
class Client extends EventEmitter {
|
class Client extends EventEmitter {
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
@@ -317,6 +319,13 @@ class Client extends EventEmitter {
|
|||||||
* @param {GroupNotification} notification GroupNotification with more information about the action
|
* @param {GroupNotification} notification GroupNotification with more information about the action
|
||||||
*/
|
*/
|
||||||
this.emit(Events.GROUP_LEAVE, notification);
|
this.emit(Events.GROUP_LEAVE, notification);
|
||||||
|
} else if (msg.subtype === 'promote' || msg.subtype === 'demote') {
|
||||||
|
/**
|
||||||
|
* Emitted when a current user is promoted to an admin or demoted to a regular user.
|
||||||
|
* @event Client#group_admin_changed
|
||||||
|
* @param {GroupNotification} notification GroupNotification with more information about the action
|
||||||
|
*/
|
||||||
|
this.emit(Events.GROUP_ADMIN_CHANGED, notification);
|
||||||
} else {
|
} else {
|
||||||
/**
|
/**
|
||||||
* Emitted when group settings are updated, such as subject, description or picture.
|
* Emitted when group settings are updated, such as subject, description or picture.
|
||||||
@@ -376,6 +385,36 @@ class Client extends EventEmitter {
|
|||||||
last_message = msg;
|
last_message = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The event notification that is received when one of
|
||||||
|
* the group participants changes thier phone number.
|
||||||
|
*/
|
||||||
|
const isParticipant = msg.type === 'gp2' && msg.subtype === 'modify';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The event notification that is received when one of
|
||||||
|
* the contacts changes thier phone number.
|
||||||
|
*/
|
||||||
|
const isContact = msg.type === 'notification_template' && msg.subtype === 'change_number';
|
||||||
|
|
||||||
|
if (isParticipant || isContact) {
|
||||||
|
/** {@link GroupNotification} object does not provide enough information about this event, so a {@link Message} object is used. */
|
||||||
|
const message = new Message(this, msg);
|
||||||
|
|
||||||
|
const newId = isParticipant ? msg.recipients[0] : msg.to;
|
||||||
|
const oldId = isParticipant ? msg.author : msg.templateParams.find(id => id !== newId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emitted when a contact or a group participant changes their phone number.
|
||||||
|
* @event Client#contact_changed
|
||||||
|
* @param {Message} message Message with more information about the event.
|
||||||
|
* @param {String} oldId The user's id (an old one) who changed their phone number
|
||||||
|
* and who triggered the notification.
|
||||||
|
* @param {String} newId The user's new id after the change.
|
||||||
|
* @param {Boolean} isContact Indicates if a contact or a group participant changed their phone number.
|
||||||
|
*/
|
||||||
|
this.emit(Events.CONTACT_CHANGED, message, oldId, newId, isContact);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.exposeFunction('onRemoveMessageEvent', (msg) => {
|
await page.exposeFunction('onRemoveMessageEvent', (msg) => {
|
||||||
@@ -407,6 +446,15 @@ class Client extends EventEmitter {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await page.exposeFunction('onChatUnreadCountEvent', async (data) =>{
|
||||||
|
const chat = await this.getChatById(data.id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emitted when the chat unread count changes
|
||||||
|
*/
|
||||||
|
this.emit(Events.UNREAD_COUNT, chat);
|
||||||
|
});
|
||||||
|
|
||||||
await page.exposeFunction('onMessageMediaUploadedEvent', (msg) => {
|
await page.exposeFunction('onMessageMediaUploadedEvent', (msg) => {
|
||||||
|
|
||||||
const message = new Message(this, msg);
|
const message = new Message(this, msg);
|
||||||
@@ -507,6 +555,26 @@ class Client extends EventEmitter {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await page.exposeFunction('onRemoveChatEvent', (chat) => {
|
||||||
|
/**
|
||||||
|
* Emitted when a chat is removed
|
||||||
|
* @event Client#chat_removed
|
||||||
|
* @param {Chat} chat
|
||||||
|
*/
|
||||||
|
this.emit(Events.CHAT_REMOVED, new Chat(this, chat));
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.exposeFunction('onArchiveChatEvent', (chat, currState, prevState) => {
|
||||||
|
/**
|
||||||
|
* Emitted when a chat is archived/unarchived
|
||||||
|
* @event Client#chat_archived
|
||||||
|
* @param {Chat} chat
|
||||||
|
* @param {boolean} currState
|
||||||
|
* @param {boolean} prevState
|
||||||
|
*/
|
||||||
|
this.emit(Events.CHAT_ARCHIVED, new Chat(this, chat), currState, prevState);
|
||||||
|
});
|
||||||
|
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
window.Store.Msg.on('change', (msg) => { window.onChangeMessageEvent(window.WWebJS.getMessageModel(msg)); });
|
window.Store.Msg.on('change', (msg) => { window.onChangeMessageEvent(window.WWebJS.getMessageModel(msg)); });
|
||||||
window.Store.Msg.on('change:type', (msg) => { window.onChangeMessageTypeEvent(window.WWebJS.getMessageModel(msg)); });
|
window.Store.Msg.on('change:type', (msg) => { window.onChangeMessageTypeEvent(window.WWebJS.getMessageModel(msg)); });
|
||||||
@@ -516,6 +584,8 @@ class Client extends EventEmitter {
|
|||||||
window.Store.AppState.on('change:state', (_AppState, state) => { window.onAppStateChangedEvent(state); });
|
window.Store.AppState.on('change:state', (_AppState, state) => { window.onAppStateChangedEvent(state); });
|
||||||
window.Store.Conn.on('change:battery', (state) => { window.onBatteryStateChangedEvent(state); });
|
window.Store.Conn.on('change:battery', (state) => { window.onBatteryStateChangedEvent(state); });
|
||||||
window.Store.Call.on('add', (call) => { window.onIncomingCall(call); });
|
window.Store.Call.on('add', (call) => { window.onIncomingCall(call); });
|
||||||
|
window.Store.Chat.on('remove', async (chat) => { window.onRemoveChatEvent(await window.WWebJS.getChatModel(chat)); });
|
||||||
|
window.Store.Chat.on('change:archive', async (chat, currState, prevState) => { window.onArchiveChatEvent(await window.WWebJS.getChatModel(chat), currState, prevState); });
|
||||||
window.Store.Msg.on('add', (msg) => {
|
window.Store.Msg.on('add', (msg) => {
|
||||||
if (msg.isNewMsg) {
|
if (msg.isNewMsg) {
|
||||||
if(msg.type === 'ciphertext') {
|
if(msg.type === 'ciphertext') {
|
||||||
@@ -526,7 +596,8 @@ class Client extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
window.Store.Chat.on('change:unreadCount', (chat) => {window.onChatUnreadCountEvent(chat);});
|
||||||
|
|
||||||
{
|
{
|
||||||
const module = window.Store.createOrUpdateReactionsModule;
|
const module = window.Store.createOrUpdateReactionsModule;
|
||||||
const ogMethod = module.createOrUpdateReactions;
|
const ogMethod = module.createOrUpdateReactions;
|
||||||
@@ -693,7 +764,7 @@ class Client extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const msg = await window.WWebJS.sendMessage(chat, message, options, sendSeen);
|
const msg = await window.WWebJS.sendMessage(chat, message, options, sendSeen);
|
||||||
return JSON.parse(JSON.stringify(msg));
|
return msg.serialize();
|
||||||
}, chatId, content, internalOptions, sendSeen);
|
}, chatId, content, internalOptions, sendSeen);
|
||||||
|
|
||||||
return new Message(this, newMessage);
|
return new Message(this, newMessage);
|
||||||
@@ -1178,6 +1249,31 @@ class Client extends EventEmitter {
|
|||||||
|
|
||||||
return blockedContacts.map(contact => ContactFactory.create(this.client, contact));
|
return blockedContacts.map(contact => ContactFactory.create(this.client, contact));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current user's profile picture.
|
||||||
|
* @param {MessageMedia} media
|
||||||
|
* @returns {Promise<boolean>} Returns true if the picture was properly updated.
|
||||||
|
*/
|
||||||
|
async setProfilePicture(media) {
|
||||||
|
const success = await this.pupPage.evaluate((chatid, media) => {
|
||||||
|
return window.WWebJS.setPicture(chatid, media);
|
||||||
|
}, this.info.wid._serialized, media);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the current user's profile picture.
|
||||||
|
* @returns {Promise<boolean>} Returns true if the picture was properly deleted.
|
||||||
|
*/
|
||||||
|
async deleteProfilePicture() {
|
||||||
|
const success = await this.pupPage.evaluate((chatid) => {
|
||||||
|
return window.WWebJS.deletePicture(chatid);
|
||||||
|
}, this.info.wid._serialized);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Client;
|
module.exports = Client;
|
||||||
|
|||||||
@@ -1,23 +1,20 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const MessageMedia = require('./MessageMedia');
|
const MessageMedia = require('./MessageMedia');
|
||||||
|
const Util = require('../util/Util');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Button spec used in Buttons constructor
|
* Button spec used in Buttons constructor
|
||||||
* @typedef {Object} ButtonSpec
|
* @typedef {Object} ButtonSpec
|
||||||
* @property {string} body - The text to show on the button.
|
|
||||||
* @property {string=} id - Custom ID to set on the button. A random one will be generated if one is not passed.
|
* @property {string=} id - Custom ID to set on the button. A random one will be generated if one is not passed.
|
||||||
* @property {string=} url - Custom URL to set on the button. Optional and will change the type of the button
|
* @property {string} body - The text to show on the button.
|
||||||
* @property {string=} number - Custom URL to set on the button. Optional and will change the type of the button
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} FormattedButtonSpec
|
* @typedef {Object} FormattedButtonSpec
|
||||||
* @property {number} index
|
* @property {string} buttonId
|
||||||
* @property {{displayText: string, url: string}=} urlButton
|
* @property {number} type
|
||||||
* @property {{displayText: string, phoneNumber: string}=} callButton
|
* @property {Object} buttonText
|
||||||
* @property {{displayText: string, id: string}=} quickReplyButton
|
|
||||||
* @property {{regularButtons: {text: string, id: string}}=} regularButtons
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,9 +26,8 @@ class Buttons {
|
|||||||
* @param {ButtonSpec[]} buttons - See {@link ButtonSpec}
|
* @param {ButtonSpec[]} buttons - See {@link ButtonSpec}
|
||||||
* @param {string?} title
|
* @param {string?} title
|
||||||
* @param {string?} footer
|
* @param {string?} footer
|
||||||
* @param {boolean?} templateOverride
|
|
||||||
*/
|
*/
|
||||||
constructor(body, buttons, title, footer, templateOverride = false) {
|
constructor(body, buttons, title, footer) {
|
||||||
/**
|
/**
|
||||||
* Message body
|
* Message body
|
||||||
* @type {string|MessageMedia}
|
* @type {string|MessageMedia}
|
||||||
@@ -63,58 +59,24 @@ class Buttons {
|
|||||||
*/
|
*/
|
||||||
this.buttons = this._format(buttons);
|
this.buttons = this._format(buttons);
|
||||||
if(!this.buttons.length){ throw '[BT01] No buttons';}
|
if(!this.buttons.length){ throw '[BT01] No buttons';}
|
||||||
|
|
||||||
/**
|
|
||||||
* Override buttons with templates
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
this.useTemplateButtons = templateOverride;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates button array from simple array
|
* Creates button array from simple array
|
||||||
* @param {ButtonSpec[]} buttons
|
* @param {ButtonSpec[]} buttons
|
||||||
* @returns {FormattedButtonSpec[]}
|
* @returns {FormattedButtonSpec[]}
|
||||||
|
* @example
|
||||||
|
* Input: [{id:'customId',body:'button1'},{body:'button2'},{body:'button3'},{body:'button4'}]
|
||||||
|
* Returns: [{ buttonId:'customId',buttonText:{'displayText':'button1'},type: 1 },{buttonId:'n3XKsL',buttonText:{'displayText':'button2'},type:1},{buttonId:'NDJk0a',buttonText:{'displayText':'button3'},type:1}]
|
||||||
*/
|
*/
|
||||||
_format(buttons){
|
_format(buttons){
|
||||||
// Limit the buttons (max 3 of regular and 3 of special buttons) 5 buttons total at the same time
|
buttons = buttons.slice(0,3); // phone users can only see 3 buttons, so lets limit this
|
||||||
const templateButtons = buttons.filter(button => button.url || button.number);
|
return buttons.map((btn) => {
|
||||||
const regularButtons = buttons.filter(button => !button.url && !button.number);
|
return {'buttonId':btn.id ? String(btn.id) : Util.generateHash(6),'buttonText':{'displayText':btn.body},'type':1};
|
||||||
buttons = regularButtons.concat(templateButtons);
|
|
||||||
|
|
||||||
return buttons.map((button, index) => {
|
|
||||||
if (button.url && button.number && button.id) throw 'Only pick one of the following (url/number/id)';
|
|
||||||
if (button.number) {
|
|
||||||
console.log('[WARNING] THIS FEATURE (CALL BUTTONS) IS UNSTABLE AND IS NOT TESTED OR EXPECTED TO WORK ON ALL PLATFORMS. Help test this feature with us on https://github.com/wwebjs/buttons-test');
|
|
||||||
return {
|
|
||||||
index,
|
|
||||||
callButton: {
|
|
||||||
displayText: button.body,
|
|
||||||
phoneNumber: button.number || ''
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else if (button.url) {
|
|
||||||
console.log('[WARNING] THIS FEATURE (URL BUTTONS) IS UNSTABLE AND IS NOT TESTED OR EXPECTED TO WORK ON ALL PLATFORMS. Help test this feature with us on https://github.com/wwebjs/buttons-test');
|
|
||||||
return {
|
|
||||||
index,
|
|
||||||
urlButton: {
|
|
||||||
displayText: button.body,
|
|
||||||
url: button.url || ''
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
index,
|
|
||||||
quickReplyButton: {
|
|
||||||
displayText: button.body,
|
|
||||||
id: button.id || `${index}`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Buttons;
|
module.exports = Buttons;
|
||||||
@@ -75,6 +75,12 @@ class Chat extends Base {
|
|||||||
*/
|
*/
|
||||||
this.muteExpiration = data.muteExpiration;
|
this.muteExpiration = data.muteExpiration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last message fo chat
|
||||||
|
* @type {Message}
|
||||||
|
*/
|
||||||
|
this.lastMessage = data.lastMessage ? new Message(super.client, data.lastMessage) : undefined;
|
||||||
|
|
||||||
return super._patch(data);
|
return super._patch(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -213,6 +213,31 @@ class GroupChat extends Chat {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the group's picture.
|
||||||
|
* @returns {Promise<boolean>} Returns true if the picture was properly deleted. This can return false if the user does not have the necessary permissions.
|
||||||
|
*/
|
||||||
|
async deletePicture() {
|
||||||
|
const success = await this.client.pupPage.evaluate((chatid) => {
|
||||||
|
return window.WWebJS.deletePicture(chatid);
|
||||||
|
}, this.id._serialized);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the group's picture.
|
||||||
|
* @param {MessageMedia} media
|
||||||
|
* @returns {Promise<boolean>} Returns true if the picture was properly updated. This can return false if the user does not have the necessary permissions.
|
||||||
|
*/
|
||||||
|
async setPicture(media) {
|
||||||
|
const success = await this.client.pupPage.evaluate((chatid, media) => {
|
||||||
|
return window.WWebJS.setPicture(chatid, media);
|
||||||
|
}, this.id._serialized, media);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the invite code for a specific group
|
* Gets the invite code for a specific group
|
||||||
* @returns {Promise<string>} Group's invite code
|
* @returns {Promise<string>} Group's invite code
|
||||||
|
|||||||
@@ -2,38 +2,16 @@
|
|||||||
|
|
||||||
const Util = require('../util/Util');
|
const Util = require('../util/Util');
|
||||||
|
|
||||||
/**
|
|
||||||
* Section spec used in List constructor
|
|
||||||
* @typedef {Object} SectionSpec
|
|
||||||
* @property {string=} title - The title of the section, can be empty on the first section only.
|
|
||||||
* @property {RowSpec[]} rows - The rows of the section.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Row spec used in List constructor
|
|
||||||
* @typedef {Object} RowSpec
|
|
||||||
* @property {string} title - The text to show on the row.
|
|
||||||
* @property {string=} id - Custom ID to set on the row. A random one will be generated if one is not passed.
|
|
||||||
* @property {string=} description - Custom description for the row, will appear after clicked in the list response message (appended)
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formatted section spec
|
|
||||||
* @typedef {Object} FormattedSectionSpec
|
|
||||||
* @property {string} title
|
|
||||||
* @property {{rowId: string; title: string; description: string}[]} rows
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message type List
|
* Message type List
|
||||||
*/
|
*/
|
||||||
class List {
|
class List {
|
||||||
/**
|
/**
|
||||||
* @param {string} body - A text body, no media.
|
* @param {string} body
|
||||||
* @param {string} buttonText - The text to put on the click to open button.
|
* @param {string} buttonText
|
||||||
* @param {Array<SectionSpec>} sections - The sections of the list
|
* @param {Array<any>} sections
|
||||||
* @param {string?} title - Custom boldfaced title property
|
* @param {string?} title
|
||||||
* @param {string?} footer - Custom footer added in a small font to the end of the message
|
* @param {string?} footer
|
||||||
*/
|
*/
|
||||||
constructor(body, buttonText, sections, title, footer) {
|
constructor(body, buttonText, sections, title, footer) {
|
||||||
/**
|
/**
|
||||||
@@ -71,28 +49,23 @@ class List {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates section array from simple array
|
* Creates section array from simple array
|
||||||
* @param {Array<SectionSpec>} sections
|
* @param {Array<any>} sections
|
||||||
* @returns {Array<FormattedSectionSpec>}
|
* @returns {Array<any>}
|
||||||
|
* @example
|
||||||
|
* Input: [{title:'sectionTitle',rows:[{id:'customId', title:'ListItem2', description: 'desc'},{title:'ListItem2'}]}}]
|
||||||
|
* Returns: [{'title':'sectionTitle','rows':[{'rowId':'customId','title':'ListItem1','description':'desc'},{'rowId':'oGSRoD','title':'ListItem2','description':''}]}]
|
||||||
*/
|
*/
|
||||||
_format(sections) {
|
_format(sections){
|
||||||
if(!sections.length) {
|
if(!sections.length){throw '[LT02] List without sections';}
|
||||||
throw '[LT02] List without sections';
|
if(sections.length > 1 && sections.filter(s => typeof s.title == 'undefined').length > 1){throw '[LT05] You can\'t have more than one empty title.';}
|
||||||
}
|
return sections.map( (section) =>{
|
||||||
if(sections.length > 1 && sections.filter(section => (typeof section.title == 'undefined' )|| section.title == '' ).length > 1) {
|
if(!section.rows.length){throw '[LT03] Section without rows';}
|
||||||
throw '[LT05] You can\'t have more than one empty title.';
|
|
||||||
}
|
|
||||||
return sections.map((section, index) => {
|
|
||||||
if(!section.rows.length) {
|
|
||||||
throw '[LT03] Section without rows';
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
title: section.title ? section.title : undefined,
|
title: section.title ? section.title : undefined,
|
||||||
rows: section.rows.map((row, rowIndex) => {
|
rows: section.rows.map( (row) => {
|
||||||
if (!row.title) {
|
if(!row.title){throw '[LT04] Row without title';}
|
||||||
throw `[LT04] Row without title at section index ${index} and row index ${rowIndex}`;
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
rowId: row.id ? row.id : Util.generateHash(8),
|
rowId: row.id ? row.id : Util.generateHash(6),
|
||||||
title: row.title,
|
title: row.title,
|
||||||
description: row.description ? row.description : ''
|
description: row.description ? row.description : ''
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ const MessageMedia = require('./MessageMedia');
|
|||||||
const Location = require('./Location');
|
const Location = require('./Location');
|
||||||
const Order = require('./Order');
|
const Order = require('./Order');
|
||||||
const Payment = require('./Payment');
|
const Payment = require('./Payment');
|
||||||
const { MessageTypes } = require('../util/Constants');
|
const Reaction = require('./Reaction');
|
||||||
|
const {MessageTypes} = require('../util/Constants');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a Message on WhatsApp
|
* Represents a Message on WhatsApp
|
||||||
@@ -134,6 +135,12 @@ class Message extends Base {
|
|||||||
*/
|
*/
|
||||||
this.hasQuotedMsg = data.quotedMsg ? true : false;
|
this.hasQuotedMsg = data.quotedMsg ? true : false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether there are reactions to the message
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
this.hasReaction = data.hasReaction ? true : false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates the duration of the message in seconds
|
* Indicates the duration of the message in seconds
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@@ -529,6 +536,44 @@ class Message extends Base {
|
|||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reaction List
|
||||||
|
* @typedef {Object} ReactionList
|
||||||
|
* @property {string} id Original emoji
|
||||||
|
* @property {string} aggregateEmoji aggregate emoji
|
||||||
|
* @property {boolean} hasReactionByMe Flag who sent the reaction
|
||||||
|
* @property {Array<Reaction>} senders Reaction senders, to this message
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the reactions associated with the given message
|
||||||
|
* @return {Promise<ReactionList[]>}
|
||||||
|
*/
|
||||||
|
async getReactions() {
|
||||||
|
if (!this.hasReaction) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reactions = await this.client.pupPage.evaluate(async (msgId) => {
|
||||||
|
const msgReactions = await window.Store.Reactions.find(msgId);
|
||||||
|
if (!msgReactions || !msgReactions.reactions.length) return null;
|
||||||
|
return msgReactions.reactions.serialize();
|
||||||
|
}, this.id._serialized);
|
||||||
|
|
||||||
|
if (!reactions) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reactions.map(reaction => {
|
||||||
|
reaction.senders = reaction.senders.map(sender => {
|
||||||
|
sender.timestamp = Math.round(sender.timestamp / 1000);
|
||||||
|
return new Reaction(this.client, sender);
|
||||||
|
});
|
||||||
|
return reaction;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Message;
|
module.exports = Message;
|
||||||
|
|||||||
@@ -36,15 +36,20 @@ exports.Events = {
|
|||||||
AUTHENTICATED: 'authenticated',
|
AUTHENTICATED: 'authenticated',
|
||||||
AUTHENTICATION_FAILURE: 'auth_failure',
|
AUTHENTICATION_FAILURE: 'auth_failure',
|
||||||
READY: 'ready',
|
READY: 'ready',
|
||||||
|
CHAT_REMOVED: 'chat_removed',
|
||||||
|
CHAT_ARCHIVED: 'chat_archived',
|
||||||
MESSAGE_RECEIVED: 'message',
|
MESSAGE_RECEIVED: 'message',
|
||||||
MESSAGE_CREATE: 'message_create',
|
MESSAGE_CREATE: 'message_create',
|
||||||
MESSAGE_REVOKED_EVERYONE: 'message_revoke_everyone',
|
MESSAGE_REVOKED_EVERYONE: 'message_revoke_everyone',
|
||||||
MESSAGE_REVOKED_ME: 'message_revoke_me',
|
MESSAGE_REVOKED_ME: 'message_revoke_me',
|
||||||
MESSAGE_ACK: 'message_ack',
|
MESSAGE_ACK: 'message_ack',
|
||||||
|
UNREAD_COUNT: 'unread_count',
|
||||||
MESSAGE_REACTION: 'message_reaction',
|
MESSAGE_REACTION: 'message_reaction',
|
||||||
MEDIA_UPLOADED: 'media_uploaded',
|
MEDIA_UPLOADED: 'media_uploaded',
|
||||||
|
CONTACT_CHANGED: 'contact_changed',
|
||||||
GROUP_JOIN: 'group_join',
|
GROUP_JOIN: 'group_join',
|
||||||
GROUP_LEAVE: 'group_leave',
|
GROUP_LEAVE: 'group_leave',
|
||||||
|
GROUP_ADMIN_CHANGED: 'group_admin_changed',
|
||||||
GROUP_UPDATE: 'group_update',
|
GROUP_UPDATE: 'group_update',
|
||||||
QR_RECEIVED: 'qr',
|
QR_RECEIVED: 'qr',
|
||||||
LOADING_SCREEN: 'loading_screen',
|
LOADING_SCREEN: 'loading_screen',
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ exports.ExposeStore = (moduleRaidStr) => {
|
|||||||
window.Store.ProfilePic = window.mR.findModule('profilePicResync')[0];
|
window.Store.ProfilePic = window.mR.findModule('profilePicResync')[0];
|
||||||
window.Store.PresenceUtils = window.mR.findModule('sendPresenceAvailable')[0];
|
window.Store.PresenceUtils = window.mR.findModule('sendPresenceAvailable')[0];
|
||||||
window.Store.ChatState = window.mR.findModule('sendChatStateComposing')[0];
|
window.Store.ChatState = window.mR.findModule('sendChatStateComposing')[0];
|
||||||
window.Store.GroupParticipants = window.mR.findModule('promoteParticipants')[1];
|
window.Store.GroupParticipants = window.mR.findModule('promoteParticipants')[0];
|
||||||
window.Store.JoinInviteV4 = window.mR.findModule('sendJoinGroupViaInviteV4')[0];
|
window.Store.JoinInviteV4 = window.mR.findModule('sendJoinGroupViaInviteV4')[0];
|
||||||
window.Store.findCommonGroups = window.mR.findModule('findCommonGroups')[0].findCommonGroups;
|
window.Store.findCommonGroups = window.mR.findModule('findCommonGroups')[0].findCommonGroups;
|
||||||
window.Store.StatusUtils = window.mR.findModule('setMyStatus')[0];
|
window.Store.StatusUtils = window.mR.findModule('setMyStatus')[0];
|
||||||
@@ -58,11 +58,12 @@ exports.ExposeStore = (moduleRaidStr) => {
|
|||||||
...window.mR.findModule('toWebpSticker')[0],
|
...window.mR.findModule('toWebpSticker')[0],
|
||||||
...window.mR.findModule('addWebpMetadata')[0]
|
...window.mR.findModule('addWebpMetadata')[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
window.Store.GroupUtils = {
|
window.Store.GroupUtils = {
|
||||||
...window.mR.findModule('createGroup')[0],
|
...window.mR.findModule('createGroup')[0],
|
||||||
...window.mR.findModule('setGroupDescription')[0],
|
...window.mR.findModule('setGroupDescription')[0],
|
||||||
...window.mR.findModule('sendExitGroup')[0]
|
...window.mR.findModule('sendExitGroup')[0],
|
||||||
|
...window.mR.findModule('sendSetPicture')[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!window.Store.Chat._find) {
|
if (!window.Store.Chat._find) {
|
||||||
@@ -74,251 +75,6 @@ exports.ExposeStore = (moduleRaidStr) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following was implemented and inspired from wppconnect/wa-js at
|
|
||||||
// https://github.com/wppconnect-team/wa-js/tree/main/src/chat/functions/prepareMessageButtons.ts
|
|
||||||
|
|
||||||
// Find proxy modules
|
|
||||||
window.findProxyModel = (name) => {
|
|
||||||
const baseName = name.replace(/Model$/, '');
|
|
||||||
|
|
||||||
const names = [baseName];
|
|
||||||
|
|
||||||
// ChatModel => "chat"
|
|
||||||
names.push(baseName.replace(/^(\w)/, (l) => l.toLowerCase()));
|
|
||||||
|
|
||||||
// CartItemModel => "cart-item"
|
|
||||||
// ProductListModel => "product_list"
|
|
||||||
const parts = baseName.split(/(?=[A-Z])/);
|
|
||||||
|
|
||||||
names.push(parts.join('-').toLowerCase());
|
|
||||||
names.push(parts.join('_').toLowerCase());
|
|
||||||
|
|
||||||
const results = window.mR.findModule((m) =>
|
|
||||||
names.includes(
|
|
||||||
m.default?.prototype?.proxyName ||
|
|
||||||
m[name]?.prototype?.proxyName ||
|
|
||||||
m[baseName]?.prototype?.proxyName
|
|
||||||
)
|
|
||||||
)[0];
|
|
||||||
|
|
||||||
return results.default || results[name] || results[baseName];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to modify functions.
|
|
||||||
// This function simply just runs the callback you provide with the original code in the first argument and all the arguments passed to that function.
|
|
||||||
window.injectToFunction = (selector, callback) => {
|
|
||||||
const oldFunct = window.mR.findModule(selector.name)[selector.index][selector.property];
|
|
||||||
window.mR.findModule(selector.name)[selector.index][selector.property] = (...args) => callback(oldFunct, args);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Find Template models
|
|
||||||
window.Store.TemplateButtonModel = window.findProxyModel('TemplateButtonModel');
|
|
||||||
window.Store.TemplateButtonCollection = window.mR.findModule('TemplateButtonCollection')[0].TemplateButtonCollection;
|
|
||||||
|
|
||||||
// Find quick reply models
|
|
||||||
window.Store.ReplyButtonModel = window.findProxyModel('ReplyButtonModel');
|
|
||||||
window.Store.ButtonCollection = window.mR.findModule('ButtonCollection')[0].ButtonCollection;
|
|
||||||
|
|
||||||
// Modify functions
|
|
||||||
window.injectToFunction({
|
|
||||||
index: 0,
|
|
||||||
name: 'createMsgProtobuf',
|
|
||||||
property: 'createMsgProtobuf'
|
|
||||||
}, (func, args) => {
|
|
||||||
const [message] = args;
|
|
||||||
const proto = func(...args);
|
|
||||||
if (message.hydratedButtons) {
|
|
||||||
const hydratedTemplate = {
|
|
||||||
hydratedButtons: message.hydratedButtons,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (message.footer) {
|
|
||||||
hydratedTemplate.hydratedFooterText = message.footer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.caption) {
|
|
||||||
hydratedTemplate.hydratedContentText = message.caption;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.title) {
|
|
||||||
hydratedTemplate.hydratedTitleText = message.title;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proto.conversation) {
|
|
||||||
hydratedTemplate.hydratedContentText = proto.conversation;
|
|
||||||
delete proto.conversation;
|
|
||||||
} else if (proto.extendedTextMessage?.text) {
|
|
||||||
hydratedTemplate.hydratedContentText = proto.extendedTextMessage.text;
|
|
||||||
delete proto.extendedTextMessage;
|
|
||||||
} else {
|
|
||||||
// Search media part in message
|
|
||||||
let found;
|
|
||||||
const mediaPart = [
|
|
||||||
'documentMessage',
|
|
||||||
'imageMessage',
|
|
||||||
'locationMessage',
|
|
||||||
'videoMessage',
|
|
||||||
];
|
|
||||||
for (const part of mediaPart) {
|
|
||||||
if (part in proto) {
|
|
||||||
found = part;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
return proto;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Media message doesn't allow title
|
|
||||||
hydratedTemplate[found] = proto[found];
|
|
||||||
|
|
||||||
// Copy title to caption if not setted
|
|
||||||
if (
|
|
||||||
hydratedTemplate.hydratedTitleText &&
|
|
||||||
!hydratedTemplate.hydratedContentText
|
|
||||||
) {
|
|
||||||
hydratedTemplate.hydratedContentText =
|
|
||||||
hydratedTemplate.hydratedTitleText;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove title for media messages
|
|
||||||
delete hydratedTemplate.hydratedTitleText;
|
|
||||||
|
|
||||||
if (found === 'locationMessage') {
|
|
||||||
if (
|
|
||||||
!hydratedTemplate.hydratedContentText &&
|
|
||||||
(message[found].name || message[found].address)
|
|
||||||
) {
|
|
||||||
hydratedTemplate.hydratedContentText =
|
|
||||||
message[found].name && message[found].address
|
|
||||||
? `${message[found].name}\n${message[found].address}`
|
|
||||||
: message[found].name || message[found].address || '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure a content text;
|
|
||||||
hydratedTemplate.hydratedContentText =
|
|
||||||
hydratedTemplate.hydratedContentText || ' ';
|
|
||||||
|
|
||||||
delete proto[found];
|
|
||||||
}
|
|
||||||
|
|
||||||
proto.templateMessage = {
|
|
||||||
hydratedTemplate,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return proto;
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
window.injectToFunction({
|
|
||||||
index: 0,
|
|
||||||
name: 'createMsgProtobuf',
|
|
||||||
property: 'createMsgProtobuf'
|
|
||||||
}, (func, args) => {
|
|
||||||
const proto = func(...args);
|
|
||||||
if (proto.templateMessage) {
|
|
||||||
proto.viewOnceMessage = {
|
|
||||||
message: {
|
|
||||||
templateMessage: proto.templateMessage,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
delete proto.templateMessage;
|
|
||||||
}
|
|
||||||
if (proto.buttonsMessage) {
|
|
||||||
proto.viewOnceMessage = {
|
|
||||||
message: {
|
|
||||||
buttonsMessage: proto.buttonsMessage,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
delete proto.buttonsMessage;
|
|
||||||
}
|
|
||||||
if (proto.listMessage) {
|
|
||||||
proto.viewOnceMessage = {
|
|
||||||
message: {
|
|
||||||
listMessage: proto.listMessage,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
delete proto.listMessage;
|
|
||||||
}
|
|
||||||
return proto;
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
window.injectToFunction({
|
|
||||||
index: 0,
|
|
||||||
name: 'typeAttributeFromProtobuf',
|
|
||||||
property: 'typeAttributeFromProtobuf'
|
|
||||||
}, function callback(func, args) {
|
|
||||||
const [proto] = args;
|
|
||||||
|
|
||||||
if (proto.ephemeralMessage) {
|
|
||||||
const { message } = proto.ephemeralMessage;
|
|
||||||
return message ? callback(func, [message]) : 'text';
|
|
||||||
}
|
|
||||||
if (proto.deviceSentMessage) {
|
|
||||||
const { message } = proto.deviceSentMessage;
|
|
||||||
return message ? callback(func, [message]) : 'text';
|
|
||||||
}
|
|
||||||
if (proto.viewOnceMessage) {
|
|
||||||
const { message } = proto.viewOnceMessage;
|
|
||||||
return message ? callback(func, [message]) : 'text';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
proto.buttonsMessage?.headerType === 1 ||
|
|
||||||
proto.buttonsMessage?.headerType === 2
|
|
||||||
) {
|
|
||||||
return 'text';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proto.listMessage) {
|
|
||||||
return 'text';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proto.templateMessage?.hydratedTemplate) {
|
|
||||||
const keys = Object.keys(proto.templateMessage?.hydratedTemplate);
|
|
||||||
const messagePart = [
|
|
||||||
'documentMessage',
|
|
||||||
'imageMessage',
|
|
||||||
'locationMessage',
|
|
||||||
'videoMessage',
|
|
||||||
];
|
|
||||||
if (messagePart.some((part) => keys.includes(part))) {
|
|
||||||
return 'media';
|
|
||||||
}
|
|
||||||
return 'text';
|
|
||||||
}
|
|
||||||
|
|
||||||
return func(...args);
|
|
||||||
});
|
|
||||||
|
|
||||||
window.injectToFunction({
|
|
||||||
index: 0,
|
|
||||||
name: 'mediaTypeFromProtobuf',
|
|
||||||
property: 'mediaTypeFromProtobuf'
|
|
||||||
}, (func, args) => {
|
|
||||||
const [proto] = args;
|
|
||||||
if (proto.templateMessage?.hydratedTemplate) {
|
|
||||||
return func(proto.templateMessage.hydratedTemplate);
|
|
||||||
}
|
|
||||||
return func(...args);
|
|
||||||
});
|
|
||||||
|
|
||||||
window.injectToFunction({
|
|
||||||
index: 0,
|
|
||||||
name: 'encodeMaybeMediaType',
|
|
||||||
property: 'encodeMaybeMediaType',
|
|
||||||
}, (func, args) => {
|
|
||||||
const [type] = args;
|
|
||||||
if (type === 'button') {
|
|
||||||
return window.mR.findModule('DROP_ATTR')[0].DROP_ATTR;
|
|
||||||
}
|
|
||||||
return func(...args);
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO remove these once everybody has been updated to WWebJS with legacy sessions removed
|
// TODO remove these once everybody has been updated to WWebJS with legacy sessions removed
|
||||||
const _linkPreview = window.mR.findModule('queryLinkPreview');
|
const _linkPreview = window.mR.findModule('queryLinkPreview');
|
||||||
if (_linkPreview && _linkPreview[0] && _linkPreview[0].default) {
|
if (_linkPreview && _linkPreview[0] && _linkPreview[0].default) {
|
||||||
@@ -351,71 +107,6 @@ exports.LoadUtils = () => {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
window.WWebJS.prepareMessageButtons = (buttonsOptions) => {
|
|
||||||
const returnObject = {};
|
|
||||||
if (!buttonsOptions.buttons) {
|
|
||||||
return returnObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
returnObject.title = buttonsOptions.title;
|
|
||||||
returnObject.footer = buttonsOptions.footer;
|
|
||||||
|
|
||||||
if (buttonsOptions.useTemplateButtons) {
|
|
||||||
returnObject.isFromTemplate = true;
|
|
||||||
returnObject.hydratedButtons = buttonsOptions.buttons;
|
|
||||||
returnObject.buttons = new window.Store.TemplateButtonCollection();
|
|
||||||
|
|
||||||
returnObject.buttons.add(
|
|
||||||
returnObject.hydratedButtons.map((button, index) => {
|
|
||||||
const i = `${null != button.index ? button.index : index}`;
|
|
||||||
|
|
||||||
if (button.urlButton) {
|
|
||||||
return new window.Store.TemplateButtonModel({
|
|
||||||
id: i,
|
|
||||||
displayText: button.urlButton?.displayText,
|
|
||||||
url: button.urlButton?.url,
|
|
||||||
subtype: 'url',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button.callButton) {
|
|
||||||
return new window.Store.TemplateButtonModel({
|
|
||||||
id: i,
|
|
||||||
displayText: button.callButton.displayText,
|
|
||||||
phoneNumber: button.callButton.phoneNumber,
|
|
||||||
subtype: 'call',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return new window.Store.TemplateButtonModel({
|
|
||||||
id: i,
|
|
||||||
displayText: button.quickReplyButton?.displayText,
|
|
||||||
selectionId: button.quickReplyButton?.id,
|
|
||||||
subtype: 'quick_reply',
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
returnObject.isDynamicReplyButtonsMsg = true;
|
|
||||||
|
|
||||||
returnObject.dynamicReplyButtons = buttonsOptions.buttons.map((button, index) => ({
|
|
||||||
buttonId: button.quickReplyButton.id.toString() || `${index}`,
|
|
||||||
buttonText: {displayText: button.quickReplyButton?.displayText},
|
|
||||||
type: 1,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// For UI only
|
|
||||||
returnObject.replyButtons = new window.Store.ButtonCollection();
|
|
||||||
returnObject.replyButtons.add(returnObject.dynamicReplyButtons.map((button) => new window.Store.ReplyButtonModel({
|
|
||||||
id: button.buttonId,
|
|
||||||
displayText: button.buttonText?.displayText || undefined,
|
|
||||||
})));
|
|
||||||
|
|
||||||
}
|
|
||||||
return returnObject;
|
|
||||||
};
|
|
||||||
|
|
||||||
window.WWebJS.sendMessage = async (chat, content, options = {}) => {
|
window.WWebJS.sendMessage = async (chat, content, options = {}) => {
|
||||||
let attOptions = {};
|
let attOptions = {};
|
||||||
if (options.attachment) {
|
if (options.attachment) {
|
||||||
@@ -499,7 +190,7 @@ exports.LoadUtils = () => {
|
|||||||
delete options.linkPreview;
|
delete options.linkPreview;
|
||||||
|
|
||||||
// Not supported yet by WhatsApp Web on MD
|
// Not supported yet by WhatsApp Web on MD
|
||||||
if (!window.Store.MDBackend) {
|
if(!window.Store.MDBackend) {
|
||||||
const link = window.Store.Validators.findLink(content);
|
const link = window.Store.Validators.findLink(content);
|
||||||
if (link) {
|
if (link) {
|
||||||
const preview = await window.Store.Wap.queryLinkPreview(link.url);
|
const preview = await window.Store.Wap.queryLinkPreview(link.url);
|
||||||
@@ -509,9 +200,9 @@ exports.LoadUtils = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let buttonOptions = {};
|
let buttonOptions = {};
|
||||||
if (options.buttons) {
|
if(options.buttons){
|
||||||
let caption;
|
let caption;
|
||||||
if (options.buttons.type === 'chat') {
|
if (options.buttons.type === 'chat') {
|
||||||
content = options.buttons.body;
|
content = options.buttons.body;
|
||||||
@@ -519,17 +210,24 @@ exports.LoadUtils = () => {
|
|||||||
} else {
|
} else {
|
||||||
caption = options.caption ? options.caption : ' '; //Caption can't be empty
|
caption = options.caption ? options.caption : ' '; //Caption can't be empty
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonOptions = window.WWebJS.prepareMessageButtons(options.buttons);
|
|
||||||
buttonOptions = {
|
buttonOptions = {
|
||||||
...buttonOptions,
|
productHeaderImageRejected: false,
|
||||||
|
isFromTemplate: false,
|
||||||
|
isDynamicReplyButtonsMsg: true,
|
||||||
|
title: options.buttons.title ? options.buttons.title : undefined,
|
||||||
|
footer: options.buttons.footer ? options.buttons.footer : undefined,
|
||||||
|
dynamicReplyButtons: options.buttons.buttons,
|
||||||
|
replyButtons: options.buttons.buttons,
|
||||||
caption: caption
|
caption: caption
|
||||||
};
|
};
|
||||||
delete options.buttons;
|
delete options.buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
let listOptions = {};
|
let listOptions = {};
|
||||||
if (options.list) {
|
if(options.list){
|
||||||
|
if(window.Store.Conn.platform === 'smba' || window.Store.Conn.platform === 'smbi'){
|
||||||
|
throw '[LT01] Whatsapp business can\'t send this yet';
|
||||||
|
}
|
||||||
listOptions = {
|
listOptions = {
|
||||||
type: 'list',
|
type: 'list',
|
||||||
footer: options.list.footer,
|
footer: options.list.footer,
|
||||||
@@ -574,6 +272,7 @@ exports.LoadUtils = () => {
|
|||||||
...ephemeralFields,
|
...ephemeralFields,
|
||||||
...locationOptions,
|
...locationOptions,
|
||||||
...attOptions,
|
...attOptions,
|
||||||
|
...(attOptions.toJSON ? attOptions.toJSON() : {}),
|
||||||
...quotedMsgOptions,
|
...quotedMsgOptions,
|
||||||
...vcardOptions,
|
...vcardOptions,
|
||||||
...buttonOptions,
|
...buttonOptions,
|
||||||
@@ -729,7 +428,15 @@ exports.LoadUtils = () => {
|
|||||||
await window.Store.GroupMetadata.update(chatWid);
|
await window.Store.GroupMetadata.update(chatWid);
|
||||||
res.groupMetadata = chat.groupMetadata.serialize();
|
res.groupMetadata = chat.groupMetadata.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res.lastMessage = null;
|
||||||
|
if (res.msgs && res.msgs.length) {
|
||||||
|
const lastMessage = window.Store.Msg.get(chat.lastReceivedKey._serialized);
|
||||||
|
if (lastMessage) {
|
||||||
|
res.lastMessage = window.WWebJS.getMessageModel(lastMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delete res.msgs;
|
delete res.msgs;
|
||||||
delete res.msgUnsyncedButtonReplyMsgs;
|
delete res.msgUnsyncedButtonReplyMsgs;
|
||||||
delete res.unsyncedButtonReplies;
|
delete res.unsyncedButtonReplies;
|
||||||
@@ -928,4 +635,73 @@ exports.LoadUtils = () => {
|
|||||||
]);
|
]);
|
||||||
await window.Store.Socket.deprecatedCastStanza(stanza);
|
await window.Store.Socket.deprecatedCastStanza(stanza);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.WWebJS.cropAndResizeImage = async (media, options = {}) => {
|
||||||
|
if (!media.mimetype.includes('image'))
|
||||||
|
throw new Error('Media is not an image');
|
||||||
|
|
||||||
|
if (options.mimetype && !options.mimetype.includes('image'))
|
||||||
|
delete options.mimetype;
|
||||||
|
|
||||||
|
options = Object.assign({ size: 640, mimetype: media.mimetype, quality: .75, asDataUrl: false }, options);
|
||||||
|
|
||||||
|
const img = await new Promise ((resolve, reject) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => resolve(img);
|
||||||
|
img.onerror = reject;
|
||||||
|
img.src = `data:${media.mimetype};base64,${media.data}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const sl = Math.min(img.width, img.height);
|
||||||
|
const sx = Math.floor((img.width - sl) / 2);
|
||||||
|
const sy = Math.floor((img.height - sl) / 2);
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = options.size;
|
||||||
|
canvas.height = options.size;
|
||||||
|
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.drawImage(img, sx, sy, sl, sl, 0, 0, options.size, options.size);
|
||||||
|
|
||||||
|
const dataUrl = canvas.toDataURL(options.mimetype, options.quality);
|
||||||
|
|
||||||
|
if (options.asDataUrl)
|
||||||
|
return dataUrl;
|
||||||
|
|
||||||
|
return Object.assign(media, {
|
||||||
|
mimetype: options.mimeType,
|
||||||
|
data: dataUrl.replace(`data:${options.mimeType};base64,`, '')
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.WWebJS.setPicture = async (chatid, media) => {
|
||||||
|
const thumbnail = await window.WWebJS.cropAndResizeImage(media, { asDataUrl: true, mimetype: 'image/jpeg', size: 96 });
|
||||||
|
const profilePic = await window.WWebJS.cropAndResizeImage(media, { asDataUrl: true, mimetype: 'image/jpeg', size: 640 });
|
||||||
|
|
||||||
|
const chatWid = window.Store.WidFactory.createWid(chatid);
|
||||||
|
try {
|
||||||
|
const collection = window.Store.ProfilePicThumb.get(chatid);
|
||||||
|
if (!collection.canSet()) return;
|
||||||
|
|
||||||
|
const res = await window.Store.GroupUtils.sendSetPicture(chatWid, thumbnail, profilePic);
|
||||||
|
return res ? res.status === 200 : false;
|
||||||
|
} catch (err) {
|
||||||
|
if(err.name === 'ServerStatusCodeError') return false;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.WWebJS.deletePicture = async (chatid) => {
|
||||||
|
const chatWid = window.Store.WidFactory.createWid(chatid);
|
||||||
|
try {
|
||||||
|
const collection = window.Store.ProfilePicThumb.get(chatid);
|
||||||
|
if (!collection.canDelete()) return;
|
||||||
|
|
||||||
|
const res = await window.Store.GroupUtils.requestDeletePicture(chatWid);
|
||||||
|
return res ? res.status === 200 : false;
|
||||||
|
} catch (err) {
|
||||||
|
if(err.name === 'ServerStatusCodeError') return false;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user