]> git.ipfire.org Git - ipfire.org.git/blame - src/backend/asterisk.py
people: Drop conferences page
[ipfire.org.git] / src / backend / asterisk.py
CommitLineData
d6c41da2
MT
1#!/usr/bin/python3
2
3import asyncio
4import datetime
5import logging
6import panoramisk
4235ba55 7import urllib.parse
d6c41da2 8
4235ba55 9from . import accounts
d6c41da2
MT
10from . import misc
11from .decorators import *
12
13loop = asyncio.get_event_loop()
14
15class Asterisk(misc.Object):
16 def init(self):
17 self.__manager = None
18
19 # Connect as soon as the event loop starts
20 loop.create_task(self.connect())
21
22 @property
23 def manager(self):
24 if not self.__manager:
25 raise RuntimeError("Asterisk is not connected")
26
27 return self.__manager
28
29 async def connect(self):
30 """
31 Connects to Asterisk
32 """
33 manager = panoramisk.Manager(
34 host = self.settings.get("asterisk-ami-host"),
35 username = self.settings.get("asterisk-ami-username"),
36 secret = self.settings.get("asterisk-ami-secret"),
37
38 on_connect=self._on_connect,
39 )
40
41 # Connect
42 await manager.connect()
43
44 return manager
45
46 def _on_connect(self, manager):
47 logging.debug("Connection to Asterisk established")
48
49 # Close any existing connections
50 if self.__manager:
51 self.__manager.close()
52
53 self.__manager = manager
54
55 async def ping(self):
56 manager = await self.manager()
57
58 result = manager.ping()
59 print(result)
60
61 async def get_sip_channels(self, filter=None):
62 channels = []
63
64 for data in await self.manager.send_action({"Action" : "CoreShowChannels"}):
65 # Skip header and trailer
66 if data.eventlist:
67 continue
68
69 # Parse channel
70 channel = Channel(self.backend, data)
71
72 # Apply filter
73 if filter and not channel.matches(filter):
74 continue
75
76 channels.append(channel)
77
78 return channels
79
4235ba55
MT
80 async def get_registrations(self, filter=None):
81 registrations = []
82
83 for data in await self.manager.send_action({"Action" : "PJSIPShowContacts"}):
84 # Skip header and trailer
85 if data.eventlist:
86 continue
87
88 # Parse registration
89 registration = Registration(self.backend, data)
90
91 # Apply filter
92 if filter and not registration.matches(filter):
93 continue
94
95 registrations.append(registration)
96
97 return registrations
98
00465786
MT
99 async def get_outbound_registrations(self):
100 registrations = []
101
102 for data in await self.manager.send_action({"Action" : "PJSIPShowRegistrationsOutbound"}):
103 if not data.Event == "OutboundRegistrationDetail":
104 continue
105
106 registration = OutboundRegistration(self.backend, data)
107 registrations.append(registration)
108
109 return registrations
110
8e93325b
MT
111 async def get_queues(self):
112 queues = {}
113
114 # Fetch all queues
115 for data in await self.manager.send_action({"Action" : "QueueSummary"}):
116 if not data.Event == "QueueSummary":
117 continue
118
119 queue = Queue(self.backend, data)
120 queues[queue.name] = queue
121
122 # Fetch all members
123 for data in await self.manager.send_action({"Action" : "QueueStatus"}):
124 print(data)
125
126 if not data.Event == "QueueMember":
127 continue
128
129 member = QueueMember(self.backend, data)
130
131 # Append to the matching queue
132 try:
133 queues[member.queue].members.append(member)
134 except KeyError:
135 pass
136
137 return queues.values()
138
d6c41da2
MT
139
140class Channel(misc.Object):
141 def init(self, data):
142 self.data = data
143
144 def __str__(self):
145 return self.connected_line
146
147 @property
148 def account_code(self):
149 return self.data.AccountCode
150
151 @property
152 def connected_line(self):
153 return self.data.ConnectedLineName or self.data.ConnectedLineNum
154
155 def matches(self, filter):
156 return filter in (
157 self.data.CallerIDNum,
158 )
159
160 @property
161 def duration(self):
162 h, m, s = self.data.Duration.split(":")
163
164 try:
165 h, m, s = int(h), int(m), int(s)
166 except TypeError:
167 return 0
168
169 return datetime.timedelta(hours=h, minutes=m, seconds=s)
170
171 def is_connected(self):
172 return self.data.ChannelStateDesc == "Up"
173
174 def is_ringing(self):
175 return self.data.ChannelStateDesc == "Ringing"
4235ba55
MT
176
177
178class Registration(misc.Object):
179 def init(self, data):
180 self.data = data
181
182 def __lt__(self, other):
183 if isinstance(other, self.__class__):
184 if isinstance(self.user, accounts.Account):
185 if isinstance(other.user, accounts.Account):
186 return self.user < other.user
187 else:
188 return self.user.name < other.user
189 else:
190 if isinstance(other.user, accounts.Account):
191 return self.user < other.user.name
192 else:
193 return self.user < other.user
194
195 return NotImplemented
196
197 def __str__(self):
198 return self.user_agent
199
200 def matches(self, filter):
201 return self.data.Endpoint == filter
202
203 @lazy_property
204 def uri(self):
205 return urllib.parse.urlparse(self.data.Uri)
206
207 @lazy_property
208 def uri_params(self):
209 params = {}
210
211 for param in self.uri.params.split(";"):
212 key, _, value = param.partition("=")
213
214 params[key] = value
215
216 return params
217
218 @property
219 def transport(self):
220 return self.uri_params.get("transport")
221
222 @lazy_property
223 def user(self):
224 return self.backend.accounts.get_by_sip_id(self.data.Endpoint) or self.data.Endpoint
225
226 @property
227 def address(self):
228 # Remove the user
229 user, _, address = self.uri.path.partition("@")
230
231 # Remove the port
232 address, _, port = address.rpartition(":")
233
234 return address
235
236 @property
237 def user_agent(self):
238 return self.data.UserAgent.replace("_", " ")
239
240 @property
241 def roundtrip(self):
242 return int(self.data.RoundtripUsec) / 1000
00465786
MT
243
244
245class OutboundRegistration(misc.Object):
246 def init(self, data):
247 self.data = data
248
249 def __lt__(self, other):
250 if isinstance(other, self.__class__):
251 return self.server < other.server or self.username < other.username
252
253 return NotImplemented
254
255 @lazy_property
256 def uri(self):
257 return urllib.parse.urlparse(self.data.ClientUri)
258
259 @property
260 def server(self):
261 username, _, server = self.uri.path.partition("@")
262
263 return server
264
265 @property
266 def username(self):
267 username, _, server = self.uri.path.partition("@")
268
269 return username
270
271 @property
272 def status(self):
273 return self.data.Status
8e93325b
MT
274
275
276class Queue(misc.Object):
277 def init(self, data):
278 self.data = data
279
280 self.members = []
281
282 def __str__(self):
283 return self.name
284
285 @property
286 def name(self):
287 return self.data.Queue
288
289 def is_available(self):
290 return self.data.Available == "1"
291
292 @property
293 def callers(self):
294 return int(self.data.Callers)
295
296
297class QueueMember(misc.Object):
298 def init(self, data):
299 self.data = data
300
301 def __str__(self):
302 return self.name
303
304 @property
305 def name(self):
306 return self.data.Name
307
308 @property
309 def queue(self):
310 return self.data.Queue
311
312 @property
313 def calls_taken(self):
314 return int(self.data.CallsTaken)
315
316 def is_in_call(self):
317 return self.data.InCall == "1"
318
319 @property
320 def last_call_at(self):
321 return datetime.datetime.fromtimestamp(int(self.data.LastCall))
322
323 @property
324 def logged_in_at(self):
325 return datetime.datetime.fromtimestamp(int(self.data.LoginTime))
326
327 # XXX status?