brainiac-discord 0.0.3 → 0.0.4
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cdd8beafe4070560e4caaaa465d2b1e9dde33defa1b7fdc429467864776d3c21
|
|
4
|
+
data.tar.gz: 8714538569c7c1211a93ffa567401fe54197ce975fb3946e1524550f67cacb97
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a072c85e99b507dc4cdf65ec04cb9ed1d83e3ef22ef6ba77bd2866b7e04d947920cd607e087be3c93b06d362ef2e94127903e5087b8e9b7a85c456be72684b6f
|
|
7
|
+
data.tar.gz: 481b0308e707c86fddc515ba26a079bb23c31bb1f0f0f42da00cc7d798af52a24f6f8ea40f75356675f856f037b4e7dafa4174e1d62589569379bdc36e26f401
|
|
@@ -269,6 +269,45 @@ module Brainiac
|
|
|
269
269
|
def self.completions
|
|
270
270
|
%w[setup config default map owner token status]
|
|
271
271
|
end
|
|
272
|
+
|
|
273
|
+
# Called by brainiac CLI after `agent create` — prompts for Discord user ID.
|
|
274
|
+
def self.on_agent_created(agent_key, entry)
|
|
275
|
+
return unless $stdin.tty?
|
|
276
|
+
|
|
277
|
+
display_name = entry["display_name"] || agent_key.capitalize
|
|
278
|
+
puts ""
|
|
279
|
+
puts " [Discord] Configure Discord for #{display_name}?"
|
|
280
|
+
print " Discord user ID (or blank to skip): "
|
|
281
|
+
user_id = $stdin.gets&.chomp
|
|
282
|
+
return if user_id.nil? || user_id.empty?
|
|
283
|
+
|
|
284
|
+
config_file = File.join(ENV.fetch("BRAINIAC_DIR", File.join(Dir.home, ".brainiac")), "discord.json")
|
|
285
|
+
config = File.exist?(config_file) ? JSON.parse(File.read(config_file)) : {}
|
|
286
|
+
config["user_mappings"] ||= {}
|
|
287
|
+
config["user_mappings"][display_name] = user_id
|
|
288
|
+
File.write(config_file, JSON.pretty_generate(config))
|
|
289
|
+
puts " ✓ Added #{display_name} → #{user_id} to discord.json user_mappings"
|
|
290
|
+
rescue JSON::ParserError => e
|
|
291
|
+
puts " ⚠ Could not update discord.json: #{e.message}"
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Called by brainiac CLI after `agent remove` — removes from Discord user_mappings.
|
|
295
|
+
def self.on_agent_removed(agent_key, display_name)
|
|
296
|
+
config_file = File.join(ENV.fetch("BRAINIAC_DIR", File.join(Dir.home, ".brainiac")), "discord.json")
|
|
297
|
+
return unless File.exist?(config_file)
|
|
298
|
+
|
|
299
|
+
config = JSON.parse(File.read(config_file))
|
|
300
|
+
mappings = config["user_mappings"]
|
|
301
|
+
return unless mappings
|
|
302
|
+
|
|
303
|
+
removed = mappings.delete(display_name) || mappings.delete(agent_key)
|
|
304
|
+
return unless removed
|
|
305
|
+
|
|
306
|
+
File.write(config_file, JSON.pretty_generate(config))
|
|
307
|
+
puts " [Discord] Removed #{display_name} from discord.json user_mappings"
|
|
308
|
+
rescue JSON::ParserError
|
|
309
|
+
nil
|
|
310
|
+
end
|
|
272
311
|
end
|
|
273
312
|
end
|
|
274
313
|
end
|
|
@@ -79,6 +79,27 @@ module Brainiac
|
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
+
# Start a single bot gateway connection dynamically (e.g. after agent is added).
|
|
83
|
+
# No-op if the bot is already running.
|
|
84
|
+
def start_bot!(agent_key, token)
|
|
85
|
+
@bots_mutex.synchronize do
|
|
86
|
+
return if @bots[agent_key]
|
|
87
|
+
|
|
88
|
+
@bots[agent_key] = { token: token, status: "starting", user_id: nil }
|
|
89
|
+
end
|
|
90
|
+
start_gateway_for(agent_key, token)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Stop a bot gateway connection (e.g. after agent is removed).
|
|
94
|
+
# Removes it from the bots hash so it won't be matched in detect_sender_agent.
|
|
95
|
+
def stop_bot!(agent_key)
|
|
96
|
+
info = @bots_mutex.synchronize { @bots.delete(agent_key) }
|
|
97
|
+
return unless info
|
|
98
|
+
|
|
99
|
+
info[:thread]&.kill
|
|
100
|
+
LOG.info "[Discord] Gateway stopped for #{agent_key}" if defined?(LOG)
|
|
101
|
+
end
|
|
102
|
+
|
|
82
103
|
# Summary of all bot statuses for the API endpoint.
|
|
83
104
|
def bots_status
|
|
84
105
|
@bots_mutex.synchronize do
|
|
@@ -122,7 +143,7 @@ module Brainiac
|
|
|
122
143
|
private
|
|
123
144
|
|
|
124
145
|
def start_gateway_for(agent_key, bot_token)
|
|
125
|
-
Thread.new do
|
|
146
|
+
thread = Thread.new do
|
|
126
147
|
agent_display = agent_display_name(agent_key) || agent_key.capitalize
|
|
127
148
|
bot_user_id = nil
|
|
128
149
|
|
|
@@ -136,6 +157,7 @@ module Brainiac
|
|
|
136
157
|
sleep 5
|
|
137
158
|
end
|
|
138
159
|
end
|
|
160
|
+
@bots_mutex.synchronize { @bots[agent_key][:thread] = thread if @bots[agent_key] }
|
|
139
161
|
end
|
|
140
162
|
|
|
141
163
|
def run_gateway_connection(agent_key, agent_display, bot_token, bot_user_id)
|
|
@@ -31,7 +31,10 @@ module Brainiac
|
|
|
31
31
|
channel_info, is_thread, is_dm, in_own_thread = detect_channel_context(message, channel_id, mentioned, is_reply_to_bot, bot_token,
|
|
32
32
|
bot_user_id)
|
|
33
33
|
return if should_stand_down?(in_own_thread, mentioned, is_reply_to_bot, is_bot, agent_key, mentions, content)
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
is_thread_participant = !mentioned && !in_own_thread && is_thread &&
|
|
36
|
+
thread_participant?(channel_id, message_id, bot_user_id, bot_token)
|
|
37
|
+
return unless mentioned || in_own_thread || is_dm || is_reply_to_bot || is_thread_participant
|
|
35
38
|
|
|
36
39
|
record_human_comment("discord-#{channel_id}") unless is_bot
|
|
37
40
|
|
|
@@ -158,6 +161,24 @@ module Brainiac
|
|
|
158
161
|
other_bot_mentioned
|
|
159
162
|
end
|
|
160
163
|
|
|
164
|
+
# Check if this bot was previously mentioned in the thread (making it a participant).
|
|
165
|
+
# Uses a small history fetch to avoid excessive API calls.
|
|
166
|
+
def thread_participant?(channel_id, before_message_id, bot_user_id, bot_token)
|
|
167
|
+
messages = Api.request(:get, "/channels/#{channel_id}/messages?before=#{before_message_id}&limit=50", token: bot_token)
|
|
168
|
+
return false unless messages.is_a?(Array)
|
|
169
|
+
|
|
170
|
+
bot_id_str = bot_user_id.to_s
|
|
171
|
+
messages.any? do |msg|
|
|
172
|
+
mentions = msg["mentions"] || []
|
|
173
|
+
mentions.any? { |m| m["id"].to_s == bot_id_str } ||
|
|
174
|
+
(msg["content"] || "").match?(/<@!?#{Regexp.escape(bot_id_str)}>/) ||
|
|
175
|
+
msg.dig("author", "id").to_s == bot_id_str
|
|
176
|
+
end
|
|
177
|
+
rescue StandardError => e
|
|
178
|
+
LOG.warn "[Discord] Failed to check thread participation: #{e.message}" if defined?(LOG)
|
|
179
|
+
false
|
|
180
|
+
end
|
|
181
|
+
|
|
161
182
|
def prepare_content(content, bot_user_id)
|
|
162
183
|
content.gsub(/<@!?#{bot_user_id}>/, "").strip
|
|
163
184
|
end
|
|
@@ -31,6 +31,9 @@ module Brainiac
|
|
|
31
31
|
# Register crash handler
|
|
32
32
|
register_crash_handler!
|
|
33
33
|
|
|
34
|
+
# Register agent lifecycle hooks
|
|
35
|
+
register_agent_lifecycle_hooks!
|
|
36
|
+
|
|
34
37
|
# Start all per-agent Discord bot gateway connections
|
|
35
38
|
Brainiac::Plugins::Discord::Gateway.start_all!
|
|
36
39
|
|
|
@@ -45,6 +48,30 @@ module Brainiac
|
|
|
45
48
|
|
|
46
49
|
private
|
|
47
50
|
|
|
51
|
+
def register_agent_lifecycle_hooks!
|
|
52
|
+
Brainiac.on(:agent_added) do |ctx|
|
|
53
|
+
agent_key = ctx[:agent_key]
|
|
54
|
+
entry = ctx[:entry]
|
|
55
|
+
display_name = ctx[:display_name]
|
|
56
|
+
next unless entry.is_a?(Hash)
|
|
57
|
+
|
|
58
|
+
token = (entry["env"] || {})["DISCORD_BOT_TOKEN"]
|
|
59
|
+
if token
|
|
60
|
+
# Start a gateway connection for the new agent's bot
|
|
61
|
+
Gateway.start_bot!(agent_key, token)
|
|
62
|
+
LOG.info "[Discord] Started bot for new agent: #{display_name}" if defined?(LOG)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
Brainiac.on(:agent_removed) do |ctx|
|
|
67
|
+
agent_key = ctx[:agent_key]
|
|
68
|
+
|
|
69
|
+
# Stop the gateway connection if running
|
|
70
|
+
Gateway.stop_bot!(agent_key)
|
|
71
|
+
LOG.info "[Discord] Stopped bot for removed agent: #{agent_key}" if defined?(LOG)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
48
75
|
def register_notification_handler!
|
|
49
76
|
Brainiac.on(:notify) do |ctx|
|
|
50
77
|
next unless ctx[:channel].to_s == "discord"
|