]> git.ipfire.org Git - ipfire.org.git/blob - src/backend/asterisk.py
voip: Create an extra page for debugging VoIP stuff
[ipfire.org.git] / src / backend / asterisk.py
1 #!/usr/bin/python3
2
3 import asyncio
4 import datetime
5 import logging
6 import panoramisk
7 import urllib.parse
8
9 from . import accounts
10 from . import misc
11 from .decorators import *
12
13 loop = asyncio.get_event_loop()
14
15 class 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
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
99
100 class Channel(misc.Object):
101 def init(self, data):
102 self.data = data
103
104 def __str__(self):
105 return self.connected_line
106
107 @property
108 def account_code(self):
109 return self.data.AccountCode
110
111 @property
112 def connected_line(self):
113 return self.data.ConnectedLineName or self.data.ConnectedLineNum
114
115 def matches(self, filter):
116 return filter in (
117 self.data.CallerIDNum,
118 )
119
120 @property
121 def duration(self):
122 h, m, s = self.data.Duration.split(":")
123
124 try:
125 h, m, s = int(h), int(m), int(s)
126 except TypeError:
127 return 0
128
129 return datetime.timedelta(hours=h, minutes=m, seconds=s)
130
131 def is_connected(self):
132 return self.data.ChannelStateDesc == "Up"
133
134 def is_ringing(self):
135 return self.data.ChannelStateDesc == "Ringing"
136
137
138 class Registration(misc.Object):
139 def init(self, data):
140 self.data = data
141
142 def __lt__(self, other):
143 if isinstance(other, self.__class__):
144 if isinstance(self.user, accounts.Account):
145 if isinstance(other.user, accounts.Account):
146 return self.user < other.user
147 else:
148 return self.user.name < other.user
149 else:
150 if isinstance(other.user, accounts.Account):
151 return self.user < other.user.name
152 else:
153 return self.user < other.user
154
155 return NotImplemented
156
157 def __str__(self):
158 return self.user_agent
159
160 def matches(self, filter):
161 return self.data.Endpoint == filter
162
163 @lazy_property
164 def uri(self):
165 return urllib.parse.urlparse(self.data.Uri)
166
167 @lazy_property
168 def uri_params(self):
169 params = {}
170
171 for param in self.uri.params.split(";"):
172 key, _, value = param.partition("=")
173
174 params[key] = value
175
176 return params
177
178 @property
179 def transport(self):
180 return self.uri_params.get("transport")
181
182 @lazy_property
183 def user(self):
184 return self.backend.accounts.get_by_sip_id(self.data.Endpoint) or self.data.Endpoint
185
186 @property
187 def address(self):
188 # Remove the user
189 user, _, address = self.uri.path.partition("@")
190
191 # Remove the port
192 address, _, port = address.rpartition(":")
193
194 return address
195
196 @property
197 def user_agent(self):
198 return self.data.UserAgent.replace("_", " ")
199
200 @property
201 def roundtrip(self):
202 return int(self.data.RoundtripUsec) / 1000