docs: add more documentation

This commit is contained in:
Pedro Lopez
2020-02-04 23:04:51 -04:00
parent a098d61b03
commit 2f0480c80e
9 changed files with 258 additions and 15 deletions

View File

@@ -17,6 +17,15 @@ const MessageMedia = require('./structures/MessageMedia');
/**
* Starting point for interacting with the WhatsApp Web API
* @extends {EventEmitter}
* @fires Client#qr
* @fires Client#authenticated
* @fires Client#auth_failure
* @fires Client#ready
* @fires Client#message
* @fires Client#message_create
* @fires Client#message_revoke_me
* @fires Client#message_revoke_everyone
* @fires Client#disconnected
*/
class Client extends EventEmitter {
constructor(options = {}) {
@@ -57,6 +66,11 @@ class Client extends EventEmitter {
await page.waitForSelector(KEEP_PHONE_CONNECTED_IMG_SELECTOR, { timeout: 15000 });
} catch (err) {
if (err.name === 'TimeoutError') {
/**
* Emitted when there has been an error while trying to restore an existing session
* @event Client#auth_failure
* @param {string} message
*/
this.emit(Events.AUTHENTICATION_FAILURE, 'Unable to log in. Are the session details valid?');
browser.close();
@@ -73,6 +87,11 @@ class Client extends EventEmitter {
const qrImgData = await page.$eval(QR_CANVAS_SELECTOR, canvas => [].slice.call(canvas.getContext('2d').getImageData(0,0,264,264).data));
const qr = jsQR(qrImgData, 264, 264).data;
/**
* Emitted when the QR code is received
* @event Client#qr
* @param {string} qr QR Code
*/
this.emit(Events.QR_RECEIVED, qr);
// Wait for code scan
@@ -93,6 +112,11 @@ class Client extends EventEmitter {
WAToken2: localStorage.WAToken2
};
/**
* Emitted when authentication is successful
* @event Client#authenticated
* @param {object} session Object containing session information. Can be used to restore the session.
*/
this.emit(Events.AUTHENTICATED, session);
// Check window.Store Injection
@@ -111,9 +135,21 @@ class Client extends EventEmitter {
if (!msg.isNewMsg) return;
const message = new Message(this, msg);
/**
* Emitted when a new message is created, which may include the current user's own messages.
* @event Client#message_create
* @param {Message} message The message that was created
*/
this.emit(Events.MESSAGE_CREATE, message);
if (msg.id.fromMe) return;
/**
* Emitted when a new message is received.
* @event Client#message
* @param {Message} message The message that was received
*/
this.emit(Events.MESSAGE_RECEIVED, message);
});
@@ -127,6 +163,14 @@ class Client extends EventEmitter {
if(last_message && msg.id.id === last_message.id.id) {
revoked_msg = new Message(this, last_message);
}
/**
* Emitted when a message is deleted for everyone in the chat.
* @event Client#message_revoke_everyone
* @param {Message} message The message that was revoked, in its current state. It will not contain the original message's data.
* @param {?Message} revoked_msg The message that was revoked, before it was revoked. It will contain the message's original data.
* Note that due to the way this data is captured, it may be possible that this param will be undefined.
*/
this.emit(Events.MESSAGE_REVOKED_EVERYONE, message, revoked_msg);
}
@@ -145,6 +189,12 @@ class Client extends EventEmitter {
if (!msg.isNewMsg) return;
const message = new Message(this, msg);
/**
* Emitted when a message is deleted by the current user.
* @event Client#message_revoke_me
* @param {Message} message The message that was revoked
*/
this.emit(Events.MESSAGE_REVOKED_ME, message);
});
@@ -152,6 +202,10 @@ class Client extends EventEmitter {
await page.exposeFunction('onAppStateChangedEvent', (AppState, state) => {
const ACCEPTED_STATES = [WAState.CONNECTED, WAState.OPENING, WAState.PAIRING];
if (!ACCEPTED_STATES.includes(state)) {
/**
* Emitted when the client has been disconnected
* @event Client#disconnected
*/
this.emit(Events.DISCONNECTED);
this.destroy();
}
@@ -168,9 +222,16 @@ class Client extends EventEmitter {
this.pupBrowser = browser;
this.pupPage = page;
/**
* Emitted when the client has initialized and is ready to receive messages.
* @event Client#ready
*/
this.emit(Events.READY);
}
/**
* Closes the client
*/
async destroy() {
await this.pupBrowser.close();
}
@@ -180,6 +241,7 @@ class Client extends EventEmitter {
* @param {string} chatId
* @param {string|MessageMedia} content
* @param {object} options
* @returns {Promise<Message>} Message that was just sent
*/
async sendMessage(chatId, content, options={}) {
let internalOptions = {
@@ -205,6 +267,7 @@ class Client extends EventEmitter {
/**
* Get all current chat instances
* @returns {Promise<Array<Chat>>}
*/
async getChats() {
let chats = await this.pupPage.evaluate(() => {
@@ -217,6 +280,7 @@ class Client extends EventEmitter {
/**
* Get chat instance by ID
* @param {string} chatId
* @returns {Promise<Chat>}
*/
async getChatById(chatId) {
let chat = await this.pupPage.evaluate(chatId => {
@@ -228,6 +292,7 @@ class Client extends EventEmitter {
/**
* Get all current contact instances
* @returns {Promise<Array<Contact>>}
*/
async getContacts() {
let contacts = await this.pupPage.evaluate(() => {
@@ -240,6 +305,7 @@ class Client extends EventEmitter {
/**
* Get contact instance by ID
* @param {string} contactId
* @returns {Promise<Contact>}
*/
async getContactById(contactId) {
let contact = await this.pupPage.evaluate(contactId => {
@@ -250,8 +316,8 @@ class Client extends EventEmitter {
}
/**
* Accepts an invite by code
* @param {string} inviteCode
* Accepts an invitation to join a group
* @param {string} inviteCode Invitation code
*/
async acceptInvite(inviteCode) {
const chatId = await this.pupPage.evaluate(async inviteCode => {

View File

@@ -8,6 +8,9 @@ const Contact = require('./Contact');
*/
class BusinessContact extends Contact {
_patch(data) {
/**
* The contact's business profile
*/
this.businessProfile = data.businessProfile;
return super._patch(data);

View File

@@ -14,12 +14,40 @@ class Chat extends Base {
}
_patch(data) {
/**
* ID that represents the chat
* @type {object}
*/
this.id = data.id;
/**
* Title of the chat
* @type {string}
*/
this.name = data.formattedTitle;
/**
* Indicates if the Chat is a Group Chat
* @type {boolean}
*/
this.isGroup = data.isGroup;
/**
* Indicates if the Chat is readonly
* @type {boolean}
*/
this.isReadOnly = data.isReadOnly;
/**
* Amount of messages unread
* @type {number}
*/
this.unreadCount = data.unreadCount;
/**
* Unix timestamp for when the chat was created
* @type {number}
*/
this.timestamp = data.t;
return super._patch(data);
@@ -29,6 +57,7 @@ class Chat extends Base {
* Send a message to this chat
* @param {string|MessageMedia} content
* @param {object} options
* @returns {Promise<Message>} Message that was just sent
*/
async sendMessage(content, options) {
return this.client.sendMessage(this.id._serialized, content, options);

View File

@@ -14,9 +14,33 @@ class ClientInfo extends Base {
}
_patch(data) {
/**
* Name configured to be shown in push notifications
* @type {string}
*/
this.pushname = data.pushname;
/**
* Current user ID
* @type {object}
*/
this.me = data.me;
/**
* Information about the phone this client is connected to
* @type {object}
* @property {string} wa_version WhatsApp Version running on the phone
* @property {string} os_version OS Version running on the phone (iOS or Android version)
* @property {string} device_manufacturer Device manufacturer
* @property {string} device_model Device model
* @property {string} os_build_number OS build number
*/
this.phone = data.phone;
/**
* Platform the phone is running on
* @type {string}
*/
this.platform = data.platform;
return super._patch(data);

View File

@@ -14,14 +14,46 @@ class Contact extends Base {
}
_patch(data) {
/**
* ID that represents the contact
* @type {object}
*/
this.id = data.id;
/**
* Indicates if the contact is a business contact
* @type {boolean}
*/
this.isBusiness = data.isBusiness;
/**
* Indicates if the contact is an enterprise contact
* @type {boolean}
*/
this.isEnterprise = data.isEnterprise;
this.labels = data.labels;
/**
* The contact's name, as saved by the current user
* @type {?string}
*/
this.name = data.name;
/**
* The name that the contact has configured to be shown publically
* @type {string}
*/
this.pushname = data.pushname;
this.sectionHeader = data.sectionHeader;
/**
* A shortened version of name
* @type {?string}
*/
this.shortName = data.shortName;
this.statusMute = data.statusMute;
this.type = data.type;
this.verifiedLevel = data.verifiedLevel;

View File

@@ -22,6 +22,7 @@ class GroupChat extends Chat {
/**
* Gets the date at which the group was created
* @type {date}
*/
get createdAt() {
return new Date(this.groupMetadata.creation * 1000);
@@ -29,12 +30,14 @@ class GroupChat extends Chat {
/**
* Gets the group description
* @type {string}
*/
get description() {
return this.groupMetadata.desc;
}
/**
* Gets the group participants
* @type {array}
*/
get participants() {
return this.groupMetadata.participants;
@@ -42,7 +45,7 @@ class GroupChat extends Chat {
/**
* Adds a list of participants by ID to the group
* @param {Array[string]} participantIds
* @param {Array<string>} participantIds
*/
async addParticipants(participantIds) {
return await this.client.pupPage.evaluate((chatId, participantIds) => {
@@ -52,7 +55,7 @@ class GroupChat extends Chat {
/**
* Removes a list of participants by ID to the group
* @param {Array[string]} participantIds
* @param {Array<string>} participantIds
*/
async removeParticipants(participantIds) {
return await this.client.pupPage.evaluate((chatId, participantIds) => {
@@ -62,7 +65,7 @@ class GroupChat extends Chat {
/**
* Promotes participants by IDs to admins
* @param {Array[string]} participantIds
* @param {Array<string>} participantIds
*/
async promoteParticipants(participantIds) {
return await this.client.pupPage.evaluate((chatId, participantIds) => {
@@ -72,7 +75,7 @@ class GroupChat extends Chat {
/**
* Demotes participants by IDs to regular users
* @param {Array[string]} participantIds
* @param {Array<string>} participantIds
*/
async demoteParticipants(participantIds) {
return await this.client.pupPage.evaluate((chatId, participantIds) => {
@@ -136,6 +139,7 @@ class GroupChat extends Chat {
/**
* Returns an object with information about the invite code's group
* @param {string} inviteCode
* @returns {Promise<object>} Invite information
*/
static async getInviteInfo(inviteCode) {
return await this.client.pupPage.evaluate(inviteCode => {

View File

@@ -15,17 +15,79 @@ class Message extends Base {
}
_patch(data) {
/**
* ID that represents the message
* @type {object}
*/
this.id = data.id;
/**
* Indicates if the message has media available for download
* @type {boolean}
*/
this.hasMedia = data.clientUrl ? true : false;
/**
* Message content
* @type {string}
*/
this.body = this.hasMedia ? data.caption || '' : data.body || '';
/**
* Message type
* @type {MessageTypes}
*/
this.type = data.type;
/**
* Unix timestamp for when the message was created
* @type {number}
*/
this.timestamp = data.t;
/**
* ID for the Chat that this message was sent to, except if the message was sent by the current user.
* @type {string}
*/
this.from = typeof (data.from) === 'object' ? data.from._serialized : data.from;
/**
* ID for who this message is for.
*
* If the message is sent by the current user, it will be the Chat to which the message is being sent.
* If the message is sent by another user, it will be the ID for the current user.
* @type {string}
*/
this.to = typeof (data.to) === 'object' ? data.to._serialized : data.to;
/**
* If the message was sent to a group, this field will contain the user that sent the message.
* @type {string}
*/
this.author = typeof (data.author) === 'object' ? data.author._serialized : data.author;
/**
* Indicates if the message was forwarded
* @type {boolean}
*/
this.isForwarded = data.isForwarded;
/**
* Indicates if the message was a broadcast
* @type {boolean}
*/
this.broadcast = data.broadcast;
/**
* Indicates if the message was sent by the current user
* @type {boolean}
*/
this.fromMe = data.id.fromMe;
/**
* Indicates if the message was sent as a reply to another message.
* @type {boolean}
*/
this.hasQuotedMsg = data.quotedMsg ? true : false;
return super._patch(data);
@@ -37,7 +99,7 @@ class Message extends Base {
/**
* Returns the Chat this message was sent in
* @returns {Chat}
* @returns {Promise<Chat>}
*/
getChat() {
return this.client.getChatById(this._getChatId());
@@ -45,7 +107,7 @@ class Message extends Base {
/**
* Returns the Contact this message was sent from
* @returns {Contact}
* @returns {Promise<Contact>}
*/
getContact() {
return this.client.getContactById(this._getChatId());
@@ -53,7 +115,7 @@ class Message extends Base {
/**
* Returns the quoted message, if any
* @returns {Message}
* @returns {Promise<Message>}
*/
async getQuotedMessage() {
if (!this.hasQuotedMsg) return undefined;
@@ -67,14 +129,14 @@ class Message extends Base {
}
/**
* Sends a message as a reply. If chatId is specified, it will be sent
* Sends a message as a reply to this message. If chatId is specified, it will be sent
* through the specified Chat. If not, it will send the message
* in the same Chat as the original message was sent.
*
* @param {string|MessageMedia} content
* @param {?string} chatId
* @param {object} options
* @returns {Message}
* @returns {Promise<Message>}
*/
async reply(content, chatId, options={}) {
if (!chatId) {
@@ -91,7 +153,7 @@ class Message extends Base {
/**
* Downloads and returns the attatched message media
* @returns {MessageMedia}
* @returns {Promise<MessageMedia>}
*/
async downloadMedia() {
if (!this.hasMedia) {

View File

@@ -11,12 +11,22 @@ exports.DefaultOptions = {
session: false
};
/**
* Client status
* @readonly
* @enum {number}
*/
exports.Status = {
INITIALIZING: 0,
AUTHENTICATING: 1,
READY: 3
};
/**
* Events that can be emitted by the client
* @readonly
* @enum {string}
*/
exports.Events = {
AUTHENTICATED: 'authenticated',
AUTHENTICATION_FAILURE: 'auth_failure',
@@ -29,6 +39,11 @@ exports.Events = {
DISCONNECTED: 'disconnected'
};
/**
* Message types
* @readonly
* @enum {string}
*/
exports.MessageTypes = {
TEXT: 'chat',
AUDIO: 'audio',
@@ -39,12 +54,22 @@ exports.MessageTypes = {
STICKER: 'sticker'
};
/**
* Chat types
* @readonly
* @enum {string}
*/
exports.ChatTypes = {
SOLO: 'solo',
GROUP: 'group',
UNKNOWN: 'unknown'
};
/**
* WhatsApp state
* @readonly
* @enum {string}
*/
exports.WAState = {
CONFLICT: 'CONFLICT',
CONNECTED: 'CONNECTED',

View File

@@ -1,8 +1,6 @@
'use strict';
/**
* Exposes the internal Store to the WhatsApp Web client
*/
// Exposes the internal Store to the WhatsApp Web client
exports.ExposeStore = (moduleRaidStr) => {
eval('var moduleRaid = ' + moduleRaidStr);
// eslint-disable-next-line no-undef