__init__.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. """
  2. FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
  3. Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
  4. Version: MPL 1.1
  5. The contents of this file are subject to the Mozilla Public License Version
  6. 1.1 (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.mozilla.org/MPL/
  9. Software distributed under the License is distributed on an "AS IS" basis,
  10. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. for the specific language governing rights and limitations under the
  12. License.
  13. The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
  14. The Initial Developer of the Original Code is
  15. Anthony Minessale II <anthm@freeswitch.org>
  16. Portions created by the Initial Developer are Copyright (C)
  17. the Initial Developer. All Rights Reserved.
  18. Contributor(s): Traun Leyden <tleyden@branchcut.com>
  19. """
  20. import sys
  21. from twisted.internet import reactor, defer
  22. from twisted.protocols.basic import LineReceiver
  23. from twisted.internet.protocol import Protocol, ClientFactory
  24. from twisted.python import failure
  25. import time, re
  26. from time import strftime
  27. from Queue import Queue
  28. from freepy import request
  29. import freepy.globals
  30. from freepy.globals import debug
  31. """
  32. freepy library -- connect to freeswitch mod_socket_event via python/twisted
  33. All commands currently use api instead of bgapi. For the networking model
  34. used (twisted), this seems to work well and is simpler.
  35. """
  36. DEBUG_ON = "see globals.py to turn on debugging"
  37. class FreepyDispatcher(LineReceiver):
  38. def __init__(self, conncb, discocb=None):
  39. self.delimiter='\n' # parent class uses this
  40. self.conncb=conncb
  41. self.discocb=discocb
  42. self.requestq = Queue() # queue of pending requests
  43. self.active_request = None # the current active (de-queued) request
  44. def connectionMade(self):
  45. debug("FREEPY: Connection made")
  46. self.conncb(self)
  47. def connectionLost(self, reason):
  48. if self.discocb:
  49. self.discocb(reason)
  50. debug("connectionLost: %s" % reason)
  51. def login(self, passwd):
  52. """
  53. send login request
  54. """
  55. msg = "auth %s" % passwd
  56. req = request.LoginRequest()
  57. self.requestq.put(req)
  58. self.transport.write("%s\n\n" % msg)
  59. debug(">> %s" % msg)
  60. return req.getDeferred()
  61. def _sendCommand(self, command, bgapi):
  62. """
  63. there is a lot of duplication in this object, and as many
  64. methods as possible should be changed to use this method
  65. rather than repeating the code
  66. """
  67. command = ("bgapi %s" if bgapi else "api %s") % command
  68. req = (request.BgDialoutRequest if bgapi else
  69. request.DialoutRequest)()
  70. self.requestq.put(req)
  71. self.transport.write("%s\n\n" % command)
  72. debug(">> %s" % command)
  73. return req.getDeferred()
  74. def confdialout(self, conf_name, sofia_url, bgapi=True):
  75. """
  76. Instruct conference to join a particular user via dialout
  77. @param conf_name - the name of the conference (arbitrary)
  78. @param party2dial - a freeswitch sofia url, eg, sofia/mydomain.com/foo@bar.com
  79. @return - a deferred that will be called back with a string like:
  80. Reply-Text: +OK Job-UUID: 4d410a8e-2409-11dc-99bf-a5e17fab9c65
  81. """
  82. if bgapi == True:
  83. msg = "bgapi conference %s dial %s" % (conf_name,
  84. sofia_url)
  85. req = request.BgDialoutRequest()
  86. else:
  87. msg = "api conference %s dial %s" % (conf_name,
  88. sofia_url)
  89. req = request.DialoutRequest()
  90. self.requestq.put(req)
  91. self.transport.write("%s\n\n" % msg)
  92. debug(">> %s" % msg)
  93. return req.getDeferred()
  94. def originate(self, party2dial, dest_ext_app, bgapi=True):
  95. if bgapi == True:
  96. msg = "bgapi originate %s %s" % (party2dial,
  97. dest_ext_app)
  98. req = request.BgDialoutRequest()
  99. else:
  100. msg = "api originate %s %s" % (party2dial,
  101. dest_ext_app)
  102. req = request.DialoutRequest()
  103. self.requestq.put(req)
  104. self.transport.write("%s\n\n" % msg)
  105. debug(">> %s" % msg)
  106. return req.getDeferred()
  107. def listconf(self, conf_name):
  108. """
  109. List users in a conf
  110. @param conf_name - the name of the conference (arbitrary)
  111. @return - a deferred that will be called back with an array
  112. of models.ConfMember instances
  113. """
  114. msg = "api conference %s list" % (conf_name)
  115. req = request.ListConfRequest()
  116. self.requestq.put(req)
  117. self.transport.write("%s\n\n" % msg)
  118. debug(">> %s" % msg)
  119. return req.getDeferred()
  120. def confkick(self, member_id, conf_name, bgapi=False):
  121. """
  122. Kick member_id from conf
  123. conf_name - name of conf
  124. member_id - member id of user to kick, eg, "7"
  125. returns - a deferred that will be called back with a result
  126. like:
  127. TODO: add this
  128. """
  129. if bgapi == True:
  130. msg = "bgapi conference %s kick %s" % (conf_name, member_id)
  131. req = request.BgConfKickRequest()
  132. else:
  133. msg = "api conference %s kick %s" % (conf_name, member_id)
  134. req = request.ConfKickRequest()
  135. self.requestq.put(req)
  136. self.transport.write("%s\n\n" % msg)
  137. debug(">> %s" % msg)
  138. return req.getDeferred()
  139. def confdtmf(self, member_id, conf_name, dtmf, bgapi=False):
  140. """
  141. Send dtmf to member_id or to all members
  142. conf_name - name of conf
  143. member_id - member id of user to kick, eg, "7"
  144. dtmf - a single dtmf or a string of dtms, eg "1" or "123"
  145. returns - a deferred that will be called back with a result
  146. like:
  147. TODO: add this
  148. """
  149. msg = "conference %s dtmf %s %s" % \
  150. (conf_name, member_id, dtmf)
  151. return self._sendCommand(msg, bgapi)
  152. def confsay(self, conf_name, text2speak, bgapi=False):
  153. """
  154. Speak text all members
  155. conf_name - name of conf
  156. dtmf - text to speak
  157. returns - a deferred that will be called back with a result
  158. like:
  159. TODO: add this
  160. """
  161. msg = "conference %s say %s" % (conf_name, text2speak)
  162. return self._sendCommand(msg, bgapi)
  163. def confplay(self, conf_name, snd_url, bgapi=False):
  164. """
  165. Play a file to all members
  166. conf_name - name of conf
  167. dtmf - text to speak
  168. returns - a deferred that will be called back with a result
  169. like:
  170. TODO: add this
  171. """
  172. msg = "conference %s play %s" % (conf_name, snd_url)
  173. return self._sendCommand(msg, bgapi)
  174. def confstop(self, conf_name, bgapi=False):
  175. """
  176. Stop playback of all sound files
  177. conf_name - name of conf
  178. returns - a deferred that will be called back with a result
  179. like:
  180. TODO: add this
  181. """
  182. msg = "conference %s stop" % (conf_name)
  183. return self._sendCommand(msg, bgapi)
  184. def showchannels(self, bgapi=False):
  185. """
  186. Get a list of all live channels on switch
  187. returns - a deferred that will be called back with a result
  188. <result row_count="2">
  189. <row row_id="1">
  190. <uuid>21524b8c-6d19-11dc-9380-357de4a7a612</uuid>
  191. <created>2007-09-27 11:46:01</created>
  192. <name>sofia/test/4761</name>
  193. <state>CS_LOOPBACK</state>
  194. <cid_name>FreeSWITCH</cid_name>
  195. <cid_num>0000000000</cid_num>
  196. <ip_addr></ip_addr>
  197. <dest>outgoing2endpoint-6207463</dest>
  198. <application>echo</application>
  199. <application_data></application_data>
  200. <read_codec>PCMU</read_codec>
  201. <read_rate>8000</read_rate>
  202. <write_codec>PCMU</write_codec>
  203. <write_rate>8000</write_rate>
  204. </row>
  205. ...
  206. </result>
  207. """
  208. msg = "show channels as xml"
  209. return self._sendCommand(msg, bgapi)
  210. def sofia_status_profile(self, profile_name, bgapi=False):
  211. msg = "sofia status profile %s as xml" % (profile_name)
  212. return self._sendCommand(msg, bgapi)
  213. def sofia_profile_restart(self, sofia_profile_name, bgapi = False):
  214. msg = "sofia profile %s restart" % (sofia_profile_name)
  215. return self._sendCommand(msg, bgapi)
  216. def killchan(self, uuid, bgapi = False):
  217. return self._sendCommand("uuid_kill %s" % (uuid), bgapi)
  218. def broadcast(self, uuid, path, legs, bgapi = False):
  219. msg = "uuid_broadcast %s %s %s" % (uuid, path, legs)
  220. return self._sendCommand(msg, bgapi)
  221. def transfer(self, uuid, dest_ext, legs, bgapi = False):
  222. """
  223. transfer <uuid> [-bleg|-both] <dest-exten>
  224. """
  225. msg = "uuid_transfer %s %s %s" % (uuid, legs, dest_ext)
  226. return self._sendCommand(msg, bgapi)
  227. def lineReceived(self, line):
  228. debug("<< %s" % line)
  229. if not self.active_request:
  230. # if no active request pending, we ignore
  231. # blank lines
  232. if not line.strip():
  233. return
  234. # if no active request, dequeue a new one
  235. if self.requestq.empty():
  236. # we are receiving non-empty data from fs without an
  237. # active request pending. that means that
  238. # there is a bug in the protocol handler
  239. # (or possibly in fs)
  240. raise Exception("Received line: %s w/ no pending requests" % line)
  241. self.active_request = self.requestq.get()
  242. # tell the request to process the line, and tell us
  243. # if its finished or not. if its finished, we remove it
  244. # as the active request so that a new active request will
  245. # be de-queued.
  246. finished = self.active_request.process(line)
  247. if finished == True:
  248. self.active_request = None