Baileys - 軽量フル機能のWhatsAppウェブ+マルチデバイスAPI

(Lightweight full-featured WhatsApp Web + Multi-Device API)

Created at: 2020-03-14 22:10:08
Language: JavaScript
License: MIT

Baileys - Typescript/Javascript WhatsApp Web API

Baileysは、Seleniumやその他のブラウザがWhatsApp Webとインターフェイスである必要はなく、WebSocketを使用して直接行います。セレンまたはクロミマムを実行しないと、ラムの半分のギガのように節約できます:/

Baileysは、WhatsAppのマルチデバイスおよびWebバージョンとの対話をサポートしています。

WhatsAppマルチデバイスの動作に関する彼の観察を書いてくださった@pokearaujoに感謝します。また、WhatsApp Webの動作に関する彼の観察を書いてくださった@Sigalorと、goの実装をしてくれた@Rhymenに感謝します。

Baileysはタイプセーフで、拡張可能で、使いやすいです。提供されている以上の機能が必要な場合は、拡張機能を作成するのは非常に簡単です。詳しくはこちらをご覧ください

WhatsAppボットの構築に興味がある場合は、WhatsAppInfoBotとそれを使用して構築された実際のボットであるMesscatをチェックすることをお勧めします。

こちらのドキュメントを読む ここで不和に参加してください

example.ts をチェックアウトして実行し、ライブラリの使用例を確認してください。 このスクリプトは、最も一般的なユースケースをカバーしています。 サンプル スクリプトを実行するには、リポジトリをダウンロードまたは複製し、ターミナルで次のように入力します。

  1. cd path/to/Baileys
  2. yarn
  3. yarn example

取り付ける

安定版を使用してください:

yarn add @adiwajshing/baileys

エッジバージョンを使用します(安定性の保証はありませんが、最新の修正+機能)

yarn add github:adiwajshing/baileys

次に、以下を使用してコードをインポートします。

import makeWASocket from '@adiwajshing/baileys'

単体テスト

藤堂

接続

import makeWASocket, { DisconnectReason } from '@adiwajshing/baileys'
import { Boom } from '@hapi/boom'

async function connectToWhatsApp () {
    const sock = makeWASocket({
        // can provide additional config here
        printQRInTerminal: true
    })
    sock.ev.on('connection.update', (update) => {
        const { connection, lastDisconnect } = update
        if(connection === 'close') {
            const shouldReconnect = (lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut
            console.log('connection closed due to ', lastDisconnect.error, ', reconnecting ', shouldReconnect)
            // reconnect if not logged out
            if(shouldReconnect) {
                connectToWhatsApp()
            }
        } else if(connection === 'open') {
            console.log('opened connection')
        }
    })
    sock.ev.on('messages.upsert', m => {
        console.log(JSON.stringify(m, undefined, 2))

        console.log('replying to', m.messages[0].key.remoteJid)
        await sock.sendMessage(m.messages[0].key.remoteJid!, { text: 'Hello there!' })
    })
}
// run in main file
connectToWhatsApp()

接続が成功すると、端末画面にQRコードが印刷され、携帯電話のWhatsAppでスキャンするとログインします。

注:を使用してQRを端末に自動印刷します。

qrcode-terminal
yarn add qrcode-terminal

注:WA Webのレガシーバージョン(プレマルチデバイス)をサポートするコードはv5で削除されました。標準のマルチデバイス接続のみがサポートされるようになりました。これは、WAがレガシーバージョンのサポートを完全に終了したように見えるために行われます。

接続の構成

オブジェクトを渡すことで接続を構成できます。

SocketConfig

ここでは、構造全体をデフォルト値とともに示します。

SocketConfig

type SocketConfig = {
    /** the WS url to connect to WA */
    waWebSocketUrl: string | URL
    /** Fails the connection if the socket times out in this interval */
	connectTimeoutMs: number
    /** Default timeout for queries, undefined for no timeout */
    defaultQueryTimeoutMs: number | undefined
    /** ping-pong interval for WS connection */
    keepAliveIntervalMs: number
    /** proxy agent */
	agent?: Agent
    /** pino logger */
	logger: Logger
    /** version to connect with */
    version: WAVersion
    /** override browser config */
	browser: WABrowserDescription
	/** agent used for fetch requests -- uploading/downloading media */
	fetchAgent?: Agent
    /** should the QR be printed in the terminal */
    printQRInTerminal: boolean
    /** should events be emitted for actions done by this socket connection */
    emitOwnEvents: boolean
    /** provide a cache to store media, so does not have to be re-uploaded */
    mediaCache?: NodeCache
    /** custom upload hosts to upload media to */
    customUploadHosts: MediaConnInfo['hosts']
    /** time to wait between sending new retry requests */
    retryRequestDelayMs: number
    /** time to wait for the generation of the next QR in ms */
    qrTimeout?: number;
    /** provide an auth state object to maintain the auth state */
    auth: AuthenticationState
    /** manage history processing with this control; by default will sync up everything */
    shouldSyncHistoryMessage: (msg: proto.Message.IHistorySyncNotification) => boolean
    /** transaction capability options for SignalKeyStore */
    transactionOpts: TransactionCapabilityOptions
    /** provide a cache to store a user's device list */
    userDevicesCache?: NodeCache
    /** marks the client as online whenever the socket successfully connects */
    markOnlineOnConnect: boolean
    /**
     * map to store the retry counts for failed messages;
     * used to determine whether to retry a message or not */
    msgRetryCounterMap?: MessageRetryMap
    /** width for link preview images */
    linkPreviewImageThumbnailWidth: number
    /** Should Baileys ask the phone for full history, will be received async */
    syncFullHistory: boolean
    /** Should baileys fire init queries automatically, default true */
    fireInitQueries: boolean
    /**
     * generate a high quality link preview,
     * entails uploading the jpegThumbnail to WA
     * */
    generateHighQualityLinkPreview: boolean

    /** options for axios */
    options: AxiosRequestConfig<any>
    /**
     * fetch a message from your store
     * implement this so that messages failed to send (solves the "this message can take a while" issue) can be retried
     * */
    getMessage: (key: proto.IMessageKey) => Promise<proto.IMessage | undefined>
}

Web ではなくデスクトップ アプリをエミュレートする

  1. ベイリーズは、デフォルトでは、クロムウェブセッションをエミュレートします
  2. デスクトップ接続をエミュレートする(そしてより多くのメッセージ履歴を受信する)場合は、これをSocket構成に追加します。
    const conn = makeWASocket({
        ...otherOpts,
        // can use Windows, Ubuntu here too
        browser: Browsers.macOS('Desktop'),
        syncFullHistory: true
    })

セッションの保存と復元

明らかに、接続するたびにQRコードをスキャンし続ける必要はありません。

したがって、資格情報を読み込んで再度ログインできます。

import makeWASocket, { BufferJSON, useMultiFileAuthState } from '@adiwajshing/baileys'
import * as fs from 'fs'

// utility function to help save the auth state in a single folder
// this function serves as a good guide to help write auth & key states for SQL/no-SQL databases, which I would recommend in any production grade system
const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')
// will use the given state to connect
// so if valid credentials are available -- it'll connect without QR
const conn = makeWASocket({ auth: state }) 
// this will be called as soon as the credentials are updated
conn.ev.on ('creds.update', saveCreds)

手記:メッセージが送受信されると、シグナルセッションの更新が必要なため、認証キー()が更新されます。そのたびに、更新されたキーを保存する必要があります(呼び出されます)。そうしないと、メッセージが受信者に届かなくなり、他の予期しない結果が生じます。関数は自動的にそれを処理しますが、他の深刻な実装では、キー状態管理に非常に注意する必要があります。

authState.keys
authState.keys.set()
useMultiFileAuthState

接続の更新をリッスンする

Baileys は、接続で何かが更新されたことを知らせるためにイベントを発生させるようになりました。このデータの構造は次のとおりです。

connection.update

type ConnectionState = {
	/** connection is now open, connecting or closed */
	connection: WAConnectionState
	/** the error that caused the connection to close */
	lastDisconnect?: {
		error: Error
		date: Date
	}
	/** is this a new login */
	isNewLogin?: boolean
	/** the current QR code */
	qr?: string
	/** has the device received all pending notifications while it was offline */
	receivedPendingNotifications?: boolean 
}

注:これはQRの更新も提供します

イベントの処理

ベイリーズは、イベントにイベント エミッタ構文を使用します。 それらはすべてうまく入力されているので、VSCodeのようなIntellisenseエディターで問題はないはずです。

イベントは次のように入力されます。

export type BaileysEventMap = {
    /** connection state has been updated -- WS closed, opened, connecting etc. */
	'connection.update': Partial<ConnectionState>
    /** credentials updated -- some metadata, keys or something */
    'creds.update': Partial<AuthenticationCreds>
    /** history sync, everything is reverse chronologically sorted */
    'messaging-history.set': {
        chats: Chat[]
        contacts: Contact[]
        messages: WAMessage[]
        isLatest: boolean
    }
    /** upsert chats */
    'chats.upsert': Chat[]
    /** update the given chats */
    'chats.update': Partial<Chat>[]
    /** delete chats with given ID */
    'chats.delete': string[]
    /** presence of contact in a chat updated */
    'presence.update': { id: string, presences: { [participant: string]: PresenceData } }

    'contacts.upsert': Contact[]
    'contacts.update': Partial<Contact>[]

    'messages.delete': { keys: WAMessageKey[] } | { jid: string, all: true }
    'messages.update': WAMessageUpdate[]
    'messages.media-update': { key: WAMessageKey, media?: { ciphertext: Uint8Array, iv: Uint8Array }, error?: Boom }[]
    /**
     * add/update the given messages. If they were received while the connection was online,
     * the update will have type: "notify"
     *  */
    'messages.upsert': { messages: WAMessage[], type: MessageUpsertType }
    /** message was reacted to. If reaction was removed -- then "reaction.text" will be falsey */
    'messages.reaction': { key: WAMessageKey, reaction: proto.IReaction }[]

    'message-receipt.update': MessageUserReceiptUpdate[]

    'groups.upsert': GroupMetadata[]
    'groups.update': Partial<GroupMetadata>[]
    /** apply an action to participants in a group */
    'group-participants.update': { id: string, participants: string[], action: ParticipantAction }

    'blocklist.set': { blocklist: string[] }
    'blocklist.update': { blocklist: string[], type: 'add' | 'remove' }
    /** Receive an update on a call, including when the call was received, rejected, accepted */
    'call': WACallEvent[]
}

これらのイベントは次のように聞くことができます。

const sock = makeWASocket()
sock.ev.on('messages.upsert', ({ messages }) => {
    console.log('got messages', messages)
})

データ ストアの実装

Baileysには、チャット、連絡先、またはメッセージ用の事実上のストレージは付属していません。ただし、単純なメモリ内実装が提供されています。ストアは、チャットの更新、新しいメッセージ、メッセージの更新などをリッスンして、常に最新バージョンのデータを保持します。

次のように使用できます。

import makeWASocket, { makeInMemoryStore } from '@adiwajshing/baileys'
// the store maintains the data of the WA connection in memory
// can be written out to a file & read from it
const store = makeInMemoryStore({ })
// can be read from a file
store.readFromFile('./baileys_store.json')
// saves the state to a file every 10s
setInterval(() => {
    store.writeToFile('./baileys_store.json')
}, 10_000)

const sock = makeWASocket({ })
// will listen from this socket
// the store can listen from a new socket once the current socket outlives its lifetime
store.bind(sock.ev)

sock.ev.on('chats.set', () => {
    // can use "store.chats" however you want, even after the socket dies out
    // "chats" => a KeyedDB instance
    console.log('got chats', store.chats.all())
})

sock.ev.on('contacts.set', () => {
    console.log('got contacts', Object.values(store.contacts))
})

ストアは、ストアを利用してデータ取得を高速化するなど、いくつかの簡単な機能も提供します。

loadMessages

手記:誰かのチャット履歴全体をメモリに保存することはRAMのひどい浪費であるため、特にMD接続用に独自のデータストアを構築することを強くお勧めします。

メッセージの送信

1つの関数ですべてのタイプのメッセージを送信します。

メディア以外のメッセージ

import { MessageType, MessageOptions, Mimetype } from '@adiwajshing/baileys'

const id = 'abcd@s.whatsapp.net' // the WhatsApp ID 
// send a simple text!
const sentMsg  = await sock.sendMessage(id, { text: 'oh hello there' })
// send a reply messagge
const sentMsg  = await sock.sendMessage(id, { text: 'oh hello there' }, { quoted: message })
// send a mentions message
const sentMsg  = await sock.sendMessage(id, { text: '@12345678901', mentions: ['12345678901@s.whatsapp.net'] })
// send a location!
const sentMsg  = await sock.sendMessage(
    id, 
    { location: { degreesLatitude: 24.121231, degreesLongitude: 55.1121221 } }
)
// send a contact!
const vcard = 'BEGIN:VCARD\n' // metadata of the contact card
            + 'VERSION:3.0\n' 
            + 'FN:Jeff Singh\n' // full name
            + 'ORG:Ashoka Uni;\n' // the organization of the contact
            + 'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n' // WhatsApp ID + phone number
            + 'END:VCARD'
const sentMsg  = await sock.sendMessage(
    id,
    { 
        contacts: { 
            displayName: 'Jeff', 
            contacts: [{ vcard }] 
        }
    }
)

// send a buttons message!
const buttons = [
  {buttonId: 'id1', buttonText: {displayText: 'Button 1'}, type: 1},
  {buttonId: 'id2', buttonText: {displayText: 'Button 2'}, type: 1},
  {buttonId: 'id3', buttonText: {displayText: 'Button 3'}, type: 1}
]

const buttonMessage = {
    text: "Hi it's button message",
    footer: 'Hello World',
    buttons: buttons,
    headerType: 1
}

const sendMsg = await sock.sendMessage(id, buttonMessage)

//send a template message!
const templateButtons = [
    {index: 1, urlButton: {displayText: '⭐ Star Baileys on GitHub!', url: 'https://github.com/adiwajshing/Baileys'}},
    {index: 2, callButton: {displayText: 'Call me!', phoneNumber: '+1 (234) 5678-901'}},
    {index: 3, quickReplyButton: {displayText: 'This is a reply, just like normal buttons!', id: 'id-like-buttons-message'}},
]

const templateMessage = {
    text: "Hi it's a template message",
    footer: 'Hello World',
    templateButtons: templateButtons
}

const sendMsg = await sock.sendMessage(id, templateMessage)

// send a list message!
const sections = [
    {
	title: "Section 1",
	rows: [
	    {title: "Option 1", rowId: "option1"},
	    {title: "Option 2", rowId: "option2", description: "This is a description"}
	]
    },
   {
	title: "Section 2",
	rows: [
	    {title: "Option 3", rowId: "option3"},
	    {title: "Option 4", rowId: "option4", description: "This is a description V2"}
	]
    },
]

const listMessage = {
  text: "This is a list",
  footer: "nice footer, link: https://google.com",
  title: "Amazing boldfaced list title",
  buttonText: "Required, text on the button to view the list",
  sections
}

const sendMsg = await sock.sendMessage(id, listMessage)

const reactionMessage = {
    react: {
        text: "💖", // use an empty string to remove the reaction
        key: message.key
    }
}

const sendMsg = await sock.sendMessage(id, reactionMessage)

リンクプレビュー付きのメッセージの送信

  1. デフォルトでは、WA MDはWebから送信されたときにリンク生成を行いません
  2. ベイリーズには、これらのリンクプレビューのコンテンツを生成する機能があります
  3. この関数の使用を有効にするには、依存関係としてプロジェクトに追加します。
    link-preview-js
    yarn add link-preview-js
  4. リンクを送信する:
// send a link
const sentMsg  = await sock.sendMessage(id, { text: 'Hi, this was sent using https://github.com/adiwajshing/baileys' })

メディア メッセージ

メディア(ビデオ、ステッカー、画像)の送信は、これまで以上に簡単で効率的です。

  • バッファ、ローカル URL、またはリモート URL を指定できます。
  • メディア URL を指定する場合、Baileys はバッファー全体をメモリにロードしません。メディアを読み取り可能なストリームとして暗号化することもできます。
import { MessageType, MessageOptions, Mimetype } from '@adiwajshing/baileys'
// Sending gifs
await sock.sendMessage(
    id, 
    { 
        video: fs.readFileSync("Media/ma_gif.mp4"), 
        caption: "hello!",
        gifPlayback: true
    }
)

await sock.sendMessage(
    id, 
    { 
        video: "./Media/ma_gif.mp4", 
        caption: "hello!",
        gifPlayback: true
    }
)

// send an audio file
await sock.sendMessage(
    id, 
    { audio: { url: "./Media/audio.mp3" }, mimetype: 'audio/mp4' }
    { url: "Media/audio.mp3" }, // can send mp3, mp4, & ogg
)

// send a buttons message with image header!
const buttons = [
  {buttonId: 'id1', buttonText: {displayText: 'Button 1'}, type: 1},
  {buttonId: 'id2', buttonText: {displayText: 'Button 2'}, type: 1},
  {buttonId: 'id3', buttonText: {displayText: 'Button 3'}, type: 1}
]

const buttonMessage = {
    image: {url: 'https://example.com/image.jpeg'},
    caption: "Hi it's button message",
    footer: 'Hello World',
    buttons: buttons,
    headerType: 4
}

const sendMsg = await sock.sendMessage(id, buttonMessage)

//send a template message with an image **attached**!
const templateButtons = [
  {index: 1, urlButton: {displayText: '⭐ Star Baileys on GitHub!', url: 'https://github.com/adiwajshing/Baileys'}},
  {index: 2, callButton: {displayText: 'Call me!', phoneNumber: '+1 (234) 5678-901'}},
  {index: 3, quickReplyButton: {displayText: 'This is a reply, just like normal buttons!', id: 'id-like-buttons-message'}},
]

const buttonMessage = {
    text: "Hi it's a template message",
    footer: 'Hello World',
    templateButtons: templateButtons,
    image: {url: 'https://example.com/image.jpeg'}
}

const sendMsg = await sock.sendMessage(id, templateMessage)

筆記

  • id
    は、メッセージの送信先のユーザーまたはグループの WhatsApp ID です。
    • 次の形式である必要があります
      [country code][phone number]@s.whatsapp.net
      • 人々の例: .
        +19999999999@s.whatsapp.net
      • グループの場合は、 の形式である必要があります。
        123456789-123345@g.us
    • ブロードキャストリストの場合は、.
      [timestamp of creation]@broadcast
    • ストーリーの場合、ID は です。
      status@broadcast
  • メディアメッセージの場合、サムネイルは、画像とステッカーを追加した場合、またはまたはを使用してプロジェクトの依存関係として自動的に生成できます。ビデオのサムネイルも自動的に生成できますが、システムにインストールしておく必要があります。
    jimp
    sharp
    yarn add jimp
    yarn add sharp
    ffmpeg
  • その他の生成オプション: メッセージに関する追加情報。次のオプションの値を指定できます。
    const info: MessageOptions = {
        quoted: quotedMessage, // the message you want to quote
        contextInfo: { forwardingScore: 2, isForwarded: true }, // some random context info (can show a forwarded message with this too)
        timestamp: Date(), // optional, if you want to manually set the timestamp of the message
        caption: "hello there!", // (for media messages) the caption to send with the media (cannot be sent with stickers though)
        jpegThumbnail: "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.
                                    Do not enter this field if you want to automatically generate a thumb
                                */
        mimetype: Mimetype.pdf, /* (for media messages) specify the type of media (optional for all media types except documents),
                                    import {Mimetype} from '@adiwajshing/baileys'
                                */
        fileName: 'somefile.pdf', // (for media messages) file name for the media
        /* will send audio messages as voice notes, if set to true */
        ptt: true,
        /** Should it send as a disappearing messages. 
         * By default 'chat' -- which follows the setting of the chat */
        ephemeralExpiration: WA_DEFAULT_EPHEMERAL
    }

メッセージの転送

const msg = getMessageFromStore('455@s.whatsapp.net', 'HSJHJWH7323HSJSJ') // implement this on your end
await sock.sendMessage('1234@s.whatsapp.net', { forward: msg }) // WA forward the message!

メッセージを読む

一連のメッセージ キーには、既読として明示的にマークする必要があります。 マルチデバイスでは、Baileys Webの場合のように「チャット」全体を既読にすることはできません。 これは、未読メッセージを追跡する必要があることを意味します。

const key = {
    remoteJid: '1234-123@g.us',
    id: 'AHASHH123123AHGA', // id of the message you want to read
    participant: '912121232@s.whatsapp.net' // the ID of the user that sent the  message (undefined for individual chats)
}
// pass to readMessages function
// can pass multiple keys to read multiple messages as well
await sock.readMessages([key])

メッセージ ID は、既読としてマークするメッセージの一意の識別子です。 では、を使用してアクセスできます。

WAMessage
messageID
messageID = message.key.id

プレゼンスの更新

await sock.sendPresenceUpdate('available', id) 

これにより、あなたがオンライン、オフライン、タイピングなどであるかどうかを人/グループに知らせることができます。

id

presence
次のいずれかになります。

type WAPresence = 'unavailable' | 'available' | 'composing' | 'recording' | 'paused'

プレゼンスは約 10 秒後に期限切れになります。

手記:WhatsAppのマルチデバイスバージョンでは、デスクトップクライアントがアクティブな場合、WAはデバイスにプッシュ通知を送信しません。上記の通知を受け取りたい場合は、ベイリーズクライアントをオフラインにマークします

sock.sendPresenceUpdate('unavailable')

メディアメッセージのダウンロード

受け取ったメディアを保存する場合

import { writeFile } from 'fs/promises'
import { downloadMediaMessage } from '@adiwajshing/baileys'

sock.ev.on('messages.upsert', async ({ messages }) => {
    const m = messages[0]

    if (!m.message) return // if there is no text or media message
    const messageType = Object.keys (m.message)[0]// get what type of message it is -- text, image, video
    // if the message is an image
    if (messageType === 'imageMessage') {
        // download the message
        const buffer = await downloadMediaMessage(
            m,
            'buffer',
            { },
            { 
                logger,
                // pass this so that baileys can request a reupload of media
                // that has been deleted
                reuploadRequest: sock.updateMediaMessage
            }
        )
        // save to file
        await writeFile('./my-download.jpeg', buffer)
    }
}

手記:WhatsAppは、サーバーから古いメディアを自動的に削除します。デバイスが上記のメディアにアクセスするには、そのメディアを持つ別のデバイスで再アップロードが必要です。これは、以下を使用して実現できます。

const updatedMediaMsg = await sock.updateMediaMessage(msg)

メッセージの削除

const jid = '1234@s.whatsapp.net' // can also be a group
const response = await sock.sendMessage(jid, { text: 'hello!' }) // send a message
// sends a message to delete the given message
// this deletes the message for everyone
await sock.sendMessage(jid, { delete: response.key })

注:自分での削除は、(次のセクション)を介してサポートされています

chatModify

チャットの変更

WAは、暗号化された形式の通信を使用して、チャット/アプリの更新を送信します。これは主に実装されており、次の更新を送信できます。

  • チャットをアーカイブする

    const lastMsgInChat = await getLastMessageInChat('123456@s.whatsapp.net') // implement this on your end
    await sock.chatModify({ archive: true, lastMessages: [lastMsgInChat] }, '123456@s.whatsapp.net')
  • チャットのミュート/ミュート解除

    // mute for 8 hours
    await sock.chatModify({ mute: 8*60*60*1000 }, '123456@s.whatsapp.net', [])
    // unmute
    await sock.chatModify({ mute: null }, '123456@s.whatsapp.net', [])
  • チャットを既読/未読にする

    const lastMsgInChat = await getLastMessageInChat('123456@s.whatsapp.net') // implement this on your end
    // mark it unread
    await sock.chatModify({ markRead: false, lastMessages: [lastMsgInChat] }, '123456@s.whatsapp.net')
  • 「メッセージ」を削除する

    await sock.chatModify(
      { clear: { messages: [{ id: 'ATWYHDNNWU81732J', fromMe: true, timestamp: "1654823909" }] } }, 
      '123456@s.whatsapp.net', 
      []
      )
  • Delete a chat

    const lastMsgInChat = await getLastMessageInChat('123456@s.whatsapp.net') // implement this on your end
    await sock.chatModify({
      delete: true,
      lastMessages: [{ key: lastMsgInChat.key, messageTimestamp: lastMsgInChat.messageTimestamp }]
    },
    '123456@s.whatsapp.net')
  • Pin/unpin a chat

    await sock.chatModify({
      pin: true // or `false` to unpin
    },
    '123456@s.whatsapp.net')

Note: if you mess up one of your updates, WA can log you out of all your devices and you'll have to log in again.

Disappearing Messages

const jid = '1234@s.whatsapp.net' // can also be a group
// turn on disappearing messages
await sock.sendMessage(
    jid, 
    // this is 1 week in seconds -- how long you want messages to appear for
    { disappearingMessagesInChat: WA_DEFAULT_EPHEMERAL }
)
// will send as a disappearing message
await sock.sendMessage(jid, { text: 'hello' }, { ephemeralExpiration: WA_DEFAULT_EPHEMERAL })
// turn off disappearing messages
await sock.sendMessage(
    jid, 
    { disappearingMessagesInChat: false }
)

Misc

  • To check if a given ID is on WhatsApp
    const id = '123456'
    const [result] = await sock.onWhatsApp(id)
    if (result.exists) console.log (`${id} exists on WhatsApp, as jid: ${result.jid}`)
  • To query chat history on a group or with someone TODO, if possible
  • To get the status of some person
    const status = await sock.fetchStatus("xyz@s.whatsapp.net")
    console.log("status: " + status)
  • To change your profile status
    const status = 'Hello World!'
    await sock.updateProfileStatus(status)
  • To change your profile name
    const name = 'My name'
    await sock.updateProfileName(name)
  • To get the display picture of some person/group
    // for low res picture
    const ppUrl = await sock.profilePictureUrl("xyz@g.us")
    console.log("download profile picture from: " + ppUrl)
    // for high res picture
    const ppUrl = await sock.profilePictureUrl("xyz@g.us", 'image')
  • To change your display picture or a group's
    const jid = '111234567890-1594482450@g.us' // can be your own too
    await sock.updateProfilePicture(jid, { url: './new-profile-picture.jpeg' })
  • To get someone's presence (if they're typing or online)
    // the presence update is fetched and called here
    sock.ev.on('presence-update', json => console.log(json))
    // request updates for a chat
    await sock.presenceSubscribe("xyz@s.whatsapp.net") 
  • To block or unblock user
    await sock.updateBlockStatus("xyz@s.whatsapp.net", "block") // Block user
    await sock.updateBlockStatus("xyz@s.whatsapp.net", "unblock") // Unblock user
  • To get a business profile, such as description or category
    const profile = await sock.getBusinessProfile("xyz@s.whatsapp.net")
    console.log("business description: " + profile.description + ", category: " + profile.category)

Of course, replace with an actual ID.

xyz

Groups

  • To create a group
    // title & participants
    const group = await sock.groupCreate("My Fab Group", ["1234@s.whatsapp.net", "4564@s.whatsapp.net"])
    console.log ("created group with id: " + group.gid)
    sock.sendMessage(group.id, { text: 'hello there' }) // say hello to everyone on the group
  • To add/remove people to a group or demote/promote people
    // id & people to add to the group (will throw error if it fails)
    const response = await sock.groupParticipantsUpdate(
        "abcd-xyz@g.us", 
        ["abcd@s.whatsapp.net", "efgh@s.whatsapp.net"],
        "add" // replace this parameter with "remove", "demote" or "promote"
    )
  • To change the group's subject
    await sock.groupUpdateSubject("abcd-xyz@g.us", "New Subject!")
  • To change the group's description
    await sock.groupUpdateDescription("abcd-xyz@g.us", "New Description!")
  • To change group settings
    // only allow admins to send messages
    await sock.groupSettingUpdate("abcd-xyz@g.us", 'announcement')
    // allow everyone to send messages
    await sock.groupSettingUpdate("abcd-xyz@g.us", 'not_announcement')
    // allow everyone to modify the group's settings -- like display picture etc.
    await sock.groupSettingUpdate("abcd-xyz@g.us", 'unlocked')
    // only allow admins to modify the group's settings
    await sock.groupSettingUpdate("abcd-xyz@g.us", 'locked')
  • To leave a group
    await sock.groupLeave("abcd-xyz@g.us") // (will throw error if it fails)
  • To get the invite code for a group
    const code = await sock.groupInviteCode("abcd-xyz@g.us")
    console.log("group code: " + code)
  • To revoke the invite code in a group
    const code = await sock.groupRevokeInvite("abcd-xyz@g.us")
    console.log("New group code: " + code)
  • To query the metadata of a group
    const metadata = await sock.groupMetadata("abcd-xyz@g.us") 
    console.log(metadata.id + ", title: " + metadata.subject + ", description: " + metadata.desc)
  • To join the group using the invitation code
    const response = await sock.groupAcceptInvite("xxx")
    console.log("joined to: " + response)
    Of course, replace with invitation code.
    xxx
  • To get group info by invite code
    const response = await sock.groupGetInviteInfo("xxx")
    console.log("group information: " + response)
  • To join the group using groupInviteMessage
    const response = await sock.groupAcceptInviteV4("abcd@s.whatsapp.net", groupInviteMessage)
    console.log("joined to: " + response)
    Of course, replace with invitation code.
    xxx

Broadcast Lists & Stories

Note: messages currently cannot be sent to broadcast lists from the MD version.

  • You can send messages to broadcast lists the same way you send messages to groups & individual chats.
  • Right now, WA Web does not support creating broadcast lists, but you can still delete them.
  • Broadcast IDs are in the format
    12345678@broadcast
  • To query a broadcast list's recipients & name:
    const bList = await sock.getBroadcastListInfo("1234@broadcast")
    console.log (`list name: ${bList.name}, recps: ${bList.recipients}`)

Writing Custom Functionality

Baileys is written with custom functionality in mind. Instead of forking the project & re-writing the internals, you can simply write your own extensions.

First, enable the logging of unhandled messages from WhatsApp by setting:

const sock = makeWASocket({
    logger: P({ level: 'debug' }),
})

これにより、WhatsAppが送信するあらゆる種類のメッセージをコンソールで確認できます。

いくつかの例:

  1. お使いの携帯電話のバッテリーのパーセンテージを追跡する機能。 ログを有効にすると、バッテリーに関するメッセージがコンソールにポップアップ表示されます。

    {"level":10,"fromMe":false,"frame":{"tag":"ib","attrs":{"from":"@s.whatsapp.net"},"content":[{"tag":"edge_routing","attrs":{},"content":[{"tag":"routing_info","attrs":{},"content":{"type":"Buffer","data":[8,2,8,5]}}]}]},"msg":"communication"} 

    「フレーム」は受信したメッセージであり、3つのコンポーネントがあります。

    • tag
      - このフレームの内容(例えば、メッセージには "message"があります)
    • attrs
      -- いくつかのメタデータを持つ文字列のキーと値のペア(通常はメッセージのIDを含む)
    • content
      -- 実際のデータ(例えば、メッセージノードには実際のメッセージコンテンツが含まれます)
    • この形式の詳細については、こちらをご覧ください

    以下を使用して、イベントのコールバックを登録できます。

    // for any message with tag 'edge_routing'
    sock.ws.on(`CB:edge_routing`, (node: BinaryNode) => { })
    // for any message with tag 'edge_routing' and id attribute = abcd
    sock.ws.on(`CB:edge_routing,id:abcd`, (node: BinaryNode) => { })
    // for any message with tag 'edge_routing', id attribute = abcd & first content node routing_info
    sock.ws.on(`CB:edge_routing,id:abcd,routing_info`, (node: BinaryNode) => { })

手記

このライブラリはもともとアショカ大学のCS-2362のプロジェクトであり、WhatsAppとは一切提携していません。ご自身の判断で使用してください。これで人々にスパムを送信しないでください。

また、このリポジトリはlibsignal-nodeを使用しているため、GPL 3でライセンスされています。