smart_message 0.0.10 → 0.0.12

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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy-github-pages.yml +38 -0
  3. data/.gitignore +5 -0
  4. data/CHANGELOG.md +30 -0
  5. data/Gemfile.lock +35 -4
  6. data/README.md +169 -71
  7. data/Rakefile +29 -4
  8. data/docs/assets/images/ddq_architecture.svg +130 -0
  9. data/docs/assets/images/dlq_architecture.svg +115 -0
  10. data/docs/assets/images/enhanced-dual-publishing.svg +136 -0
  11. data/docs/assets/images/enhanced-fluent-api.svg +149 -0
  12. data/docs/assets/images/enhanced-microservices-routing.svg +115 -0
  13. data/docs/assets/images/enhanced-pattern-matching.svg +107 -0
  14. data/docs/assets/images/fluent-api-demo.svg +59 -0
  15. data/docs/assets/images/performance-comparison.svg +161 -0
  16. data/docs/assets/images/redis-basic-architecture.svg +53 -0
  17. data/docs/assets/images/redis-enhanced-architecture.svg +88 -0
  18. data/docs/assets/images/redis-queue-architecture.svg +101 -0
  19. data/docs/assets/images/smart_message.jpg +0 -0
  20. data/docs/assets/images/smart_message_walking.jpg +0 -0
  21. data/docs/assets/images/smartmessage_architecture_overview.svg +173 -0
  22. data/docs/assets/images/transport-comparison-matrix.svg +171 -0
  23. data/docs/assets/javascripts/mathjax.js +17 -0
  24. data/docs/assets/stylesheets/extra.css +51 -0
  25. data/docs/{addressing.md → core-concepts/addressing.md} +5 -7
  26. data/docs/{architecture.md → core-concepts/architecture.md} +78 -138
  27. data/docs/{dispatcher.md → core-concepts/dispatcher.md} +21 -21
  28. data/docs/{message_filtering.md → core-concepts/message-filtering.md} +2 -3
  29. data/docs/{message_processing.md → core-concepts/message-processing.md} +17 -17
  30. data/docs/{troubleshooting.md → development/troubleshooting.md} +7 -7
  31. data/docs/{examples.md → getting-started/examples.md} +115 -89
  32. data/docs/{getting-started.md → getting-started/quick-start.md} +47 -18
  33. data/docs/guides/redis-queue-getting-started.md +697 -0
  34. data/docs/guides/redis-queue-patterns.md +889 -0
  35. data/docs/guides/redis-queue-production.md +1091 -0
  36. data/docs/index.md +64 -0
  37. data/docs/{dead_letter_queue.md → reference/dead-letter-queue.md} +2 -3
  38. data/docs/{logging.md → reference/logging.md} +1 -1
  39. data/docs/{message_deduplication.md → reference/message-deduplication.md} +1 -0
  40. data/docs/{proc_handlers_summary.md → reference/proc-handlers.md} +7 -6
  41. data/docs/{serializers.md → reference/serializers.md} +3 -5
  42. data/docs/{transports.md → reference/transports.md} +133 -11
  43. data/docs/transports/memory-transport.md +374 -0
  44. data/docs/transports/redis-enhanced-transport.md +524 -0
  45. data/docs/transports/redis-queue-transport.md +1304 -0
  46. data/docs/transports/redis-transport-comparison.md +496 -0
  47. data/docs/transports/redis-transport.md +509 -0
  48. data/examples/README.md +98 -5
  49. data/examples/city_scenario/911_emergency_call_flow.svg +99 -0
  50. data/examples/city_scenario/README.md +515 -0
  51. data/examples/city_scenario/ai_visitor_intelligence_flow.svg +108 -0
  52. data/examples/city_scenario/citizen.rb +195 -0
  53. data/examples/city_scenario/city_diagram.svg +125 -0
  54. data/examples/city_scenario/common/health_monitor.rb +80 -0
  55. data/examples/city_scenario/common/logger.rb +30 -0
  56. data/examples/city_scenario/emergency_dispatch_center.rb +270 -0
  57. data/examples/city_scenario/fire_department.rb +446 -0
  58. data/examples/city_scenario/fire_emergency_flow.svg +95 -0
  59. data/examples/city_scenario/health_department.rb +100 -0
  60. data/examples/city_scenario/health_monitoring_system.svg +130 -0
  61. data/examples/city_scenario/house.rb +244 -0
  62. data/examples/city_scenario/local_bank.rb +217 -0
  63. data/examples/city_scenario/messages/emergency_911_message.rb +81 -0
  64. data/examples/city_scenario/messages/emergency_resolved_message.rb +43 -0
  65. data/examples/city_scenario/messages/fire_dispatch_message.rb +43 -0
  66. data/examples/city_scenario/messages/fire_emergency_message.rb +45 -0
  67. data/examples/city_scenario/messages/health_check_message.rb +22 -0
  68. data/examples/city_scenario/messages/health_status_message.rb +35 -0
  69. data/examples/city_scenario/messages/police_dispatch_message.rb +46 -0
  70. data/examples/city_scenario/messages/silent_alarm_message.rb +38 -0
  71. data/examples/city_scenario/police_department.rb +316 -0
  72. data/examples/city_scenario/redis_monitor.rb +129 -0
  73. data/examples/city_scenario/redis_stats.rb +743 -0
  74. data/examples/city_scenario/room_for_improvement.md +240 -0
  75. data/examples/city_scenario/security_emergency_flow.svg +95 -0
  76. data/examples/city_scenario/service_internal_architecture.svg +154 -0
  77. data/examples/city_scenario/smart_message_ai_agent.rb +364 -0
  78. data/examples/city_scenario/start_demo.sh +236 -0
  79. data/examples/city_scenario/stop_demo.sh +106 -0
  80. data/examples/city_scenario/visitor.rb +631 -0
  81. data/examples/{10_message_deduplication.rb → memory/01_message_deduplication_demo.rb} +1 -1
  82. data/examples/{09_dead_letter_queue_demo.rb → memory/02_dead_letter_queue_demo.rb} +13 -40
  83. data/examples/{01_point_to_point_orders.rb → memory/03_point_to_point_orders.rb} +1 -1
  84. data/examples/{02_publish_subscribe_events.rb → memory/04_publish_subscribe_events.rb} +2 -2
  85. data/examples/{03_many_to_many_chat.rb → memory/05_many_to_many_chat.rb} +4 -4
  86. data/examples/{show_me.rb → memory/06_pretty_print_demo.rb} +1 -1
  87. data/examples/{05_proc_handlers.rb → memory/07_proc_handlers_demo.rb} +2 -2
  88. data/examples/{06_custom_logger_example.rb → memory/08_custom_logger_demo.rb} +17 -14
  89. data/examples/{07_error_handling_scenarios.rb → memory/09_error_handling_demo.rb} +4 -4
  90. data/examples/{08_entity_addressing_basic.rb → memory/10_entity_addressing_basic.rb} +8 -8
  91. data/examples/{08_entity_addressing_with_filtering.rb → memory/11_entity_addressing_with_filtering.rb} +6 -6
  92. data/examples/{09_regex_filtering_microservices.rb → memory/12_regex_filtering_microservices.rb} +2 -2
  93. data/examples/{10_header_block_configuration.rb → memory/13_header_block_configuration.rb} +6 -6
  94. data/examples/{11_global_configuration_example.rb → memory/14_global_configuration_demo.rb} +19 -8
  95. data/examples/{show_logger.rb → memory/15_logger_demo.rb} +1 -1
  96. data/examples/memory/README.md +163 -0
  97. data/examples/memory/memory_transport_architecture.svg +90 -0
  98. data/examples/memory/point_to_point_pattern.svg +94 -0
  99. data/examples/memory/publish_subscribe_pattern.svg +125 -0
  100. data/examples/{04_redis_smart_home_iot.rb → redis/01_smart_home_iot_demo.rb} +5 -5
  101. data/examples/redis/README.md +230 -0
  102. data/examples/redis/alert_system_flow.svg +127 -0
  103. data/examples/redis/dashboard_status_flow.svg +107 -0
  104. data/examples/redis/device_command_flow.svg +113 -0
  105. data/examples/redis/redis_transport_architecture.svg +115 -0
  106. data/examples/{smart_home_iot_dataflow.md → redis/smart_home_iot_dataflow.md} +4 -116
  107. data/examples/redis/smart_home_system_architecture.svg +133 -0
  108. data/examples/redis_enhanced/README.md +319 -0
  109. data/examples/redis_enhanced/enhanced_01_basic_patterns.rb +233 -0
  110. data/examples/redis_enhanced/enhanced_02_fluent_api.rb +331 -0
  111. data/examples/redis_enhanced/enhanced_03_dual_publishing.rb +281 -0
  112. data/examples/redis_enhanced/enhanced_04_advanced_routing.rb +419 -0
  113. data/examples/redis_queue/01_basic_messaging.rb +221 -0
  114. data/examples/redis_queue/01_comprehensive_examples.rb +508 -0
  115. data/examples/redis_queue/02_pattern_routing.rb +405 -0
  116. data/examples/redis_queue/03_fluent_api.rb +422 -0
  117. data/examples/redis_queue/04_load_balancing.rb +486 -0
  118. data/examples/redis_queue/05_microservices.rb +735 -0
  119. data/examples/redis_queue/06_emergency_alerts.rb +777 -0
  120. data/examples/redis_queue/07_queue_management.rb +587 -0
  121. data/examples/redis_queue/README.md +366 -0
  122. data/examples/redis_queue/enhanced_01_basic_patterns.rb +233 -0
  123. data/examples/redis_queue/enhanced_02_fluent_api.rb +331 -0
  124. data/examples/redis_queue/enhanced_03_dual_publishing.rb +281 -0
  125. data/examples/redis_queue/enhanced_04_advanced_routing.rb +419 -0
  126. data/examples/redis_queue/redis_queue_architecture.svg +148 -0
  127. data/ideas/README.md +41 -0
  128. data/ideas/agents.md +1001 -0
  129. data/ideas/database_transport.md +980 -0
  130. data/ideas/improvement.md +359 -0
  131. data/ideas/meshage.md +1788 -0
  132. data/ideas/message_discovery.md +178 -0
  133. data/ideas/message_schema.md +1381 -0
  134. data/lib/smart_message/.idea/.gitignore +8 -0
  135. data/lib/smart_message/.idea/markdown.xml +6 -0
  136. data/lib/smart_message/.idea/misc.xml +4 -0
  137. data/lib/smart_message/.idea/modules.xml +8 -0
  138. data/lib/smart_message/.idea/smart_message.iml +16 -0
  139. data/lib/smart_message/.idea/vcs.xml +6 -0
  140. data/lib/smart_message/addressing.rb +15 -0
  141. data/lib/smart_message/base.rb +0 -2
  142. data/lib/smart_message/configuration.rb +1 -1
  143. data/lib/smart_message/logger.rb +15 -4
  144. data/lib/smart_message/plugins.rb +5 -2
  145. data/lib/smart_message/serializer.rb +14 -0
  146. data/lib/smart_message/transport/redis_enhanced_transport.rb +399 -0
  147. data/lib/smart_message/transport/redis_queue_transport.rb +555 -0
  148. data/lib/smart_message/transport/registry.rb +1 -0
  149. data/lib/smart_message/transport.rb +34 -1
  150. data/lib/smart_message/version.rb +1 -1
  151. data/lib/smart_message.rb +5 -52
  152. data/mkdocs.yml +184 -0
  153. data/p2p_plan.md +326 -0
  154. data/p2p_roadmap.md +287 -0
  155. data/smart_message.gemspec +2 -0
  156. data/smart_message.svg +51 -0
  157. metadata +170 -44
  158. data/docs/README.md +0 -57
  159. data/examples/dead_letters.jsonl +0 -12
  160. data/examples/temp.txt +0 -94
  161. data/examples/tmux_chat/README.md +0 -283
  162. data/examples/tmux_chat/bot_agent.rb +0 -278
  163. data/examples/tmux_chat/human_agent.rb +0 -199
  164. data/examples/tmux_chat/room_monitor.rb +0 -160
  165. data/examples/tmux_chat/shared_chat_system.rb +0 -328
  166. data/examples/tmux_chat/start_chat_demo.sh +0 -190
  167. data/examples/tmux_chat/stop_chat_demo.sh +0 -22
  168. /data/docs/{properties.md → core-concepts/properties.md} +0 -0
  169. /data/docs/{ideas_to_think_about.md → development/ideas.md} +0 -0
@@ -1,278 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # examples/tmux_chat/bot_agent.rb
3
- #
4
- # Bot agent for tmux chat visualization
5
-
6
- require_relative 'shared_chat_system'
7
-
8
- class BotChatAgent < BaseAgent
9
- def initialize(bot_id:, name:, capabilities: [])
10
- @capabilities = capabilities
11
- @command_count = 0
12
- super(agent_id: bot_id, name: name, agent_type: 'bot')
13
-
14
- log_display("🤖 Bot #{@name} online!")
15
- log_display("🔧 Capabilities: #{@capabilities.join(', ')}")
16
- log_display("⚡ Listening for commands and messages...")
17
- log_display("")
18
- end
19
-
20
- def setup_subscriptions
21
- # Subscribe to bot commands and chat messages
22
- BotCommandMessage.subscribe("BotChatAgent.handle_bot_command_#{@agent_id}")
23
- ChatMessage.subscribe("BotChatAgent.handle_chat_message_#{@agent_id}")
24
-
25
- # Register this instance
26
- @@bots ||= {}
27
- @@bots[@agent_id] = self
28
- end
29
-
30
- def run
31
- # Start processing messages and wait
32
- begin
33
- while true
34
- sleep(1)
35
- end
36
- rescue Interrupt
37
- shutdown
38
- end
39
- end
40
-
41
- # Class method routing for SmartMessage
42
- def self.method_missing(method_name, *args)
43
- if method_name.to_s.start_with?('handle_bot_command_')
44
- bot_id = method_name.to_s.split('_').last
45
- bot = (@@bots ||= {})[bot_id]
46
- bot&.handle_bot_command(*args)
47
- elsif method_name.to_s.start_with?('handle_chat_message_')
48
- bot_id = method_name.to_s.split('_').last
49
- bot = (@@bots ||= {})[bot_id]
50
- bot&.handle_chat_message(*args)
51
- else
52
- super
53
- end
54
- end
55
-
56
- def handle_bot_command(wrapper)
57
- message_header, message_payload = wrapper.split
58
- command_data = JSON.parse(message_payload)
59
-
60
- # Only handle commands in rooms we're in and commands we can handle
61
- return unless @active_rooms.include?(command_data['room_id'])
62
- return unless can_handle_command?(command_data['command'])
63
-
64
- @command_count += 1
65
- log_display("⚡ Processing command: /#{command_data['command']} (#{@command_count})")
66
-
67
- process_command(command_data)
68
- end
69
-
70
- def handle_chat_message(wrapper)
71
- message_header, message_payload = wrapper.split
72
- chat_data = JSON.parse(message_payload)
73
-
74
- # Only process messages from rooms we're in and not our own messages
75
- return unless @active_rooms.include?(chat_data['room_id'])
76
- return if chat_data['sender_id'] == @agent_id
77
-
78
- # Don't respond to other bots to avoid infinite loops
79
- return if chat_data['message_type'] == 'bot'
80
-
81
- # Log the message
82
- log_display("👁️ [#{chat_data['room_id']}] #{chat_data['sender_name']}: #{chat_data['content']}")
83
-
84
- # Check if it's a bot command
85
- if chat_data['content'].start_with?('/')
86
- handle_inline_command(chat_data)
87
- else
88
- # Respond to certain keywords from human users only
89
- respond_to_keywords(chat_data)
90
- end
91
- end
92
-
93
- def can_handle_command?(command)
94
- @capabilities.include?(command)
95
- end
96
-
97
- private
98
-
99
- def handle_inline_command(chat_data)
100
- content = chat_data['content']
101
- command_parts = content[1..-1].split(' ')
102
- command = command_parts.first
103
- parameters = command_parts[1..-1]
104
-
105
- return unless can_handle_command?(command)
106
-
107
- # Create bot command message
108
- bot_command = BotCommandMessage.new(
109
- command_id: "CMD-#{@agent_id}-#{Time.now.to_i}-#{rand(1000)}",
110
- room_id: chat_data['room_id'],
111
- user_id: chat_data['sender_id'],
112
- user_name: chat_data['sender_name'],
113
- command: command,
114
- parameters: parameters,
115
- timestamp: Time.now.iso8601,
116
- from: chat_data['sender_id']
117
- )
118
-
119
- bot_command.publish
120
- end
121
-
122
- def process_command(command_data)
123
- case command_data['command']
124
- when 'weather'
125
- handle_weather_command(command_data)
126
- when 'joke'
127
- handle_joke_command(command_data)
128
- when 'help'
129
- handle_help_command(command_data)
130
- when 'stats'
131
- handle_stats_command(command_data)
132
- when 'time'
133
- handle_time_command(command_data)
134
- when 'echo'
135
- handle_echo_command(command_data)
136
- else
137
- send_bot_response(
138
- room_id: command_data['room_id'],
139
- content: "🤷‍♂️ Sorry, I don't know how to handle /#{command_data['command']}"
140
- )
141
- end
142
- end
143
-
144
- def respond_to_keywords(chat_data)
145
- content = chat_data['content'].downcase
146
-
147
- if content.include?('hello') || content.include?('hi')
148
- send_bot_response(
149
- room_id: chat_data['room_id'],
150
- content: "Hello #{chat_data['sender_name']}! 👋"
151
- )
152
- elsif content.include?('help') && !content.start_with?('/')
153
- send_bot_response(
154
- room_id: chat_data['room_id'],
155
- content: "Type /help to see my commands! 🤖"
156
- )
157
- elsif content.include?('thank')
158
- send_bot_response(
159
- room_id: chat_data['room_id'],
160
- content: "You're welcome! 😊"
161
- )
162
- end
163
- end
164
-
165
- def handle_weather_command(command_data)
166
- location = command_data['parameters'].first || 'your location'
167
- weather_responses = [
168
- "☀️ It's sunny and 72°F in #{location}!",
169
- "🌧️ Looks like rain and 65°F in #{location}",
170
- "❄️ Snow expected, 32°F in #{location}",
171
- "⛅ Partly cloudy, 68°F in #{location}",
172
- "🌪️ Tornado warning in #{location}! (Just kidding, it's nice)"
173
- ]
174
-
175
- # Simulate API delay
176
- Thread.new do
177
- sleep(0.5)
178
- send_bot_response(
179
- room_id: command_data['room_id'],
180
- content: weather_responses.sample
181
- )
182
- end
183
- end
184
-
185
- def handle_joke_command(command_data)
186
- jokes = [
187
- "Why don't scientists trust atoms? Because they make up everything! 😄",
188
- "Why did the scarecrow win an award? He was outstanding in his field! 🌾",
189
- "What do you call a fake noodle? An impasta! 🍝",
190
- "Why don't eggs tell jokes? They'd crack each other up! 🥚",
191
- "What do you call a sleeping bull? A bulldozer! 😴",
192
- "Why don't robots ever panic? They have enough bytes! 🤖"
193
- ]
194
-
195
- send_bot_response(
196
- room_id: command_data['room_id'],
197
- content: jokes.sample
198
- )
199
- end
200
-
201
- def handle_help_command(command_data)
202
- help_text = @capabilities.map do |cmd|
203
- " /#{cmd} - #{get_command_description(cmd)}"
204
- end.join("\n")
205
-
206
- send_bot_response(
207
- room_id: command_data['room_id'],
208
- content: "🤖 #{@name} Commands:\n#{help_text}"
209
- )
210
- end
211
-
212
- def handle_stats_command(command_data)
213
- send_bot_response(
214
- room_id: command_data['room_id'],
215
- content: "📊 Bot Stats:\n" +
216
- " • Active rooms: #{@active_rooms.length}\n" +
217
- " • Commands processed: #{@command_count}\n" +
218
- " • Capabilities: #{@capabilities.length}\n" +
219
- " • Uptime: #{Time.now.strftime('%H:%M:%S')}"
220
- )
221
- end
222
-
223
- def handle_time_command(command_data)
224
- timezone = command_data['parameters'].first || 'local'
225
- current_time = Time.now.strftime('%Y-%m-%d %H:%M:%S')
226
-
227
- send_bot_response(
228
- room_id: command_data['room_id'],
229
- content: "🕒 Current time (#{timezone}): #{current_time}"
230
- )
231
- end
232
-
233
- def handle_echo_command(command_data)
234
- message = command_data['parameters'].join(' ')
235
- if message.empty?
236
- message = "Echo! Echo! Echo! 📢"
237
- end
238
-
239
- send_bot_response(
240
- room_id: command_data['room_id'],
241
- content: "🔊 Echo: #{message}"
242
- )
243
- end
244
-
245
- def get_command_description(command)
246
- descriptions = {
247
- 'weather' => 'Get weather information',
248
- 'joke' => 'Tell a random joke',
249
- 'help' => 'Show this help message',
250
- 'stats' => 'Show bot statistics',
251
- 'time' => 'Show current time',
252
- 'echo' => 'Echo your message'
253
- }
254
-
255
- descriptions[command] || 'No description available'
256
- end
257
-
258
- def send_bot_response(room_id:, content:)
259
- send_message(room_id: room_id, content: content, message_type: 'bot')
260
- log_display("💬 Replied to [#{room_id}]: #{content}")
261
- end
262
- end
263
-
264
- # Main execution
265
- if __FILE__ == $0
266
- if ARGV.length < 2
267
- puts "Usage: #{$0} <bot_id> <name> [capability1,capability2,...]"
268
- puts "Example: #{$0} helpbot HelpBot help,stats,time"
269
- exit 1
270
- end
271
-
272
- bot_id = ARGV[0]
273
- name = ARGV[1]
274
- capabilities = ARGV[2] ? ARGV[2].split(',') : ['help']
275
-
276
- bot = BotChatAgent.new(bot_id: bot_id, name: name, capabilities: capabilities)
277
- bot.run
278
- end
@@ -1,199 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # examples/tmux_chat/human_agent.rb
3
- #
4
- # Human chat agent for tmux visualization
5
-
6
- require_relative 'shared_chat_system'
7
-
8
- begin
9
- require 'io/console'
10
- rescue LoadError
11
- # Console methods may not be available, handled gracefully
12
- end
13
-
14
- class HumanChatAgent < BaseAgent
15
- def initialize(user_id:, name:)
16
- @message_counter = 0
17
- super(agent_id: user_id, name: name, agent_type: 'human')
18
-
19
- log_display("👤 Human agent #{@name} ready!")
20
- log_display("Commands: /join <room>, /leave <room>, /list, /quit")
21
- log_display("Type messages to send to current rooms")
22
- log_display("")
23
- end
24
-
25
- def setup_subscriptions
26
- # Subscribe to chat messages
27
- ChatMessage.subscribe("HumanChatAgent.handle_chat_message_#{@agent_id}")
28
- SystemNotificationMessage.subscribe("HumanChatAgent.handle_system_notification_#{@agent_id}")
29
-
30
- # Register this instance for method dispatch
31
- @@agents ||= {}
32
- @@agents[@agent_id] = self
33
- end
34
-
35
- def start_interactive_session
36
- while true
37
- begin
38
- print "> "
39
- input = STDIN.gets&.chomp
40
- break if input.nil?
41
-
42
- next if input.strip.empty?
43
-
44
- if input.start_with?('/')
45
- handle_command(input)
46
- else
47
- send_to_active_rooms(input)
48
- end
49
- rescue Interrupt
50
- break
51
- end
52
- end
53
-
54
- shutdown
55
- end
56
-
57
- # Class method routing for SmartMessage
58
- def self.method_missing(method_name, *args)
59
- if method_name.to_s.start_with?('handle_chat_message_')
60
- user_id = method_name.to_s.split('_').last
61
- agent = (@@agents ||= {})[user_id]
62
- agent&.handle_chat_message(*args)
63
- elsif method_name.to_s.start_with?('handle_system_notification_')
64
- user_id = method_name.to_s.split('_').last
65
- agent = (@@agents ||= {})[user_id]
66
- agent&.handle_system_notification(*args)
67
- else
68
- super
69
- end
70
- end
71
-
72
- def handle_chat_message(wrapper)
73
- message_header, message_payload = wrapper.split
74
- chat_data = JSON.parse(message_payload)
75
-
76
- # Only process messages from rooms we're in and not our own messages
77
- return unless @active_rooms.include?(chat_data['room_id'])
78
- return if chat_data['sender_id'] == @agent_id
79
-
80
- sender_emoji = case chat_data['message_type']
81
- when 'bot' then '🤖'
82
- when 'system' then '🔔'
83
- else '👤'
84
- end
85
-
86
- log_display("#{sender_emoji} [#{chat_data['room_id']}] #{chat_data['sender_name']}: #{chat_data['content']}")
87
-
88
- # Auto-respond if mentioned
89
- if chat_data['mentions']&.include?(@name.downcase) || chat_data['content'].include?("@#{@name}")
90
- respond_to_mention(chat_data)
91
- end
92
- end
93
-
94
- def handle_system_notification(wrapper)
95
- message_header, message_payload = wrapper.split
96
- notif_data = JSON.parse(message_payload)
97
-
98
- # Only process notifications from rooms we're in
99
- return unless @active_rooms.include?(notif_data['room_id'])
100
-
101
- log_display("🔔 [#{notif_data['room_id']}] #{notif_data['content']}")
102
- end
103
-
104
- private
105
-
106
- def handle_command(input)
107
- parts = input[1..-1].split(' ')
108
- command = parts[0]
109
- args = parts[1..-1]
110
-
111
- case command
112
- when 'join'
113
- if args.empty?
114
- log_display("❌ Usage: /join <room_id>")
115
- else
116
- join_room(args[0])
117
- update_display_header
118
- end
119
- when 'leave'
120
- if args.empty?
121
- log_display("❌ Usage: /leave <room_id>")
122
- else
123
- leave_room(args[0])
124
- update_display_header
125
- end
126
- when 'list'
127
- log_display("📋 Active rooms: #{@active_rooms.empty? ? 'none' : @active_rooms.join(', ')}")
128
- when 'quit', 'exit'
129
- log_display("👋 Goodbye!")
130
- exit(0)
131
- when 'help'
132
- log_display("📖 Commands:")
133
- log_display(" /join <room> - Join a chat room")
134
- log_display(" /leave <room> - Leave a chat room")
135
- log_display(" /list - List active rooms")
136
- log_display(" /quit - Exit the chat")
137
- else
138
- log_display("❌ Unknown command: /#{command}. Type /help for help.")
139
- end
140
- end
141
-
142
- def send_to_active_rooms(message)
143
- if @active_rooms.empty?
144
- log_display("❌ You're not in any rooms. Use /join <room> to join a room.")
145
- return
146
- end
147
-
148
- @active_rooms.each do |room_id|
149
- send_message(room_id: room_id, content: message)
150
- end
151
- end
152
-
153
- def respond_to_mention(chat_data)
154
- responses = [
155
- "Thanks for mentioning me!",
156
- "I'm here, what's up?",
157
- "How can I help?",
158
- "Yes, I saw that!",
159
- "Interesting point!"
160
- ]
161
-
162
- # Delay response slightly to make it feel natural
163
- Thread.new do
164
- sleep(0.5 + rand)
165
- send_message(
166
- room_id: chat_data['room_id'],
167
- content: responses.sample
168
- )
169
- end
170
- end
171
-
172
- def update_display_header
173
- # Move cursor to update rooms line
174
- print "\033[4;9H" # Move to line 4, column 9
175
- print "#{@active_rooms.join(', ').ljust(42)}"
176
- # Move cursor to bottom (default to 40 lines if console unavailable)
177
- max_lines = begin
178
- IO.console&.winsize&.first || 40
179
- rescue
180
- 40
181
- end
182
- print "\033[#{max_lines};1H"
183
- end
184
- end
185
-
186
- # Main execution
187
- if __FILE__ == $0
188
- if ARGV.length < 2
189
- puts "Usage: #{$0} <user_id> <name>"
190
- puts "Example: #{$0} alice Alice"
191
- exit 1
192
- end
193
-
194
- user_id = ARGV[0]
195
- name = ARGV[1]
196
-
197
- agent = HumanChatAgent.new(user_id: user_id, name: name)
198
- agent.start_interactive_session
199
- end
@@ -1,160 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # examples/tmux_chat/room_monitor.rb
3
- #
4
- # Room activity monitor for tmux chat visualization
5
-
6
- require_relative 'shared_chat_system'
7
-
8
- begin
9
- require 'io/console'
10
- rescue LoadError
11
- # Console methods may not be available, handled gracefully
12
- end
13
-
14
- class RoomMonitor < BaseAgent
15
- def initialize(room_id)
16
- @room_id = room_id
17
- @message_count = 0
18
- @user_count = 0
19
- @last_activity = Time.now
20
- @participants = Set.new
21
-
22
- super(agent_id: "monitor-#{room_id}", name: "Room Monitor", agent_type: 'monitor')
23
-
24
- join_room(@room_id)
25
- log_display("🏠 Monitoring room: #{@room_id}")
26
- log_display("📊 Waiting for activity...")
27
- log_display("")
28
- end
29
-
30
- def setup_subscriptions
31
- ChatMessage.subscribe("RoomMonitor.handle_chat_message_#{@agent_id}")
32
- SystemNotificationMessage.subscribe("RoomMonitor.handle_system_notification_#{@agent_id}")
33
-
34
- @@monitors ||= {}
35
- @@monitors[@agent_id] = self
36
- end
37
-
38
- def run
39
- # Update display every few seconds
40
- begin
41
- while true
42
- sleep(2)
43
- update_stats_display
44
- end
45
- rescue Interrupt
46
- shutdown
47
- end
48
- end
49
-
50
- # Class method routing
51
- def self.method_missing(method_name, *args)
52
- if method_name.to_s.start_with?('handle_chat_message_')
53
- monitor_id = method_name.to_s.split('_', 4).last
54
- monitor = (@@monitors ||= {})[monitor_id]
55
- monitor&.handle_chat_message(*args)
56
- elsif method_name.to_s.start_with?('handle_system_notification_')
57
- monitor_id = method_name.to_s.split('_', 4).last
58
- monitor = (@@monitors ||= {})[monitor_id]
59
- monitor&.handle_system_notification(*args)
60
- else
61
- super
62
- end
63
- end
64
-
65
- def handle_chat_message(wrapper)
66
- message_header, message_payload = wrapper.split
67
- chat_data = JSON.parse(message_payload)
68
-
69
- return unless chat_data['room_id'] == @room_id
70
- return if chat_data['sender_id'] == @agent_id
71
-
72
- @message_count += 1
73
- @last_activity = Time.now
74
- @participants.add(chat_data['sender_name'])
75
-
76
- sender_emoji = case chat_data['message_type']
77
- when 'bot' then '🤖'
78
- when 'system' then '🔔'
79
- else '👤'
80
- end
81
-
82
- # Show the message with metadata
83
- timestamp = Time.now.strftime("%H:%M:%S")
84
- log_display("#{sender_emoji} [#{timestamp}] #{chat_data['sender_name']}: #{chat_data['content']}")
85
-
86
- # Check for commands
87
- if chat_data['content'].start_with?('/')
88
- log_display("⚡ Command detected: #{chat_data['content']}")
89
- end
90
-
91
- # Check for mentions
92
- if chat_data['mentions'] && !chat_data['mentions'].empty?
93
- log_display("🏷️ Mentions: #{chat_data['mentions'].join(', ')}")
94
- end
95
- end
96
-
97
- def handle_system_notification(wrapper)
98
- message_header, message_payload = wrapper.split
99
- notif_data = JSON.parse(message_payload)
100
-
101
- return unless notif_data['room_id'] == @room_id
102
-
103
- case notif_data['notification_type']
104
- when 'user_joined'
105
- @user_count += 1
106
- log_display("📥 #{notif_data['content']}")
107
- when 'user_left'
108
- @user_count = [@user_count - 1, 0].max
109
- log_display("📤 #{notif_data['content']}")
110
- else
111
- log_display("🔔 #{notif_data['content']}")
112
- end
113
-
114
- @last_activity = Time.now
115
- end
116
-
117
- private
118
-
119
- def update_stats_display
120
- # Move cursor to update stats
121
- time_since_activity = Time.now - @last_activity
122
- activity_status = if time_since_activity < 10
123
- "🟢 Active"
124
- elsif time_since_activity < 60
125
- "🟡 Quiet (#{time_since_activity.to_i}s ago)"
126
- else
127
- "🔴 Inactive (#{(time_since_activity / 60).to_i}m ago)"
128
- end
129
-
130
- # Update the header area with stats
131
- print "\033[2;2H" # Move to line 2
132
- room_info = " ROOM: #{@room_id} | #{activity_status} "
133
- print room_info.center(50)
134
-
135
- print "\033[4;2H" # Move to line 4
136
- stats_info = " Messages: #{@message_count} | Participants: #{@participants.size} "
137
- print stats_info.center(50)
138
-
139
- # Move cursor back to bottom (default to 40 lines if console unavailable)
140
- max_lines = begin
141
- IO.console&.winsize&.first || 40
142
- rescue
143
- 40
144
- end
145
- print "\033[#{max_lines};1H"
146
- end
147
- end
148
-
149
- # Main execution
150
- if __FILE__ == $0
151
- if ARGV.empty?
152
- puts "Usage: #{$0} <room_id>"
153
- puts "Example: #{$0} general"
154
- exit 1
155
- end
156
-
157
- room_id = ARGV[0]
158
- monitor = RoomMonitor.new(room_id)
159
- monitor.run
160
- end