]>
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 | ||
731637a8 | 110 | def call(self, caller, callee, caller_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, | |
731637a8 | 117 | "CallerID" : caller_id or caller, |
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): | |
41313ef0 MT |
210 | format = self.status.get("Nativeformats") |
211 | ||
212 | return format.lstrip("(").rstrip(")") | |
5ac74b02 MT |
213 | |
214 | @staticmethod | |
215 | def _format_number(number): | |
216 | # Replace 00 by + | |
217 | if number and number.startswith("00"): | |
218 | return "+%s" % number[2:] | |
219 | ||
220 | return number | |
221 | ||
222 | @property | |
223 | def caller(self): | |
224 | num = self.status.get("CallerIDNum") | |
225 | ||
226 | return self._format_number(num) | |
227 | ||
228 | @property | |
229 | def caller_name(self): | |
230 | name = self.status.get("CallerIDName") | |
231 | ||
232 | if name in ("", "<unknown>"): | |
233 | return None | |
234 | ||
235 | return name | |
236 | ||
237 | @property | |
238 | def callee(self): | |
56851b01 MT |
239 | if self.application == "ConfBridge": |
240 | return self.data # Will have the conference room number | |
241 | ||
61b16172 MT |
242 | elif self.application == "Echo": |
243 | return None | |
244 | ||
991a0808 | 245 | elif self.application in ("VoiceMail", "VoiceMailMain"): |
56851b01 MT |
246 | try: |
247 | user, rest = self.data.split("@", 1) | |
248 | except: | |
249 | return self.data | |
250 | ||
251 | return user | |
252 | ||
253 | num = self.status.get("EffectiveConnectedLineNum") or self.status.get("DNID") | |
5ac74b02 MT |
254 | |
255 | return self._format_number(num) | |
256 | ||
56851b01 MT |
257 | @property |
258 | def application(self): | |
259 | return self.status.get("Application") | |
260 | ||
261 | @property | |
262 | def data(self): | |
263 | return self.status.get("Data") | |
264 | ||
5ac74b02 MT |
265 | |
266 | class Peer(object): | |
267 | def __init__(self, manager, peer_id): | |
268 | self.manager = manager | |
269 | self.id = peer_id | |
270 | ||
271 | self.data = self.manager._send_action("SIPShowPeer", { "Peer" : self.id }) | |
272 | ||
273 | def __repr__(self): | |
274 | return "<%s %s>" % (self.__class__.__name__, self.id) | |
275 | ||
276 | def __eq__(self, other): | |
277 | return self.id == other.id | |
278 | ||
279 | def __lt__(self, other): | |
280 | return self.id < other.id |