action_bot_cr

Version, currently 0.1.02 versions

github.com/OrelSokolov/action_bot.cr

No description declared in shard.yml.

0 stars
0 dependents
License: not declared

Installation

# Add this to your shard.yml
dependencies:
  action_bot_cr:
    github: OrelSokolov/action_bot.cr
    version: ~> 0.1.0

Then run:

shards install

shard.yml

Crystal
>= 1.21.0

Dependencies

Runtime Dependencies

README

action_bot.cr

Bots as first-class framework citizens: a unified message contract over messenger platforms (Telegram Bot API, WhatsApp Cloud API, web/SSE) — an original CRails component, not a port: Rails has no bot framework, so there is no Ruby original to mirror. The transport layer follows the ActionMailer/ActionMailbox idiom (ActionBot::Base DSL + adapters); the AI-agent loop (Brain/Tools) is deliberately left to a separate ActiveAgent shard that plugs in via reply.stream without touching the bot contract. Built oracle-first per the project rules: Telegram inbound fixtures are validated by the real telegram-bot-ruby 2.8.1 (vendored in oracle/vendor), WhatsApp fixtures follow the Cloud API webhook schema (whatsapp_sdk field layout), and outbound payloads are byte-identical to the documented API request bodies. Suite: 39 oracle cases + 9 integration specs (19/19).

⚠️ EXPERIMENTAL FRAMEWORK, DO NOT USE IN PRODUCTION YET.

Installation

dependencies:
  action_bot_cr:
    github: OrelSokolov/action_bot.cr
shards install

Depends on active_support.cr and active_job.cr.

Usage

class SupportBot < ActionBot::Base
  adapter "telegram"
  command "start" { |m| reply.text "Hi!" }
  message { |m| reply.text "You said: #{m.text}" }
  callback_query { |q| reply.text "Selected: #{q.data}" }
end

# dispatch a normalized Update (from webhook, poller or specs)
SupportBot.new.dispatch(update)

# streaming replies (editMessageText on Telegram, real SSE on web)
reply.stream do |emit|
  chunks.each { |c| emit << c }
end

Adapters are configured via ENV (TELEGRAM_BOT_TOKEN, TELEGRAM_SECRET_TOKEN, WHATSAPP_ACCESS_TOKEN, WHATSAPP_PHONE_NUMBER_ID, WHATSAPP_APP_SECRET, WHATSAPP_VERIFY_TOKEN; *_API_BASE / TEST_* vars point them at fakes). HTTP endpoints come with the shard: POST /bot/webhooks/:platform/:bot (secret-checked, WhatsApp hub.challenge GET) and GET /bot/sse/:bot (chunks → data: events, final → event: done). Railties provide crails generate bot NAME --platform=…, config/bots.yml and crails bot:poll (getUpdates poller).

Feature comparison

Rails has no bot framework, so the table compares with the de-facto Ruby stack this shard replaces: telegram-bot-ruby 2.8.1 + whatsapp_sdk

  • a hand-rolled webhook controller.

Core contract

FeatureUsual stack (telegram-bot-ruby + whatsapp_sdk)action_bot.crStatus
Unified delivery port deliver(OutboundMessage) → DeliveryReceiptone adapter per platform, no shared contractDeliveryAdapter module-interface✅
Unified ingress parse_update(JSON::Any) → Updateper-gem types (Telegram::Bot::Types, whatsapp_sdk)IngressAdapter, normalized Update/Message/Chat/Participant✅
Bot DSL command/message/callback_query/eventroll your own dispatcherActionBot::Base, compile-time dispatch✅
reply builder (text/photo/stream)API calls inline in handlersReplyBuilder over any adapter✅
Streaming replies in chatn/a (no platform streams natively)StreamingDelivery: progressive edits on Telegram (throttled ~1 edit/s), honest SSE on web✅ (emulated via edits)
Webhook authenticityper-platform middlewareWebhookVerification: Telegram secret_token, WhatsApp X-Hub-Signature-256 HMAC (constant-time)✅
Testing without networkmock the gem's HTTP clientTestAdapter records deliveries (ActiveJob :test-adapter pattern)✅

Platforms

Featuretelegram-bot-ruby 2.8.1 / whatsapp_sdkaction_bot.crStatus
Telegram: message/command/photo/callback/left_chat_member parsing, sendMessage/sendPhoto✅Same, fixtures validated by the real gem✅
Telegram: getUpdates pollermanual loopcrails bot:poll / poller✅
WhatsApp: text messages API, hub.challenge, HMAC webhooks✅Same✅
WhatsApp: media/interactive/template sends✅Text only⚠️ partial
Slack (or other platforms)separate gemsnot implemented (P1)❌
deliver_later via ActiveJobn/anot implemented (P1)❌
Conversation state (contexts, flows)gems like telegram-bot-ruby's sessionsnot implemented (P1)❌
AI-agent loop (Brain, tools, chat/stream_chat)e.g. ruby-openai by handseparate ActiveAgent shard (P2), bridges via reply.stream❌ (by design)

Railties / E2E

FeatureUsual stackaction_bot.crStatus
Generator (app/bots/, adapter wiring, route mounting, config/bots.yml)hand-rolledcrails generate bot NAME --platform=…✅
Webhook + SSE mounting under /bot/…routes by handauto-mounted (prefix-matched)✅
E2E smoke (crails new → generate → webhook 403/200/404, byte-exact sendMessage)—verified (tmp/agent_smoke)✅
600-bots benchmark—planned (bench/results/600-bots.md)❌

Deviations & Crystal-specific notes

  • Symbol typing: Chat#type is a Symbol (:private/:group/:channel).
  • Streaming into platform chats is emulated with progressive message edits — no messenger streams natively; the web side gets real SSE from the same reply.stream pipeline.
  • The Telegram token contains : and is escaped with URI.encode_path in API-call paths.
  • App mounting uses prefix matching (starts_with?("/bot/webhooks")), not exact routes: the path carries :platform/:bot segments.
  • dispatch(update) resets a cached reply only on re-dispatch, so a pre-injected transport (SSE) survives the first dispatch.
  • Contracts are frozen module-interfaces (deliver/stream/parse/verify) — additive evolution only.

Full details: PLAN.md (Russian).

Testing

crystal spec   # self-contained: fake HTTP servers, no PostgreSQL/Redis needed

Oracle: 39 cases recorded with the real telegram-bot-ruby 2.8.1 (oracle/cases.rb → oracle/expected.yml; Telegram fixtures parsed by the vendored gem, WhatsApp checked against the Cloud API schema); the Crystal specs replay them 1:1 without edits, plus 9 integration specs (webhooks, SSE, poller, byte-exact outbound payloads).