flow_chat 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.cliff.toml +74 -0
  3. data/.github/workflows/ci.yml +2 -3
  4. data/.github/workflows/release.yml +56 -0
  5. data/.standard.yml +4 -0
  6. data/CHANGELOG.md +22 -0
  7. data/CLAUDE.md +327 -0
  8. data/CONTRIBUTING.md +134 -0
  9. data/Gemfile +1 -0
  10. data/README.md +290 -105
  11. data/Rakefile +5 -1
  12. data/SECURITY.md +42 -349
  13. data/docs/architecture.md +510 -0
  14. data/docs/async-background-processing.md +298 -0
  15. data/docs/configuration.md +556 -226
  16. data/docs/factory-pattern.md +355 -0
  17. data/docs/gateway-context-variables.md +171 -0
  18. data/docs/gateway-development.md +723 -0
  19. data/docs/getting-started.md +429 -0
  20. data/docs/instrumentation.md +264 -153
  21. data/docs/platforms/telegram.md +1013 -0
  22. data/docs/platforms/ussd.md +693 -0
  23. data/docs/platforms/whatsapp.md +1395 -0
  24. data/docs/testing.md +243 -365
  25. data/examples/custom_session_id_example.rb +119 -0
  26. data/examples/http_controller.rb +11 -11
  27. data/examples/intercom_configuration_example.rb +118 -0
  28. data/examples/intercom_controller.rb +194 -0
  29. data/examples/multi_tenant_whatsapp_controller.rb +4 -4
  30. data/examples/ussd_controller.rb +9 -9
  31. data/examples/whatsapp_controller.rb +2 -2
  32. data/flow_chat.gemspec +4 -0
  33. data/lib/flow_chat/{base_app.rb → app.rb} +16 -9
  34. data/lib/flow_chat/async_job.rb +176 -0
  35. data/lib/flow_chat/config.rb +10 -30
  36. data/lib/flow_chat/{base_executor.rb → executor.rb} +6 -11
  37. data/lib/flow_chat/factory.rb +94 -0
  38. data/lib/flow_chat/gateway_async_support.rb +106 -0
  39. data/lib/flow_chat/generic_async_job.rb +30 -0
  40. data/lib/flow_chat/http/gateway/simple.rb +81 -33
  41. data/lib/flow_chat/http/renderer.rb +3 -3
  42. data/lib/flow_chat/instrumentation/setup.rb +1 -1
  43. data/lib/flow_chat/instrumentation.rb +23 -0
  44. data/lib/flow_chat/intercom/client.rb +155 -0
  45. data/lib/flow_chat/intercom/configuration.rb +149 -0
  46. data/lib/flow_chat/intercom/gateway/intercom_api.rb +396 -0
  47. data/lib/flow_chat/intercom/renderer.rb +71 -0
  48. data/lib/flow_chat/phone_number_util.rb +37 -35
  49. data/lib/flow_chat/processor.rb +188 -0
  50. data/lib/flow_chat/renderers/markdown_support.rb +58 -0
  51. data/lib/flow_chat/session/middleware.rb +25 -9
  52. data/lib/flow_chat/telegram/client.rb +240 -0
  53. data/lib/flow_chat/telegram/configuration.rb +118 -0
  54. data/lib/flow_chat/telegram/gateway/bot_api.rb +300 -0
  55. data/lib/flow_chat/telegram/middleware/choice_mapper.rb +35 -0
  56. data/lib/flow_chat/telegram/renderer.rb +125 -0
  57. data/lib/flow_chat/telegram.rb +7 -0
  58. data/lib/flow_chat/ussd/gateway/nalo.rb +24 -4
  59. data/lib/flow_chat/ussd/middleware/pagination.rb +9 -5
  60. data/lib/flow_chat/ussd/renderer.rb +1 -1
  61. data/lib/flow_chat/version.rb +1 -1
  62. data/lib/flow_chat/whatsapp/client.rb +144 -13
  63. data/lib/flow_chat/whatsapp/configuration.rb +1 -1
  64. data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +132 -96
  65. data/lib/flow_chat/whatsapp/id_generator.rb +124 -0
  66. data/lib/flow_chat/whatsapp/middleware/choice_mapper.rb +147 -0
  67. data/lib/flow_chat/whatsapp/renderer.rb +145 -11
  68. data/lib/flow_chat.rb +11 -1
  69. data/lib/tasks/release.rake +165 -0
  70. metadata +84 -25
  71. data/docs/flows.md +0 -320
  72. data/docs/http-gateway-protocol.md +0 -432
  73. data/docs/images/simulator.png +0 -0
  74. data/docs/media.md +0 -153
  75. data/docs/sessions.md +0 -433
  76. data/docs/ussd-setup.md +0 -322
  77. data/docs/whatsapp-setup.md +0 -162
  78. data/examples/whatsapp_message_job.rb +0 -113
  79. data/lib/flow_chat/base_processor.rb +0 -146
  80. data/lib/flow_chat/http/app.rb +0 -6
  81. data/lib/flow_chat/http/middleware/executor.rb +0 -24
  82. data/lib/flow_chat/http/processor.rb +0 -33
  83. data/lib/flow_chat/session/rails_session_store.rb +0 -68
  84. data/lib/flow_chat/ussd/app.rb +0 -6
  85. data/lib/flow_chat/ussd/gateway/nsano.rb +0 -96
  86. data/lib/flow_chat/ussd/middleware/executor.rb +0 -24
  87. data/lib/flow_chat/ussd/processor.rb +0 -39
  88. data/lib/flow_chat/whatsapp/app.rb +0 -29
  89. data/lib/flow_chat/whatsapp/middleware/executor.rb +0 -24
  90. data/lib/flow_chat/whatsapp/processor.rb +0 -32
  91. data/lib/flow_chat/whatsapp/send_job_support.rb +0 -79
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 419f60cf88904f992a2bcf4e93646e68adaee3fcdac5a96d080708b199a2d8a6
4
- data.tar.gz: 24ffafb2b588fdaee9775422e8057c073ce133d693dd6e78f19ef32978d40f88
3
+ metadata.gz: 8cfb138af20136fca40bdc0d7b054e8144ab92e2089c6c446e8c06f5104600f9
4
+ data.tar.gz: c4ae6902cffe3bcada4c3efe7b72f544fa9ec246570fa72df1b27c977c4f52db
5
5
  SHA512:
6
- metadata.gz: b49e73727d931e33d9ebff02c0809f31bd3adb7478335be76272961b04db219d98b10664675e32b45998bbcaa9d2d33de3644b84d3d924dc719d797341cd3fe0
7
- data.tar.gz: 789fe300b2e3863b88e11a347bf2b205440427b2a70a6cec0d3995bd5a5efc4210dfc66e571412ca79cf642d93ba35890ba6d5270eb1c3b23a727b6beb62f326
6
+ metadata.gz: f1e719da60370142efbd2b1a44cbb24236e67291340f9015ddd38411b8180cc2f3216d5e56a50464e23a4657d5184ec80ae85225b86e20f1e6673fcc607c1941
7
+ data.tar.gz: ac3fda919d3fe2bfc939be1454b3e4a4edaf8d4c8d18869244c1492a0bc593d186e0ed5fae626ef035a6905e83433d0197560ceac6bc5bce2a87ffcaa53a3ddd
data/.cliff.toml ADDED
@@ -0,0 +1,74 @@
1
+ # git-cliff configuration file
2
+ # https://git-cliff.org/docs/configuration
3
+
4
+ [changelog]
5
+ # changelog header
6
+ header = """
7
+ # Changelog\n
8
+ All notable changes to this project will be documented in this file.\n
9
+ """
10
+ # template for the changelog body
11
+ body = """
12
+ {% if version %}\
13
+ ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
14
+ {% else %}\
15
+ ## [Unreleased]
16
+ {% endif %}\
17
+ {% for group, commits in commits | group_by(attribute="group") %}
18
+ ### {{ group | upper_first }}
19
+ {% for commit in commits %}
20
+ - {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\
21
+ {% endfor %}
22
+ {% endfor %}\n
23
+ """
24
+ # remove the leading and trailing whitespace from the template
25
+ trim = true
26
+ # changelog footer
27
+ footer = """
28
+ <!-- generated by git-cliff -->
29
+ """
30
+
31
+ [git]
32
+ # parse the commits based on https://www.conventionalcommits.org
33
+ conventional_commits = true
34
+ # filter out the commits that are not conventional
35
+ filter_unconventional = true
36
+ # process each line of a commit as an individual commit
37
+ split_commits = false
38
+ # regex for preprocessing the commit messages
39
+ commit_preprocessors = [
40
+ { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/radioactive-labs/flow_chat/issues/${2}))" },
41
+ ]
42
+ # regex for parsing and grouping commits
43
+ commit_parsers = [
44
+ { message = "^feat", group = "Features" },
45
+ { message = "^fix", group = "Bug Fixes" },
46
+ { message = "^doc", group = "Documentation" },
47
+ { message = "^perf", group = "Performance" },
48
+ { message = "^refactor", group = "Refactoring" },
49
+ { message = "^style", group = "Styling" },
50
+ { message = "^test", group = "Testing" },
51
+ { message = "^chore\\(release\\): prepare for", skip = true },
52
+ { message = "^chore", group = "Miscellaneous Tasks" },
53
+ { body = ".*security", group = "Security" },
54
+ ]
55
+ # protect breaking changes from being skipped due to matching a skipping commit_parser
56
+ protect_breaking_commits = false
57
+ # filter out the commits that are not matched by commit parsers
58
+ filter_commits = false
59
+ # glob pattern for matching git tags
60
+ tag_pattern = "v[0-9]*"
61
+ # regex for skipping tags
62
+ skip_tags = ""
63
+ # regex for ignoring tags
64
+ ignore_tags = ""
65
+ # sort the tags topologically
66
+ topo_order = false
67
+ # sort the commits inside sections by oldest/newest order
68
+ sort_commits = "oldest"
69
+
70
+ [bump]
71
+ # Pre-1.0 semver: a feature bumps the minor, a breaking change also bumps the
72
+ # minor (not the major) while we're on 0.x. Fixes bump the patch.
73
+ features_always_bump_minor = true
74
+ breaking_always_bump_major = false
@@ -39,6 +39,5 @@ jobs:
39
39
  ruby-version: '3.2'
40
40
  bundler-cache: true
41
41
 
42
- - name: Run RuboCop
43
- run: bundle exec rubocop
44
- continue-on-error: true
42
+ - name: Run Standard
43
+ run: bundle exec standardrb
@@ -0,0 +1,56 @@
1
+ name: Release
2
+
3
+ # Publishing to RubyGems happens from a laptop (rake release:publish). This
4
+ # workflow only cuts the GitHub Release from the pushed tag — it never pushes to
5
+ # a registry, so it can't race the local publish. workflow_dispatch is kept as a
6
+ # manual escape hatch.
7
+ on:
8
+ push:
9
+ tags:
10
+ - 'v*'
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: write
15
+
16
+ jobs:
17
+ release:
18
+ runs-on: ubuntu-latest
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ with:
23
+ fetch-depth: 0
24
+
25
+ - name: Set up Ruby
26
+ uses: ruby/setup-ruby@v1
27
+ with:
28
+ ruby-version: '3.2'
29
+ bundler-cache: true
30
+
31
+ - name: Extract version from tag
32
+ id: version
33
+ run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
34
+
35
+ # Build the gem only to attach it to the GitHub Release — it is NOT pushed
36
+ # to RubyGems here (the laptop already did that via rake release:publish).
37
+ - name: Build gem
38
+ run: gem build flow_chat.gemspec
39
+
40
+ - name: Install git-cliff
41
+ uses: taiki-e/install-action@v2
42
+ with:
43
+ tool: git-cliff
44
+
45
+ - name: Generate release notes
46
+ run: |
47
+ git-cliff --config .cliff.toml --tag ${{ github.ref_name }} --strip all > RELEASE_NOTES.md
48
+
49
+ - name: Create GitHub Release
50
+ uses: softprops/action-gh-release@v1
51
+ with:
52
+ body_path: RELEASE_NOTES.md
53
+ files: |
54
+ flow_chat-${{ steps.version.outputs.VERSION }}.gem
55
+ env:
56
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
data/.standard.yml ADDED
@@ -0,0 +1,4 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/standardrb/standard
3
+ ---
4
+ ruby_version: 3.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.9.0] - 2026-07-09
6
+
7
+ ### Features
8
+
9
+ - Multi-platform gateways, composition architecture, and async processing ([#1](https://github.com/radioactive-labs/flow_chat/issues/1))
10
+
11
+ ### Miscellaneous Tasks
12
+
13
+ - Add git-cliff changelog and release rake tasks
14
+ - Add security policy and require RubyGems MFA
15
+
16
+ ### Styling
17
+
18
+ - Adopt StandardRB and format the codebase
19
+
20
+ ## [0.1.0] - 2024-04-04
21
+
22
+ <!-- generated by git-cliff -->
data/CLAUDE.md ADDED
@@ -0,0 +1,327 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Development Commands
6
+
7
+ ### Testing
8
+ - `rake test` or `bundle exec rake test` - Run all tests
9
+ - `ruby -Itest test/unit/specific_test.rb` - Run a single test file
10
+ - `ruby -Itest test/unit/specific_test.rb -n test_method_name` - Run a specific test method
11
+
12
+ ### Gem Development
13
+ - `bundle install` - Install dependencies
14
+ - `rake build` - Build the gem
15
+ - `rake release` - Build and release the gem (requires proper credentials)
16
+
17
+ ### Rails Integration Testing
18
+ - Use `rails runner` instead of `rails console` for scripting
19
+ - For production logs, use Rails.logger with block syntax: `Rails.logger.warn { "message" }`
20
+
21
+ ## Architecture Overview
22
+
23
+ FlowChat is a Rails framework for building conversational interfaces across multiple platforms (USSD, WhatsApp, Telegram, HTTP) using a **composition-based architecture** with **pluggable gateways**.
24
+
25
+ ### Core Components
26
+
27
+ #### Processor (`lib/flow_chat/processor.rb`)
28
+ - Central orchestrator that builds and executes the middleware stack
29
+ - Configures gateways, session stores, and middleware
30
+ - Entry point: `FlowChat::Processor.new(controller) do |config|`
31
+
32
+ #### Gateway
33
+ - Platform-specific request/response handling
34
+ - Built-in gateways: `FlowChat::Ussd::Gateway::Nalo`, `FlowChat::Whatsapp::Gateway::CloudApi`, `FlowChat::Telegram::Gateway::BotApi`, `FlowChat::Http::Gateway::Simple`
35
+ - Custom gateways implement: `initialize(app, *args)` and `call(context)`
36
+
37
+ #### App (`lib/flow_chat/app.rb`)
38
+ - Unified interface for flows to interact with users
39
+ - Key method: `screen(key) { |prompt| ... }` for conversation logic
40
+ - Platform-agnostic accessors: `msisdn`, `user_id`, `platform`, etc.
41
+
42
+ #### Flow (`lib/flow_chat/flow.rb`)
43
+ - Simple base class containing conversation logic
44
+ - Initialize with `app` instance, implement flow methods
45
+
46
+ #### Session (`lib/flow_chat/session/`)
47
+ - Configurable session boundaries: `:flow`, `:platform`, `:gateway`, `:url`
48
+ - Session stores: `CacheSessionStore`
49
+ - Session IDs generated based on boundaries and identifiers
50
+
51
+ #### Factory Pattern (`lib/flow_chat/factory.rb`)
52
+ - Centralized processor configuration system
53
+ - Register configurations once, use everywhere
54
+ - Methods: `register(name, &block)`, `execute(name, controller:)`
55
+ - Eliminates duplication between webhook and background contexts
56
+ - Works seamlessly with `GenericAsyncJob` for async processing
57
+
58
+ #### Async Background Processing (`lib/flow_chat/async_job.rb`, `gateway_async_support.rb`, `generic_async_job.rb`)
59
+ - Decouple flow processing from webhook request-response cycles
60
+ - Base class: `FlowChat::AsyncJob` for custom background jobs
61
+ - `GenericAsyncJob`: Built-in job that uses Factory pattern (no custom job needed)
62
+ - `BackgroundController` mimics controller interface in background context
63
+ - `GatewayAsyncSupport` concern for gateways to detect and enqueue async jobs
64
+ - Automatic detection: async enqueue, background execute, or inline processing
65
+ - Supported gateways: WhatsApp Cloud API, Intercom API, HTTP Simple (not USSD)
66
+
67
+ ### Middleware Stack Architecture
68
+
69
+ ```
70
+ Gateway -> Session::Middleware -> Custom Middleware -> Executor -> Flow
71
+ ```
72
+
73
+ - **Gateway**: Parses platform-specific requests, renders responses
74
+ - **Session::Middleware**: Manages session boundaries and storage
75
+ - **Custom Middleware**: Business logic, authentication, logging
76
+ - **Executor**: Instantiates flows and handles interrupts
77
+ - **Flow**: Business logic using `app.screen()` for conversation
78
+
79
+ ### Key Patterns
80
+
81
+ #### Screen-Based Navigation
82
+ ```ruby
83
+ def registration_flow
84
+ email = app.screen(:email) { |p| p.ask "Enter email:", validate: email_validator }
85
+ name = app.screen(:name) { |p| p.ask "Enter name:", transform: ->(input) { input.titleize } }
86
+ app.say "Welcome #{name}!"
87
+ end
88
+ ```
89
+
90
+ #### Platform Configuration
91
+ ```ruby
92
+ processor = FlowChat::Processor.new(self) do |config|
93
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
94
+ config.use_session_store FlowChat::Session::CacheSessionStore
95
+ config.use_session_config(boundaries: [:flow], identifier: :msisdn)
96
+ end
97
+ ```
98
+
99
+ #### Multi-Platform Support
100
+ Same flow code works across USSD, WhatsApp, Telegram, and HTTP by using platform-agnostic `app.screen()` calls.
101
+
102
+ #### Factory Pattern with Async
103
+ The recommended approach for async processing using centralized configuration:
104
+
105
+ ```ruby
106
+ # Register factory once in initializer
107
+ FlowChat::Factory.register :whatsapp do |controller|
108
+ processor = FlowChat::Processor.new(controller) do |config|
109
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
110
+ config.use_session_store FlowChat::Session::CacheSessionStore
111
+ config.use_async(factory: :whatsapp) # Self-referencing for async
112
+ end
113
+ processor.run(WhatsAppFlow, :start)
114
+ end
115
+
116
+ # Use in webhook controller - one line!
117
+ FlowChat::Factory.execute(:whatsapp, controller: self)
118
+ ```
119
+
120
+ **How it works:**
121
+ 1. Webhook calls `Factory.execute(:whatsapp)`
122
+ 2. Factory builds processor with `use_async(factory: :whatsapp)`
123
+ 3. Gateway enqueues `GenericAsyncJob` with `factory: :whatsapp` param
124
+ 4. Background job executes `Factory.execute(:whatsapp)` again
125
+ 5. Gateway detects background context and processes inline
126
+
127
+ **Benefits:**
128
+ - No custom job class needed (`GenericAsyncJob` handles it automatically)
129
+ - Configuration defined once, works in both webhook and background contexts
130
+ - Webhook returns immediately (< 100ms), flow processes in background
131
+ - Automatic prevention of double-enqueueing
132
+
133
+ See [docs/factory-pattern.md](docs/factory-pattern.md) and [docs/async-background-processing.md](docs/async-background-processing.md) for details.
134
+
135
+ #### Custom Async Jobs
136
+ For advanced cases, create custom job classes with job params:
137
+
138
+ ```ruby
139
+ class MyFlowJob < FlowChat::AsyncJob
140
+ def execute(controller, **job_params)
141
+ deployment_id = job_params[:deployment_id]
142
+ # ... custom logic with job params
143
+ end
144
+ end
145
+
146
+ # Use with job params
147
+ config.use_async(MyFlowJob, deployment_id: 123)
148
+ ```
149
+
150
+ ## File Structure
151
+
152
+ ### Core Library (`lib/flow_chat/`)
153
+ - `processor.rb` - Main orchestrator, middleware stack builder
154
+ - `app.rb` - Unified conversation interface
155
+ - `flow.rb` - Base flow class
156
+ - `executor.rb` - Flow execution and interrupt handling
157
+ - `context.rb` - Request context management
158
+ - `config.rb` - Global configuration
159
+ - `factory.rb` - Centralized processor configuration registry
160
+ - `async_job.rb` - Background processing base class and controllers
161
+ - `generic_async_job.rb` - Factory-based async job (no custom class needed)
162
+ - `gateway_async_support.rb` - Async detection and enqueueing concern for gateways
163
+
164
+ ### Platform Gateways
165
+ - `ussd/gateway/nalo.rb` - USSD platform integration (async not supported)
166
+ - `whatsapp/gateway/cloud_api.rb` - WhatsApp Business API integration (async supported)
167
+ - `telegram/gateway/bot_api.rb` - Telegram Bot API integration (async supported)
168
+ - `http/gateway/simple.rb` - HTTP/JSON API integration (async supported)
169
+ - `intercom/gateway/intercom_api.rb` - Intercom customer support integration (async supported)
170
+ - All gateways include `GatewayAsyncSupport` concern for unified async handling
171
+
172
+ ### Session Management (`session/`)
173
+ - `middleware.rb` - Session boundary and ID generation
174
+ - `rails_session_store.rb` - Rails session integration
175
+ - `cache_session_store.rb` - Rails cache integration
176
+
177
+ ### Platform-Specific (`ussd/`, `whatsapp/`, `telegram/`, `http/`, `intercom/`)
178
+ - `renderer.rb` - Platform-specific response formatting
179
+ - `middleware/` - Platform-specific processing logic
180
+ - `intercom/client.rb` - Intercom REST API integration
181
+ - `intercom/configuration.rb` - Intercom credentials and settings
182
+ - `intercom/conversation_manager.rb` - Generic conversation management utilities
183
+
184
+ ### Testing (`test/`)
185
+ - `test_helper.rb` - Test setup with mock Rails environment
186
+ - `unit/` - Unit tests for individual components
187
+ - `integration/` - Integration tests for full flow scenarios
188
+ - `e2e/` - End-to-end tests for platform-specific features
189
+
190
+ ## Testing Approach
191
+
192
+ - Uses Minitest with custom test helpers
193
+ - Mock Rails environment for testing without full Rails app
194
+ - Test flows using `mock_controller` and session store mocks
195
+ - Platform-specific tests verify gateway behavior
196
+ - Integration tests verify full request/response cycles
197
+
198
+ ## Configuration Patterns
199
+
200
+ ### Global Configuration
201
+ ```ruby
202
+ # Logger injection into middleware stack (defaults to true in Rails development)
203
+ FlowChat::Config.inject_middleware_logger = true
204
+
205
+ # Other global configs
206
+ FlowChat::Config.logger = Rails.logger
207
+ FlowChat::Config.combine_validation_error_with_message = true
208
+ ```
209
+
210
+ ### Session Configuration
211
+ ```ruby
212
+ config.use_session_config(
213
+ boundaries: [:flow, :platform], # Session isolation
214
+ identifier: :msisdn, # Session key type
215
+ hash_identifiers: true # Privacy protection
216
+ )
217
+ ```
218
+
219
+ ### Multi-Tenancy Support
220
+ ```ruby
221
+ config.use_url_isolation # tenant1.app.com vs tenant2.app.com
222
+ config.use_cross_platform_sessions # Share sessions between USSD/WhatsApp
223
+ config.use_durable_sessions # Use user_id instead of request_id
224
+ ```
225
+
226
+ ### Intercom Integration
227
+
228
+ FlowChat provides comprehensive Intercom integration for customer support workflows with proper webhook validation and API client functionality.
229
+
230
+ #### Configuration
231
+ ```ruby
232
+ # Rails credentials (config/credentials.yml.enc)
233
+ intercom:
234
+ access_token: "your_intercom_access_token"
235
+ client_secret: "your_intercom_client_secret"
236
+ skip_signature_validation: false # Optional: disable webhook validation for testing
237
+
238
+ # Or environment variables
239
+ INTERCOM_ACCESS_TOKEN=your_intercom_access_token
240
+ INTERCOM_CLIENT_SECRET=your_intercom_client_secret
241
+ INTERCOM_SKIP_SIGNATURE_VALIDATION=false
242
+ ```
243
+
244
+ #### Gateway Setup
245
+ ```ruby
246
+ # Basic Intercom gateway setup (default webhook topics)
247
+ config.use_gateway FlowChat::Intercom::Gateway::IntercomApi
248
+ config.use_session_config(boundaries: [:conversation], identifier: :conversation_id)
249
+
250
+ # With custom configuration
251
+ intercom_config = FlowChat::Intercom::Configuration.get(:my_config)
252
+ config.use_gateway FlowChat::Intercom::Gateway::IntercomApi, intercom_config
253
+
254
+ # Additional webhook topics (e.g., to include admin events)
255
+ # Note: Default topics (user.created, user.replied) are always included
256
+ config.use_gateway FlowChat::Intercom::Gateway::IntercomApi, nil, [
257
+ "conversation.admin.assigned",
258
+ "conversation.admin.replied"
259
+ ]
260
+
261
+ # Custom config AND additional webhook topics
262
+ config.use_gateway FlowChat::Intercom::Gateway::IntercomApi, intercom_config, [
263
+ "conversation.admin.assigned",
264
+ "conversation.admin.replied"
265
+ ]
266
+ ```
267
+
268
+ **Default Webhook Topics:**
269
+ - `conversation.user.created` - New conversation started by user
270
+ - `conversation.user.replied` - User replied in existing conversation
271
+
272
+ **Additional Available Topics:**
273
+ - `conversation.admin.assigned` - Admin assigned to conversation
274
+ - `conversation.admin.replied` - Admin replied to conversation
275
+ - `conversation.admin.closed` - Admin closed conversation
276
+ - See [Intercom webhook docs](https://developers.intercom.com/docs/references/webhooks/webhook-models/) for full list
277
+
278
+ #### Webhook Setup
279
+ 1. Add your HTTPS endpoint URL in Intercom Developer Hub → Configure → Webhooks
280
+ 2. Intercom validates your endpoint with HEAD request (handled automatically)
281
+ 3. Webhook notifications are validated using X-Hub-Signature with client_secret
282
+
283
+ #### FlowChat Client API
284
+ The FlowChat Intercom client provides only the core methods needed by the gateway:
285
+
286
+ ```ruby
287
+ # Access within a flow via context
288
+ client = context["intercom.client"]
289
+ conversation_id = context["request.conversation_id"]
290
+
291
+ # Send a message (used by gateway automatically)
292
+ client.send_message(conversation_id, "Hello!", choices: nil, media: nil)
293
+ ```
294
+
295
+ **Note:** Conversations are automatically assigned to the configured admin when messages are sent (Intercom's default behavior). Each admin can change this in their personal settings if desired.
296
+
297
+ **Important:** For business logic (tags, assignment, state management, fetching conversations), use the official `intercom` gem directly in your application:
298
+
299
+ ```ruby
300
+ # In your application code, use the official gem for business logic
301
+ intercom = Intercom::Client.new(token: access_token)
302
+
303
+ # Tag management
304
+ intercom.tags.tag(name: "AI_HANDLING", conversations: [{id: conversation_id}])
305
+
306
+ # Assignment (override default auto-assignment)
307
+ intercom.conversations.reply(id: conversation_id, message_type: "assignment", admin_id: admin_id)
308
+
309
+ # State management
310
+ intercom.conversations.reply(id: conversation_id, message_type: "closed")
311
+
312
+ # Fetch conversation details
313
+ conversation = intercom.conversations.find(id: conversation_id)
314
+ ```
315
+
316
+ #### Error Handling
317
+ - Rate limiting: `RateLimitError` with retry-after information
318
+ - Authentication: `ConfigurationError` for invalid tokens
319
+ - API errors: Proper handling for Intercom gem exceptions (ResourceNotFound, AuthenticationError, etc.)
320
+
321
+ ## Instrumentation
322
+
323
+ FlowChat includes comprehensive instrumentation via `FlowChat::Instrumentation`:
324
+ - Flow execution events
325
+ - Session creation events
326
+ - Platform-specific metrics
327
+ - Error tracking and logging
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,134 @@
1
+ # Contributing to Plutonium
2
+
3
+ ## Commit Message Convention
4
+
5
+ This project follows [Conventional Commits](https://www.conventionalcommits.org/) for automated changelog generation and versioning.
6
+
7
+ ### Format
8
+
9
+ ```
10
+ <type>[optional scope]: <description>
11
+
12
+ [optional body]
13
+
14
+ [optional footer(s)]
15
+ ```
16
+
17
+ ### Types
18
+
19
+ - **feat**: A new feature (triggers MINOR version bump)
20
+ - **fix**: A bug fix (triggers PATCH version bump)
21
+ - **docs**: Documentation only changes
22
+ - **style**: Changes that don't affect code meaning (formatting, etc)
23
+ - **refactor**: Code change that neither fixes a bug nor adds a feature
24
+ - **perf**: Performance improvement
25
+ - **test**: Adding or updating tests
26
+ - **chore**: Maintenance tasks (dependencies, tooling, etc)
27
+
28
+ ### Breaking Changes
29
+
30
+ Add `BREAKING CHANGE:` in the footer or `!` after type to trigger a MAJOR version bump:
31
+
32
+ ```
33
+ feat!: remove deprecated API
34
+
35
+ BREAKING CHANGE: The old API has been removed. Use new API instead.
36
+ ```
37
+
38
+ ### Examples
39
+
40
+ ```bash
41
+ # Feature (bumps 0.26.11 -> 0.27.0)
42
+ git commit -m "feat: add field-level options support for input definitions"
43
+
44
+ # Bug fix (bumps 0.26.11 -> 0.26.12)
45
+ git commit -m "fix: resolve inheritance issue with controller_for"
46
+
47
+ # Breaking change (bumps 0.26.11 -> 1.0.0)
48
+ git commit -m "feat!: redesign definition DSL
49
+
50
+ BREAKING CHANGE: The definition DSL has been completely redesigned.
51
+ See migration guide for details."
52
+
53
+ # With scope
54
+ git commit -m "feat(ui): add new table component"
55
+ git commit -m "fix(forms): correct hint display on validation errors"
56
+
57
+ # Documentation
58
+ git commit -m "docs: update definition structure guide"
59
+ ```
60
+
61
+ ## Release Process
62
+
63
+ ### Option 1: Automated (Recommended)
64
+
65
+ ```bash
66
+ # See what the next version should be
67
+ rake release:next_version
68
+
69
+ # Prepare a new release (updates version, generates changelog)
70
+ rake release:prepare[0.27.0]
71
+
72
+ # Review changes
73
+ git diff
74
+
75
+ # Full automated release (prepare, commit, tag, push, publish)
76
+ rake release:full[0.27.0]
77
+ ```
78
+
79
+ ### Option 2: Manual
80
+
81
+ ```bash
82
+ # 1. Update version in lib/plutonium/version.rb
83
+ # 2. Generate changelog
84
+ git-cliff --tag v0.27.0 -o CHANGELOG.md
85
+
86
+ # 3. Commit and tag
87
+ git add -A
88
+ git commit -m "chore(release): prepare for v0.27.0"
89
+ git tag v0.27.0
90
+ git push origin main --tags
91
+
92
+ # 4. GitHub Actions will automatically publish to RubyGems
93
+ ```
94
+
95
+ ## Version Bumping Rules
96
+
97
+ Following semantic versioning:
98
+
99
+ - **MAJOR** (X.0.0): Breaking changes
100
+ - **MINOR** (0.X.0): New features (backwards compatible)
101
+ - **PATCH** (0.0.X): Bug fixes (backwards compatible)
102
+
103
+ The automation determines the version bump based on commits since the last tag:
104
+ - Any commit with `BREAKING CHANGE:` or `!` after type → MAJOR
105
+ - Any `feat:` commits → MINOR
106
+ - Any `fix:` commits → PATCH
107
+
108
+ ## Development Setup
109
+
110
+ ```bash
111
+ # Install dependencies
112
+ bundle install
113
+
114
+ # Run tests
115
+ bundle exec rspec
116
+
117
+ # Install git-cliff for changelog generation (optional)
118
+ brew install git-cliff # macOS
119
+ # or
120
+ cargo install git-cliff # via Rust
121
+ ```
122
+
123
+ ## Pull Request Process
124
+
125
+ 1. Fork the repository
126
+ 2. Create a feature branch: `git checkout -b feat/my-feature`
127
+ 3. Make your changes with conventional commits
128
+ 4. Run tests: `bundle exec rspec`
129
+ 5. Push and create a pull request
130
+ 6. The PR title should also follow conventional commit format
131
+
132
+ ## Questions?
133
+
134
+ Open an issue or discussion on GitHub!
data/Gemfile CHANGED
@@ -8,3 +8,4 @@ gem "minitest", "~> 5.0"
8
8
  gem "minitest-reporters", "~> 1.4"
9
9
  gem "ostruct"
10
10
  gem "webmock", "~> 3.18"
11
+ gem "standard", "~> 1.3"