Skip to content

add support for Buttons and Lists (CTA limited)#119

Merged
clairton merged 9 commits intoclairton:mainfrom
rodrigo-gmengue:buttonsAdjust
Nov 17, 2025
Merged

add support for Buttons and Lists (CTA limited)#119
clairton merged 9 commits intoclairton:mainfrom
rodrigo-gmengue:buttonsAdjust

Conversation

@rodrigo-gmengue
Copy link
Collaborator

@rodrigo-gmengue rodrigo-gmengue commented Nov 17, 2025

This application adds support for buttons via Whaileys.
There's the UNOAPI_NATIVE_FLOW_BUTTONS environment variable for those interested in testing buttons with functionality (CTA) (currently only functional on Android) or by default, quick reply (compatible with web/Android/iOS).

Summary by CodeRabbit

  • New Features

    • Enhanced interactive message handling with support for native WhatsApp flow buttons and media headers.
  • Bug Fixes

    • Improved credential persistence by awaiting completion and ensuring immediate save on initial validation.
  • Chores

    • Bumped version to 2.5.19.
    • Updated baileys dependency to 6.3.10.

@coderabbitai
Copy link

coderabbitai bot commented Nov 17, 2025

Walkthrough

This change introduces environment variable-driven mode configuration across npm scripts, improves credential persistence through await enforcement and return value propagation, enhances interactive message handling with native WhatsApp flow support and fallback options, and adds dependency injection for contact handling in the web module.

Changes

Cohort / File(s) Summary
Configuration & Gitignore
.gitignore, package.json
Adds .vscode/ to ignored paths. Bumps version to 2.5.19 and updates baileys to 6.3.10. Prefixes all npm scripts with UNOAPI_MODE environment variable (simple, web, waker, standalone, worker, bulker, broker, bridge, cloud) for mode-specific initialization.
Environment & Defaults
src/cloud.ts, src/defaults.ts
Sets UNOAPI_CLOUD=true at startup. Adds UNOAPI_NATIVE_FLOW_BUTTONS boolean constant, defaulting to false unless env var is explicitly set to 'false' (inverted logic).
Credential Persistence Services
src/services/auth_state.ts, src/services/session_redis.ts, src/services/socket.ts
Makes saveCreds() await writeData() completion. Updates writeData() in session_redis to return setAuth() result. Adds immediate saveCreds() invocation in firstSaveCreds() when phone numbers match.
Interactive Message Transformation
src/services/transformer.ts
Enhances toBaileysMessageContent() with new interactive header/media handling path. Adds branching logic for sections vs. buttons-based payloads. Conditionally uses native WhatsApp interactive messages or fallback classic buttonsMessage based on UNOAPI_NATIVE_FLOW_BUTTONS flag with try/catch protected filename/mimetype detection.
Application Bootstrap & DI
src/web.ts
Imports contact implementations (ContactBaileys, ContactDummy), routing middleware, and listener classes. Adds mode-based branching to select concrete or dummy contact handler. Extends App constructor signature with injectRouteDummy and contactType! parameters.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application<br/>(via npm script)
    participant Env as Environment<br/>Variables
    participant Web as web.ts<br/>Bootstrap
    participant Contact as Contact<br/>Handler
    participant Transformer as transformer.ts<br/>(Message Flow)
    participant Baileys as Baileys

    App->>Env: UNOAPI_MODE=web
    Env-->>Web: Set MODE & detect cloud
    Web->>Contact: Load ContactBaileys<br/>or ContactDummy
    Contact-->>Web: contactType selected
    
    Note over Web,Contact: App initialized with DI
    
    Transformer->>Env: Check UNOAPI_NATIVE_FLOW_BUTTONS
    alt Native Flow Enabled
        Transformer->>Transformer: Build interactive message<br/>with buttons array
        Transformer->>Baileys: Send native interactive
    else Native Flow Disabled (default)
        Transformer->>Transformer: Build classic<br/>buttonsMessage
        Transformer->>Baileys: Send legacy format
    end
Loading
sequenceDiagram
    participant Socket as socket.ts
    participant Auth as auth_state.ts
    participant Redis as session_redis.ts
    participant Storage as Persistent<br/>Storage

    Socket->>Socket: firstSaveCreds() triggered
    alt Phone number matches config
        Socket->>Auth: saveCreds()
        Auth->>Redis: writeData()
        Redis->>Storage: setAuth(...)
        Storage-->>Redis: result
        Redis-->>Auth: return result
        Auth->>Auth: await completion
        Auth-->>Socket: ✓ Creds persisted
    else Number mismatch
        Socket-->>Socket: Skip persistence
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

  • src/services/transformer.ts: Most complex change with multiple branching paths (native vs. classic flow), new media handling logic, and nested conditional structures—requires detailed understanding of interactive message formats and baileys compatibility.
  • src/web.ts: Dependency injection changes and constructor signature expansion need validation of parameter ordering and impact on app initialization flow.
  • package.json: Repetitive but systematic; verify all UNOAPI_MODE values are correctly mapped to their respective entry points.
  • Credential persistence chain (auth_state.ts, session_redis.ts, socket.ts): Validate that await/return value propagation doesn't introduce race conditions or unexpected behavior in credential save workflows.

Possibly related PRs

  • Pairing code #97: Modifies overlapping files (src/defaults.ts and src/services/socket.ts) with related configuration and socket connection initialization logic.

Poem

🐰 Whiskers twitching with delight
Environment modes hop through the night,
Native buttons bloom, or fade to grey,
Credentials saved—no more delay,
The web boots up with DI's might! 🚀

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'add support for Buttons and Lists (CTA limited)' accurately reflects the main changes in the PR, which include implementing button and list message support with CTA functionality limitations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

📝 Customizable high-level summaries are now available!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  - Provide custom instructions to shape the summary (bullet lists, tables, contributor stats, etc.).
  - Use `high_level_summary_in_walkthrough` to move the summary from the description to the walkthrough section.

  Example:

  > "Create a concise high-level summary as a bullet-point list. Then include a Markdown table showing lines added and removed by each contributing author."

  Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/web.ts (2)

62-68: Consider validating UNOAPI_MODE to prevent silent fallback.

The current logic defaults to ContactDummy for any non-'cloud' value of UNOAPI_MODE. If an invalid or misspelled mode is provided, the application will silently use the dummy implementation rather than failing fast.

Consider adding validation:

 let contactType: Contact
-if (process.env.UNOAPI_MODE == 'cloud') {
+const validModes = ['cloud', 'web', 'simple', 'standalone', 'worker', 'bulker', 'broker', 'bridge', 'waker']
+if (!validModes.includes(process.env.UNOAPI_MODE || '')) {
+  throw new Error(`Invalid UNOAPI_MODE: ${process.env.UNOAPI_MODE}. Must be one of: ${validModes.join(', ')}`)
+}
+if (process.env.UNOAPI_MODE === 'cloud') {
   let listener: Listener = new ListenerBaileys(outgoing, broadcast, getConfigRedis)
   contactType = new ContactBaileys(listener, getConfigRedis, getClientBaileys, onNewLogin)
 } else {
   contactType = new ContactDummy()
 }

64-64: Review listener variable scope.

The listener variable is block-scoped and only used for the ContactBaileys constructor. Consider whether it should be declared in a wider scope for potential future use, or if this scoping is intentional.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd89fa9 and f68e543.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (9)
  • .gitignore (1 hunks)
  • package.json (3 hunks)
  • src/cloud.ts (1 hunks)
  • src/defaults.ts (1 hunks)
  • src/services/auth_state.ts (1 hunks)
  • src/services/session_redis.ts (1 hunks)
  • src/services/socket.ts (1 hunks)
  • src/services/transformer.ts (2 hunks)
  • src/web.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/services/auth_state.ts (2)
src/services/session.ts (1)
  • writeData (1-3)
src/services/session_file.ts (2)
  • key (49-59)
  • phone (6-92)
src/services/session_redis.ts (1)
src/services/redis.ts (1)
  • setAuth (348-352)
src/web.ts (6)
src/services/client.ts (1)
  • Contact (10-14)
src/services/listener.ts (1)
  • Listener (3-5)
src/services/contact_baileys.ts (1)
  • ContactBaileys (8-53)
src/services/client_baileys.ts (2)
  • getClientBaileys (49-85)
  • logout (408-412)
src/services/contact_dummy.ts (1)
  • ContactDummy (3-7)
src/app.ts (1)
  • App (21-101)
src/services/transformer.ts (1)
src/defaults.ts (1)
  • UNOAPI_NATIVE_FLOW_BUTTONS (30-31)
🪛 Biome (2.1.2)
src/services/transformer.ts

[error] 233-233: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 234-234: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 235-235: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 236-236: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 237-237: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (9)
src/services/auth_state.ts (1)

47-50: saveCreds awaiting persistence looks correct

Awaiting writeData('', creds) aligns saveCreds with the updated session backends and ensures callers only proceed after credential persistence completes.

.gitignore (1)

12-13: Ignoring local tooling/config directories is sensible

Adding .vscode/ (and keeping .yarnrc.yml) in .gitignore avoids committing local/editor configuration, which is standard practice.

src/services/session_redis.ts (1)

10-18: writeData now correctly propagates Redis write completion

Returning setAuth(getBase(key), data, …) from the async writeData ensures callers truly await the Redis persistence and see any failures.

src/services/socket.ts (1)

151-165: Immediate first credential save is a good safety improvement

On first valid creds.update, switching currentSaveCreds to saveCreds and immediately await‑ing it ensures the initial session state is durably stored before further use. The added await is localized and should be safe.

If you notice any unexpected latency spikes on initial login, consider logging timing around saveCreds() to confirm Redis/storage performance.

src/services/transformer.ts (2)

7-7: New UNOAPI_NATIVE_FLOW_BUTTONS import is consistent with usage

Importing UNOAPI_NATIVE_FLOW_BUTTONS here matches its use to branch between legacy quick‑reply buttons and native flow interactive messages. Once the flag logic in defaults.ts is corrected, this wiring will behave as intended.


230-456: Wrap case 'interactive': in block braces to satisfy noSwitchDeclarations lint rule (line 229)

Verified that case 'interactive': at line 229 declares multiple const bindings without block scope. Biome's noSwitchDeclarations rule (enabled via "recommended": true in biome.json) flags this pattern. The suggested fix—wrapping the case body in braces—is correct and will resolve the lint error without any runtime changes.

-    case 'interactive':
+    case 'interactive': {
       // Build payload according to whaileys / baileys interactive format
       // ... rest of case body
       }
       break
+    }
package.json (1)

18-44: Verify cross-platform compatibility of inline environment variables.

The inline environment variable syntax (e.g., UNOAPI_MODE=simple) works on Unix-like systems but will fail on Windows CMD/PowerShell. Consider using cross-env package for cross-platform compatibility.

For Windows users, these scripts will fail. If Windows support is required:

"dependencies": {
  "cross-env": "^7.0.3"
}

Then update scripts like:

"dev": "cross-env UNOAPI_MODE=simple tsx watch src/index.ts",
src/cloud.ts (1)

4-4: LGTM: Proper environment variable initialization order.

Setting UNOAPI_CLOUD after dotenv.config() and before importing dependent modules ensures the flag is available when needed.

src/web.ts (1)

31-40: LGTM: Clean dependency injection pattern for mode-based contact handling.

The conditional wiring of Contact implementation based on UNOAPI_MODE follows good DI principles and enables testability through the dummy implementation.

Also applies to: 62-68, 70-70

"audio2textjs": "^1.0.5",
"awesome-phonenumber": "^6.8.0",
"baileys": "npm:whaileys@6.3.9",
"baileys": "npm:whaileys@6.3.10",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the Baileys library version for security and compatibility.

The update from whaileys@6.3.9 to whaileys@6.3.10 should be verified for security advisories and breaking changes, especially since this PR adds button support functionality.


🌐 Web query:

What are the changes and security advisories for whaileys version 6.3.10?

💡 Result:

I can find that — do you mean the Zoom client (version 6.3.10) or a different project/package named "whaileys"? If you mean Zoom 6.3.10 I can summarize its changes and security advisories now.


The initial search was inconclusive. Let me try a more specific search for the whaileys package:

Let me try a more direct search for the whaileys package:

Let me search for the Baileys library directly, as whaileys appears to be a fork or wrapper:

Unable to locate package whaileys@6.3.10.

The package "whaileys" does not exist on the public npm registry, and Baileys only has versions up to 6.7.10, with 6.3.0 and 6.3.1 available — there is no 6.3.10 version. The npm alias in package.json attempts to map baileys to npm:whaileys@6.3.10, which would fail during installation. Verify that the intended package name and version are correct.

🤖 Prompt for AI Agents
In package.json around line 87, the dependency entry "baileys":
"npm:whaileys@6.3.10" references a non‑existent package/version and will fail
install; update this entry to the correct package and version (either point to
the real Baileys version, e.g. "baileys": "6.7.10", or remove the npm alias and
use the intended package name/version), or if you intentionally depended on a
private fork, replace the alias with a valid install source (git URL or correct
npm package and version); after updating, run npm install to verify the
dependency resolves.

Comment on lines +30 to +31
export const UNOAPI_NATIVE_FLOW_BUTTONS: boolean =
process.env.UNOAPI_NATIVE_FLOW_BUTTONS === _undefined ? false : process.env.UNOAPI_NATIVE_FLOW_BUTTONS == 'false'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

UNOAPI_NATIVE_FLOW_BUTTONS logic is inverted

The flag currently becomes true only when UNOAPI_NATIVE_FLOW_BUTTONS === 'false', meaning UNOAPI_NATIVE_FLOW_BUTTONS=false in the environment enables native buttons, while UNOAPI_NATIVE_FLOW_BUTTONS=true disables them. This is the opposite of the intended “enable native flow with CTA” behavior and will be very confusing operationally.

Switch the logic so that 'true' enables native flow and the default remains false:

-export const UNOAPI_NATIVE_FLOW_BUTTONS: boolean =
-  process.env.UNOAPI_NATIVE_FLOW_BUTTONS === _undefined ? false : process.env.UNOAPI_NATIVE_FLOW_BUTTONS == 'false'
+export const UNOAPI_NATIVE_FLOW_BUTTONS: boolean =
+  process.env.UNOAPI_NATIVE_FLOW_BUTTONS === _undefined
+    ? false
+    : process.env.UNOAPI_NATIVE_FLOW_BUTTONS == 'true'

That way:

  • Not set → false (fallback to classic quick‑reply buttons)
  • "true"true (enable native flow/CTA buttons)
  • Anything else (including "false") → false.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const UNOAPI_NATIVE_FLOW_BUTTONS: boolean =
process.env.UNOAPI_NATIVE_FLOW_BUTTONS === _undefined ? false : process.env.UNOAPI_NATIVE_FLOW_BUTTONS == 'false'
export const UNOAPI_NATIVE_FLOW_BUTTONS: boolean =
process.env.UNOAPI_NATIVE_FLOW_BUTTONS === _undefined
? false
: process.env.UNOAPI_NATIVE_FLOW_BUTTONS == 'true'
🤖 Prompt for AI Agents
In src/defaults.ts around lines 30-31, the environment flag logic is inverted:
it currently treats the string "false" as enabling native flow. Change the
expression so UNOAPI_NATIVE_FLOW_BUTTONS is true only when
process.env.UNOAPI_NATIVE_FLOW_BUTTONS === 'true' (optionally normalize case),
and otherwise false; ensure undefined or any other value yields false and remove
reliance on _undefined.

@clairton clairton merged commit 3989988 into clairton:main Nov 17, 2025
1 check passed
@coderabbitai coderabbitai bot mentioned this pull request Dec 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants