{"_id":"@revizahoshii/hoshino","_rev":"2-e73a18048680e8a00c30d5e643efd9c6","name":"@revizahoshii/hoshino","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@revizahoshii/hoshino","version":"1.0.0","keywords":["whatsapp","whatsapp-bot","reviza","hoshino","baileys","zapo"],"author":{"url":"https://revizayowa.biz.id/","name":"Reviza D Kink"},"license":"MIT","_id":"@revizahoshii/hoshino@1.0.0","maintainers":[{"name":"revizahoshii","email":"revizahoshii9@gmail.com"}],"contributors":[{"url":"https://github.com/vinikjkkj","name":"vinikjkkj","email":"contact@vinicius.email"}],"homepage":"https://github.com/revizahoshii-no/lreviza-baileys#readme","bugs":{"url":"https://github.com/revizahoshii-no/lreviza-baileys/issues"},"dist":{"shasum":"87044c1fbe1538e3799490325717b8e90258c16c","tarball":"https://registry.npmjs.org/@revizahoshii/hoshino/-/hoshino-1.0.0.tgz","fileCount":1075,"integrity":"sha512-2lTLRx+Xe5zJLhDbnhAqHEAoMDH9RZV6vc/Oj4wZfE8CNgAesV/RY+Cc7KiK0v0bhZS45kV99eJxzWky9yC3fg==","signatures":[{"sig":"MEUCIGHomJexEDLv7BX0ODDcHLhQoX8jZgtOm7fy5iuIFpEyAiEAiD6Mz3a9rFuDbhowzvbM4tV2uotTEokgNFWpwAFIW6s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":8478440},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm/index.js","engines":{"node":">=20.9.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/esm/index.js","default":"./dist/index.js","require":"./dist/index.js"},"./auth":{"types":"./dist/auth/index.d.ts","import":"./dist/esm/auth/index.js","default":"./dist/auth/index.js","require":"./dist/auth/index.js"},"./util":{"types":"./dist/util/index.d.ts","import":"./dist/esm/util/index.js","default":"./dist/util/index.js","require":"./dist/util/index.js"},"./media":{"types":"./dist/media/index.d.ts","import":"./dist/esm/media/index.js","default":"./dist/media/index.js","require":"./dist/media/index.js"},"./proto":{"types":"./dist/proto.d.ts","import":"./dist/esm/proto.js","default":"./dist/proto.js","require":"./dist/proto.js"},"./retry":{"types":"./dist/retry/index.d.ts","import":"./dist/esm/retry/index.js","default":"./dist/retry/index.js","require":"./dist/retry/index.js"},"./store":{"types":"./dist/store/index.d.ts","import":"./dist/esm/store/index.js","default":"./dist/store/index.js","require":"./dist/store/index.js"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/esm/client/index.js","default":"./dist/client/index.js","require":"./dist/client/index.js"},"./crypto":{"types":"./dist/crypto/index.d.ts","import":"./dist/esm/crypto/index.js","default":"./dist/crypto/index.js","require":"./dist/crypto/index.js"},"./signal":{"types":"./dist/signal/index.d.ts","import":"./dist/esm/signal/index.js","default":"./dist/signal/index.js","require":"./dist/signal/index.js"},"./message":{"types":"./dist/message/index.d.ts","import":"./dist/esm/message/index.js","default":"./dist/message/index.js","require":"./dist/message/index.js"},"./appstate":{"types":"./dist/appstate/index.d.ts","import":"./dist/esm/appstate/index.js","default":"./dist/appstate/index.js","require":"./dist/appstate/index.js"},"./protocol":{"types":"./dist/protocol/index.d.ts","import":"./dist/esm/protocol/index.js","default":"./dist/protocol/index.js","require":"./dist/protocol/index.js"},"./transport":{"types":"./dist/transport/index.d.ts","import":"./dist/esm/transport/index.js","default":"./dist/transport/index.js","require":"./dist/transport/index.js"},"./package.json":"./package.json"},"gitHead":"a589fdb2e252a36b2fd2ee206e6fa3c38f1293ea","scripts":{"lint":"eslint . --ext .ts,.js,.cjs","test":"npm run test:structure && node --import tsx --test --test-skip-pattern \"\\\\[flow\\\\]\" \"src/**/__tests__/**/*.test.ts\"","build":"npm run clean && npm run build:cjs && npm run build:esm && npm run build:types && npm run build:esm:finalize","clean":"node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"","format":"prettier . --write","example":"tsx examples/example.ts","prepare":"npm run build","lint:fix":"eslint . --ext .ts,.js,.cjs --fix","bench:all":"npm run bench:media && npm run bench:binary && npm run bench:crypto && npm run bench:store:memory","build:cjs":"tsc -p tsconfig.build.cjs.json && tsc-alias -p tsconfig.build.cjs.json","build:esm":"tsc -p tsconfig.build.esm.json && tsc-alias -p tsconfig.build.esm.json","changeset":"changeset","test:flow":"node --import tsx --test --test-concurrency=1 \"src/**/__tests__/**/*.flow.test.ts\"","typecheck":"tsc -p tsconfig.json --noEmit","preinstall":"node ./scripts/check-node-version.cjs","test:watch":"node --import tsx --test --watch --test-skip-pattern \"\\\\[flow\\\\]\" \"src/**/__tests__/**/*.test.ts\"","bench:media":"node --expose-gc --import tsx bench/media-streaming.bench.ts","build:types":"tsc -p tsconfig.build.types.json && tsc-alias -p tsconfig.build.types.json","test:stores":"node ./scripts/test-stores.cjs","bench:binary":"node --expose-gc --import tsx bench/binary-codec.bench.ts","bench:crypto":"node --expose-gc --import tsx bench/crypto-core.bench.ts","format:check":"prettier . --check","bench:comment":"node ./scripts/build-bench-comment.cjs","test:coverage":"npm run test:structure && c8 --all --include \"src/**/*.ts\" --exclude \"src/**/*.d.ts\" --exclude \"src/**/__tests__/**\" --exclude \"src/**/types.ts\" --exclude \"src/store/contracts/**/*.ts\" --reporter text --reporter html --reporter lcov node --import tsx --test --test-skip-pattern \"\\\\[flow\\\\]\" \"src/**/__tests__/**/*.test.ts\"","test:packages":"turbo run test","typecheck:all":"npm run typecheck && npm run typecheck:examples && npm run typecheck:bench && npm run typecheck:packages","build:packages":"turbo run build","proto:generate":"node ./scripts/generate-proto.cjs","test:structure":"node ./scripts/check-tests-structure.cjs","release:publish":"npm run build && npm run build:packages && changeset publish","test:flow:watch":"node --import tsx --test --watch --test-concurrency=1 \"src/**/__tests__/**/*.flow.test.ts\"","typecheck:bench":"tsc -p bench/tsconfig.json","changeset:status":"changeset status --verbose","version:packages":"changeset version","bench:store:memory":"node --expose-gc --import tsx bench/store-memory.bench.ts","build:esm:finalize":"node ./scripts/finalize-esm-build.cjs --proto-bridge","test:coverage:open":"node -e \"require('node:child_process').execSync(process.platform === 'win32' ? 'start coverage\\\\index.html' : process.platform === 'darwin' ? 'open coverage/index.html' : 'xdg-open coverage/index.html', { stdio: 'inherit' })\"","typecheck:examples":"tsc -p examples/tsconfig.json","typecheck:packages":"turbo run typecheck"},"_npmUser":{"name":"revizahoshii","email":"revizahoshii9@gmail.com"},"overrides":{"qs":"6.15.2","ws":"^8.20.1","flatted":"^3.4.2","picomatch@4":"^4.0.4","picomatch@<2.3.2":"2.3.2","brace-expansion@1":"1.1.13","brace-expansion@2":"^2.0.3","brace-expansion@4":"^4.0.2","brace-expansion@5":"^5.0.6"},"repository":{"url":"git+https://github.com/revizahoshii-no/lreviza-baileys.git","type":"git"},"workspaces":["packages/*"],"_npmVersion":"11.6.0","description":"Fork Zapo oleh Reviza — tombol interaktif, AI Rich, album, kutipan bercentang biru, dan auto-follow saluran.","directories":{},"_nodeVersion":"24.8.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"npm@10.0.0","devDependencies":{"c8":"^11.0.0","ws":"^8.20.1","tsx":"^4.20.6","pino":"^10.3.1","turbo":"^2.9.14","eslint":"^9.39.4","prettier":"^3.8.1","@types/ws":"^8.18.1","tsc-alias":"^1.8.16","argo-codec":"^0.2.1","typescript":"^5.7.3","@types/node":"^22.13.14","pino-pretty":"^13.1.3","better-sqlite3":"^12.6.2","@changesets/cli":"^2.30.0","eslint-plugin-n":"^17.24.0","typescript-eslint":"^8.56.1","eslint-plugin-import":"^2.32.0","@vinikjkkj/eslint-config":"1.0.2","@typescript-eslint/parser":"^8.56.1","eslint-import-resolver-typescript":"^4.4.4"},"peerDependencies":{"ws":"^8.20.1","pino":"^9.0.0","argo-codec":"^0.2.1","pino-pretty":"^13.0.0"},"peerDependenciesMeta":{"ws":{"optional":true},"pino":{"optional":true},"argo-codec":{"optional":true},"pino-pretty":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/hoshino_1.0.0_1789962758248_0.5968078287497944","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"_id":"@revizahoshii/hoshino@1.1.0","bugs":{"url":"https://github.com/revizahoshii-no/lreviza-baileys/issues"},"dist":{"shasum":"3a3aeecbd27701bb4e9d5636bf02acab21c20c0c","tarball":"https://registry.npmjs.org/@revizahoshii/hoshino/-/hoshino-1.1.0.tgz","fileCount":1084,"integrity":"sha512-kMndGAS3lz3IZnxEB4ii0lSzags0rHGZha1Oy24FicEMA77u+348ubbCXs3LxMmWUJoDKmSmnoInatSVjXdj4w==","signatures":[{"sig":"MEUCIAbtuSVrEJQjoozv98O9QNmNl7aIVyn6g9wnCO14KmJ1AiEAjeHEWxBLpEiM4lMQCy0rJe1zuddxX5hz9Vv2XplA+TI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBaVFv6kyds2GxNI1dTdrU5Y5GeJgUg+8WaXbgIaKjopAiEAwtl9e/LPRP2ZmGpm3+s7ch7K5c2rWtGNRZJVvTvapUk="}],"unpackedSize":8549951},"main":"dist/index.js","name":"@revizahoshii/hoshino","types":"dist/index.d.ts","author":{"url":"https://revizayowa.biz.id/","name":"Reviza D Kink"},"module":"dist/esm/index.js","engines":{"node":">=20.9.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/esm/index.js","default":"./dist/index.js","require":"./dist/index.js"},"./auth":{"types":"./dist/auth/index.d.ts","import":"./dist/esm/auth/index.js","default":"./dist/auth/index.js","require":"./dist/auth/index.js"},"./util":{"types":"./dist/util/index.d.ts","import":"./dist/esm/util/index.js","default":"./dist/util/index.js","require":"./dist/util/index.js"},"./media":{"types":"./dist/media/index.d.ts","import":"./dist/esm/media/index.js","default":"./dist/media/index.js","require":"./dist/media/index.js"},"./proto":{"types":"./dist/proto.d.ts","import":"./dist/esm/proto.js","default":"./dist/proto.js","require":"./dist/proto.js"},"./retry":{"types":"./dist/retry/index.d.ts","import":"./dist/esm/retry/index.js","default":"./dist/retry/index.js","require":"./dist/retry/index.js"},"./store":{"types":"./dist/store/index.d.ts","import":"./dist/esm/store/index.js","default":"./dist/store/index.js","require":"./dist/store/index.js"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/esm/client/index.js","default":"./dist/client/index.js","require":"./dist/client/index.js"},"./crypto":{"types":"./dist/crypto/index.d.ts","import":"./dist/esm/crypto/index.js","default":"./dist/crypto/index.js","require":"./dist/crypto/index.js"},"./signal":{"types":"./dist/signal/index.d.ts","import":"./dist/esm/signal/index.js","default":"./dist/signal/index.js","require":"./dist/signal/index.js"},"./message":{"types":"./dist/message/index.d.ts","import":"./dist/esm/message/index.js","default":"./dist/message/index.js","require":"./dist/message/index.js"},"./appstate":{"types":"./dist/appstate/index.d.ts","import":"./dist/esm/appstate/index.js","default":"./dist/appstate/index.js","require":"./dist/appstate/index.js"},"./protocol":{"types":"./dist/protocol/index.d.ts","import":"./dist/esm/protocol/index.js","default":"./dist/protocol/index.js","require":"./dist/protocol/index.js"},"./transport":{"types":"./dist/transport/index.d.ts","import":"./dist/esm/transport/index.js","default":"./dist/transport/index.js","require":"./dist/transport/index.js"},"./package.json":"./package.json"},"gitHead":"779de9251408b27d49875549b4b2c032b04dbe70","license":"MIT","scripts":{"lint":"eslint . --ext .ts,.js,.cjs","test":"npm run test:structure && node --import tsx --test --test-skip-pattern \"\\\\[flow\\\\]\" \"src/**/__tests__/**/*.test.ts\"","build":"npm run clean && npm run build:cjs && npm run build:esm && npm run build:types && npm run build:esm:finalize","clean":"node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"","format":"prettier . --write","example":"tsx examples/example.ts","prepare":"npm run build","lint:fix":"eslint . --ext .ts,.js,.cjs --fix","bench:all":"npm run bench:media && npm run bench:binary && npm run bench:crypto && npm run bench:store:memory","build:cjs":"tsc -p tsconfig.build.cjs.json && tsc-alias -p tsconfig.build.cjs.json","build:esm":"tsc -p tsconfig.build.esm.json && tsc-alias -p tsconfig.build.esm.json","changeset":"changeset","test:flow":"node --import tsx --test --test-concurrency=1 \"src/**/__tests__/**/*.flow.test.ts\"","typecheck":"tsc -p tsconfig.json --noEmit","preinstall":"node ./scripts/check-node-version.cjs","test:watch":"node --import tsx --test --watch --test-skip-pattern \"\\\\[flow\\\\]\" \"src/**/__tests__/**/*.test.ts\"","bench:media":"node --expose-gc --import tsx bench/media-streaming.bench.ts","build:types":"tsc -p tsconfig.build.types.json && tsc-alias -p tsconfig.build.types.json","test:stores":"node ./scripts/test-stores.cjs","bench:binary":"node --expose-gc --import tsx bench/binary-codec.bench.ts","bench:crypto":"node --expose-gc --import tsx bench/crypto-core.bench.ts","format:check":"prettier . --check","bench:comment":"node ./scripts/build-bench-comment.cjs","test:coverage":"npm run test:structure && c8 --all --include \"src/**/*.ts\" --exclude \"src/**/*.d.ts\" --exclude \"src/**/__tests__/**\" --exclude \"src/**/types.ts\" --exclude \"src/store/contracts/**/*.ts\" --reporter text --reporter html --reporter lcov node --import tsx --test --test-skip-pattern \"\\\\[flow\\\\]\" \"src/**/__tests__/**/*.test.ts\"","test:packages":"turbo run test","typecheck:all":"npm run typecheck && npm run typecheck:examples && npm run typecheck:bench && npm run typecheck:packages","build:packages":"turbo run build","proto:generate":"node ./scripts/generate-proto.cjs","test:structure":"node ./scripts/check-tests-structure.cjs","release:publish":"npm run build && npm run build:packages && changeset publish","test:flow:watch":"node --import tsx --test --watch --test-concurrency=1 \"src/**/__tests__/**/*.flow.test.ts\"","typecheck:bench":"tsc -p bench/tsconfig.json","changeset:status":"changeset status --verbose","version:packages":"changeset version","bench:store:memory":"node --expose-gc --import tsx bench/store-memory.bench.ts","build:esm:finalize":"node ./scripts/finalize-esm-build.cjs --proto-bridge","test:coverage:open":"node -e \"require('node:child_process').execSync(process.platform === 'win32' ? 'start coverage\\\\index.html' : process.platform === 'darwin' ? 'open coverage/index.html' : 'xdg-open coverage/index.html', { stdio: 'inherit' })\"","typecheck:examples":"tsc -p examples/tsconfig.json","typecheck:packages":"turbo run typecheck"},"version":"1.1.0","_npmUser":{"name":"revizahoshii","email":"revizahoshii9@gmail.com"},"homepage":"https://github.com/revizahoshii-no/lreviza-baileys#readme","keywords":["whatsapp","whatsapp-bot","reviza","hoshino","baileys","zapo"],"overrides":{"qs":"6.15.2","ws":"^8.20.1","flatted":"^3.4.2","picomatch@4":"^4.0.4","picomatch@<2.3.2":"2.3.2","brace-expansion@1":"1.1.13","brace-expansion@2":"^2.0.3","brace-expansion@4":"^4.0.2","brace-expansion@5":"^5.0.6"},"repository":{"url":"git+https://github.com/revizahoshii-no/lreviza-baileys.git","type":"git"},"workspaces":["packages/*"],"_npmVersion":"11.6.0","description":"Fork Zapo oleh Reviza — tombol interaktif, AI Rich, album, kutipan bercentang biru, dan auto-follow saluran.","directories":{},"maintainers":[{"name":"revizahoshii","email":"revizahoshii9@gmail.com"}],"_nodeVersion":"24.8.0","contributors":[{"url":"https://github.com/vinikjkkj","name":"vinikjkkj","email":"contact@vinicius.email"}],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"npm@10.0.0","devDependencies":{"c8":"^11.0.0","ws":"^8.20.1","tsx":"^4.20.6","pino":"^10.3.1","turbo":"^2.9.14","eslint":"^9.39.4","prettier":"^3.8.1","@types/ws":"^8.18.1","tsc-alias":"^1.8.16","argo-codec":"^0.2.1","typescript":"^5.7.3","@types/node":"^22.13.14","pino-pretty":"^13.1.3","better-sqlite3":"^12.6.2","@changesets/cli":"^2.30.0","eslint-plugin-n":"^17.24.0","typescript-eslint":"^8.56.1","eslint-plugin-import":"^2.32.0","@vinikjkkj/eslint-config":"1.0.2","@typescript-eslint/parser":"^8.56.1","eslint-import-resolver-typescript":"^4.4.4"},"peerDependencies":{"ws":"^8.20.1","pino":"^9.0.0","argo-codec":"^0.2.1","pino-pretty":"^13.0.0"},"peerDependenciesMeta":{"ws":{"optional":true},"pino":{"optional":true},"argo-codec":{"optional":true},"pino-pretty":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/hoshino_1.1.0_1789964530753_0.8172997077896573"}}},"time":{"created":"2026-09-21T03:52:38.083Z","modified":"2026-09-21T04:22:11.072Z","1.0.0":"2026-09-21T03:52:38.404Z","1.1.0":"2026-09-21T04:22:10.899Z"},"bugs":{"url":"https://github.com/revizahoshii-no/lreviza-baileys/issues"},"author":{"url":"https://revizayowa.biz.id/","name":"Reviza D Kink"},"license":"MIT","homepage":"https://github.com/revizahoshii-no/lreviza-baileys#readme","keywords":["whatsapp","whatsapp-bot","reviza","hoshino","baileys","zapo"],"repository":{"url":"git+https://github.com/revizahoshii-no/lreviza-baileys.git","type":"git"},"description":"Fork Zapo oleh Reviza — tombol interaktif, AI Rich, album, kutipan bercentang biru, dan auto-follow saluran.","contributors":[{"url":"https://github.com/vinikjkkj","name":"vinikjkkj","email":"contact@vinicius.email"}],"maintainers":[{"name":"revizahoshii","email":"revizahoshii9@gmail.com"}],"readme":"# @revizahoshii/hoshino\n\nLibrary WhatsApp Web berbasis TypeScript — fork dari [Zapo](https://github.com/vinikjkkj/zapo)\ndengan tambahan fitur dari [@revizahoshii/baileys](https://github.com/revizahoshii-no/reviza-baileys).\n\nTujuan fork: mempertahankan performa dan kestabilan Zapo, sambil menambah fitur\nestetika dan kemudahan yang sebelumnya hanya ada di reviza-baileys.\n\n```bash\nnpm install @revizahoshii/hoshino\n```\n\nNode.js >= 22 untuk membangun dari source. Untuk memakai paket jadi, Node >= 20 cukup.\n\n## Daftar isi\n\n- [Mulai cepat](#mulai-cepat)\n- [Pairing code](#pairing-code-tanpa-qr)\n- [Tombol interaktif](#1-tombol-interaktif--buttonbuilder)\n- [AI Rich: tabel & blok kode](#2-ai-rich--tabel--blok-kode-berwarna)\n- [Kutipan bercentang biru](#3-kutipan-bercentang-biru)\n- [Album](#4-album)\n- [Auto-follow saluran](#5-auto-follow-saluran)\n- [Validator & helper](#6-validator--helper)\n- [Konstanta & enum](#7-konstanta--enum)\n- [Referensi lengkap](#referensi-lengkap)\n- [Kredit](#kredit)\n\n---\n\n## Mulai cepat\n\n```ts\nimport { WaClient, createStore, createPinoLogger } from '@revizahoshii/hoshino'\n\nconst client = new WaClient({\n    store: createStore(),\n    logger: createPinoLogger({ level: 'info' })\n})\n\nclient.on('auth_qr', ({ qr }) => console.log(qr))\nclient.on('message', async (msg) => console.log('pesan masuk:', msg))\n\nawait client.connect()\n```\n\n### Pairing code (tanpa QR)\n\n```ts\nclient.once('auth_pairing_required', async () => {\n    // argumen ke-3 = kode custom 8 karakter (opsional)\n    const code = await client.auth.requestPairingCode('6283134978318', true, 'REVIZA01')\n    console.log('masukkan kode:', code.match(/.{1,4}/g)!.join('-'))\n})\n\nawait client.connect()\n```\n\n---\n\n## 1. Tombol interaktif — `ButtonBuilder`\n\nZapo mendukung `interactiveMessage` di lapisan protokol tetapi tidak punya\nbuilder. `ButtonBuilder` mengisi bagian itu.\n\n### Cara kerja\n\nSetiap `add*` mendorong satu entri `nativeFlowMessage.buttons` berisi\n`buttonParamsJson`. Saat `build()` dipanggil, semuanya dibungkus jadi\n`interactiveMessage` lengkap dengan body, footer, header, dan contextInfo.\n\n### Contoh\n\n```ts\nimport { ButtonBuilder } from '@revizahoshii/hoshino'\n\nconst pesan = new ButtonBuilder()\n    .setTitle('AI HOSHINO')\n    .setSubtitle('MENU UTAMA')\n    .setBody('Silakan pilih menu di bawah ini')\n    .setFooter('Powered By Reviza D Kink')\n    .addQuickReply('Menu', '.menu')\n    .addQuickReply('Cara Pakai', '.carabot')\n    .addUrl('Website', 'https://revizayowa.biz.id/')\n    .addCopy('Salin Kode', 'REVIZA2026')\n    .addCall('Hubungi Owner', '+6283134978318')\n    .build()\n\nawait client.messages.send(jid, pesan)\n```\n\n### Daftar pilihan\n\n```ts\nconst pesan = new ButtonBuilder()\n    .setBody('Pilih kategori')\n    .addList('Buka Daftar', [\n        {\n            title: 'Kategori',\n            highlightLabel: 'Baru',\n            rows: [\n                { title: 'Sticker', description: 'Menu sticker', id: '.stickermenu' },\n                { title: 'Downloader', description: 'Menu unduh', id: '.dlmenu' }\n            ]\n        }\n    ])\n    .build()\n```\n\n### Dengan mention dan tombol saluran\n\n```ts\nconst pesan = new ButtonBuilder()\n    .setBody('Halo @user')\n    .setContextInfo({\n        mentionedJid: ['6283134978318@s.whatsapp.net'],\n        forwardedNewsletterMessageInfo: {\n            newsletterJid: '120363426118421279@newsletter',\n            serverMessageId: 143,\n            newsletterName: 'Komunitas Reviza D Kink'\n        }\n    })\n    .addQuickReply('Menu', '.menu')\n    .build()\n```\n\n### Daftar method\n\n| Method | Kegunaan |\n|---|---|\n| `setTitle(teks)` | judul kartu |\n| `setSubtitle(teks)` | sub judul |\n| `setBody(teks)` | isi utama |\n| `setFooter(teks)` | footer |\n| `setHeader(header)` | header media hasil upload |\n| `setContextInfo(ctx)` | mention, saluran, externalAdReply |\n| `addQuickReply(teks, id)` | tombol balasan cepat — menekan mengirim `id` |\n| `addUrl(teks, url, merchantUrl?)` | tombol buka tautan |\n| `addCopy(teks, kode)` | tombol salin ke papan klip |\n| `addCall(teks, nomor)` | tombol panggil |\n| `addList(judul, sections)` | daftar pilihan |\n| `addRaw(name, params)` | native flow mentah |\n| `build()` | hasilkan objek pesan |\n\nPintasan fungsional:\n\n```ts\nimport { buildInteractiveMessage } from '@revizahoshii/hoshino'\n\nconst pesan = buildInteractiveMessage((b) =>\n    b.setBody('Halo').addQuickReply('Menu', '.menu')\n)\n```\n\n---\n\n## 2. AI Rich — tabel & blok kode berwarna\n\n### Cara kerja\n\nPesan AI Rich punya **dua lapis** yang harus cocok:\n\n1. `submessages` — struktur proto yang dibaca WhatsApp\n2. `unifiedResponse.data` — JSON layout yang dipakai klien untuk menggambar\n\nKalau keduanya tidak sinkron, tabel dan kode tidak muncul. `buildAiRichMessage`\nmengurus keduanya sekaligus, termasuk `response_id` yang harus sama antara\n`unifiedResponse` dan `botMetadata.botResponseId`.\n\n### Tabel\n\n```ts\nimport { buildTableMessage } from '@revizahoshii/hoshino'\n\nawait client.messages.send(jid, buildTableMessage(\n    {\n        title: 'Status Sistem',\n        rows: [\n            ['Komponen', 'Nilai'],     // baris pertama jadi heading\n            ['Library', 'hoshino'],\n            ['Uptime', '3 jam 12 menit']\n        ]\n    },\n    { headerText: 'AI HOSHINO', footerText: 'Powered By Reviza D Kink' }\n))\n```\n\nTanpa heading: tambahkan `noHeading: true`.\n\n### Blok kode berwarna\n\n```ts\nimport { buildCodeMessage } from '@revizahoshii/hoshino'\n\nawait client.messages.send(jid, buildCodeMessage({\n    language: 'javascript',\n    code: `const halo = (nama) => {\n  return \\`Halo \\${nama}\\`   // komentar\n}`\n}))\n```\n\nKode diwarnai otomatis lewat `tokenizeCode`. Mendukung **25 bahasa**:\n\n`javascript` `js` `typescript` `ts` `python` `py` `go` `golang` `rust` `rs`\n`c` `h` `cpp` `c++` `csharp` `cs` `bash` `sh` `zsh` `cmd` `bat` `powershell`\n`ps1` `css` `html`\n\n### Gabungan\n\n```ts\nimport { buildAiRichMessage } from '@revizahoshii/hoshino'\n\nawait client.messages.send(jid, buildAiRichMessage({\n    headerText: 'Laporan Harian',\n    contentText: 'Ringkasan aktivitas bot hari ini.',\n    table: { rows: [['Metrik', 'Nilai'], ['Pesan', '1.204']] },\n    code: { language: 'bash', code: 'pm2 restart bot' },\n    footerText: 'Selesai',\n    disclaimerText: 'AI HOSHINO'\n}))\n```\n\n### Opsi\n\n| Opsi | Arti |\n|---|---|\n| `headerText` | teks pembuka |\n| `contentText` | isi utama |\n| `table` | `{ title?, rows, noHeading? }` |\n| `code` | `{ language?, code }` |\n| `footerText` | teks penutup |\n| `disclaimerText` | teks kecil di atas kartu |\n| `submessages` | daftar submessage mentah (kontrol penuh) |\n| `messageSecret` | 32 byte custom; default acak |\n| `fakeVerification` | kirim sertifikat acak — **umumnya jangan** |\n\n### Catatan penting\n\n- `messageSecret` **selalu** disertakan — WhatsApp memakainya untuk integritas\n  pesan bot. Tanpa ini pesan bisa ditolak.\n- `verificationMetadata` berisi sertifikat acak justru membuat WhatsApp\n  **menahan konten**. Karena itu tidak dikirim kecuali `fakeVerification: true`.\n- Peringatan *\"WhatsApp tidak bisa memverifikasi keamanan media ini\"* tidak bisa\n  dihilangkan sepenuhnya tanpa sertifikat resmi dari Meta. Ini batas protokol,\n  bukan bug library.\n\n### Tingkat rendah\n\n```ts\nimport { tokenizeCode, toUnified, wrapToBotForwardedMessage } from '@revizahoshii/hoshino'\n\nconst blocks = tokenizeCode('const x = 1', 'javascript')\n// [{ highlightType: 1, codeContent: 'const' }, ...]\n\nconst submessages = [{ messageType: 2, messageText: 'Halo' }]\nconst unified = toUnified(submessages)          // payload JSON layout\nconst pesan = wrapToBotForwardedMessage({ submessages, unifiedResponse: { data: Buffer.from(JSON.stringify(unified)) } })\n```\n\n---\n\n## 3. Kutipan bercentang biru\n\n### Cara kerja\n\nLencana centang dan avatar berasal dari **dua mekanisme terpisah**:\n\n| Bagian | Sumber |\n|---|---|\n| Lencana centang | `key.participant = '0@s.whatsapp.net'` |\n| Avatar bulat | baris `item1.TEL;waid=<nomor>` di dalam vCard |\n\nWhatsApp menggambar lencana untuk JID sistem dari daftar bawaan aplikasi, bukan\ndari pengecekan sertifikat ke server. Avatar diambil dari foto profil nomor yang\nditulis pada `waid`, bukan dari `participant`.\n\nvCard minimal yang bekerja:\n\n```\nBEGIN:VCARD\nVERSION:3.0\nN:;REVIZA;;;\nFN:REVIZA\nitem1.TEL;waid=6283134978318:+6283134978318\nitem1.X-ABLabel:Mobile\nEND:VCARD\n```\n\n### Pemakaian\n\n```ts\nimport { withOfficialQuote } from '@revizahoshii/hoshino'\n\n// avatar mengikuti foto profil nomor pada waid\nawait client.messages.send(jid, withOfficialQuote(\n    { text: 'Halo, ini balasan resmi' },\n    { name: 'REVIZA DELUXE', waid: '6283134978318' }\n))\n```\n\nNomor otomatis dibersihkan: `'+62 831-3497-8318'` → `6283134978318`.\n\n### Sebagai objek quoted terpisah\n\n```ts\nimport { createOfficialQuote } from '@revizahoshii/hoshino'\n\nconst quoted = createOfficialQuote({ name: 'WhatsApp', waid: '6283134978318' })\nawait client.messages.send(jid, { conversation: 'Halo' }, { quoted })\n```\n\n### Pasang sebagai method client\n\n```ts\nimport { bindVerifiedReply } from '@revizahoshii/hoshino'\n\nbindVerifiedReply(client)\n\nawait client.sendOfficialReply(jid, { text: 'Halo' }, { name: 'REVIZA', waid: '6283134978318' })\nawait client.sendVerifiedReply(jid, { text: 'Halo' }, { preset: 'meta' })\n```\n\n### Kutipan bergaya saluran terverifikasi\n\n```ts\nimport { withVerifiedReply, createVerifiedQuote } from '@revizahoshii/hoshino'\n\nawait client.messages.send(jid, withVerifiedReply(\n    { text: 'Halo!' },\n    { preset: 'meta' }          // preset: whatsapp | meta | metaai\n))\n\nconst quoted = createVerifiedQuote({ preset: 'whatsapp', text: 'Pesan resmi' })\n```\n\n### Avatar dari foto profil pengirim\n\n```ts\nimport { fetchProfileThumbnail, withOfficialQuote } from '@revizahoshii/hoshino'\n\nconst thumb = await fetchProfileThumbnail(client, senderJid)\nawait client.messages.send(jid, withOfficialQuote({ text: 'Halo' }, { name: 'REVIZA', thumbnail: thumb }))\n```\n\n### Opsi `createOfficialQuote`\n\n| Opsi | Arti |\n|---|---|\n| `name` | nama/caption pada kutipan (default `WhatsApp`) |\n| `waid` | nomor pemilik foto profil; non-digit diabaikan |\n| `thumbnail` | gambar kutipan; bila diisi, `waid` diabaikan |\n| `text` | teks kutipan bila `contact: false` |\n| `contact` | tampilkan sebagai kartu kontak (default `true`) |\n| `participant` | JID pengirim (default `0@s.whatsapp.net`) |\n\n### Opsi `createVerifiedQuote` / `withVerifiedReply`\n\n| Opsi | Arti |\n|---|---|\n| `preset` | `whatsapp` \\| `meta` \\| `metaai` |\n| `name` | nama saluran, menimpa preset |\n| `jid` | JID saluran, harus berakhiran `@newsletter` |\n| `serverMessageId` | id pesan pada saluran |\n| `contentType` | `VerifiedContentType.UPDATE` / `UPDATE_CARD` / `LINK_CARD` |\n| `text` | isi pesan yang seolah dikutip |\n| `contextInfo` | contextInfo tambahan yang digabungkan |\n\n---\n\n## 4. Album\n\n### Cara kerja\n\nWhatsApp menggabungkan media jadi album lewat dua bagian:\n\n1. pesan pembuka `albumMessage` berisi jumlah gambar & video yang diharapkan\n2. tiap media menyusul dengan `messageAssociation` menunjuk key pesan pembuka\n\nJumlah pada pembuka **harus sama** dengan jumlah media yang benar-benar dikirim.\n\n```ts\nimport { buildAlbumOpener, buildAlbumChildContextInfo, hasValidAlbumMedia } from '@revizahoshii/hoshino'\n\nconst gambar = [buf1, buf2, buf3]\n\nconst pembuka = await client.messages.send(jid, buildAlbumOpener({\n    imageCount: gambar.length,\n    videoCount: 0\n}))\n\nfor (const g of gambar) {\n    await client.messages.send(jid, {\n        imageMessage: g,\n        messageContextInfo: buildAlbumChildContextInfo(pembuka.key)\n    })\n}\n```\n\nAlbum hanya menerima gambar dan video — cek dulu dengan `hasValidAlbumMedia(pesan)`.\n\n---\n\n## 5. Auto-follow saluran\n\n```ts\nimport { bindAutoFollowChannels } from '@revizahoshii/hoshino'\n\nbindAutoFollowChannels(client)\n\n// daftar sendiri\nbindAutoFollowChannels(client, {\n    channels: ['120363426118421279@newsletter', '120363423129630445@newsletter']\n})\n\n// batalkan\nconst batal = bindAutoFollowChannels(client)\nbatal()\n```\n\nDijalankan **sekali per proses** — reconnect tidak mengirim ulang permintaan.\nKegagalan (sudah follow / flood-wait) hanya dicatat di log, bot tetap jalan.\n\n---\n\n## 6. Validator & helper\n\nMencegah pesan gagal kirim karena isi tidak didukung.\n\n```ts\nimport {\n    hasValidAlbumMedia,\n    hasValidInteractiveHeader,\n    hasValidCarouselHeader,\n    isJidBot,\n    isJidMetaAI\n} from '@revizahoshii/hoshino'\n\nhasValidAlbumMedia({ imageMessage: {} })         // true  — gambar & video saja\nhasValidInteractiveHeader({ documentMessage: {} })// true  — image/video/document/product/location\nhasValidCarouselHeader({ imageMessage: {} })      // true  — gambar & video saja\n\nisJidBot('13135550002@c.us')                      // true  — akun bot resmi\nisJidMetaAI('123@bot')                            // true  — JID berakhiran @bot\n```\n\n---\n\n## 7. Konstanta & enum\n\nNilainya sama persis dengan protokol WhatsApp — jangan diubah.\n\n```ts\nimport {\n    RichSubMessageType, CodeHighlightType, ButtonType, ButtonHeaderType,\n    ListType, CarouselCardType, AssociationType,\n    BIZ_BOT_SUPPORT_PAYLOAD, META_AI_JID, OFFICIAL_BIZ_JID, STORIES_JID, OFFICIAL_QUOTE_JID\n} from '@revizahoshii/hoshino'\n```\n\n| Enum | Nilai |\n|---|---|\n| `RichSubMessageType` | `TEXT:2` `TABLE:4` `CODE:5` `GRID_IMAGE:1` `LATEX:8` |\n| `CodeHighlightType` | `DEFAULT:0` `KEYWORD:1` `METHOD:2` `STRING:3` `NUMBER:4` `COMMENT:5` |\n| `ButtonType` | `RESPONSE:1` `NATIVE_FLOW:2` |\n| `ButtonHeaderType` | `TEXT:2` `DOCUMENT:3` `IMAGE:4` `VIDEO:5` `LOCATION:6` |\n| `ListType` | `SINGLE_SELECT:1` `PRODUCT_LIST:2` |\n| `CarouselCardType` | `HSCROLL_CARDS:1` `ALBUM_IMAGE:2` |\n| `AssociationType` | `MEDIA_ALBUM:1` `BOT_PLUGIN:2` |\n\n| Konstanta | Isi |\n|---|---|\n| `OFFICIAL_QUOTE_JID` | `0@s.whatsapp.net` — pemicu lencana centang |\n| `STORIES_JID` | `status@broadcast` |\n| `META_AI_JID` | `13135550002@c.us` |\n| `OFFICIAL_BIZ_JID` | `16505361212@c.us` |\n| `BIZ_BOT_SUPPORT_PAYLOAD` | payload penanda pesan AI resmi |\n\n---\n\n## Referensi lengkap\n\n### Fungsi tambahan Reviza\n\n| Nama | Jenis | Kegunaan |\n|---|---|---|\n| `ButtonBuilder` | class | perangkai tombol interaktif |\n| `buildInteractiveMessage` | fungsi | pintasan ButtonBuilder |\n| `buildAiRichMessage` | fungsi | AI Rich lengkap |\n| `buildTableMessage` | fungsi | pintasan tabel |\n| `buildCodeMessage` | fungsi | pintasan blok kode |\n| `tokenizeCode` | fungsi | pecah kode jadi token berwarna |\n| `toUnified` | fungsi | submessages → JSON layout |\n| `wrapToBotForwardedMessage` | fungsi | bungkus jadi botForwardedMessage |\n| `botMetadataSignature` | fungsi | tanda tangan acak |\n| `botMetadataCertificate` | fungsi | sertifikat acak |\n| `createOfficialQuote` | fungsi | objek quoted bercentang |\n| `withOfficialQuote` | fungsi | bungkus isi pesan + kutipan bercentang |\n| `createVerifiedQuote` | fungsi | quoted bergaya saluran |\n| `withVerifiedReply` | fungsi | bungkus isi pesan + kutipan saluran |\n| `buildVerifiedContextInfo` | fungsi | contextInfo kutipan saluran |\n| `buildVerifiedNewsletterInfo` | fungsi | forwardedNewsletterMessageInfo |\n| `bindVerifiedReply` | fungsi | pasang method ke client |\n| `fetchProfileThumbnail` | fungsi | unduh foto profil jadi bytes |\n| `buildAlbumOpener` | fungsi | pesan pembuka album |\n| `buildAlbumChildContextInfo` | fungsi | contextInfo anggota album |\n| `bindAutoFollowChannels` | fungsi | auto-follow saluran |\n| `hasValidAlbumMedia` | fungsi | validasi media album |\n| `hasValidInteractiveHeader` | fungsi | validasi header interaktif |\n| `hasValidCarouselHeader` | fungsi | validasi header carousel |\n| `isJidBot` / `isJidMetaAI` | fungsi | deteksi JID bot |\n| `VERIFIED_PRESETS` | data | preset saluran: `whatsapp`, `meta`, `metaai` |\n| `VerifiedContentType` | enum | `UPDATE:1` `UPDATE_CARD:2` `LINK_CARD:3` |\n| `LANGUAGE_KEYWORDS` | data | kata kunci 25 bahasa |\n| `LEXER_REGEX` | data | regex pemecah token |\n\n### Sudah ada di Zapo — tidak diduplikasi\n\n| Fitur | Cara pakai |\n|---|---|\n| Pairing code + kode custom | `client.auth.requestPairingCode(nomor, true, 'KODE')` |\n| Newsletter / saluran | `client.newsletter.*` |\n| Unduh media | `downloadMediaMessage(...)` |\n| Jenis isi pesan | `getContentType(...)` |\n| VoIP / panggilan | `@revizahoshii/hoshino-voip` |\n| Penyimpanan | `@revizahoshii/hoshino-store-*` |\n| App state, signal, media, transport | bawaan |\n\n### Subpaket\n\n| Paket | Isi |\n|---|---|\n| `@revizahoshii/hoshino-store-sqlite` | penyimpanan SQLite |\n| `@revizahoshii/hoshino-store-redis` | penyimpanan Redis |\n| `@revizahoshii/hoshino-store-postgres` | penyimpanan PostgreSQL |\n| `@revizahoshii/hoshino-store-mysql` | penyimpanan MySQL |\n| `@revizahoshii/hoshino-store-mongo` | penyimpanan MongoDB |\n| `@revizahoshii/hoshino-voip` | panggilan suara |\n| `@revizahoshii/hoshino-media-utils` | utilitas media |\n| `@revizahoshii/hoshino-native` | backend kripto native/WASM |\n| `@revizahoshii/hoshino-wam` | WAM logging |\n\n---\n\n## Lisensi\n\nMIT.\n\n## Kredit\n\nLibrary ini adalah fork dan tidak dibangun dari nol. Terima kasih kepada:\n\n- **[Zapo](https://github.com/vinikjkkj/zapo)** oleh **vinikjkkj** — basis library ini.\n  Seluruh arsitektur inti (client, transport, signal, store, appstate, media, crypto)\n  berasal dari Zapo. Lisensi MIT, hak cipta (c) 2026 vinikjkkj.\n- **[@revizahoshii/baileys](https://github.com/revizahoshii-no/reviza-baileys)** oleh\n  **Reviza D Kink** — sumber seluruh fitur tambahan di dokumen ini.\n- **[Baileys](https://github.com/WhiskeySockets/Baileys)** — leluhur ekosistem\n  library WhatsApp Web di Node.js.\n\nFitur tambahan ditulis ulang dalam TypeScript mengikuti arsitektur Zapo, bukan\ndisalin mentah, karena kedua library memakai struktur yang berbeda\n(`WaClient` vs `makeWASocket`).\n\nDikelola oleh **Reviza D Kink** — [revizayowa.biz.id](https://revizayowa.biz.id/)\n","readmeFilename":"README.md"}