diff --git a/README.md b/README.md index aadca74..4bd3bc9 100644 --- a/README.md +++ b/README.md @@ -151,12 +151,14 @@ To note: - `id` is the WhatsApp ID of the person or group you're sending the message to. - It must be in the format ```[country code][phone number]@s.whatsapp.net```, for example ```+19999999999@s.whatsapp.net``` for people. For groups, it must be in the format ``` 123456789-123345@g.us ```. - **Do not attach** `@c.us` for individual people IDs, It won't work. + - Please do not explicitly disable ID validation (in `MessageOptions`) because then your messages may fail for no apparent reason. - For media messages, the thumbnail can be generated automatically for images & stickers. Thumbnails for videos can also be generated automatically, though, you need to have `ffmpeg` installed on your system. - **MessageOptions**: some extra info about the message. It can have the following __optional__ values: ``` ts const info: MessageOptions = { quoted: quotedMessage, // the message you want to quote - timestamp: Date() // optional, if you want to manually set the timestamp of the message + timestamp: Date(), // optional, if you want to manually set the timestamp of the message + validateID: true, // if you want to validate the ID before sending the message, true by default caption: "hello there!", // (for media messages) the caption to send with the media (cannot be sent with stickers though) thumbnail: "23GD#4/==", /* (for location & media messages) has to be a base 64 encoded JPEG if you want to send a custom thumb, or set to null if you don't want to send a thumbnail. diff --git a/package.json b/package.json index e2319c4..2c5c556 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@adiwajshing/baileys", - "version": "2.0.0", + "version": "2.0.1", "description": "WhatsApp Web API", "homepage": "https://github.com/adiwajshing/Baileys", "main": "lib/WAClient/WAClient.js", @@ -18,7 +18,7 @@ ], "scripts": { "prepare": "npm run build", - "test": "mocha --timeout 30000 -r ts-node/register */Tests.ts", + "test": "mocha --timeout 30000 -r ts-node/register src/*/Tests.ts", "lint": "eslint '*/*.ts' --quiet --fix", "build": "tsc", "example": "npx ts-node Example/example.ts" diff --git a/src/WAClient/Constants.ts b/src/WAClient/Constants.ts index bf65d4b..b7c4860 100644 --- a/src/WAClient/Constants.ts +++ b/src/WAClient/Constants.ts @@ -34,15 +34,14 @@ export enum MessageType { document = 'documentMessage', audio = 'audioMessage', } -/** - * Tells us what kind of message it is - */ -export const MessageStubTypes = { - 20: 'addedToGroup', - 32: 'leftGroup', - 39: 'createdGroup', -} +export const WAMessageType = function () { + const types = proto.WebMessageInfo.WEB_MESSAGE_INFO_STUBTYPE + const dict: Record = {} + Object.keys(types).forEach(element => dict[ types[element] ] = element) + return dict +}() export const HKDFInfoKeys = (function () { + const dict: Record = {} dict[MessageType.image] = 'WhatsApp Image Keys' dict[MessageType.video] = 'WhatsApp Audio Keys' @@ -65,10 +64,12 @@ export interface MessageOptions { caption?: string thumbnail?: string mimetype?: Mimetype + validateID?: boolean } export interface MessageStatusUpdate { from: string to: string + /** Which participant caused the update (only for groups) */ participant?: string timestamp: Date /** Message IDs read/delivered */ diff --git a/src/WAClient/Messages.ts b/src/WAClient/Messages.ts index dd3e42d..0d4e7f8 100644 --- a/src/WAClient/Messages.ts +++ b/src/WAClient/Messages.ts @@ -13,7 +13,7 @@ import { } from './Constants' import { generateMessageID, sha256, hmacSign, aesEncrypWithIV, randomBytes } from '../WAConnection/Utils' import { WAMessageContent, WAMetric, WAFlag } from '../WAConnection/Constants' -import { generateThumbnail, getMediaKeys } from './Utils' +import { validateJIDForSending, generateThumbnail, getMediaKeys } from './Utils' export default class WhatsAppWebMessages extends WhatsAppWebBase { /** @@ -48,6 +48,9 @@ export default class WhatsAppWebMessages extends WhatsAppWebBase { type: MessageType, options: MessageOptions = {}, ) { + if (options.validateID === true || !('validateID' in options)) { + validateJIDForSending (id) + } let m: any = {} switch (type) { case MessageType.text: diff --git a/src/WAClient/Tests.ts b/src/WAClient/Tests.ts index 9b6a82d..affe315 100644 --- a/src/WAClient/Tests.ts +++ b/src/WAClient/Tests.ts @@ -3,7 +3,7 @@ import { MessageType, MessageOptions, Mimetype, Presence } from './Constants' import * as fs from 'fs' import * as assert from 'assert' -import { decodeMediaMessage } from './Utils' +import { decodeMediaMessage, validateJIDForSending } from './Utils' import { promiseTimeout } from '../WAConnection/Utils' require ('dotenv').config () // dotenv to load test jid @@ -61,6 +61,18 @@ WAClientTest('Messages', (client) => { assert.strictEqual(message.message.imageMessage.contextInfo.stanzaId, messages[0].key.id) }) }) +describe('Validate WhatsApp IDs', () => { + it ('should correctly validate', () => { + assert.doesNotThrow (() => validateJIDForSending ('12345@s.whatsapp.net')) + assert.doesNotThrow (() => validateJIDForSending ('919999999999@s.whatsapp.net')) + assert.doesNotThrow (() => validateJIDForSending ('10203040506@s.whatsapp.net')) + assert.doesNotThrow (() => validateJIDForSending ('12345-3478@g.us')) + assert.doesNotThrow (() => validateJIDForSending ('1234567890-34712121238@g.us')) + assert.throws (() => validateJIDForSending ('123454677@c.us')) + assert.throws (() => validateJIDForSending ('+123454677@s.whatsapp.net')) + assert.throws (() => validateJIDForSending ('+12345-3478@g.us')) + }) +}) WAClientTest('Presence', (client) => { it('should update presence', async () => { const presences = Object.values(Presence) diff --git a/src/WAClient/Utils.ts b/src/WAClient/Utils.ts index d4de821..48af9cb 100644 --- a/src/WAClient/Utils.ts +++ b/src/WAClient/Utils.ts @@ -1,4 +1,4 @@ -import { MessageType, HKDFInfoKeys, MessageOptions, MessageStubTypes } from './Constants' +import { MessageType, HKDFInfoKeys, MessageOptions, WAMessageType } from './Constants' import sharp from 'sharp' import * as fs from 'fs' import fetch from 'node-fetch' @@ -8,18 +8,28 @@ import { proto } from '../../WAMessage/WAMessage' import { randomBytes } from 'crypto' import { exec } from 'child_process' +export function validateJIDForSending (jid: string) { + const regexp = /^[0-9]{1,20}(-[0-9]{1,20}@g.us|@s.whatsapp.net)$/ + if (!regexp.test (jid)) { + throw new Error ( + `Invalid WhatsApp id: ${jid} + 1. Please ensure you suffix '@s.whatsapp.net' for individual numbers & '@g.us' for groups + 2. Please do not put any alphabets or special characters like a '+' in the number. A '-' symbol in groups is fine` + ) + } +} + /** Type of notification */ -export function getNotificationType(message: WAMessage) { +export function getNotificationType(message: WAMessage): [string, string] { if (message.message) { return ['message', Object.keys(message.message)[0]] } else if (message.messageStubType) { - return [MessageStubTypes[message.messageStubType], null] + return [WAMessageType[message.messageStubType], null] } else { return ['unknown', null] } } /** generates all the keys required to encrypt/decrypt & sign a media message */ - export function getMediaKeys(buffer, mediaType: MessageType) { // expand using HKDF to 112 bytes, also pass in the relevant app info const expandedMediaKey = hkdf(buffer, 112, HKDFInfoKeys[mediaType])