smart_message 0.0.1
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.
- checksums.yaml +7 -0
- data/.envrc +3 -0
- data/.gitignore +8 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +100 -0
- data/COMMITS.md +196 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +71 -0
- data/README.md +303 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/docs/README.md +52 -0
- data/docs/architecture.md +370 -0
- data/docs/dispatcher.md +593 -0
- data/docs/examples.md +808 -0
- data/docs/getting-started.md +235 -0
- data/docs/ideas_to_think_about.md +329 -0
- data/docs/serializers.md +575 -0
- data/docs/transports.md +501 -0
- data/docs/troubleshooting.md +582 -0
- data/examples/01_point_to_point_orders.rb +200 -0
- data/examples/02_publish_subscribe_events.rb +364 -0
- data/examples/03_many_to_many_chat.rb +608 -0
- data/examples/README.md +335 -0
- data/examples/tmux_chat/README.md +283 -0
- data/examples/tmux_chat/bot_agent.rb +272 -0
- data/examples/tmux_chat/human_agent.rb +197 -0
- data/examples/tmux_chat/room_monitor.rb +158 -0
- data/examples/tmux_chat/shared_chat_system.rb +295 -0
- data/examples/tmux_chat/start_chat_demo.sh +190 -0
- data/examples/tmux_chat/stop_chat_demo.sh +22 -0
- data/lib/simple_stats.rb +57 -0
- data/lib/smart_message/base.rb +284 -0
- data/lib/smart_message/dispatcher/.keep +0 -0
- data/lib/smart_message/dispatcher.rb +146 -0
- data/lib/smart_message/errors.rb +29 -0
- data/lib/smart_message/header.rb +20 -0
- data/lib/smart_message/logger/base.rb +8 -0
- data/lib/smart_message/logger.rb +7 -0
- data/lib/smart_message/serializer/base.rb +23 -0
- data/lib/smart_message/serializer/json.rb +22 -0
- data/lib/smart_message/serializer.rb +10 -0
- data/lib/smart_message/transport/base.rb +85 -0
- data/lib/smart_message/transport/memory_transport.rb +69 -0
- data/lib/smart_message/transport/registry.rb +59 -0
- data/lib/smart_message/transport/stdout_transport.rb +62 -0
- data/lib/smart_message/transport.rb +41 -0
- data/lib/smart_message/version.rb +7 -0
- data/lib/smart_message/wrapper.rb +43 -0
- data/lib/smart_message.rb +54 -0
- data/smart_message.gemspec +53 -0
- metadata +252 -0
@@ -0,0 +1,295 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# examples/tmux_chat/shared_chat_system.rb
|
3
|
+
#
|
4
|
+
# Shared messaging system for the tmux chat visualization
|
5
|
+
# This provides the core message types and file-based transport for inter-pane communication
|
6
|
+
|
7
|
+
require_relative '../../lib/smart_message'
|
8
|
+
require 'fileutils'
|
9
|
+
|
10
|
+
# Create shared message queues directory
|
11
|
+
SHARED_DIR = '/tmp/smart_message_chat'
|
12
|
+
FileUtils.mkdir_p(SHARED_DIR)
|
13
|
+
|
14
|
+
# File-based transport for tmux communication
|
15
|
+
class FileTransport < SmartMessage::Transport::Base
|
16
|
+
def default_options
|
17
|
+
{
|
18
|
+
queue_dir: SHARED_DIR,
|
19
|
+
poll_interval: 0.1
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def configure
|
24
|
+
@queue_dir = @options[:queue_dir]
|
25
|
+
@poll_interval = @options[:poll_interval]
|
26
|
+
@running = false
|
27
|
+
@subscriber_thread = nil
|
28
|
+
|
29
|
+
FileUtils.mkdir_p(@queue_dir)
|
30
|
+
end
|
31
|
+
|
32
|
+
def publish(message_header, message_payload)
|
33
|
+
message_data = {
|
34
|
+
header: message_header.to_h,
|
35
|
+
payload: message_payload,
|
36
|
+
timestamp: Time.now.to_f
|
37
|
+
}
|
38
|
+
|
39
|
+
# Write to room-specific queue file
|
40
|
+
room_id = extract_room_id(message_payload)
|
41
|
+
queue_file = File.join(@queue_dir, "#{room_id}.queue")
|
42
|
+
|
43
|
+
File.open(queue_file, 'a') do |f|
|
44
|
+
f.puts(JSON.generate(message_data))
|
45
|
+
f.flush
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def start_subscriber
|
50
|
+
return if @running
|
51
|
+
|
52
|
+
@running = true
|
53
|
+
@subscriber_thread = Thread.new do
|
54
|
+
processed_positions = {}
|
55
|
+
|
56
|
+
while @running
|
57
|
+
Dir.glob(File.join(@queue_dir, "*.queue")).each do |queue_file|
|
58
|
+
room_id = File.basename(queue_file, '.queue')
|
59
|
+
|
60
|
+
if File.exist?(queue_file)
|
61
|
+
lines = File.readlines(queue_file)
|
62
|
+
start_pos = processed_positions[room_id] || 0
|
63
|
+
|
64
|
+
lines[start_pos..-1].each do |line|
|
65
|
+
begin
|
66
|
+
message_data = JSON.parse(line.strip)
|
67
|
+
header = SmartMessage::Header.new(message_data['header'])
|
68
|
+
receive(header, message_data['payload'])
|
69
|
+
rescue JSON::ParserError
|
70
|
+
# Skip malformed lines
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
processed_positions[room_id] = lines.length
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
sleep(@poll_interval)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def stop_subscriber
|
84
|
+
@running = false
|
85
|
+
@subscriber_thread&.join
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def extract_room_id(payload)
|
91
|
+
begin
|
92
|
+
data = JSON.parse(payload)
|
93
|
+
data['room_id'] || 'global'
|
94
|
+
rescue
|
95
|
+
'global'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Register the file transport
|
101
|
+
SmartMessage::Transport.register(:file, FileTransport)
|
102
|
+
|
103
|
+
# Define the Chat Message
|
104
|
+
class ChatMessage < SmartMessage::Base
|
105
|
+
property :message_id
|
106
|
+
property :room_id
|
107
|
+
property :sender_id
|
108
|
+
property :sender_name
|
109
|
+
property :content
|
110
|
+
property :message_type # 'user', 'bot', 'system'
|
111
|
+
property :timestamp
|
112
|
+
property :mentions
|
113
|
+
property :metadata
|
114
|
+
|
115
|
+
config do
|
116
|
+
transport SmartMessage::Transport.create(:file)
|
117
|
+
serializer SmartMessage::Serializer::JSON.new
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.process(message_header, message_payload)
|
121
|
+
# Default processing - agents will override this
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Define Bot Command Message
|
126
|
+
class BotCommandMessage < SmartMessage::Base
|
127
|
+
property :command_id
|
128
|
+
property :room_id
|
129
|
+
property :user_id
|
130
|
+
property :user_name
|
131
|
+
property :command
|
132
|
+
property :parameters
|
133
|
+
property :timestamp
|
134
|
+
|
135
|
+
config do
|
136
|
+
transport SmartMessage::Transport.create(:file)
|
137
|
+
serializer SmartMessage::Serializer::JSON.new
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.process(message_header, message_payload)
|
141
|
+
# Default processing - bots will override this
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Define System Notification Message
|
146
|
+
class SystemNotificationMessage < SmartMessage::Base
|
147
|
+
property :notification_id
|
148
|
+
property :room_id
|
149
|
+
property :notification_type
|
150
|
+
property :content
|
151
|
+
property :timestamp
|
152
|
+
property :metadata
|
153
|
+
|
154
|
+
config do
|
155
|
+
transport SmartMessage::Transport.create(:file)
|
156
|
+
serializer SmartMessage::Serializer::JSON.new
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.process(message_header, message_payload)
|
160
|
+
# Default processing
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Base Agent class with display utilities
|
165
|
+
class BaseAgent
|
166
|
+
attr_reader :agent_id, :name, :active_rooms
|
167
|
+
|
168
|
+
def initialize(agent_id:, name:, agent_type: 'agent')
|
169
|
+
@agent_id = agent_id
|
170
|
+
@name = name
|
171
|
+
@agent_type = agent_type
|
172
|
+
@active_rooms = []
|
173
|
+
@display_buffer = []
|
174
|
+
@transport = SmartMessage::Transport.create(:file)
|
175
|
+
|
176
|
+
setup_display
|
177
|
+
setup_subscriptions
|
178
|
+
start_message_processing
|
179
|
+
end
|
180
|
+
|
181
|
+
def join_room(room_id)
|
182
|
+
return if @active_rooms.include?(room_id)
|
183
|
+
|
184
|
+
@active_rooms << room_id
|
185
|
+
log_display("📥 Joined room: #{room_id}")
|
186
|
+
|
187
|
+
send_system_notification(
|
188
|
+
room_id: room_id,
|
189
|
+
notification_type: 'user_joined',
|
190
|
+
content: "#{@name} joined the room"
|
191
|
+
)
|
192
|
+
end
|
193
|
+
|
194
|
+
def leave_room(room_id)
|
195
|
+
return unless @active_rooms.include?(room_id)
|
196
|
+
|
197
|
+
@active_rooms.delete(room_id)
|
198
|
+
log_display("📤 Left room: #{room_id}")
|
199
|
+
|
200
|
+
send_system_notification(
|
201
|
+
room_id: room_id,
|
202
|
+
notification_type: 'user_left',
|
203
|
+
content: "#{@name} left the room"
|
204
|
+
)
|
205
|
+
end
|
206
|
+
|
207
|
+
def send_message(room_id:, content:, message_type: 'user')
|
208
|
+
return unless @active_rooms.include?(room_id)
|
209
|
+
|
210
|
+
message = ChatMessage.new(
|
211
|
+
message_id: generate_message_id,
|
212
|
+
room_id: room_id,
|
213
|
+
sender_id: @agent_id,
|
214
|
+
sender_name: @name,
|
215
|
+
content: content,
|
216
|
+
message_type: message_type,
|
217
|
+
timestamp: Time.now.iso8601,
|
218
|
+
mentions: extract_mentions(content),
|
219
|
+
metadata: { agent_type: @agent_type }
|
220
|
+
)
|
221
|
+
|
222
|
+
log_display("💬 [#{room_id}] #{@name}: #{content}")
|
223
|
+
message.publish
|
224
|
+
end
|
225
|
+
|
226
|
+
def shutdown
|
227
|
+
@active_rooms.dup.each { |room_id| leave_room(room_id) }
|
228
|
+
@transport.stop_subscriber
|
229
|
+
log_display("🔴 #{@name} shutdown")
|
230
|
+
end
|
231
|
+
|
232
|
+
protected
|
233
|
+
|
234
|
+
def setup_display
|
235
|
+
puts "\033[2J\033[H" # Clear screen
|
236
|
+
puts "┌─" + "─" * 50 + "┐"
|
237
|
+
puts "│ #{@agent_type.upcase}: #{@name.center(44)} │"
|
238
|
+
puts "├─" + "─" * 50 + "┤"
|
239
|
+
puts "│ Rooms: #{@active_rooms.join(', ').ljust(42)} │"
|
240
|
+
puts "├─" + "─" * 50 + "┤"
|
241
|
+
puts "│ Messages:".ljust(51) + " │"
|
242
|
+
puts "└─" + "─" * 50 + "┘"
|
243
|
+
puts
|
244
|
+
end
|
245
|
+
|
246
|
+
def log_display(message)
|
247
|
+
timestamp = Time.now.strftime("%H:%M:%S")
|
248
|
+
puts "[#{timestamp}] #{message}"
|
249
|
+
end
|
250
|
+
|
251
|
+
def setup_subscriptions
|
252
|
+
# Override in subclasses
|
253
|
+
end
|
254
|
+
|
255
|
+
def start_message_processing
|
256
|
+
@transport.start_subscriber
|
257
|
+
end
|
258
|
+
|
259
|
+
def send_system_notification(room_id:, notification_type:, content:)
|
260
|
+
notification = SystemNotificationMessage.new(
|
261
|
+
notification_id: "NOTIF-#{Time.now.to_i}-#{rand(1000)}",
|
262
|
+
room_id: room_id,
|
263
|
+
notification_type: notification_type,
|
264
|
+
content: content,
|
265
|
+
timestamp: Time.now.iso8601,
|
266
|
+
metadata: { triggered_by: @agent_id }
|
267
|
+
)
|
268
|
+
|
269
|
+
notification.publish
|
270
|
+
end
|
271
|
+
|
272
|
+
def generate_message_id
|
273
|
+
"MSG-#{@agent_id}-#{Time.now.to_i}-#{rand(1000)}"
|
274
|
+
end
|
275
|
+
|
276
|
+
def extract_mentions(content)
|
277
|
+
content.scan(/@(\w+)/).flatten
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Cleanup utility
|
282
|
+
def cleanup_shared_queues
|
283
|
+
FileUtils.rm_rf(SHARED_DIR) if Dir.exist?(SHARED_DIR)
|
284
|
+
end
|
285
|
+
|
286
|
+
# Signal handling for cleanup
|
287
|
+
trap('INT') do
|
288
|
+
cleanup_shared_queues
|
289
|
+
exit
|
290
|
+
end
|
291
|
+
|
292
|
+
trap('TERM') do
|
293
|
+
cleanup_shared_queues
|
294
|
+
exit
|
295
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# examples/tmux_chat/start_chat_demo.sh
|
3
|
+
#
|
4
|
+
# Tmux session manager for the many-to-many chat visualization
|
5
|
+
|
6
|
+
set -e
|
7
|
+
|
8
|
+
SESSION_NAME="smart_message_chat"
|
9
|
+
CHAT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
10
|
+
|
11
|
+
# Colors for output
|
12
|
+
RED='\033[0;31m'
|
13
|
+
GREEN='\033[0;32m'
|
14
|
+
YELLOW='\033[1;33m'
|
15
|
+
BLUE='\033[0;34m'
|
16
|
+
NC='\033[0m' # No Color
|
17
|
+
|
18
|
+
echo -e "${BLUE}🚀 Starting SmartMessage Tmux Chat Demo${NC}"
|
19
|
+
echo -e "${BLUE}======================================${NC}"
|
20
|
+
|
21
|
+
# Check if tmux is installed
|
22
|
+
if ! command -v tmux &> /dev/null; then
|
23
|
+
echo -e "${RED}❌ tmux is not installed. Please install tmux first.${NC}"
|
24
|
+
echo "On macOS: brew install tmux"
|
25
|
+
echo "On Ubuntu: sudo apt-get install tmux"
|
26
|
+
exit 1
|
27
|
+
fi
|
28
|
+
|
29
|
+
# Check if Ruby is available
|
30
|
+
if ! command -v ruby &> /dev/null; then
|
31
|
+
echo -e "${RED}❌ Ruby is not installed.${NC}"
|
32
|
+
exit 1
|
33
|
+
fi
|
34
|
+
|
35
|
+
# Clean up any existing session
|
36
|
+
if tmux has-session -t $SESSION_NAME 2>/dev/null; then
|
37
|
+
echo -e "${YELLOW}🧹 Cleaning up existing session...${NC}"
|
38
|
+
tmux kill-session -t $SESSION_NAME
|
39
|
+
fi
|
40
|
+
|
41
|
+
# Clean up shared message queues
|
42
|
+
echo -e "${YELLOW}🧹 Cleaning up message queues...${NC}"
|
43
|
+
rm -rf /tmp/smart_message_chat
|
44
|
+
|
45
|
+
echo -e "${GREEN}📺 Creating tmux session layout...${NC}"
|
46
|
+
|
47
|
+
# Create new session with first window
|
48
|
+
tmux new-session -d -s $SESSION_NAME -x 120 -y 40
|
49
|
+
|
50
|
+
# Rename first window and set up control center
|
51
|
+
tmux rename-window -t $SESSION_NAME:0 "Chat-Control"
|
52
|
+
|
53
|
+
# Window 0: Control Center (2x2 layout)
|
54
|
+
echo -e "${GREEN}🏢 Setting up Control Center...${NC}"
|
55
|
+
|
56
|
+
# Top left: General room monitor
|
57
|
+
tmux send-keys -t $SESSION_NAME:0 "cd '$CHAT_DIR'" C-m
|
58
|
+
tmux send-keys -t $SESSION_NAME:0 "ruby room_monitor.rb general" C-m
|
59
|
+
|
60
|
+
# Split horizontally for top right: Tech room monitor
|
61
|
+
tmux split-window -t $SESSION_NAME:0 -h
|
62
|
+
tmux send-keys -t $SESSION_NAME:0.1 "cd '$CHAT_DIR'" C-m
|
63
|
+
tmux send-keys -t $SESSION_NAME:0.1 "ruby room_monitor.rb tech" C-m
|
64
|
+
|
65
|
+
# Split vertically (bottom left): Random room monitor
|
66
|
+
tmux split-window -t $SESSION_NAME:0.0 -v
|
67
|
+
tmux send-keys -t $SESSION_NAME:0.2 "cd '$CHAT_DIR'" C-m
|
68
|
+
tmux send-keys -t $SESSION_NAME:0.2 "ruby room_monitor.rb random" C-m
|
69
|
+
|
70
|
+
# Split vertically (bottom right): System overview
|
71
|
+
tmux split-window -t $SESSION_NAME:0.1 -v
|
72
|
+
tmux send-keys -t $SESSION_NAME:0.3 "cd '$CHAT_DIR'" C-m
|
73
|
+
tmux send-keys -t $SESSION_NAME:0.3 "echo 'SmartMessage Chat System'; echo '========================'; echo 'Rooms: general, tech, random'; echo 'Agents starting up...'; echo ''; echo 'Instructions:'; echo '1. Switch to other windows to see agents'; echo '2. In agent windows, type messages or commands'; echo '3. Use /join <room> to join rooms'; echo '4. Use /help for more commands'; tail -f /dev/null" C-m
|
74
|
+
|
75
|
+
# Wait a moment for room monitors to start
|
76
|
+
sleep 2
|
77
|
+
|
78
|
+
# Window 1: Human Agents (3 panes)
|
79
|
+
echo -e "${GREEN}👥 Setting up Human Agents...${NC}"
|
80
|
+
tmux new-window -t $SESSION_NAME -n "Human-Agents"
|
81
|
+
|
82
|
+
# Alice (left pane)
|
83
|
+
tmux send-keys -t $SESSION_NAME:1 "cd '$CHAT_DIR'" C-m
|
84
|
+
tmux send-keys -t $SESSION_NAME:1 "ruby human_agent.rb alice Alice" C-m
|
85
|
+
|
86
|
+
# Split for Bob (top right)
|
87
|
+
tmux split-window -t $SESSION_NAME:1 -h
|
88
|
+
tmux send-keys -t $SESSION_NAME:1.1 "cd '$CHAT_DIR'" C-m
|
89
|
+
tmux send-keys -t $SESSION_NAME:1.1 "ruby human_agent.rb bob Bob" C-m
|
90
|
+
|
91
|
+
# Split for Carol (bottom right)
|
92
|
+
tmux split-window -t $SESSION_NAME:1.1 -v
|
93
|
+
tmux send-keys -t $SESSION_NAME:1.2 "cd '$CHAT_DIR'" C-m
|
94
|
+
tmux send-keys -t $SESSION_NAME:1.2 "ruby human_agent.rb carol Carol" C-m
|
95
|
+
|
96
|
+
# Wait for agents to start
|
97
|
+
sleep 2
|
98
|
+
|
99
|
+
# Window 2: Bot Agents (2 panes)
|
100
|
+
echo -e "${GREEN}🤖 Setting up Bot Agents...${NC}"
|
101
|
+
tmux new-window -t $SESSION_NAME -n "Bot-Agents"
|
102
|
+
|
103
|
+
# HelpBot (left pane)
|
104
|
+
tmux send-keys -t $SESSION_NAME:2 "cd '$CHAT_DIR'" C-m
|
105
|
+
tmux send-keys -t $SESSION_NAME:2 "ruby bot_agent.rb helpbot HelpBot help,stats,time" C-m
|
106
|
+
|
107
|
+
# Split for FunBot (right pane)
|
108
|
+
tmux split-window -t $SESSION_NAME:2 -h
|
109
|
+
tmux send-keys -t $SESSION_NAME:2.1 "cd '$CHAT_DIR'" C-m
|
110
|
+
tmux send-keys -t $SESSION_NAME:2.1 "ruby bot_agent.rb funbot FunBot joke,weather,echo" C-m
|
111
|
+
|
112
|
+
# Wait for bots to start
|
113
|
+
sleep 2
|
114
|
+
|
115
|
+
# Auto-join agents to rooms for demo
|
116
|
+
echo -e "${GREEN}🏠 Auto-joining agents to rooms...${NC}"
|
117
|
+
|
118
|
+
# Alice joins general and tech
|
119
|
+
tmux send-keys -t $SESSION_NAME:1.0 "/join general" C-m
|
120
|
+
sleep 0.5
|
121
|
+
tmux send-keys -t $SESSION_NAME:1.0 "/join tech" C-m
|
122
|
+
|
123
|
+
# Bob joins general and random
|
124
|
+
tmux send-keys -t $SESSION_NAME:1.1 "/join general" C-m
|
125
|
+
sleep 0.5
|
126
|
+
tmux send-keys -t $SESSION_NAME:1.1 "/join random" C-m
|
127
|
+
|
128
|
+
# Carol joins tech and random
|
129
|
+
tmux send-keys -t $SESSION_NAME:1.2 "/join tech" C-m
|
130
|
+
sleep 0.5
|
131
|
+
tmux send-keys -t $SESSION_NAME:1.2 "/join random" C-m
|
132
|
+
|
133
|
+
# Bots join rooms
|
134
|
+
tmux send-keys -t $SESSION_NAME:2.0 "/join general" C-m
|
135
|
+
sleep 0.5
|
136
|
+
tmux send-keys -t $SESSION_NAME:2.0 "/join tech" C-m
|
137
|
+
|
138
|
+
tmux send-keys -t $SESSION_NAME:2.1 "/join general" C-m
|
139
|
+
sleep 0.5
|
140
|
+
tmux send-keys -t $SESSION_NAME:2.1 "/join random" C-m
|
141
|
+
|
142
|
+
sleep 1
|
143
|
+
|
144
|
+
# Send some initial messages to demonstrate the system
|
145
|
+
echo -e "${GREEN}💬 Sending demo messages...${NC}"
|
146
|
+
|
147
|
+
tmux send-keys -t $SESSION_NAME:1.0 "Hello everyone! I'm Alice." C-m
|
148
|
+
sleep 1
|
149
|
+
tmux send-keys -t $SESSION_NAME:1.1 "Hi Alice! Bob here." C-m
|
150
|
+
sleep 1
|
151
|
+
tmux send-keys -t $SESSION_NAME:1.2 "Carol joining the conversation!" C-m
|
152
|
+
sleep 1
|
153
|
+
tmux send-keys -t $SESSION_NAME:1.0 "/help" C-m
|
154
|
+
sleep 2
|
155
|
+
tmux send-keys -t $SESSION_NAME:1.1 "/joke" C-m
|
156
|
+
sleep 2
|
157
|
+
|
158
|
+
# Set focus to Human Agents window
|
159
|
+
tmux select-window -t $SESSION_NAME:1
|
160
|
+
|
161
|
+
echo -e "${GREEN}✅ Chat demo is ready!${NC}"
|
162
|
+
echo ""
|
163
|
+
echo -e "${BLUE}Navigation:${NC}"
|
164
|
+
echo "• Ctrl+b then 0: Control Center (room monitors)"
|
165
|
+
echo "• Ctrl+b then 1: Human Agents (Alice, Bob, Carol)"
|
166
|
+
echo "• Ctrl+b then 2: Bot Agents (HelpBot, FunBot)"
|
167
|
+
echo "• Ctrl+b then o: Cycle through panes"
|
168
|
+
echo "• Ctrl+b then arrow keys: Navigate panes"
|
169
|
+
echo ""
|
170
|
+
echo -e "${BLUE}Commands in agent panes:${NC}"
|
171
|
+
echo "• /join <room>: Join a room"
|
172
|
+
echo "• /leave <room>: Leave a room"
|
173
|
+
echo "• /list: List your active rooms"
|
174
|
+
echo "• /help: Show available commands"
|
175
|
+
echo "• /quit: Exit the agent"
|
176
|
+
echo ""
|
177
|
+
echo -e "${BLUE}Bot commands:${NC}"
|
178
|
+
echo "• /help: Show bot capabilities"
|
179
|
+
echo "• /joke: Get a random joke"
|
180
|
+
echo "• /weather <location>: Get weather"
|
181
|
+
echo "• /stats: Show bot statistics"
|
182
|
+
echo "• /time: Show current time"
|
183
|
+
echo "• /echo <message>: Echo your message"
|
184
|
+
echo ""
|
185
|
+
echo -e "${YELLOW}💡 Tip: Type messages directly to chat in your active rooms!${NC}"
|
186
|
+
echo ""
|
187
|
+
echo -e "${GREEN}🎭 Attaching to tmux session...${NC}"
|
188
|
+
|
189
|
+
# Attach to the session
|
190
|
+
tmux attach-session -t $SESSION_NAME
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# examples/tmux_chat/stop_chat_demo.sh
|
3
|
+
#
|
4
|
+
# Cleanup script for the tmux chat demo
|
5
|
+
|
6
|
+
SESSION_NAME="smart_message_chat"
|
7
|
+
|
8
|
+
echo "🧹 Stopping SmartMessage Tmux Chat Demo..."
|
9
|
+
|
10
|
+
# Kill the tmux session if it exists
|
11
|
+
if tmux has-session -t $SESSION_NAME 2>/dev/null; then
|
12
|
+
echo "🔴 Terminating tmux session..."
|
13
|
+
tmux kill-session -t $SESSION_NAME
|
14
|
+
else
|
15
|
+
echo "ℹ️ No active tmux session found."
|
16
|
+
fi
|
17
|
+
|
18
|
+
# Clean up shared message queues
|
19
|
+
echo "🗑️ Cleaning up message queues..."
|
20
|
+
rm -rf /tmp/smart_message_chat
|
21
|
+
|
22
|
+
echo "✅ Cleanup complete!"
|
data/lib/simple_stats.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# lib/simple_stats.rb
|
2
|
+
|
3
|
+
require 'ap'
|
4
|
+
|
5
|
+
# A dirt simple way of collecting statistics for use in testing
|
6
|
+
# that has a multi-level key structure
|
7
|
+
class SimpleStats
|
8
|
+
@@stat = Hash.new
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
# return the internal Hash
|
13
|
+
def stat
|
14
|
+
@@stat
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
# return the count for a specific multi-level key
|
19
|
+
def get(*args)
|
20
|
+
return 0 if args.empty?
|
21
|
+
key = get_key(args)
|
22
|
+
@@stat.key?(key) ? @@stat[key] : @@stat[key] = 0
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# increment (add) counts to a multi-level key
|
27
|
+
def add(*args, how_many: 1)
|
28
|
+
return 0 if args.empty?
|
29
|
+
key = get_key(args)
|
30
|
+
@@stat.key?(key) ? @@stat[key] += how_many : @@stat[key] = how_many
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def reset(*args)
|
35
|
+
if args.empty?
|
36
|
+
@@stat = {}
|
37
|
+
return 0
|
38
|
+
end
|
39
|
+
key = get_key(args)
|
40
|
+
@@stat[key] = 0
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# simulate a multi-level key using a level seperater
|
45
|
+
def get_key(an_array, sep:'+')
|
46
|
+
an_array.join(sep)
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# return a pretty printed representation of the statistics
|
51
|
+
def to_s
|
52
|
+
ap @@stat
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end # class SimpleStat
|
56
|
+
|
57
|
+
SS = SimpleStats
|