Class: Pbx::AmiBridge

Inherits:
Object
  • Object
show all
Defined in:
lib/pbx/ami_bridge.rb

Defined Under Namespace

Classes: EventClient

Constant Summary collapse

SENTINEL =
:__pbx_shutdown__
LIST_TIMEOUT =

Seconds to wait for a list action's aggregated reply. Longer than the gem's 5s default: on a busy PBX a full peer or queue listing is slow.

10
FRAME_DELIMITER =

AMI frames are CRLF-delimited on the wire; tolerate bare LF too.

/\r?\n\r?\n/

Instance Method Summary collapse

Constructor Details

#initialize(config, client: nil, debug: false) ⇒ AmiBridge

Returns a new instance of AmiBridge.



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/pbx/ami_bridge.rb', line 49

def initialize(config, client: nil, debug: false)
  @config = config
  @queue = Queue.new
  @debug_log = debug ? File.open("/tmp/pbx_debug.log", "a") : nil
  @client = client || EventClient.new(
    host: config.host,
    port: config.port,
    queue: @queue,
    log: @debug_log
  )
end

Instance Method Details

#connect_and_loginObject



61
62
63
64
65
66
67
68
# File 'lib/pbx/ami_bridge.rb', line 61

def 
  log "[CONNECT] Connecting to #{@config.host}:#{@config.port} as #{@config.user}"
  raise "Could not connect to #{@config.host}:#{@config.port}" unless @client.connect

  response = @client.(username: @config.user, secret: @config.secret).value(5)
  log "[CONNECT] Login response: success=#{response&.success} message=#{response&.message}"
  raise "AMI login failed: #{response&.message}" unless response&.success
end

#discover_peersObject



70
71
72
# File 'lib/pbx/ami_bridge.rb', line 70

def discover_peers
  discover_sip_peers + discover_pjsip_endpoints
end

#discover_queuesObject



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/pbx/ami_bridge.rb', line 74

def discover_queues
  log "[QUEUES] Triggering queue_status action..."
  # QueueEntry frames are ignored: the caller count is already in the
  # QueueParams Calls header.
  entries = list_entries(@client.queue_status, "QueueParams", "QueueMember")

  queues = {}
  entries.each do |data|
    case data["Event"]
    when "QueueParams"
      q = queue_from_params(data)
      queues[q.name] = q if q
    when "QueueMember"
      queue_name = data["Queue"].to_s
      next if queue_name.empty? || !queues[queue_name]

      member = member_from_event(data)
      next unless member

      existing = queues[queue_name]
      queues[queue_name] = existing.with(members: existing.members.merge(member.interface => member))
    end
  end

  log "[QUEUES] Returning #{queues.size} queues: #{queues.keys.inspect}"
  queues
rescue => e
  log "[QUEUES] Error: #{e.class}: #{e.message}\n#{e.backtrace&.first(3)&.join("\n")}"
  warn "Queue discovery failed: #{e.message}"
  {}
end

#next_eventObject



110
111
112
113
114
115
116
117
118
# File 'lib/pbx/ami_bridge.rb', line 110

def next_event
  loop do
    raw = @queue.pop
    return nil if raw == SENTINEL

    msg = translate_event(raw[:event])
    return msg if msg
  end
end

#shutdownObject



120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/pbx/ami_bridge.rb', line 120

def shutdown
  @queue.push(SENTINEL)
  begin
    @client.logoff
  rescue
    nil
  end
  begin
    @client.disconnect
  rescue
    nil
  end
  @debug_log&.close
end

#subscribeObject



106
107
108
# File 'lib/pbx/ami_bridge.rb', line 106

def subscribe
  @client.event_mask("all")
end