Skip to content

feat(agent-bot): render Typebot choice blocks as interactive buttons - #86

Merged
DavidsonGomes merged 2 commits into
evolution-foundation:developfrom
miltonsosa-info-unlp:feat/typebot-interactive-buttons
May 20, 2026
Merged

feat(agent-bot): render Typebot choice blocks as interactive buttons#86
DavidsonGomes merged 2 commits into
evolution-foundation:developfrom
miltonsosa-info-unlp:feat/typebot-interactive-buttons

Conversation

@miltonsosa-info-unlp

@miltonsosa-info-unlp miltonsosa-info-unlp commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Handles structured input_select messages from the bot runtime to create
interactive button/list messages in the CRM. The chat widget renders them as
clickable buttons and WhatsApp receives them as interactive list messages.

Dependencies

Requires evolution-foundation/evo-bot-runtime#2 to be merged first
(provides content_type and items in the postback payload).

Changes

app/controllers/webhooks/bot_runtime_controller.rb

  • Reads content_type and items params from the runtime postback.
  • Builds content_attributes: {items: [...]} and passes it to the service
    when content_type == "input_select".
  • Falls back to the existing plain-text flow when params are absent.

app/services/agent_bots/response_processor.rb

  • extract_text_from_artifacts now returns {text:, select:} instead of a
    plain string, scanning all artifact parts for both text and select types.

app/services/agent_bots/message_creator.rb

  • Creates messages with content_type: "input_select" and
    content_attributes: {items: [...]} when structured select data is present.

app/services/whatsapp/providers/base_service.rb

  • New interactive_body_text(message) helper: strips trailing numbered-list
    lines (e.g. 1. Option) from the message body before building WhatsApp
    button/list payloads, avoiding duplication of options in both text and
    buttons.
  • create_button_payload and create_list_payload use the new helper.

app/services/whatsapp/providers/evolution_go_service.rb

  • Overrides interactive_body_text with the same stripping logic.
  • send_interactive_message uses it for both button and list paths.

app/services/whatsapp/providers/evolution_service.rb

  • Same changes as evolution_go_service.rb for provider consistency.

Notes

  • db/schema.rb is intentionally excluded (auto-generated file).
  • Backward-compatible: when no content_type is provided by the runtime, the
    controller falls back to the existing plain-text message creation flow.

Summary by Sourcery

Handle structured select responses from the bot runtime by creating input_select messages with selectable items and improving WhatsApp interactive message bodies.

New Features:

  • Support creating input_select messages with structured items from bot runtime postbacks and agent bot responses.
  • Enable WhatsApp providers to send interactive button and list messages using cleaned-up body text derived from bot messages.

Enhancements:

  • Refine agent bot response processing to extract both text and select artifacts from bot responses and disable text segmentation when select items are present.
  • Extend message creation logic to propagate content_type and content_attributes, merging them with reply metadata when applicable.
  • Normalize WhatsApp interactive message body text by stripping trailing numbered option lines to avoid duplicating options in both text and buttons/lists.

- bot_runtime_controller: read content_type + items from postback payload;
  build content_attributes when content_type is input_select
- response_processor: extract_text_from_artifacts returns {text:, select:}
  hash, scanning all artifact parts for both text and select types
- message_creator: create messages with content_type: input_select and
  content_attributes: {items:[...]} when structured select data is present
- base_service: add interactive_body_text helper that strips trailing numbered
  list lines from message body before sending WhatsApp interactive payloads
- evolution_go_service: use interactive_body_text for button and list payloads
- evolution_service: use interactive_body_text for button and list payloads

Backward-compatible: falls back to plain text when no structured data present
@sourcery-ai

sourcery-ai Bot commented May 19, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds end-to-end support for structured "input_select" bot responses, wiring bot runtime payloads through the agent bot pipeline into CRM messages and WhatsApp interactive list/button payloads, while preserving backward-compatible text behavior.

Sequence diagram for input_select bot response to WhatsApp interactive message

sequenceDiagram
  participant BotRuntime
  participant BotRuntimeController
  participant MessageCreator as AgentBots_MessageCreator
  participant Message as Message_Record
  participant WhatsAppProvider
  participant WhatsAppAPI

  BotRuntime->>BotRuntimeController: postback(content, content_type=input_select, items)
  BotRuntimeController->>BotRuntimeController: build content_attributes {items}
  BotRuntimeController->>MessageCreator: create_bot_reply(content, conversation, content_type=input_select, content_attributes)
  MessageCreator->>MessageCreator: create_message_with_fallback(..., content_type, content_attributes)
  MessageCreator->>Message: create_direct_message(..., content_type=input_select, merged content_attributes)

  WhatsAppProvider->>WhatsAppProvider: send_interactive_message(phone_number, Message)
  WhatsAppProvider->>WhatsAppProvider: interactive_body_text(Message)
  WhatsAppProvider->>WhatsAppAPI: send_button_message or send_list_message(..., items, pruned body)
Loading

File-Level Changes

Change Details Files
Wire bot-runtime postback content_type/items into AgentBot message creation for input_select flows.
  • Read content_type and items from bot runtime webhook params.
  • Normalize items into {title, value} hashes for input_select messages.
  • Pass computed content_type and content_attributes into AgentBots::MessageCreator when creating the message.
app/controllers/webhooks/bot_runtime_controller.rb
Extend agent bot response processing and message creation to support select artifacts and generic content types/attributes.
  • Change artifact extraction to scan all artifacts and return a hash containing both first text and first select parts.
  • Skip text segmentation when select items are present to keep interactive messages in a single message.
  • Update MessageCreator API to accept content_type and content_attributes, propagating them through both direct and builder-based creation paths.
  • Merge existing reply-related content_attributes with any structured attributes, ensuring both survive.
app/services/agent_bots/response_processor.rb
app/services/agent_bots/message_creator.rb
Adjust WhatsApp provider base to derive cleaner body text for interactive messages and reuse it in button/list payloads.
  • Introduce interactive_body_text helper that converts HTML to WhatsApp text then strips trailing numbered-list option lines.
  • Use interactive_body_text instead of raw message content when building button and list payloads to avoid duplicated options.
app/services/whatsapp/providers/base_service.rb
Align Evolution WhatsApp providers to use interactive_body_text for interactive messages.
  • Implement interactive_body_text in evolution_go_service and evolution_service with the same numbered-list stripping logic as base.
  • Use interactive_body_text in send_button_message and send_list_message so interactive templates no longer repeat options in both text and buttons.
app/services/whatsapp/providers/evolution_go_service.rb
app/services/whatsapp/providers/evolution_service.rb

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The interactive_body_text implementation is duplicated in BaseService, EvolutionGoService, and EvolutionService; consider extracting it to a single shared implementation (e.g., only in BaseService or a concern) and reusing it to avoid divergence.
  • In BotRuntimeController#postback, you currently accept any content_type coming from params; if only a small set is supported (e.g., text, input_select), consider validating or normalizing this value before passing it through to MessageCreator to avoid unexpected message shapes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `interactive_body_text` implementation is duplicated in `BaseService`, `EvolutionGoService`, and `EvolutionService`; consider extracting it to a single shared implementation (e.g., only in `BaseService` or a concern) and reusing it to avoid divergence.
- In `BotRuntimeController#postback`, you currently accept any `content_type` coming from params; if only a small set is supported (e.g., `text`, `input_select`), consider validating or normalizing this value before passing it through to `MessageCreator` to avoid unexpected message shapes.

## Individual Comments

### Comment 1
<location path="app/services/whatsapp/providers/base_service.rb" line_range="117-126" />
<code_context>
     end
   end

+  def interactive_body_text(message)
+    content = html_to_whatsapp(message.content.to_s)
+    return content if content.blank?
+
+    lines = content.split("\n")
+    removed_any = false
+
+    while lines.any? && lines.last.strip.match?(/^\d+\.\s+\S/)
+      lines.pop
+      removed_any = true
+    end
+
+    pruned = lines.join("\n").strip
+    return content unless removed_any
+    pruned.presence || content
+  end
+
</code_context>
<issue_to_address>
**suggestion:** This `interactive_body_text` helper is duplicated across multiple providers and could be centralized.

This implementation matches the versions in `evolution_go_service` and `evolution_service`. If all WhatsApp providers are meant to behave the same, please extract this into a shared module or superclass to avoid duplication and keep behavior consistent.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread app/services/whatsapp/providers/base_service.rb
…asses

Both EvolutionGoService and EvolutionService overrode interactive_body_text
with an implementation identical to the one in BaseService. Since both classes
already inherit from BaseService, the overrides are redundant. Remove them and
rely on the single definition in the superclass.
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.

3 participants