]>
Commit | Line | Data |
---|---|---|
5ac74b02 MT |
1 | #!/usr/bin/python |
2 | ||
3 | import logging | |
4 | import socket | |
5 | import time | |
6 | ||
7 | log = logging.getLogger("asterisk") | |
8 | ||
9 | class AsteriskManager(object): | |
10 | def __init__(self, address, port=5038, username=None, password=None, timeout=10): | |
11 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
12 | self.socket.settimeout(timeout) | |
13 | self.socket.connect((address, port)) | |
14 | ||
15 | self.conn = self.socket.makefile("rw", 0) | |
16 | ||
17 | self._authenticate(username, password) | |
18 | ||
19 | def _authenticate(self, username, password): | |
20 | banner = self.conn.readline() | |
21 | ||
22 | if not banner.startswith("Asterisk Call Manager"): | |
23 | raise RuntimeError("Did not connect to an Asterisk here") | |
24 | ||
25 | self._send_action("Login", { | |
26 | "Username" : username, | |
27 | "Secret" : password, | |
28 | "Events" : "off", | |
29 | }) | |
30 | ||
31 | def _make_action_id(self): | |
32 | return "%s" % time.time() | |
33 | ||
34 | def _send_action(self, action, parameters={}): | |
35 | self._send("Action", action) | |
36 | ||
37 | action_id = self._make_action_id() | |
38 | self._send("ActionID", action_id) | |
39 | ||
40 | for k, v in parameters.items(): | |
41 | self._send(k, v) | |
42 | ||
43 | return self._submit() | |
44 | ||
45 | def _send(self, key, value): | |
46 | line = "%s: %s" % (key, value) | |
47 | log.debug("S: %s" % line) | |
48 | ||
49 | self.conn.write("%s\r\n" % line) | |
50 | ||
51 | def _recv(self): | |
52 | line = self.conn.readline() | |
53 | line = line.rstrip() | |
54 | ||
55 | log.debug("R: %s" % line) | |
56 | ||
57 | return line | |
58 | ||
59 | def _recv_response(self): | |
60 | response = {} | |
61 | ||
62 | while True: | |
63 | # Read one line | |
64 | line = self._recv() | |
65 | ||
66 | # An empty line signals end of response | |
67 | if not line: | |
68 | break | |
69 | ||
70 | k, sep, v = line.partition(": ") | |
71 | response[k] = v | |
72 | ||
73 | return response | |
74 | ||
75 | def _submit(self): | |
76 | # End command | |
77 | self.conn.write("\r\n") | |
78 | ||
79 | # Read response | |
80 | res = self._recv_response() | |
81 | ||
82 | if res["Response"] == "Error": | |
83 | raise Exception(res["Message"]) | |
84 | ||
85 | if res.get("EventList") == "start": | |
86 | events = [] | |
87 | ||
88 | while True: | |
89 | event = self._recv_response() | |
90 | ||
91 | # This is the end of the list | |
92 | if event.get("EventList") == "Complete": | |
93 | break | |
94 | ||
95 | events.append(event) | |
96 | ||
97 | # Return event list | |
98 | return events | |
99 | ||
100 | return res | |
101 | ||
102 | def ping(self): | |
103 | """ | |
104 | Sends a ping to asterisk and expects pong | |
105 | """ | |
106 | res = self._send_action("Ping") | |
107 | ||
108 | return res["Ping"] == "Pong" | |
109 | ||
909b9713 | 110 | def call(self, caller, callee, callee_id=None, timeout=30000): |
5ac74b02 | 111 | res = self._send_action("Originate", { |
909b9713 | 112 | "Channel" : "SIP/%s@kamailio" % caller, |
5ac74b02 MT |
113 | "Exten" : callee, |
114 | "Context" : "from-cli", | |
115 | "Priority" : 1, | |
116 | "Timeout" : timeout, | |
909b9713 | 117 | "CallerID" : callee_id or callee, |
5ac74b02 MT |
118 | }) |
119 | ||
120 | return res | |
121 | ||
122 | def list_channels(self): | |
123 | channels = [] | |
124 | ||
125 | for c in self._send_action("CoreShowChannels"): | |
126 | channel = Channel(self, c.get("Channel")) | |
127 | channels.append(channel) | |
128 | ||
129 | return sorted(channels) | |
130 | ||
131 | def _mailbox_status(self, mailbox): | |
132 | return self._send_action("MailboxStatus", { "Mailbox" : "%s@default" % mailbox }) | |
133 | ||
134 | def messages_waiting(self, mailbox): | |
135 | status = self._mailbox_status(mailbox) | |
136 | ||
137 | # Get messages waiting | |
138 | waiting = status.get("Waiting", 0) | |
139 | ||
140 | return int(waiting) | |
141 | ||
142 | def list_peers(self): | |
143 | peers = [] | |
144 | ||
145 | for p in self._send_action("SIPPeers"): | |
146 | peer = Peer(self, p.get("ObjectName")) | |
147 | peers.append(peer) | |
148 | ||
149 | return sorted(peers) | |
150 | ||
151 | def list_registry(self): | |
152 | print self._send_action("SIPShowRegistry") | |
153 | ||
154 | ||
155 | class Channel(object): | |
156 | def __init__(self, manager, channel_id): | |
157 | self.manager = manager | |
158 | self.id = channel_id | |
159 | ||
160 | self.status = self.manager._send_action("Status", { "Channel" : self.id })[0] | |
161 | ||
162 | def __eq__(self, other): | |
163 | return self.id == other.id | |
164 | ||
165 | def __lt__(self, other): | |
166 | # Longest first | |
167 | return not self.duration < other.duration | |
168 | ||
169 | def __repr__(self): | |
170 | return "<%s %s>" % (self.__class__.__name__, self.id) | |
171 | ||
172 | def hangup(self): | |
173 | res = self.manager._send_action("Hangup", { "Channel" : self.id }) | |
174 | ||
175 | return res["Response"] == "Success" | |
176 | ||
177 | @property | |
178 | def type(self): | |
179 | return self.status.get("Type") | |
180 | ||
181 | @property | |
182 | def application(self): | |
183 | return self.status.get("Application") | |
184 | ||
185 | @property | |
186 | def duration(self): | |
187 | seconds = self.status.get("Seconds", None) | |
188 | ||
189 | if seconds is None: | |
190 | return | |
191 | ||
192 | return int(seconds) | |
193 | ||
56851b01 MT |
194 | _states = { |
195 | "4" : "ringing", | |
196 | "6" : "connected", | |
197 | } | |
198 | ||
199 | @property | |
200 | def state(self): | |
201 | state = self.status.get("ChannelState") | |
202 | ||
203 | try: | |
204 | return self._states[state] | |
205 | except KeyError: | |
206 | return | |
207 | ||
5ac74b02 MT |
208 | @property |
209 | def format(self): | |
210 | return "%s/%s" % ( | |
211 | self.status.get("Readformat"), | |
212 | self.status.get("Writeformat"), | |
213 | ) | |
214 | ||
215 | @staticmethod | |
216 | def _format_number(number): | |
217 | # Replace 00 by + | |
218 | if number and number.startswith("00"): | |
219 | return "+%s" % number[2:] | |
220 | ||
221 | return number | |
222 | ||
223 | @property | |
224 | def caller(self): | |
225 | num = self.status.get("CallerIDNum") | |
226 | ||
227 | return self._format_number(num) | |
228 | ||
229 | @property | |
230 | def caller_name(self): | |
231 | name = self.status.get("CallerIDName") | |
232 | ||
233 | if name in ("", "<unknown>"): | |
234 | return None | |
235 | ||
236 | return name | |
237 | ||
238 | @property | |
239 | def callee(self): | |
56851b01 MT |
240 | if self.application == "ConfBridge": |
241 | return self.data # Will have the conference room number | |
242 | ||
243 | elif self.application == "VoiceMail": | |
244 | try: | |
245 | user, rest = self.data.split("@", 1) | |
246 | except: | |
247 | return self.data | |
248 | ||
249 | return user | |
250 | ||
251 | num = self.status.get("EffectiveConnectedLineNum") or self.status.get("DNID") | |
5ac74b02 MT |
252 | |
253 | return self._format_number(num) | |
254 | ||
56851b01 MT |
255 | @property |
256 | def application(self): | |
257 | return self.status.get("Application") | |
258 | ||
259 | @property | |
260 | def data(self): | |
261 | return self.status.get("Data") | |
262 | ||
5ac74b02 MT |
263 | |
264 | class Peer(object): | |
265 | def __init__(self, manager, peer_id): | |
266 | self.manager = manager | |
267 | self.id = peer_id | |
268 | ||
269 | self.data = self.manager._send_action("SIPShowPeer", { "Peer" : self.id }) | |
270 | ||
271 | def __repr__(self): | |
272 | return "<%s %s>" % (self.__class__.__name__, self.id) | |
273 | ||
274 | def __eq__(self, other): | |
275 | return self.id == other.id | |
276 | ||
277 | def __lt__(self, other): | |
278 | return self.id < other.id |