Module: Brainiac::Plugins::Discord::Gateway

Defined in:
lib/brainiac/plugins/discord/gateway.rb

Overview

Discord WebSocket gateway connections.

Each agent with a DISCORD_BOT_TOKEN gets its own persistent WebSocket connection. The gateway dispatches MESSAGE_CREATE, MESSAGE_UPDATE, and MESSAGE_REACTION_ADD events to handler functions.

Constant Summary collapse

GATEWAY_URL =
"wss://gateway.discord.gg/?v=10&encoding=json"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.botsObject (readonly)

Returns the value of attribute bots.



22
23
24
# File 'lib/brainiac/plugins/discord/gateway.rb', line 22

def bots
  @bots
end

.bots_mutexObject (readonly)

Returns the value of attribute bots_mutex.



22
23
24
# File 'lib/brainiac/plugins/discord/gateway.rb', line 22

def bots_mutex
  @bots_mutex
end

Class Method Details

.bot_countObject



24
25
26
# File 'lib/brainiac/plugins/discord/gateway.rb', line 24

def bot_count
  @bots_mutex.synchronize { @bots.size }
end

.bot_token(agent_key) ⇒ Object

Get a bot's token.



36
37
38
# File 'lib/brainiac/plugins/discord/gateway.rb', line 36

def bot_token(agent_key)
  @bots_mutex.synchronize { @bots.dig(agent_key, :token) }
end

.bot_user_id(agent_key) ⇒ Object

Get a bot's user_id.



41
42
43
# File 'lib/brainiac/plugins/discord/gateway.rb', line 41

def bot_user_id(agent_key)
  @bots_mutex.synchronize { @bots.dig(agent_key, :user_id) }
end

.bots_statusObject

Summary of all bot statuses for the API endpoint.



104
105
106
107
108
109
110
# File 'lib/brainiac/plugins/discord/gateway.rb', line 104

def bots_status
  @bots_mutex.synchronize do
    @bots.transform_values do |info|
      { status: info[:status], user_id: info[:user_id] }
    end
  end
end

.detect_sender_agent(author, current_agent_key) ⇒ Object

Detect if a message author is a known bot (local or remote). Returns the agent_key of the sender, or nil if unknown.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/brainiac/plugins/discord/gateway.rb', line 114

def detect_sender_agent(author, current_agent_key)
  sender_id = author["id"]
  sender_agent_key = nil

  @bots_mutex.synchronize do
    @bots.each do |key, info|
      if info[:user_id] == sender_id && key != current_agent_key
        sender_agent_key = key
        break
      end
    end
  end

  unless sender_agent_key
    Config.user_mappings.each do |name, discord_id|
      if discord_id == sender_id
        sender_agent_key = name.downcase
        break
      end
    end
  end

  if !sender_agent_key && defined?(LOG)
    LOG.info "[Discord:#{current_agent_key}] Ignoring unknown bot: id=#{sender_id}, username=#{author["username"]}"
  end

  sender_agent_key
end

.discord_bot_tokensObject

Collect all agent Discord bot tokens from the registry. Returns { "galen" => "token...", "glados" => "token..." }



47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/brainiac/plugins/discord/gateway.rb', line 47

def discord_bot_tokens
  tokens = {}
  AGENT_REGISTRY.each do |key, entry|
    next unless entry.is_a?(Hash)

    token = (entry["env"] || {})["DISCORD_BOT_TOKEN"]
    next unless token

    tokens[key] = token
  end
  tokens
end

.each_botObject

Iterate over bots under mutex.



29
30
31
32
33
# File 'lib/brainiac/plugins/discord/gateway.rb', line 29

def each_bot(&)
  @bots_mutex.synchronize do
    @bots.each(&)
  end
end

.start_all!Object

Start all per-agent Discord bots.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/brainiac/plugins/discord/gateway.rb', line 61

def start_all!
  tokens = discord_bot_tokens
  if tokens.empty?
    LOG.info "[Discord] No agents have DISCORD_BOT_TOKEN configured — Discord disabled" if defined?(LOG)
    return
  end

  LOG.info "[Discord] Starting #{tokens.size} bot(s): #{tokens.keys.join(", ")}" if defined?(LOG)

  @bots_mutex.synchronize do
    tokens.each do |agent_key, token|
      @bots[agent_key] = { token: token, status: "starting", user_id: nil }
    end
  end

  tokens.each do |agent_key, token|
    start_gateway_for(agent_key, token)
    sleep 1 # Stagger connections to avoid rate limits
  end
end

.start_bot!(agent_key, token) ⇒ Object

Start a single bot gateway connection dynamically (e.g. after agent is added). No-op if the bot is already running.



84
85
86
87
88
89
90
91
# File 'lib/brainiac/plugins/discord/gateway.rb', line 84

def start_bot!(agent_key, token)
  @bots_mutex.synchronize do
    return if @bots[agent_key]

    @bots[agent_key] = { token: token, status: "starting", user_id: nil }
  end
  start_gateway_for(agent_key, token)
end

.stop_bot!(agent_key) ⇒ Object

Stop a bot gateway connection (e.g. after agent is removed). Removes it from the bots hash so it won't be matched in detect_sender_agent.



95
96
97
98
99
100
101
# File 'lib/brainiac/plugins/discord/gateway.rb', line 95

def stop_bot!(agent_key)
  info = @bots_mutex.synchronize { @bots.delete(agent_key) }
  return unless info

  info[:thread]&.kill
  LOG.info "[Discord] Gateway stopped for #{agent_key}" if defined?(LOG)
end