polylingo_chat 0.1.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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +432 -0
  3. data/lib/generators/polylingo_chat/install/install_generator.rb +107 -0
  4. data/lib/generators/polylingo_chat/install/templates/README +48 -0
  5. data/lib/generators/polylingo_chat/install/templates/channels/application_cable/channel.rb +4 -0
  6. data/lib/generators/polylingo_chat/install/templates/channels/application_cable/connection.rb +30 -0
  7. data/lib/generators/polylingo_chat/install/templates/channels/polylingo_chat_channel.rb +15 -0
  8. data/lib/generators/polylingo_chat/install/templates/create_conversations.rb +9 -0
  9. data/lib/generators/polylingo_chat/install/templates/create_messages.rb +13 -0
  10. data/lib/generators/polylingo_chat/install/templates/create_participants.rb +12 -0
  11. data/lib/generators/polylingo_chat/install/templates/initializer.rb +24 -0
  12. data/lib/generators/polylingo_chat/install/templates/javascript/channels/consumer.js +15 -0
  13. data/lib/generators/polylingo_chat/install/templates/javascript/channels/index.js +2 -0
  14. data/lib/generators/polylingo_chat/install/templates/javascript/chat.js +86 -0
  15. data/lib/generators/polylingo_chat/install/templates/models/conversation.rb +7 -0
  16. data/lib/generators/polylingo_chat/install/templates/models/message.rb +14 -0
  17. data/lib/generators/polylingo_chat/install/templates/models/participant.rb +6 -0
  18. data/lib/generators/polylingo_chat/install_generator.rb +38 -0
  19. data/lib/generators/polylingo_chat/templates/INSTALL_README.md +124 -0
  20. data/lib/generators/polylingo_chat/templates/chat_channel_example.js +18 -0
  21. data/lib/generators/polylingo_chat/templates/create_polyglot_conversations.rb +9 -0
  22. data/lib/generators/polylingo_chat/templates/create_polyglot_messages.rb +13 -0
  23. data/lib/generators/polylingo_chat/templates/create_polyglot_participants.rb +12 -0
  24. data/lib/generators/polylingo_chat/templates/models/conversation.rb +7 -0
  25. data/lib/generators/polylingo_chat/templates/models/message.rb +14 -0
  26. data/lib/generators/polylingo_chat/templates/models/participant.rb +6 -0
  27. data/lib/generators/polylingo_chat/templates/polyglot.rb +53 -0
  28. data/lib/generators/polylingo_chat/templates/polylingo_chat_channel.rb +19 -0
  29. data/lib/polylingo_chat/config.rb +26 -0
  30. data/lib/polylingo_chat/engine.rb +10 -0
  31. data/lib/polylingo_chat/railtie.rb +14 -0
  32. data/lib/polylingo_chat/realtime.rb +8 -0
  33. data/lib/polylingo_chat/translate_job.rb +63 -0
  34. data/lib/polylingo_chat/translator/anthropic_client.rb +85 -0
  35. data/lib/polylingo_chat/translator/base.rb +13 -0
  36. data/lib/polylingo_chat/translator/gemini_client.rb +88 -0
  37. data/lib/polylingo_chat/translator/openai_client.rb +92 -0
  38. data/lib/polylingo_chat/translator.rb +40 -0
  39. data/lib/polylingo_chat/version.rb +3 -0
  40. data/lib/polylingo_chat.rb +11 -0
  41. metadata +138 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 03e17e594f585909ba895a3dab27bda4b0f731bd59fd12fac90dd5a0df7afe2e
4
+ data.tar.gz: e0ae5358db6d20f888e1524af38458e3457a9583bd31837a3ca6db3d33937d68
5
+ SHA512:
6
+ metadata.gz: 868e647b05dd1d14836fcbe8aa794cdb6f5ffd0dd3629255e79833e849a963bf6c7ad2b58d0bfd146aec673b2305ab2706dcb7d656bf1a53dfeb805613258478
7
+ data.tar.gz: 26d96b4e747de39519e76f4c3158215caebd940e5acbbd656f6403039904df4f7d63cfe9d627e750b884f7e9c27053be3c6b30b1c047dbc96891351095093189
data/README.md ADDED
@@ -0,0 +1,432 @@
1
+ # PolylingoChat โ€” Real-time Multilingual Chat Engine for Rails
2
+
3
+ PolylingoChat is a plug-and-play Rails Engine that adds real-time chat to your Rails app. Use it as a **chat-only engine** or enable **AI-powered translation** for multilingual conversations.
4
+
5
+ Perfect for marketplaces, SaaS apps, CRMs, support systems, or global communities.
6
+
7
+ ---
8
+
9
+ ## โœจ Features
10
+
11
+ - ๐Ÿ”ฅ **Real-time chat** using ActionCable with Solid Cable (database-backed)
12
+ - ๐Ÿš€ **One-command installer** โ€” everything set up automatically
13
+ - ๐ŸŒ **Optional AI translation** - works with or without translation
14
+ - โš™๏ธ **Multi-provider support** โ€” OpenAI, Anthropic Claude, or Google Gemini
15
+ - ๐Ÿงฉ **Rails Engine** โ€” mounts directly inside your app
16
+ - ๐Ÿ‘ฅ **Conversations, Participants & Messages** models included
17
+ - ๐Ÿ”Œ **Works with any ActiveJob backend** (Sidekiq, Solid Queue, Delayed Job, etc.)
18
+ - ๐Ÿ” **Secure & scoped** ActionCable channels
19
+ - ๐Ÿงฑ **Extendable architecture** (custom UI, custom providers, custom storage)
20
+ - ๐Ÿ’พ **Built-in caching** support for translations
21
+ - ๐Ÿงช **RSpec testing** framework included
22
+ - ๐Ÿ—„๏ธ **No Redis required** โ€” uses Solid Cable for WebSocket persistence
23
+
24
+ ---
25
+
26
+ ## ๐Ÿ“‹ Requirements
27
+
28
+ - Ruby >= 2.7.0
29
+ - Rails >= 6.0
30
+ - A background job processor (Sidekiq, Solid Queue, or Delayed Job)
31
+ - **Optional:** AI API key (only if you want translation)
32
+
33
+ **Note:** PolylingoChat uses **Solid Cable** (database-backed ActionCable) by default, so Redis is optional.
34
+
35
+ ---
36
+
37
+ ## ๐Ÿš€ Installation
38
+
39
+ ### 1. Add the gem
40
+ ```ruby
41
+ # Gemfile
42
+ gem "polylingo_chat", github: "AdwareTechnologies/polylingo_chat"
43
+
44
+ # Add a background job processor
45
+ gem 'sidekiq' # or 'solid_queue' or 'delayed_job_active_record'
46
+ ```
47
+
48
+ ### 2. Install and generate
49
+ ```bash
50
+ bundle install
51
+ bin/rails generate polylingo_chat:install
52
+ bin/rails db:migrate
53
+ ```
54
+
55
+ **The installer automatically:**
56
+ - โœ… Creates migrations for Conversations, Participants, and Messages
57
+ - โœ… Generates model files with associations
58
+ - โœ… Sets up ActionCable channels (PolylinguoChatChannel)
59
+ - โœ… Creates JavaScript files for real-time chat
60
+ - โœ… Configures Solid Cable in `config/cable.yml`
61
+ - โœ… Downloads ActionCable ESM module to `vendor/javascript`
62
+ - โœ… Updates `config/routes.rb` with chat routes
63
+ - โœ… Updates `config/importmap.rb` with required pins
64
+ - โœ… Updates `app/javascript/application.js` with chat import
65
+ - โœ… Creates `config/initializers/polylingo_chat.rb` for configuration
66
+
67
+ ### 3. Configure ActiveJob
68
+ ```ruby
69
+ # config/application.rb or config/environments/production.rb
70
+ config.active_job.queue_adapter = :sidekiq # or :solid_queue, :delayed_job, :async
71
+ ```
72
+
73
+ ### 4. Add preferred_language to User (optional, only if using translation)
74
+ ```bash
75
+ bin/rails generate migration AddPreferredLanguageToUsers preferred_language:string
76
+ bin/rails db:migrate
77
+ ```
78
+
79
+ ### 5. Configure PolylingoChat
80
+
81
+ Edit `config/initializers/polylingo_chat.rb` (created by installer):
82
+
83
+ **Option A: Chat-Only Mode (No Translation)**
84
+ ```ruby
85
+ PolylingoChat.configure do |config|
86
+ # Leave api_key as nil for chat-only mode
87
+ config.api_key = nil
88
+
89
+ config.queue_adapter = :sidekiq
90
+ config.async = true
91
+ end
92
+ ```
93
+
94
+ **Option B: With AI Translation**
95
+ ```ruby
96
+ PolylingoChat.configure do |config|
97
+ # Enable translation by setting API key
98
+ config.provider = :openai # or :anthropic, :gemini
99
+ config.api_key = ENV['OPENAI_API_KEY']
100
+ config.model = 'gpt-4o-mini'
101
+
102
+ config.queue_adapter = :sidekiq
103
+ config.default_language = 'en'
104
+ config.cache_store = Rails.cache
105
+ config.timeout = 15
106
+ config.async = true
107
+ end
108
+ ```
109
+
110
+ ### 6. Configure ActionCable Authentication
111
+
112
+ The installer creates `app/channels/application_cable/connection.rb` with authentication logic. For development, it accepts `user_id` from WebSocket params:
113
+
114
+ ```ruby
115
+ # app/channels/application_cable/connection.rb
116
+ def find_verified_user
117
+ user_id = request.params[:user_id] || cookies.encrypted[:user_id]
118
+
119
+ if user_id && (verified_user = User.find_by(id: user_id))
120
+ verified_user
121
+ else
122
+ reject_unauthorized_connection
123
+ end
124
+ end
125
+ ```
126
+
127
+ The JavaScript consumer automatically passes `window.currentUserId` to authenticate. Make sure to set this in your views:
128
+
129
+ ```erb
130
+ <!-- app/views/conversations/show.html.erb -->
131
+ <script>
132
+ window.conversationId = <%= @conversation.id %>;
133
+ window.currentUserId = <%= current_user.id %>;
134
+ </script>
135
+ ```
136
+
137
+ **For production:** Replace the `find_verified_user` method with your actual authentication (Devise, session, JWT, etc.)
138
+
139
+ ### 7. Start your background worker
140
+ ```bash
141
+ bundle exec sidekiq # or bin/rails solid_queue:start, or bin/rails jobs:work
142
+ ```
143
+
144
+ ---
145
+
146
+ ## ๐ŸŽฏ Usage
147
+
148
+ ### 1. Set up your view
149
+
150
+ First, expose the conversation and user IDs to JavaScript in your chat view:
151
+
152
+ ```erb
153
+ <!-- app/views/conversations/show.html.erb -->
154
+ <script>
155
+ window.conversationId = <%= @conversation.id %>;
156
+ window.currentUserId = <%= current_user.id %>;
157
+ </script>
158
+
159
+ <div id="messages">
160
+ <!-- Messages will appear here via ActionCable -->
161
+ </div>
162
+ ```
163
+
164
+ ### 2. Send a message
165
+
166
+ ```ruby
167
+ # Create a conversation
168
+ conversation = Conversation.create!(title: "Project Discussion")
169
+
170
+ # Add participants
171
+ Participant.create!(conversation: conversation, user: user1)
172
+ Participant.create!(conversation: conversation, user: user2)
173
+
174
+ # Send a message
175
+ Message.create!(
176
+ conversation: conversation,
177
+ sender: user1,
178
+ body: "Hello! How are you?"
179
+ )
180
+ ```
181
+
182
+ **What happens:**
183
+ - Message is saved to database
184
+ - Background job is enqueued automatically
185
+ - **Without API key:** Message is broadcast as-is to all participants
186
+ - **With API key:** Message is translated to each participant's preferred language
187
+ - All participants see the message in real-time via ActionCable
188
+
189
+ ### 3. Real-time updates with ActionCable
190
+
191
+ The installer creates JavaScript files that handle real-time updates automatically. The key file is `app/javascript/chat.js`:
192
+
193
+ ```javascript
194
+ // This file is automatically generated by the installer
195
+ // It connects to PolylinguoChatChannel and handles incoming messages
196
+
197
+ import consumer from "channels/consumer"
198
+
199
+ consumer.subscriptions.create({
200
+ channel: "PolylinguoChatChannel",
201
+ conversation_id: window.conversationId
202
+ }, {
203
+ received(data) {
204
+ // data.message - message text (translated if translation enabled)
205
+ // data.original - original text
206
+ // data.sender_id - sender's ID
207
+ // data.translated - boolean (true if translation was used)
208
+ console.log("Received:", data.message)
209
+ console.log("Was translated:", data.translated)
210
+
211
+ // The generated code automatically appends messages to #messages div
212
+ }
213
+ })
214
+ ```
215
+
216
+ **You don't need to write this code** โ€” the installer creates it for you!
217
+
218
+ ---
219
+
220
+ ## ๐ŸŒ Translation (Optional)
221
+
222
+ ### When to Use Translation
223
+
224
+ Translation is **optional**. Use it when:
225
+ - โœ… You have a global user base speaking different languages
226
+ - โœ… You want automatic message translation
227
+ - โœ… You're willing to pay for AI API usage
228
+
229
+ Skip translation when:
230
+ - โŒ All users speak the same language
231
+ - โŒ You want a simple chat without AI costs
232
+ - โŒ You'll handle translation elsewhere
233
+
234
+ ### AI Providers
235
+
236
+ #### OpenAI
237
+ ```ruby
238
+ config.provider = :openai
239
+ config.api_key = ENV['OPENAI_API_KEY']
240
+ config.model = 'gpt-4o-mini' # Fast & cost-effective
241
+ ```
242
+ Get key: https://platform.openai.com/api-keys
243
+
244
+ **Models:** `gpt-4o-mini`, `gpt-4o`, `gpt-3.5-turbo`
245
+
246
+ #### Anthropic Claude
247
+ ```ruby
248
+ config.provider = :anthropic
249
+ config.api_key = ENV['ANTHROPIC_API_KEY']
250
+ config.model = 'claude-3-5-sonnet-20241022'
251
+ ```
252
+ Get key: https://console.anthropic.com/
253
+
254
+ **Models:** `claude-3-5-sonnet-20241022`, `claude-3-5-haiku-20241022`, `claude-3-opus-20240229`
255
+
256
+ #### Google Gemini
257
+ ```ruby
258
+ config.provider = :gemini
259
+ config.api_key = ENV['GOOGLE_API_KEY']
260
+ config.model = 'gemini-1.5-flash'
261
+ ```
262
+ Get key: https://ai.google.dev/
263
+
264
+ **Models:** `gemini-1.5-flash`, `gemini-1.5-pro`
265
+
266
+ ---
267
+
268
+ ## โš™๏ธ Configuration Options
269
+
270
+ | Option | Type | Default | Description |
271
+ |--------|------|---------|-------------|
272
+ | `provider` | Symbol | `:openai` | AI provider (`:openai`, `:anthropic`, `:gemini`) |
273
+ | `api_key` | String | `nil` | API key - leave nil for chat-only mode |
274
+ | `model` | String | `'gpt-4o-mini'` | Model name for the provider |
275
+ | `queue_adapter` | Symbol | `nil` | Which ActiveJob adapter you're using (informational) |
276
+ | `default_language` | String | `'en'` | Default target language (ISO 639-1 code) |
277
+ | `cache_store` | Cache | `nil` | Rails cache store for caching translations |
278
+ | `async` | Boolean | `true` | Enable async processing via ActiveJob |
279
+ | `timeout` | Integer | `15` | API request timeout in seconds |
280
+
281
+ ---
282
+
283
+ ## ๐Ÿ—๏ธ How It Works
284
+
285
+ ### Message Flow
286
+
287
+ 1. User sends a message โ†’ saved to database
288
+ 2. `Message#after_create_commit` enqueues job
289
+ 3. Job runs in background via your ActiveJob adapter
290
+ 4. **If API key present:** Message translated for each participant
291
+ 5. **If no API key:** Original message used for all participants
292
+ 6. Messages broadcast via ActionCable (using Solid Cable)
293
+ 7. Recipients see messages in real-time
294
+
295
+ ### Models
296
+
297
+ **Conversation** - Chat conversation with many participants and messages
298
+
299
+ **Participant** - Join table linking Users to Conversations
300
+
301
+ **Message** - Individual chat message with translation tracking
302
+
303
+ ### Solid Cable (Database-Backed WebSockets)
304
+
305
+ PolylingoChat uses **Solid Cable** instead of Redis for ActionCable. Benefits:
306
+
307
+ - โœ… **No Redis dependency** - One less service to manage
308
+ - โœ… **Message persistence** - WebSocket messages stored in database
309
+ - โœ… **Simpler deployment** - Works anywhere your database works
310
+ - โœ… **Better for development** - No need to run Redis locally
311
+ - โœ… **Cost-effective** - No separate Redis hosting required
312
+
313
+ Solid Cable uses your existing database (SQLite, PostgreSQL, MySQL) to store WebSocket messages with configurable retention:
314
+
315
+ ```yaml
316
+ # config/cable.yml (configured automatically by installer)
317
+ development:
318
+ adapter: solid_cable
319
+ polling_interval: 0.1.seconds
320
+ message_retention: 1.day
321
+ ```
322
+
323
+ The installer runs `bin/rails solid_cable:install` automatically, which creates the necessary migrations.
324
+
325
+ ---
326
+
327
+ ## ๐Ÿงช Running Tests
328
+
329
+ ```bash
330
+ bundle exec rspec
331
+
332
+ # Current status:
333
+ # 29 examples, 0 failures
334
+ # Line Coverage: 91.02%
335
+ ```
336
+
337
+ ---
338
+
339
+ ## ๐Ÿš€ Performance Tips
340
+
341
+ ### 1. Enable Caching (if using translation)
342
+ ```ruby
343
+ config.cache_store = Rails.cache
344
+ ```
345
+
346
+ ### 2. Choose the Right Model
347
+ - **High-volume:** `gpt-4o-mini`, `claude-3-5-haiku`, `gemini-1.5-flash`
348
+ - **Quality:** `gpt-4o`, `claude-3-5-sonnet`, `gemini-1.5-pro`
349
+
350
+ ### 3. Scale Your Job Processor
351
+
352
+ **Sidekiq:**
353
+ ```yaml
354
+ # config/sidekiq.yml
355
+ :concurrency: 25
356
+ :queues:
357
+ - [polylingo_chat_translations, 10]
358
+ - [default, 5]
359
+ ```
360
+
361
+ **Solid Queue:**
362
+ ```yaml
363
+ # config/solid_queue.yml
364
+ workers:
365
+ - queues: polylingo_chat_translations
366
+ threads: 5
367
+ processes: 3
368
+ ```
369
+
370
+ ---
371
+
372
+ ## ๐Ÿ”’ Security
373
+
374
+ 1. **API Keys:** Use environment variables, never commit
375
+ 2. **User Scoping:** Ensure users can only access their conversations
376
+ 3. **ActionCable Auth:** Secure channels with authentication
377
+ 4. **Rate Limiting:** Consider limiting message creation
378
+ 5. **Content Filtering:** Add profanity/spam filters if needed
379
+
380
+ ---
381
+
382
+ ## ๐Ÿ’ก Use Cases
383
+
384
+ ### Chat-Only Mode (No Translation)
385
+ Perfect for:
386
+ - Internal team chat
387
+ - Customer support (same language)
388
+ - Community forums
389
+ - Simple messaging features
390
+
391
+ ### With Translation
392
+ Perfect for:
393
+ - Global marketplaces
394
+ - International teams
395
+ - Multi-language support systems
396
+ - Cross-border collaboration
397
+
398
+ ---
399
+
400
+ ## ๐Ÿค Contributing
401
+
402
+ 1. Fork the repository
403
+ 2. Create your feature branch
404
+ 3. Write tests for your changes
405
+ 4. Commit and push
406
+ 5. Open a Pull Request
407
+
408
+ ---
409
+
410
+ ## ๐Ÿ“ License
411
+
412
+ MIT License
413
+
414
+ ---
415
+
416
+ ## ๐Ÿ‘ค Author
417
+
418
+ **Shoaib Malik**
419
+ - Email: shoaib2109@gmail.com
420
+ - GitHub: [@shoaibmalik786](https://github.com/shoaibmalik786)
421
+
422
+ ---
423
+
424
+ ## ๐Ÿ“ž Support
425
+
426
+ - ๐Ÿ› **Issues:** [GitHub Issues](https://github.com/shoaibmalik786/polylingo_chat/issues)
427
+ - ๐Ÿ“– **Documentation:** [GitHub Wiki](https://github.com/shoaibmalik786/polylingo_chat/wiki)
428
+ - ๐Ÿ’ฌ **Discussions:** [GitHub Discussions](https://github.com/shoaibmalik786/polylingo_chat/discussions)
429
+
430
+ ---
431
+
432
+ **Made with โค๏ธ for the global Rails community**
@@ -0,0 +1,107 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module PolylingoChat
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ source_root File.expand_path('templates', __dir__)
10
+
11
+ desc "Installs PolylingoChat with real-time multilingual chat"
12
+
13
+ def self.next_migration_number(dirname)
14
+ next_migration_number = current_migration_number(dirname) + 1
15
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
16
+ end
17
+
18
+ def copy_migrations
19
+ migration_template "create_conversations.rb", "db/migrate/create_conversations.rb"
20
+ migration_template "create_participants.rb", "db/migrate/create_participants.rb"
21
+ migration_template "create_messages.rb", "db/migrate/create_messages.rb"
22
+ end
23
+
24
+ def create_models
25
+ template "models/conversation.rb", "app/models/conversation.rb"
26
+ template "models/participant.rb", "app/models/participant.rb"
27
+ template "models/message.rb", "app/models/message.rb"
28
+ end
29
+
30
+ def create_channels
31
+ empty_directory "app/channels/application_cable"
32
+ template "channels/application_cable/channel.rb", "app/channels/application_cable/channel.rb"
33
+ template "channels/application_cable/connection.rb", "app/channels/application_cable/connection.rb"
34
+ template "channels/polylingo_chat_chat_channel.rb", "app/channels/polylingo_chat_chat_channel.rb"
35
+ end
36
+
37
+ def create_javascript_files
38
+ empty_directory "app/javascript/channels"
39
+ template "javascript/chat.js", "app/javascript/chat.js"
40
+ template "javascript/channels/consumer.js", "app/javascript/channels/consumer.js"
41
+ template "javascript/channels/index.js", "app/javascript/channels/index.js"
42
+ end
43
+
44
+ def configure_cable
45
+ if File.exist?("config/cable.yml")
46
+ gsub_file "config/cable.yml", /^development:\s*\n\s+adapter:\s+\w+.*$/m do |match|
47
+ "development:\n adapter: solid_cable\n polling_interval: 0.1.seconds\n message_retention: 1.day"
48
+ end
49
+ end
50
+ end
51
+
52
+ def setup_solid_cable
53
+ say "Setting up Solid Cable...", :green
54
+ rails_command "solid_cable:install", abort_on_failure: false
55
+ end
56
+
57
+ def download_actioncable
58
+ empty_directory "vendor/javascript"
59
+ say "Downloading ActionCable ESM module...", :green
60
+ run "curl -o vendor/javascript/@rails--actioncable.js https://ga.jspm.io/npm:@rails/actioncable@7.1.3/app/assets/javascripts/actioncable.esm.js"
61
+ end
62
+
63
+ def add_routes
64
+ route 'mount ActionCable.server => "/cable"'
65
+ route <<~RUBY
66
+ resources :conversations do
67
+ resources :messages, only: [:create]
68
+ end
69
+ RUBY
70
+ end
71
+
72
+ def update_importmap
73
+ if File.exist?("config/importmap.rb")
74
+ append_to_file "config/importmap.rb" do
75
+ <<~RUBY
76
+
77
+ # PolylingoChat real-time chat
78
+ pin "@rails/actioncable", to: "@rails--actioncable.js"
79
+ pin_all_from "app/javascript/channels", under: "channels"
80
+ pin "chat", to: "chat.js"
81
+ RUBY
82
+ end
83
+ end
84
+ end
85
+
86
+ def update_application_js
87
+ if File.exist?("app/javascript/application.js")
88
+ append_to_file "app/javascript/application.js" do
89
+ <<~JS
90
+
91
+ // PolylingoChat real-time chat
92
+ import "chat"
93
+ JS
94
+ end
95
+ end
96
+ end
97
+
98
+ def create_initializer
99
+ template "initializer.rb", "config/initializers/polylingo_chat.rb"
100
+ end
101
+
102
+ def show_readme
103
+ readme "README" if behavior == :invoke
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,48 @@
1
+ ===============================================================================
2
+
3
+ PolylingoChat has been installed successfully!
4
+
5
+ Next steps:
6
+
7
+ 1. Run migrations:
8
+ rails db:migrate
9
+
10
+ 2. Ensure your User model has a `preferred_language` column (optional):
11
+ rails g migration AddPreferredLanguageToUsers preferred_language:string
12
+ rails db:migrate
13
+
14
+ 3. Configure your AI provider in config/initializers/polylingo_chat.rb:
15
+ - Set config.api_key with your API key
16
+ - Choose your provider (:openai, :anthropic, or :gemini)
17
+ - Or leave api_key as nil for chat-only mode (no translation)
18
+
19
+ 4. Set up Solid Cable for production:
20
+ - Already configured for development
21
+ - For production, ensure database is configured in config/cable.yml
22
+
23
+ 5. Import chat.js in your application.js:
24
+ // Add to app/javascript/application.js
25
+ import "chat"
26
+
27
+ 6. Add this to your User model if it doesn't exist:
28
+ # app/models/user.rb
29
+ class User < ApplicationRecord
30
+ has_many :participants, dependent: :destroy
31
+ has_many :conversations, through: :participants
32
+ has_many :messages, foreign_key: 'sender_id', dependent: :destroy
33
+ end
34
+
35
+ 7. In your conversation view, add these window variables:
36
+ <script>
37
+ window.conversationId = <%= @conversation.id %>;
38
+ window.currentUserId = <%= current_user.id %>;
39
+ </script>
40
+
41
+ 8. Add a messages container in your view:
42
+ <div id="messages">
43
+ <!-- Messages will appear here in real-time -->
44
+ </div>
45
+
46
+ For more information, visit: https://github.com/yourusername/polylingo_chat
47
+
48
+ ===============================================================================
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Channel < ActionCable::Channel::Base
3
+ end
4
+ end
@@ -0,0 +1,30 @@
1
+ module ApplicationCable
2
+ class Connection < ActionCable::Connection::Base
3
+ identified_by :current_user
4
+
5
+ def connect
6
+ self.current_user = find_verified_user
7
+ end
8
+
9
+ private
10
+
11
+ def find_verified_user
12
+ # TODO: Implement your authentication logic here
13
+ # Example with Devise:
14
+ # if verified_user = env['warden'].user
15
+ # verified_user
16
+ # else
17
+ # reject_unauthorized_connection
18
+ # end
19
+
20
+ # For development/testing, accept user_id from request params or cookies:
21
+ user_id = request.params[:user_id] || cookies.encrypted[:user_id]
22
+
23
+ if user_id && (verified_user = User.find_by(id: user_id))
24
+ verified_user
25
+ else
26
+ reject_unauthorized_connection
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ class PolylinguoChatChannel < ApplicationCable::Channel
2
+ def subscribed
3
+ conversation_id = params[:conversation_id]
4
+
5
+ # Subscribe to conversation-level channel for demo
6
+ stream_from "conversation_#{conversation_id}"
7
+
8
+ # Also subscribe to user-specific channel for production use
9
+ stream_from "polylingo_chat_recipient_#{current_user.id}"
10
+ end
11
+
12
+ def unsubscribed
13
+ # Cleanup when channel is unsubscribed
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ class CreateConversations < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ create_table :conversations do |t|
4
+ t.string :title
5
+ t.boolean :private, default: true
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ class CreateMessages < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ create_table :messages do |t|
4
+ t.references :sender, null: false, foreign_key: { to_table: :users }
5
+ t.references :conversation, null: false, foreign_key: true
6
+ t.text :body
7
+ t.string :language
8
+ t.text :translated_body
9
+ t.boolean :translated, default: false
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ class CreateParticipants < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ create_table :participants do |t|
4
+ t.references :user, null: false, foreign_key: true
5
+ t.references :conversation, null: false, foreign_key: true
6
+ t.string :role
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :participants, [:user_id, :conversation_id], unique: true
11
+ end
12
+ end