├── ISSUES ├── LICENSE.txt ├── MANIFEST ├── MANIFEST.in ├── README.md ├── build └── lib │ └── tropo.py ├── dist ├── README ├── ciscotropo-webapi-python-0.1.4.tar.gz ├── tropo-webapi-python-0.1.0.tar.gz ├── tropo-webapi-python-0.1.1.tar.gz └── tropo-webapi-python-0.1.2.tar.gz ├── samples ├── OutboundSMS.py ├── README ├── ReceiveSMS.py ├── appengine │ ├── GoogleS3.py │ ├── app.yaml │ ├── main.py │ └── setup.py ├── fire-app-with-token.py ├── gh-12-test_voice.py ├── gh-14.test_call.py ├── gh-14.test_call_MMS.py ├── gh-14.test_message.py ├── gh-14.test_say.py ├── gh-14.test_transfer.py ├── gh-14_test.py ├── gh-17.test.py ├── gh-20-test_ask.py ├── gh-20-test_ask.testresult ├── gh-21.choices.py ├── gh-22.transfer.py ├── gh-23.ask_timeout.py ├── gh-24.ask_log.py ├── gh-25.conference.py ├── gh-26.on.py ├── gh-5.hello_cgi.py ├── itty_hello_world.py ├── itty_session_api.py ├── recordMultiUrl_test.py ├── record_test.py ├── sample_conf.py ├── session_test.py ├── session_testMMS.py ├── startRecordingMultiUrl_test.py ├── startRecording_test.py ├── test_answer.py ├── test_generalLogSecurity.py ├── tropo_11258_transferOnTest.py ├── tropo_11263_multi_ask_multi_actions.py ├── tropo_11270_transferTest.py └── wait_test.py ├── setup.py ├── test ├── test.py ├── test1.py └── tropoTestMachineDetection.py └── tropo.py /ISSUES: -------------------------------------------------------------------------------- 1 | Known issues with this python module for the Tropo Web API can be found at: 2 | 3 | http://github.com/tropo/tropo-webapi-python/issues 4 | 5 | Please also use that URL to raise any issues, report any bugs or add any suggestions or requests. 6 | 7 | Thanks. 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Voxeo Corporation 2 | All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | LICENSE 2 | MANIFEST.in 3 | README 4 | setup.py 5 | tropo.py 6 | test/test.py 7 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in LICENSE 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PyPI version (tropo-webapi-python)](https://img.shields.io/pypi/v/tropo-webapi-sdk-python.svg?style=flat-square)](https://pypi.org/project/tropo-webapi-sdk-python/) 2 | [![GitHub license](https://img.shields.io/github/license/tropo/tropo-webapi-python.svg?style=for-the-badge)](https://github.com/tropo/tropo-webapi-python/blob/master/LICENSE.txt) 3 | 4 | 5 | 6 | # Tropo Python Module 7 | 8 | tropo - The TropoPython module. This module implements a set of classes and methods for manipulating the 9 | Web API for the Tropo cloud communications service at http://www.tropo.com/ 10 | 11 | For more information about the Tropo Web API that is used with this module, please see: 12 | 13 | http://www.tropo.com/docs/webapi/ 14 | 15 | As this python module is still in development, please report any bugs or issues by raising an issue here: 16 | 17 | http://github.com/tropo/tropo-webapi-python/issues 18 | 19 | ## File 20 | 21 | http://github.com/tropo/tropo-webapi-python/blob/master/tropo.py 22 | 23 | ## Description 24 | 25 | Usage: 26 | 27 | ```python 28 | from tropo import Tropo 29 | 30 | tropo = Tropo() 31 | tropo.say("Hello, World") 32 | json = tropo.RenderJson() 33 | ``` 34 | 35 | You can write this JSON back to standard output to get Tropo to perform 36 | the action. For example, on Google Appengine you might write something like: 37 | 38 | handler.response.out.write(json) 39 | 40 | Much of the time, a you will interact with Tropo by examining the Result 41 | object and communicating back to Tropo via the Tropo class methods, such 42 | as "say". In some cases, you'll want to build a class object directly such as in : 43 | 44 | ```python 45 | choices = tropo.Choices("[5 digits]").obj 46 | 47 | tropo.ask(choices, 48 | say="Please enter your 5 digit zip code.", 49 | attempts=3, bargein=True, name="zip", timeout=5, voice="dave") 50 | ... 51 | ``` 52 | 53 | ## Note on Python versions 54 | 55 | This module is for python 2.x and requires python 2.5 or higher. 56 | There is a separate version available for python 3.x at: 57 | 58 | https://github.com/tropo/tropo-webapi-python/tree/python3 59 | 60 | ## License 61 | 62 | MIT, see LICENSE.txt 63 | 64 | ## Tests 65 | 66 | Run testsuite by issuing: 67 | 68 | cd test 69 | python test.py 70 | 71 | ## Classes 72 | ```python 73 | Ask 74 | Call 75 | Choices 76 | Conference 77 | Hangup 78 | Message 79 | On 80 | Record 81 | Redirect 82 | Reject 83 | Result 84 | Say 85 | Session 86 | StartRecording 87 | StopRecording 88 | Transfer 89 | Tropo 90 | unittest.TestCase(__builtin__.object) 91 | TestTropoPython 92 | 93 | class Ask 94 | | Class representing the "ask" Tropo action. Builds an "ask" JSON object. 95 | | Class constructor arg: choices, a Choices object 96 | | Convenience function: Tropo.ask() 97 | | Class constructor options: attempts, bargein, choices, minConfidence, name, recognizer, required, say, timeout, voice 98 | | 99 | | Request information from the caller and wait for a response. 100 | | (See https://www.tropo.com/docs/webapi/ask.htm) 101 | | 102 | | { "ask": { 103 | | "attempts": Integer, 104 | | "bargein": Boolean, 105 | | "choices": Object, #Required 106 | | "minConfidence": Integer, 107 | | "name": String, 108 | | "recognizer": String, 109 | | "required": Boolean, 110 | | "say": Object, 111 | | "timeout": Float, 112 | | "voice": String } } 113 | | 114 | | Methods defined here: 115 | | 116 | | __init__(self, choices, **options) 117 | 118 | class Call 119 | | Class representing the "call" Tropo action. Builds a "call" JSON object. 120 | | Class constructor arg: to, a String 121 | | Class constructor options: answerOnMedia, channel, _from, headers, name, network, recording, required, timeout 122 | | Convenience function: Tropo.call() 123 | | 124 | | (See https://www.tropo.com/docs/webapi/call.htm) 125 | | 126 | | { "call": { 127 | | "to": String or Array,#Required 128 | | "answerOnMedia": Boolean, 129 | | "channel": string, 130 | | "from": string, 131 | | "headers": Object, 132 | | "name": String, 133 | | "network": String, 134 | | "recording": Array or Object, 135 | | "required": Boolean, 136 | | "timeout": Float } } 137 | | 138 | | Methods defined here: 139 | | 140 | | __init__(self, to, **options) 141 | 142 | class Choices 143 | | Class representing choice made by a user. Builds a "choices" JSON object. 144 | | Class constructor options: terminator, mode 145 | | 146 | | (See https://www.tropo.com/docs/webapi/ask.htm) 147 | | 148 | | Methods defined here: 149 | | 150 | | __init__(self, value, **options) 151 | 152 | class Conference 153 | | Class representing the "conference" Tropo action. Builds a "conference" JSON object. 154 | | Class constructor arg: id, a String 155 | | Convenience function: Tropo.conference() 156 | | Class constructor options: mute, name, playTones, required, terminator 157 | | 158 | | (See https://www.tropo.com/docs/webapi/conference.htm) 159 | | 160 | | { "conference": { 161 | | "id": String,#Required 162 | | "mute": Boolean, 163 | | "name": String, 164 | | "playTones": Boolean, 165 | | "required": Boolean, 166 | | "terminator": String } } 167 | | 168 | | Methods defined here: 169 | | 170 | | __init__(self, id, **options) 171 | 172 | class Hangup 173 | | Class representing the "hangup" Tropo action. Builds a "hangup" JSON object. 174 | | Class constructor arg: 175 | | Class constructor options: 176 | | Convenience function: Tropo.hangup() 177 | | 178 | | (See https://www.tropo.com/docs/webapi/hangup.htm) 179 | | 180 | | { "hangup": { } } 181 | | 182 | | Methods defined here: 183 | | 184 | | __init__(self) 185 | 186 | class Message 187 | | Class representing the "message" Tropo action. Builds a "message" JSON object. 188 | | Class constructor arg: say_obj, a Say object 189 | | Class constructor arg: to, a String 190 | | Class constructor options: answerOnMedia, channel, _from, name, network, required, timeout, voice 191 | | Convenience function: Tropo.message() 192 | | 193 | | (See https://www.tropo.com/docs/webapi/message.htm) 194 | | { "message": { 195 | | "say": Object,#Required 196 | | "to": String or Array,#Required 197 | | "answerOnMedia": Boolean, 198 | | "channel": string, 199 | | "from": Object, 200 | | "name": String, 201 | | "network": String, 202 | | "required": Boolean, 203 | | "timeout": Float, 204 | | "voice": String } } 205 | | 206 | | Methods defined here: 207 | | 208 | | __init__(self, say_obj, to, **options) 209 | 210 | class On 211 | | Class representing the "on" Tropo action. Builds an "on" JSON object. 212 | | Class constructor arg: event, a String 213 | | Class constructor options: name,next,required,say 214 | | Convenience function: Tropo.on() 215 | | 216 | | (See https://www.tropo.com/docs/webapi/on.htm) 217 | | 218 | | { "on": { 219 | | "event": String,#Required 220 | | "name": String, 221 | | "next": String, 222 | | "required": Boolean, 223 | | "say": Object } } 224 | | 225 | | Methods defined here: 226 | | 227 | | __init__(self, event, **options) 228 | 229 | class Record 230 | | Class representing the "record" Tropo action. Builds a "record" JSON object. 231 | | Class constructor arg: 232 | | Class constructor options: attempts, bargein, beep, choices, format, maxSilence, maxTime, method, minConfidence, name, password, required, say, timeout, transcription, url, username 233 | | Convenience function: Tropo.record() 234 | | 235 | | 236 | | 237 | | (See https://www.tropo.com/docs/webapi/record.htm) 238 | | 239 | | { "record": { 240 | | "attempts": Integer, 241 | | "bargein": Boolean, 242 | | "beep": Boolean, 243 | | "choices": Object, 244 | | "format": String, 245 | | "maxSilence": Float, 246 | | "maxTime": Float, 247 | | "method": String, 248 | | "minConfidence": Integer, 249 | | "name": String, 250 | | "password": String, 251 | | "required": Boolean, 252 | | "say": Object, 253 | | "timeout": Float, 254 | | "transcription": Array or Object, 255 | | "url": String,#Required ????? 256 | | "username": String } } 257 | | 258 | | Methods defined here: 259 | | 260 | | __init__(self, **options) 261 | 262 | class Redirect 263 | | Class representing the "redirect" Tropo action. Builds a "redirect" JSON object. 264 | | Class constructor arg: to, a String 265 | | Class constructor options: name, required 266 | | Convenience function: Tropo.redirect() 267 | | 268 | | (See https://www.tropo.com/docs/webapi/redirect.htm) 269 | | 270 | | { "redirect": { 271 | | "to": Object,#Required 272 | | "name": String, 273 | | "required": Boolean } } 274 | | 275 | | Methods defined here: 276 | | 277 | | __init__(self, to, **options) 278 | 279 | class Reject 280 | | Class representing the "reject" Tropo action. Builds a "reject" JSON object. 281 | | Class constructor arg: 282 | | Class constructor options: 283 | | Convenience function: Tropo.reject() 284 | | 285 | | (See https://www.tropo.com/docs/webapi/reject.htm) 286 | | 287 | | { "reject": { } } 288 | | 289 | | Methods defined here: 290 | | 291 | | __init__(self) 292 | 293 | class Result 294 | | Returned anytime a request is made to the Tropo Web API. 295 | | Method: getValue 296 | | (See https://www.tropo.com/docs/webapi/result.htm) 297 | | 298 | | { "result": { 299 | | "actions": Array or Object, 300 | | "complete": Boolean, 301 | | "error": String, 302 | | "sequence": Integer, 303 | | "sessionDuration": Integer, 304 | | "sessionId": String, 305 | | "state": String } } 306 | | 307 | | Methods defined here: 308 | | 309 | | __init__(self, result_json) 310 | | 311 | | getValue(self) 312 | | Get the value of the previously POSTed Tropo action. 313 | 314 | class Say 315 | | Class representing the "say" Tropo action. Builds a "say" JSON object. 316 | | Class constructor arg: message, a String, or a List of Strings 317 | | Class constructor options: attempts, bargein, choices, minConfidence, name, recognizer, required, say, timeout, voice, _as 318 | | Convenience function: Tropo.say() 319 | | 320 | | (See https://www.tropo.com/docs/webapi/say.htm) 321 | | 322 | | { "say": { 323 | | "as": String, 324 | | "name": String, 325 | | "required": Boolean, 326 | | "value": String #Required 327 | | } } 328 | | 329 | | Methods defined here: 330 | | 331 | | __init__(self, message, **options) 332 | 333 | class Session 334 | | Session is the payload sent as an HTTP POST to your web application when a new session arrives. 335 | | (See https://www.tropo.com/docs/webapi/session.htm) 336 | | 337 | | Because 'from' is a reserved word in Python, the session object's 'from' property is called 338 | | fromaddress in the Python library 339 | | 340 | | Methods defined here: 341 | | 342 | | __init__(self, session_json) 343 | 344 | class StartRecording 345 | | Class representing the "startRecording" Tropo action. Builds a "startRecording" JSON object. 346 | | Class constructor arg: url, a String 347 | | Class constructor options: format, method, username, password 348 | | Convenience function: Tropo.startRecording() 349 | | 350 | | (See https://www.tropo.com/docs/webapi/startrecording.htm) 351 | | 352 | | { "startRecording": { 353 | | "format": String, 354 | | "method": String, 355 | | "url": String,#Required 356 | | "username": String, 357 | | "password": String } } 358 | | 359 | | Methods defined here: 360 | | 361 | | __init__(self, url, **options) 362 | 363 | class StopRecording 364 | | Class representing the "stopRecording" Tropo action. Builds a "stopRecording" JSON object. 365 | | Class constructor arg: 366 | | Class constructor options: 367 | | Convenience function: Tropo.stopRecording() 368 | | 369 | | (See https://www.tropo.com/docs/webapi/stoprecording.htm) 370 | | { "stopRecording": { } } 371 | | 372 | | Methods defined here: 373 | | 374 | | __init__(self) 375 | 376 | class TestTropoPython(unittest.TestCase) 377 | | Class implementing a set of unit tests for TropoPython. 378 | | 379 | | Method resolution order: 380 | | TestTropoPython 381 | | unittest.TestCase 382 | | __builtin__.object 383 | | 384 | | Methods defined here: 385 | | 386 | | test_ask(self) 387 | | Test the "ask" Tropo class method. 388 | | 389 | | test_call(self) 390 | | Test the "call" Tropo class method. 391 | | 392 | | test_conference(self) 393 | | Test the "conference" Tropo class method. 394 | | 395 | | test_hangup(self) 396 | | Test the "hangup" Tropo class method. 397 | | 398 | | test_list_say(self) 399 | | Test the "say" Tropo class method, when a list of Strings is passed to it. 400 | | 401 | | test_message(self) 402 | | Test the "message" Tropo class method. 403 | | 404 | | test_on(self) 405 | | Test the "on" Tropo class method. 406 | | 407 | | test_record(self) 408 | | Test the "record" Tropo class method. 409 | | 410 | | test_redirect(self) 411 | | Test the "redirect" Tropo class method. 412 | | 413 | | test_reject(self) 414 | | Test the "reject" Tropo class method. 415 | | 416 | | test_say(self) 417 | | Test the "say" Tropo class method. 418 | | 419 | | test_startRecording(self) 420 | | Test the "startRecording" Tropo class method. 421 | | 422 | | test_stopRecording(self) 423 | | Test the "stopRecording" Tropo class method. 424 | | 425 | | test_transfer(self) 426 | | Test the "transfer" Tropo class method. 427 | | 428 | | ---------------------------------------------------------------------- 429 | | Data and other attributes defined here: 430 | | 431 | | ID = 'foo' 432 | | 433 | | MY_PHONE = '6021234567' 434 | | 435 | | RECORDING_URL = '/receive_recording.py' 436 | | 437 | | S3_URL = 'http://s3.amazonaws.com/xxx_s3_bucket/hello.wav' 438 | | 439 | | TO = '8005551212' 440 | | 441 | | ---------------------------------------------------------------------- 442 | | Methods inherited from unittest.TestCase: 443 | | 444 | | __call__(self, *args, **kwds) 445 | | 446 | | __init__(self, methodName='runTest') 447 | | Create an instance of the class that will use the named test 448 | | method when executed. Raises a ValueError if the instance does 449 | | not have a method with the specified name. 450 | | 451 | | __repr__(self) 452 | | 453 | | __str__(self) 454 | | 455 | | assertAlmostEqual = failUnlessAlmostEqual(self, first, second, places=7, msg=None) 456 | | Fail if the two objects are unequal as determined by their 457 | | difference rounded to the given number of decimal places 458 | | (default 7) and comparing to zero. 459 | | 460 | | Note that decimal places (from zero) are usually not the same 461 | | as significant digits (measured from the most signficant digit). 462 | | 463 | | assertAlmostEquals = failUnlessAlmostEqual(self, first, second, places=7, msg=None) 464 | | Fail if the two objects are unequal as determined by their 465 | | difference rounded to the given number of decimal places 466 | | (default 7) and comparing to zero. 467 | | 468 | | Note that decimal places (from zero) are usually not the same 469 | | as significant digits (measured from the most signficant digit). 470 | | 471 | | assertEqual = failUnlessEqual(self, first, second, msg=None) 472 | | Fail if the two objects are unequal as determined by the '==' 473 | | operator. 474 | | 475 | | assertEquals = failUnlessEqual(self, first, second, msg=None) 476 | | Fail if the two objects are unequal as determined by the '==' 477 | | operator. 478 | | 479 | | assertFalse = failIf(self, expr, msg=None) 480 | | Fail the test if the expression is true. 481 | | 482 | | assertNotAlmostEqual = failIfAlmostEqual(self, first, second, places=7, msg=None) 483 | | Fail if the two objects are equal as determined by their 484 | | difference rounded to the given number of decimal places 485 | | (default 7) and comparing to zero. 486 | | 487 | | Note that decimal places (from zero) are usually not the same 488 | | as significant digits (measured from the most signficant digit). 489 | | 490 | | assertNotAlmostEquals = failIfAlmostEqual(self, first, second, places=7, msg=None) 491 | | Fail if the two objects are equal as determined by their 492 | | difference rounded to the given number of decimal places 493 | | (default 7) and comparing to zero. 494 | | 495 | | Note that decimal places (from zero) are usually not the same 496 | | as significant digits (measured from the most signficant digit). 497 | | 498 | | assertNotEqual = failIfEqual(self, first, second, msg=None) 499 | | Fail if the two objects are equal as determined by the '==' 500 | | operator. 501 | | 502 | | assertNotEquals = failIfEqual(self, first, second, msg=None) 503 | | Fail if the two objects are equal as determined by the '==' 504 | | operator. 505 | | 506 | | assertRaises = failUnlessRaises(self, excClass, callableObj, *args, **kwargs) 507 | | Fail unless an exception of class excClass is thrown 508 | | by callableObj when invoked with arguments args and keyword 509 | | arguments kwargs. If a different type of exception is 510 | | thrown, it will not be caught, and the test case will be 511 | | deemed to have suffered an error, exactly as for an 512 | | unexpected exception. 513 | | 514 | | assertTrue = failUnless(self, expr, msg=None) 515 | | Fail the test unless the expression is true. 516 | | 517 | | assert_ = failUnless(self, expr, msg=None) 518 | | Fail the test unless the expression is true. 519 | | 520 | | countTestCases(self) 521 | | 522 | | debug(self) 523 | | Run the test without collecting errors in a TestResult 524 | | 525 | | defaultTestResult(self) 526 | | 527 | | fail(self, msg=None) 528 | | Fail immediately, with the given message. 529 | | 530 | | failIf(self, expr, msg=None) 531 | | Fail the test if the expression is true. 532 | | 533 | | failIfAlmostEqual(self, first, second, places=7, msg=None) 534 | | Fail if the two objects are equal as determined by their 535 | | difference rounded to the given number of decimal places 536 | | (default 7) and comparing to zero. 537 | | 538 | | Note that decimal places (from zero) are usually not the same 539 | | as significant digits (measured from the most signficant digit). 540 | | 541 | | failIfEqual(self, first, second, msg=None) 542 | | Fail if the two objects are equal as determined by the '==' 543 | | operator. 544 | | 545 | | failUnless(self, expr, msg=None) 546 | | Fail the test unless the expression is true. 547 | | 548 | | failUnlessAlmostEqual(self, first, second, places=7, msg=None) 549 | | Fail if the two objects are unequal as determined by their 550 | | difference rounded to the given number of decimal places 551 | | (default 7) and comparing to zero. 552 | | 553 | | Note that decimal places (from zero) are usually not the same 554 | | as significant digits (measured from the most signficant digit). 555 | | 556 | | failUnlessEqual(self, first, second, msg=None) 557 | | Fail if the two objects are unequal as determined by the '==' 558 | | operator. 559 | | 560 | | failUnlessRaises(self, excClass, callableObj, *args, **kwargs) 561 | | Fail unless an exception of class excClass is thrown 562 | | by callableObj when invoked with arguments args and keyword 563 | | arguments kwargs. If a different type of exception is 564 | | thrown, it will not be caught, and the test case will be 565 | | deemed to have suffered an error, exactly as for an 566 | | unexpected exception. 567 | | 568 | | id(self) 569 | | 570 | | run(self, result=None) 571 | | 572 | | setUp(self) 573 | | Hook method for setting up the test fixture before exercising it. 574 | | 575 | | shortDescription(self) 576 | | Returns a one-line description of the test, or None if no 577 | | description has been provided. 578 | | 579 | | The default implementation of this method returns the first line of 580 | | the specified test method's docstring. 581 | | 582 | | tearDown(self) 583 | | Hook method for deconstructing the test fixture after testing it. 584 | | 585 | | ---------------------------------------------------------------------- 586 | | Data descriptors inherited from unittest.TestCase: 587 | | 588 | | __dict__ 589 | | dictionary for instance variables (if defined) 590 | | 591 | | __weakref__ 592 | | list of weak references to the object (if defined) 593 | | 594 | | ---------------------------------------------------------------------- 595 | | Data and other attributes inherited from unittest.TestCase: 596 | | 597 | | failureException = 598 | | Assertion failed. 599 | 600 | class Transfer 601 | | Class representing the "transfer" Tropo action. Builds a "transfer" JSON object. 602 | | Class constructor arg: to, a String, or List 603 | | Class constructor options: answerOnMedia, choices, _from, name, required, terminator 604 | | Convenience function: Tropo.transfer() 605 | | 606 | | (See https://www.tropo.com/docs/webapi/transfer.htm) 607 | | { "transfer": { 608 | | "to": String or Array,#Required 609 | | "answerOnMedia": Boolean, 610 | | "choices": Object, 611 | | "from": Object, 612 | | "name": String, 613 | | "required": Boolean, 614 | | "terminator": String, 615 | | "timeout": Float } } 616 | | 617 | | Methods defined here: 618 | | 619 | | __init__(self, to, **options) 620 | 621 | class Tropo 622 | | This is the top level class for all the Tropo web api actions. 623 | | The methods of this class implement individual Tropo actions. 624 | | Individual actions are each methods on this class. 625 | | 626 | | Each method takes one or more required arguments, followed by optional 627 | | arguments expressed as key=value pairs. 628 | | 629 | | The optional arguments for these methods are described here: 630 | | https://www.tropo.com/docs/webapi/ 631 | | 632 | | Methods defined here: 633 | | 634 | | PrettyJson(self) 635 | | Render a Tropo object into a Json string, pretty-printed with indents. 636 | | 637 | | RenderJson(self) 638 | | Render a Tropo object into a Json string. 639 | | 640 | | __init__(self) 641 | | 642 | | ask(self, choices, **options) 643 | | Sends a prompt to the user and optionally waits for a response. 644 | | Arguments: "choices" is a Choices object 645 | | See https://www.tropo.com/docs/webapi/ask.htm 646 | | 647 | | call(self, to, **options) 648 | | Places a call or sends an IM, Twitter, or SMS message. To start a call, use the Session API to tell Tropo to launch your code. 649 | | 650 | | Arguments: to is a String. 651 | | Argument: **options is a set of optional keyword arguments. Use "_from" instead of "from". 652 | | See https://www.tropo.com/docs/webapi/call.htm 653 | | 654 | | conference(self, id, **options) 655 | | This object allows multiple lines in separate sessions to be conferenced together so that the parties on each line can talk to each other simultaneously. 656 | | This is a voice channel only feature. 657 | | Argument: "id" is a String 658 | | Argument: **options is a set of optional keyword arguments. 659 | | See https://www.tropo.com/docs/webapi/conference.htm 660 | | 661 | | hangup(self) 662 | | This method instructs Tropo to "hang-up" or disconnect the session associated with the current session. 663 | | See https://www.tropo.com/docs/webapi/hangup.htm 664 | | 665 | | message(self, say_obj, to, **options) 666 | | A shortcut method to create a session, say something, and hang up, all in one step. This is particularly useful for sending out a quick SMS or IM. 667 | | 668 | | Argument: "say_obj" is a Say object 669 | | Argument: "to" is a String 670 | | Argument: **options is a set of optional keyword arguments. Use "_from" instead of "from". 671 | | See https://www.tropo.com/docs/webapi/message.htm 672 | | 673 | | on(self, event, **options) 674 | | Adds an event callback so that your application may be notified when a particular event occurs. 675 | | Possible events are: "continue", "error", "incomplete" and "hangup". 676 | | Argument: event is an event 677 | | Argument: **options is a set of optional keyword arguments. 678 | | See https://www.tropo.com/docs/webapi/on.htm 679 | | 680 | | record(self, **options) 681 | | Plays a prompt (audio file or text to speech) and optionally waits for a response from the caller that is recorded. 682 | | Argument: **options is a set of optional keyword arguments. 683 | | See https://www.tropo.com/docs/webapi/record.htm 684 | | 685 | | redirect(self, id, **options) 686 | | Forwards an incoming call to another destination / phone number before answering it. 687 | | Argument: id is a String 688 | | Argument: **options is a set of optional keyword arguments. 689 | | See https://www.tropo.com/docs/webapi/redirect.htm 690 | | 691 | | reject(self) 692 | | Allows Tropo applications to reject incoming sessions before they are answered. 693 | | See https://www.tropo.com/docs/webapi/reject.htm 694 | | 695 | | say(self, message, **options) 696 | | When the current session is a voice channel this key will either play a message or an audio file from a URL. 697 | | In the case of an text channel it will send the text back to the user via i nstant messaging or SMS. 698 | | Argument: message is a string 699 | | Argument: **options is a set of optional keyword arguments. Use "_as" instead of "as". 700 | | See https://www.tropo.com/docs/webapi/say.htm 701 | | 702 | | startRecording(self, url, **options) 703 | | Allows Tropo applications to begin recording the current session. 704 | | Argument: url is a string 705 | | Argument: **options is a set of optional keyword arguments. 706 | | See https://www.tropo.com/docs/webapi/startrecording.htm 707 | | 708 | | stopRecording(self) 709 | | Stops a previously started recording. 710 | | See https://www.tropo.com/docs/webapi/stoprecording.htm 711 | | 712 | | transfer(self, to, **options) 713 | | Transfers an already answered call to another destination / phone number. 714 | | Argument: to is a string 715 | | Argument: **options is a set of optional keyword arguments. Use "_from" instead of "from". 716 | | See https://www.tropo.com/docs/webapi/transfer.htm 717 | ``` 718 | -------------------------------------------------------------------------------- /build/lib/tropo.py: -------------------------------------------------------------------------------- 1 | """ 2 | The TropoPython module. This module implements a set of classes and methods for manipulating the Voxeo Tropo WebAPI. 3 | 4 | Usage: 5 | 6 | ---- 7 | from tropo import Tropo 8 | 9 | tropo = Tropo() 10 | tropo.say("Hello, World") 11 | json = tropo.RenderJson() 12 | ---- 13 | 14 | You can write this JSON back to standard output to get Tropo to perform 15 | the action. For example, on Google Appengine you might write something like: 16 | 17 | handler.response.out.write(json) 18 | 19 | Much of the time, a you will interact with Tropo by examining the Result 20 | object and communicating back to Tropo via the Tropo class methods, such 21 | as "say". In some cases, you'll want to build a class object directly such as in : 22 | 23 | choices = tropo.Choices("[5 digits]").obj 24 | 25 | tropo.ask(choices, 26 | say="Please enter your 5 digit zip code.", 27 | attempts=3, bargein=True, name="zip", timeout=5, voice="dave") 28 | ... 29 | 30 | NOTE: This module requires python 2.5 or higher. 31 | 32 | """ 33 | 34 | try: 35 | import cjson as jsonlib 36 | jsonlib.dumps = jsonlib.encode 37 | jsonlib.loads = jsonlib.decode 38 | except ImportError: 39 | try: 40 | from django.utils import simplejson as jsonlib 41 | except ImportError: 42 | try: 43 | import simplejson as jsonlib 44 | except ImportError: 45 | import json as jsonlib 46 | 47 | class TropoAction(object): 48 | """ 49 | Class representing the base Tropo action. 50 | Two properties are provided in order to avoid defining the same attributes for every action. 51 | """ 52 | @property 53 | def json(self): 54 | return self._dict 55 | 56 | @property 57 | def obj(self): 58 | return {self.action: self._dict} 59 | 60 | class Ask(TropoAction): 61 | """ 62 | Class representing the "ask" Tropo action. Builds an "ask" JSON object. 63 | Class constructor arg: choices, a Choices object 64 | Convenience function: Tropo.ask() 65 | Class constructor options: attempts, bargein, choices, minConfidence, name, recognizer, required, say, timeout, voice 66 | 67 | Request information from the caller and wait for a response. 68 | (See https://www.tropo.com/docs/webapi/ask) 69 | 70 | { "ask": { 71 | "attempts": Integer, 72 | "allowSignals": String or Array, 73 | "bargein": Boolean, 74 | "choices": Object, #Required 75 | "interdigitTimeout": Float, 76 | "minConfidence": Integer, 77 | "name": String, 78 | "recognizer": String, 79 | "required": Boolean, 80 | "say": Object, 81 | "sensitivity": Float, 82 | "speechCompleteTimeout": Float, 83 | "speechIncompleteTimeout": Float, 84 | "timeout": Float, 85 | "voice": String, 86 | 87 | } } 88 | 89 | """ 90 | action = 'ask' 91 | options_array = ['attempts', 'allowSignals', 'bargein', 'choices', 'interdigitTimeout', 'minConfidence', 'name', 'recognizer', 'required', 'say', 'sensitivity', 'speechCompleteTimeout', 'speechIncompleteTimeout', 'timeout', 'voice'] 92 | 93 | def __init__(self, choices, **options): 94 | self._dict = {} 95 | if (isinstance(choices, basestring)): 96 | self._dict['choices'] = Choices(choices).json 97 | else: 98 | # self._dict['choices'] = choices['choices'] 99 | self._dict['choices'] = choices.json 100 | for opt in self.options_array: 101 | if opt in options: 102 | if ((opt == 'say') and (isinstance(options['say'], basestring))): 103 | self._dict['say'] = Say(options['say']).json 104 | else: 105 | self._dict[opt] = options[opt] 106 | 107 | class Call(TropoAction): 108 | """ 109 | Class representing the "call" Tropo action. Builds a "call" JSON object. 110 | Class constructor arg: to, a String 111 | Class constructor options: answerOnMedia, channel, from, headers, name, network, recording, required, timeout, machineDetection 112 | Convenience function: Tropo.call() 113 | 114 | (See https://www.tropo.com/docswebapi/call) 115 | 116 | { "call": { 117 | "to": String or Array,#Required 118 | "answerOnMedia": Boolean, 119 | "allowSignals": String or Array 120 | "channel": string, 121 | "from": string, 122 | "headers": Object, 123 | "name": String, 124 | "network": String, 125 | "recording": Array or Object, 126 | "required": Boolean, 127 | "timeout": Float. 128 | "machineDetection: Boolean or Object" } } 129 | """ 130 | action = 'call' 131 | options_array = ['answerOnMedia', 'allowSignals', 'channel', '_from', 'headers', 'name', 'network', 'recording', 'required', 'timeout', 'machineDetection'] 132 | 133 | def __init__(self, to, **options): 134 | self._dict = {'to': to} 135 | for opt in self.options_array: 136 | if opt in options: 137 | if (opt == "_from"): 138 | self._dict['from'] = options[opt] 139 | else: 140 | self._dict[opt] = options[opt] 141 | 142 | 143 | 144 | class Choices(TropoAction): 145 | """ 146 | Class representing choice made by a user. Builds a "choices" JSON object. 147 | Class constructor options: terminator, mode 148 | 149 | (See https://www.tropo.com/docs/webapi/ask) 150 | """ 151 | action = 'choices' 152 | options_array = ['terminator', 'mode'] 153 | 154 | def __init__(self, value, **options): 155 | self._dict = {'value': value} 156 | for opt in self.options_array: 157 | if opt in options: 158 | self._dict[opt] = options[opt] 159 | 160 | class Conference(TropoAction): 161 | """ 162 | Class representing the "conference" Tropo action. Builds a "conference" JSON object. 163 | Class constructor arg: id, a String 164 | Convenience function: Tropo.conference() 165 | Class constructor options: mute, name, playTones, required, terminator 166 | 167 | (See https://www.tropo.com/docs/webapi/conference) 168 | 169 | { "conference": { 170 | "id": String,#Required 171 | "allowSignals": String or Array, 172 | "interdigitTimeout":Integer, 173 | "mute": Boolean, 174 | "name": String, 175 | "playTones": Boolean, 176 | "required": Boolean, 177 | "terminator": String, 178 | "joinPrompt": Object, 179 | "leavePrompt": Object } } 180 | """ 181 | action = 'conference' 182 | options_array = ['allowSignals', 'interdigitTimeout', 'mute', 'name', 'playTones', 'required', 'terminator', 'joinPrompt', 'leavePrompt'] 183 | 184 | def __init__(self, id, **options): 185 | self._dict = {'id': id} 186 | for opt in self.options_array: 187 | if opt in options: 188 | self._dict[opt] = options[opt] 189 | 190 | class Hangup(TropoAction): 191 | """ 192 | Class representing the "hangup" Tropo action. Builds a "hangup" JSON object. 193 | Class constructor arg: 194 | Class constructor options: 195 | Convenience function: Tropo.hangup() 196 | 197 | (See https://www.tropo.com/docs/webapi/hangup) 198 | 199 | { "hangup": { } } 200 | """ 201 | action = 'hangup' 202 | 203 | def __init__(self): 204 | self._dict = {} 205 | 206 | 207 | class JoinPrompt(TropoAction): 208 | """ 209 | Class representing join prompts for the conference method. Builds a "joinPrompt" JSON object. 210 | Class constructor options: value, voice 211 | 212 | (See https://www.tropo.com/docs/webapi/conference) 213 | """ 214 | action = 'joinPrompt' 215 | options_array = ['voice'] 216 | 217 | def __init__(self, value, **options): 218 | self._dict = {'value': value} 219 | for opt in self.options_array: 220 | if opt in options: 221 | self._dict[opt] = options[opt] 222 | 223 | class LeavePrompt(TropoAction): 224 | """ 225 | Class representing leave prompts for the conference method. Builds a "leavePrompt" JSON object. 226 | Class constructor options: value, voice 227 | 228 | (See https://www.tropo.com/docs/webapi/conference) 229 | """ 230 | action = 'leavePrompt' 231 | options_array = ['voice'] 232 | 233 | def __init__(self, value, **options): 234 | self._dict = {'value': value} 235 | for opt in self.options_array: 236 | if opt in options: 237 | self._dict[opt] = options[opt] 238 | 239 | 240 | class MachineDetection(TropoAction): 241 | """ 242 | Class representing machine detection for the call method. Builds a "machineDetection" JSON object. 243 | Class constructor options: introduction, voice 244 | 245 | (See https://www.tropo.com/docs/webapi/call) 246 | """ 247 | action = 'machineDetection' 248 | options_array = ['introduction', 'voice'] 249 | 250 | def __init__(self, introduction, **options): 251 | self._dict = {'introduction': introduction} 252 | for opt in self.options_array: 253 | if opt in options: 254 | self._dict[opt] = options[opt] 255 | 256 | 257 | class Message(TropoAction): 258 | """ 259 | Class representing the "message" Tropo action. Builds a "message" JSON object. 260 | Class constructor arg: say_obj, a Say object 261 | Class constructor arg: to, a String 262 | Class constructor options: answerOnMedia, channel, from, name, network, required, timeout, voice 263 | Convenience function: Tropo.message() 264 | 265 | (See https://www.tropo.com/docs/webapi/message) 266 | { "message": { 267 | "say": Object,#Required 268 | "to": String or Array,#Required 269 | "answerOnMedia": Boolean, 270 | "channel": string, 271 | "from": String, 272 | "name": String, 273 | "network": String, 274 | "required": Boolean, 275 | "timeout": Float, 276 | "voice": String } } 277 | """ 278 | action = 'message' 279 | options_array = ['answerOnMedia', 'channel', '_from', 'name', 'network', 'required', 'timeout', 'voice'] 280 | 281 | def __init__(self, say_obj, to, **options): 282 | self._dict = {'say': say_obj['say'], 'to': to} 283 | for opt in self.options_array: 284 | if opt in options: 285 | if (opt == "_from"): 286 | self._dict['from'] = options[opt] 287 | else: 288 | self._dict[opt] = options[opt] 289 | 290 | 291 | class On(TropoAction): 292 | """ 293 | Class representing the "on" Tropo action. Builds an "on" JSON object. 294 | Class constructor arg: event, a String 295 | Class constructor options: name,next,required,say 296 | Convenience function: Tropo.on() 297 | 298 | (See https://www.tropo.com/docs/webapi/on) 299 | 300 | { "on": { 301 | "event": String,#Required 302 | "name": String, 303 | "next": String, 304 | "required": Boolean, 305 | "say": Object 306 | "voice": String } } 307 | """ 308 | action = 'on' 309 | options_array = ['name','next','required','say', 'voice', 'ask', 'message', 'wait'] 310 | 311 | def __init__(self, event, **options): 312 | self._dict = {} 313 | for opt in self.options_array: 314 | if opt in options: 315 | if ((opt == 'say') and (isinstance(options['say'], basestring))): 316 | if('voice' in options): 317 | self._dict['say'] = Say(options['say'], voice=options['voice']).json 318 | else: 319 | self._dict['say'] = Say(options['say']).json 320 | 321 | elif ((opt == 'ask') and (isinstance(options['ask'], basestring))): 322 | if('voice' in options): 323 | self._dict['ask'] = Ask(options['ask'], voice=options['voice']).json 324 | else: 325 | self._dict['ask'] = Ask(options['ask']).json 326 | 327 | elif ((opt == 'message') and (isinstance(options['message'], basestring))): 328 | if('voice' in options): 329 | self._dict['message'] = Message(options['message'], voice=options['voice']).json 330 | else: 331 | self._dict['message'] = Message(options['message']).json 332 | 333 | elif ((opt == 'wait') and (isinstance(options['wait'], basestring))): 334 | self._dict['wait'] = Wait(options['wait']).json 335 | 336 | elif(opt != 'voice'): 337 | self._dict[opt] = options[opt] 338 | 339 | self._dict['event'] = event 340 | 341 | class Record(TropoAction): 342 | """ 343 | Class representing the "record" Tropo action. Builds a "record" JSON object. 344 | Class constructor arg: 345 | Class constructor options: attempts, bargein, beep, choices, format, maxSilence, maxTime, method, minConfidence, name, password, required, say, timeout, transcription, url, username 346 | Convenience function: Tropo.record() 347 | 348 | (See https://www.tropo.com/docs/webapi/record) 349 | 350 | { "record": { 351 | "asyncUpload": Boolean, 352 | "allowSignals": Boolean, 353 | "attempts": Integer, 354 | "bargein": Boolean, 355 | "beep": Boolean, 356 | "choices": Object, 357 | "format": String, 358 | "interdigitTimeout": Float, 359 | "maxSilence": Float, 360 | "maxTime": Float, 361 | "method": String, 362 | "name": String, 363 | "password": String, 364 | "required": Boolean, 365 | "say": Object, 366 | "timeout": Float, 367 | "transcription": Array or Object, 368 | "url": String, #Required 369 | "username": String, 370 | "voice": String} } 371 | """ 372 | action = 'record' 373 | options_array = ['asyncUpload', 'attempts', 'bargein', 'beep', 'choices', 'format', 'maxSilence', 'maxTime', 'method', 'name', 'password', 'required', 'say', 'timeout', 'transcription', 'username', 'allowSignals', 'voice', 'interdigitTimeout'] 374 | def __init__(self, url, **options): 375 | self._dict = {'url': url} 376 | for opt in self.options_array: 377 | if opt in options: 378 | if ((opt == 'say') and (isinstance(options['say'], basestring))): 379 | self._dict['say'] = Say(options['say']).json 380 | else: 381 | self._dict[opt] = options[opt] 382 | 383 | class Redirect(TropoAction): 384 | """ 385 | Class representing the "redirect" Tropo action. Builds a "redirect" JSON object. 386 | Class constructor arg: to, a String 387 | Class constructor options: name, required 388 | Convenience function: Tropo.redirect() 389 | 390 | (See https://www.tropo.com/docs/webapi/redirect) 391 | 392 | { "redirect": { 393 | "to": Object,#Required 394 | "name": String, 395 | "required": Boolean } } 396 | """ 397 | action = 'redirect' 398 | options_array = ['name', 'required'] 399 | 400 | def __init__(self, to, **options): 401 | self._dict = {'to': to} 402 | for opt in self.options_array: 403 | if opt in options: 404 | self._dict[opt] = options[opt] 405 | 406 | class Reject(TropoAction): 407 | """ 408 | Class representing the "reject" Tropo action. Builds a "reject" JSON object. 409 | Class constructor arg: 410 | Class constructor options: 411 | Convenience function: Tropo.reject() 412 | 413 | (See https://www.tropo.com/docs/webapi/reject) 414 | 415 | { "reject": { } } 416 | """ 417 | action = 'reject' 418 | 419 | def __init__(self): 420 | self._dict = {} 421 | 422 | class Say(TropoAction): 423 | """ 424 | Class representing the "say" Tropo action. Builds a "say" JSON object. 425 | Class constructor arg: message, a String, or a List of Strings 426 | Class constructor options: attempts, bargein, choices, minConfidence, name, recognizer, required, say, timeout, voice 427 | Convenience function: Tropo.say() 428 | 429 | (See https://www.tropo.com/docs/webapi/say) 430 | 431 | { "say": { 432 | "allowSignals": String or Array, 433 | "voice": String, 434 | "as": String, 435 | "name": String, 436 | "required": Boolean, 437 | "value": String #Required 438 | } } 439 | """ 440 | action = 'say' 441 | # added _as because 'as' is reserved 442 | options_array = ['_as', 'name', 'required', 'voice', 'allowSignals'] 443 | 444 | def __init__(self, message, **options): 445 | dict = {} 446 | for opt in self.options_array: 447 | if opt in options: 448 | if (opt == "_as"): 449 | dict['as'] = options[opt] 450 | else: 451 | dict[opt] = options[opt] 452 | self._list = [] 453 | if (isinstance (message, list)): 454 | for mess in message: 455 | new_dict = dict.copy() 456 | new_dict['value'] = mess 457 | self._list.append(new_dict) 458 | else: 459 | dict['value'] = message 460 | self._list.append(dict) 461 | 462 | @property 463 | def json(self): 464 | return self._list[0] if len(self._list) == 1 else self._list 465 | 466 | @property 467 | def obj(self): 468 | return {self.action: self._list[0]} if len(self._list) == 1 else {self.action: self._list} 469 | 470 | class StartRecording(TropoAction): 471 | """ 472 | Class representing the "startRecording" Tropo action. Builds a "startRecording" JSON object. 473 | Class constructor arg: url, a String 474 | Class constructor options: format, method, username, password 475 | Convenience function: Tropo.startRecording() 476 | 477 | (See https://www.tropo.com/docs/webapi/startrecording) 478 | 479 | { "startRecording": { 480 | "asyncUpload":Boolean, 481 | "format": String, 482 | "method": String, 483 | "url": String,#Required 484 | "username": String, 485 | "password": String, 486 | "transcriptionID": String 487 | "transcriptionEmailFormat":String 488 | "transcriptionOutURI": String} } 489 | """ 490 | action = 'startRecording' 491 | options_array = ['asyncUpload', 'format', 'method', 'username', 'password', 'transcriptionID', 'transcriptionEmailFormat', 'transcriptionOutURI'] 492 | def __init__(self, url, **options): 493 | self._dict = {'url': url} 494 | for opt in self.options_array: 495 | if opt in options: 496 | self._dict[opt] = options[opt] 497 | 498 | class StopRecording(TropoAction): 499 | """ 500 | Class representing the "stopRecording" Tropo action. Builds a "stopRecording" JSON object. 501 | Class constructor arg: 502 | Class constructor options: 503 | Convenience function: Tropo.stopRecording() 504 | 505 | (See https://www.tropo.com/docs/webapi/stoprecording) 506 | { "stopRecording": { } } 507 | """ 508 | action = 'stopRecording' 509 | 510 | def __init__(self): 511 | self._dict = {} 512 | 513 | class Transfer(TropoAction): 514 | """ 515 | Class representing the "transfer" Tropo action. Builds a "transfer" JSON object. 516 | Class constructor arg: to, a String, or List 517 | Class constructor options: answerOnMedia, choices, from, name, required, terminator 518 | Convenience function: Tropo.transfer() 519 | 520 | (See https://www.tropo.com/docs/webapi/transfer) 521 | { "transfer": { 522 | "to": String or Array,#Required 523 | "allowSignals":String or Array, 524 | "answerOnMedia": Boolean, 525 | "choices": Object, 526 | "from": String, 527 | "headers": Object, 528 | "interdigitTimeout":Float, 529 | "machineDetection": Boolean or Object 530 | "name": String, 531 | "playTones":Boolean, 532 | "required": Boolean, 533 | "ringRepeat":Integer, 534 | "terminator": String, 535 | "timeout": Float, 536 | "voice": String, 537 | } } 538 | """ 539 | action = 'transfer' 540 | options_array = ['answerOnMedia', 'choices', '_from', 'name', 'on', 'required', 'allowSignals', 'headers', 'interdigitTimeout', 'ringRepeat', 'timeout', 'machineDetection', 'playTones', 'voice'] 541 | def __init__(self, to, **options): 542 | self._dict = {'to': to} 543 | for opt in self.options_array: 544 | if opt in options: 545 | if opt == "on": 546 | whisper = [] 547 | for key, val in options['on'].iteritems(): 548 | newDict = {} 549 | 550 | if(key == "ask"): 551 | newDict['ask'] = val 552 | newDict['event'] = 'connect' 553 | 554 | elif(key == "say"): 555 | newDict['say'] = val 556 | newDict['event'] = 'connect' 557 | 558 | elif(key == "wait"): 559 | newDict['wait'] = val 560 | newDict['event'] = 'connect' 561 | 562 | elif(key == "message"): 563 | newDict['message'] = val 564 | newDict['event'] = 'connect' 565 | 566 | elif(key == "ring"): 567 | newDict['say'] = val 568 | newDict['event'] = 'ring' 569 | 570 | whisper.append(newDict) 571 | 572 | self._dict['on'] = whisper 573 | if (opt == '_from'): 574 | self._dict['from'] = options['_from'] 575 | elif(opt == 'choices'): 576 | self._dict['choices'] = options['choices'] 577 | elif(opt != 'on'): 578 | self._dict[opt] = options[opt] 579 | 580 | class Wait(TropoAction): 581 | """ 582 | Class representing the "wait" Tropo action. Builds a "wait" JSON object. 583 | Class constructor arg: milliseconds, an Integer 584 | Class constructor options: allowSignals 585 | Convenience function: Tropo.wait() 586 | 587 | (See https://www.tropo.com/docs/webapi/wait) 588 | { "wait": { 589 | "milliseconds": Integer,#Required 590 | "allowSignals": String or Array 591 | """ 592 | 593 | action = 'wait' 594 | options_array = ['allowSignals'] 595 | 596 | def __init__(self, milliseconds, **options): 597 | self._dict = {'milliseconds': milliseconds} 598 | for opt in self.options_array: 599 | if opt in options: 600 | self._dict[opt] = options[opt] 601 | 602 | class Result(object): 603 | """ 604 | Returned anytime a request is made to the Tropo Web API. 605 | Method: getValue 606 | (See https://www.tropo.com/docs/webapi/result) 607 | 608 | { "result": { 609 | "actions": Array or Object, 610 | "calledId": String, 611 | "callId": String, 612 | "complete": Boolean, 613 | "connectedDuration": Integer, 614 | "duration": Integer, 615 | "error": String, 616 | "sequence": Integer, 617 | "sessionDuration": Integer, 618 | "sessionId": String, 619 | "state": String, 620 | "userType": String} } 621 | """ 622 | options_array = ['actions','complete','error','sequence', 'sessionDuration', 'sessionId', 'state', 'userType', 'connectedDuration', 'duration', 'calledID', 'callId'] 623 | def __init__(self, result_json): 624 | result_data = jsonlib.loads(result_json) 625 | result_dict = result_data['result'] 626 | 627 | for opt in self.options_array: 628 | if result_dict.get(opt, False): 629 | setattr(self, '_%s' % opt, result_dict[opt]) 630 | 631 | def getValue(self): 632 | """ 633 | Get the value of the previously POSTed Tropo action. 634 | """ 635 | actions = self._actions 636 | 637 | if (type (actions) is list): 638 | dict = actions[0] 639 | else: 640 | dict = actions 641 | # return dict['value'] Fixes issue 17 642 | return dict['value'] 643 | 644 | 645 | def getUserType(self): 646 | """ 647 | Get the userType of the previously POSTed Tropo action. 648 | """ 649 | userType = self._userType 650 | return userType 651 | 652 | # # **Tue May 17 07:17:38 2011** -- egilchri 653 | 654 | def getInterpretation(self): 655 | """ 656 | Get the value of the previously POSTed Tropo action. 657 | """ 658 | actions = self._actions 659 | 660 | if (type (actions) is list): 661 | dict = actions[0] 662 | else: 663 | dict = actions 664 | return dict['interpretation'] 665 | 666 | # # **Tue May 17 07:17:38 2011** -- egilchri 667 | 668 | 669 | class Session(object): 670 | """ 671 | Session is the payload sent as an HTTP POST to your web application when a new session arrives. 672 | (See https://www.tropo.com/docs/webapi/session) 673 | 674 | Because 'from' is a reserved word in Python, the session object's 'from' property is called 675 | fromaddress in the Python library 676 | """ 677 | def __init__(self, session_json): 678 | session_data = jsonlib.loads(session_json) 679 | session_dict = session_data['session'] 680 | for key in session_dict: 681 | val = session_dict[key] 682 | if key == "from": 683 | setattr(self, "fromaddress", val) 684 | else: 685 | setattr(self, key, val) 686 | setattr(self, 'dict', session_dict) 687 | 688 | 689 | class Tropo(object): 690 | """ 691 | This is the top level class for all the Tropo web api actions. 692 | The methods of this class implement individual Tropo actions. 693 | Individual actions are each methods on this class. 694 | 695 | Each method takes one or more required arguments, followed by optional 696 | arguments expressed as key=value pairs. 697 | 698 | The optional arguments for these methods are described here: 699 | https://www.tropo.com/docs/webapi/ 700 | """ 701 | def __init__(self): 702 | self._steps = [] 703 | 704 | # # **Sun May 15 21:05:01 2011** -- egilchri 705 | def setVoice(self, voice): 706 | self.voice = voice 707 | 708 | # # end **Sun May 15 21:05:01 2011** -- egilchri 709 | 710 | def ask(self, choices, **options): 711 | """ 712 | Sends a prompt to the user and optionally waits for a response. 713 | Arguments: "choices" is a Choices object 714 | See https://www.tropo.com/docs/webapi/ask 715 | """ 716 | # # **Sun May 15 21:21:29 2011** -- egilchri 717 | 718 | # Settng the voice in this method call has priority. 719 | # Otherwise, we can pick up the voice from the Tropo object, 720 | # if it is set there. 721 | if hasattr (self, 'voice'): 722 | if (not 'voice' in options): 723 | options['voice'] = self.voice 724 | 725 | # # **Sun May 15 21:21:29 2011** -- egilchri 726 | 727 | self._steps.append(Ask(choices, **options).obj) 728 | 729 | 730 | def call (self, to, **options): 731 | """ 732 | Places a call or sends an an IM, Twitter, or SMS message. To start a call, use the Session API to tell Tropo to launch your code. 733 | Arguments: to is a String. 734 | Argument: **options is a set of optional keyword arguments. 735 | See https://www.tropo.com/docs/webapi/call 736 | """ 737 | self._steps.append(Call (to, **options).obj) 738 | 739 | def conference(self, id, **options): 740 | """ 741 | This object allows multiple lines in separate sessions to be conferenced together so that the parties on each line can talk to each other simultaneously. 742 | This is a voice channel only feature. 743 | Argument: "id" is a String 744 | Argument: **options is a set of optional keyword arguments. 745 | See https://www.tropo.com/docs/webapi/conference 746 | """ 747 | self._steps.append(Conference(id, **options).obj) 748 | 749 | def hangup(self): 750 | """ 751 | This method instructs Tropo to "hang-up" or disconnect the session associated with the current session. 752 | See https://www.tropo.com/docs/webapi/hangup 753 | """ 754 | self._steps.append(Hangup().obj) 755 | 756 | def message (self, say_obj, to, **options): 757 | """ 758 | A shortcut method to create a session, say something, and hang up, all in one step. This is particularly useful for sending out a quick SMS or IM. 759 | Argument: "say_obj" is a Say object 760 | Argument: "to" is a String 761 | Argument: **options is a set of optional keyword arguments. 762 | See https://www.tropo.com/docs/webapi/message 763 | """ 764 | if isinstance(say_obj, basestring): 765 | say = Say(say_obj).obj 766 | else: 767 | say = say_obj 768 | self._steps.append(Message(say, to, **options).obj) 769 | 770 | def on(self, event, **options): 771 | """ 772 | Adds an event callback so that your application may be notified when a particular event occurs. 773 | Possible events are: "continue", "error", "incomplete" and "hangup". 774 | Argument: event is an event 775 | Argument: **options is a set of optional keyword arguments. 776 | See https://www.tropo.com/docs/webapi/on 777 | """ 778 | if hasattr (self, 'voice'): 779 | if (not 'voice' in options): 780 | options['voice'] = self.voice 781 | 782 | 783 | self._steps.append(On(event, **options).obj) 784 | 785 | def record(self, **options): 786 | """ 787 | Plays a prompt (audio file or text to speech) and optionally waits for a response from the caller that is recorded. 788 | Argument: **options is a set of optional keyword arguments. 789 | See https://www.tropo.com/docs/webapi/record 790 | """ 791 | self._steps.append(Record(**options).obj) 792 | 793 | def redirect(self, id, **options): 794 | """ 795 | Forwards an incoming call to another destination / phone number before answering it. 796 | Argument: id is a String 797 | Argument: **options is a set of optional keyword arguments. 798 | See https://www.tropo.com/docs/webapi/redirect 799 | """ 800 | self._steps.append(Redirect(id, **options).obj) 801 | 802 | def reject(self): 803 | """ 804 | Allows Tropo applications to reject incoming sessions before they are answered. 805 | See https://www.tropo.com/docs/webapi/reject 806 | """ 807 | self._steps.append(Reject().obj) 808 | 809 | def say(self, message, **options): 810 | """ 811 | When the current session is a voice channel this key will either play a message or an audio file from a URL. 812 | In the case of an text channel it will send the text back to the user via i nstant messaging or SMS. 813 | Argument: message is a string 814 | Argument: **options is a set of optional keyword arguments. 815 | See https://www.tropo.com/docs/webapi/say 816 | """ 817 | #voice = self.voice 818 | # # **Sun May 15 21:21:29 2011** -- egilchri 819 | 820 | # Settng the voice in this method call has priority. 821 | # Otherwise, we can pick up the voice from the Tropo object, 822 | # if it is set there. 823 | if hasattr (self, 'voice'): 824 | if (not 'voice' in options): 825 | options['voice'] = self.voice 826 | # # **Sun May 15 21:21:29 2011** -- egilchri 827 | 828 | self._steps.append(Say(message, **options).obj) 829 | 830 | def startRecording(self, url, **options): 831 | """ 832 | Allows Tropo applications to begin recording the current session. 833 | Argument: url is a string 834 | Argument: **options is a set of optional keyword arguments. 835 | See https://www.tropo.com/docs/webapi/startrecording 836 | """ 837 | self._steps.append(StartRecording(url, **options).obj) 838 | 839 | def stopRecording(self): 840 | """ 841 | Stops a previously started recording. 842 | See https://www.tropo.com/docs/webapi/stoprecording 843 | """ 844 | self._steps.append(StopRecording().obj) 845 | 846 | def transfer(self, to, **options): 847 | """ 848 | Transfers an already answered call to another destination / phone number. 849 | Argument: to is a string 850 | Argument: **options is a set of optional keyword arguments. 851 | See https://www.tropo.com/docs/webapi/transfer 852 | """ 853 | self._steps.append(Transfer(to, **options).obj) 854 | 855 | def wait(self, milliseconds, **options): 856 | """ 857 | Allows the thread to sleep for a given amount of time in milliseconds 858 | Argument: milliseconds is an Integer 859 | Argument: **options is a set of optional keyword arguments. 860 | See https://www.tropo.com/docs/webapi/wait 861 | """ 862 | self._steps.append(Wait(milliseconds, **options).obj) 863 | 864 | def RenderJson(self, pretty=False): 865 | """ 866 | Render a Tropo object into a Json string. 867 | """ 868 | steps = self._steps 869 | topdict = {} 870 | topdict['tropo'] = steps 871 | if pretty: 872 | try: 873 | json = jsonlib.dumps(topdict, indent=4, sort_keys=False) 874 | except TypeError: 875 | json = jsonlib.dumps(topdict) 876 | else: 877 | json = jsonlib.dumps(topdict) 878 | return json 879 | 880 | if __name__ == '__main__': 881 | print """ 882 | 883 | This is the Python web API for http://www.tropo.com/ 884 | 885 | To run the test suite, please run: 886 | 887 | cd test 888 | python test.py 889 | 890 | 891 | """ 892 | 893 | 894 | -------------------------------------------------------------------------------- /dist/README: -------------------------------------------------------------------------------- 1 | We rename our tar ball to ciscotropo-webapi-python-xx.yy.zz.tar.gz 2 | -------------------------------------------------------------------------------- /dist/ciscotropo-webapi-python-0.1.4.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tropo/tropo-webapi-python/f87772644a6b45066a4c5218f0c1f6467b64ab3c/dist/ciscotropo-webapi-python-0.1.4.tar.gz -------------------------------------------------------------------------------- /dist/tropo-webapi-python-0.1.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tropo/tropo-webapi-python/f87772644a6b45066a4c5218f0c1f6467b64ab3c/dist/tropo-webapi-python-0.1.0.tar.gz -------------------------------------------------------------------------------- /dist/tropo-webapi-python-0.1.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tropo/tropo-webapi-python/f87772644a6b45066a4c5218f0c1f6467b64ab3c/dist/tropo-webapi-python-0.1.1.tar.gz -------------------------------------------------------------------------------- /dist/tropo-webapi-python-0.1.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tropo/tropo-webapi-python/f87772644a6b45066a4c5218f0c1f6467b64ab3c/dist/tropo-webapi-python-0.1.2.tar.gz -------------------------------------------------------------------------------- /samples/OutboundSMS.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from itty import * 4 | from tropo import Tropo 5 | import datetime 6 | 7 | 8 | 9 | @post('/index.json') 10 | def index(request): 11 | currenttime = datetime.datetime.now() 12 | t = Tropo() 13 | sayobjOutbound = "Now is " + str(currenttime) 14 | t.message(sayobjOutbound, to="+1 725-419-2113", network="SMS") 15 | print t.RenderJson() 16 | return t.RenderJson() 17 | 18 | run_itty(server='wsgiref', host='192.168.26.1', port=8041) 19 | #run_itty(config='sample_conf') -------------------------------------------------------------------------------- /samples/README: -------------------------------------------------------------------------------- 1 | These sample applications demonstrate the usage of this "tropo" python module with 2 | the Tropo cloud communications service at http://www.tropo.com 3 | 4 | To use these examples you need to have: 5 | 6 | 1. an account at http://www.tropo.com/ (free for developers) 7 | 2. a web server where your app runs that is publicly accessible over the Internet 8 | 9 | For #2, you could use either: 10 | - a hosted service such as Google App Engine 11 | - your own server running a separate web server such as "itty" 12 | ( http://github.com/toastdriven/itty ) 13 | - a server running an existing web server that can execute python programs 14 | 15 | Sample applications will eventually be provided for all these types. 16 | -------------------------------------------------------------------------------- /samples/ReceiveSMS.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from itty import * 4 | from tropo import Tropo, Session, JoinPrompt, LeavePrompt 5 | from urllib import urlencode 6 | from urllib2 import urlopen 7 | 8 | 9 | 10 | @post('/index.json') 11 | def index(request): 12 | session = Session(request.body) 13 | print 'request.body begin' 14 | print request.body 15 | print 'request.body end' 16 | t = Tropo() 17 | smsContent = session.initialText 18 | #t.call(to=session.parameters['callToNumber'], network='SIP') 19 | #t.say(session.parameters['message']) 20 | """ 21 | t = Tropo() 22 | t.call(to="xiangjun_yu@192.168.26.1:5678") 23 | t.say("wo shi yi ke xiao xiao cao") 24 | """ 25 | #base_url = 'http://192.168.26.21:8080/gateway/sessions' 26 | base_url = 'https://api.tropo.com/1.0/sessions' 27 | #token = '4c586866434c4c59746f4361796b634477600d49434d434874584d4546496e70536c706749436841476b684371' # Insert your token here Application ID: 301 28 | token = '6c77565670494a6b474f646a5658436b514658724a0055674f4e735041764f665463626b535472616869746768' # Insert your fire-app-with-token.py token here 29 | action = 'create' 30 | #number = 'sip:xiangjun_yu@10.140.254.55:5678' # change to the Jabber ID to which you want to send the message 31 | #number = 'sip:frank@172.16.22.128:5678' # change to the Jabber ID to which you want to send the message 32 | #number = '+861891020382' # change to the Jabber ID to which you want to send the message 33 | number = '+86134766549249' # change to the Jabber ID to which you want to send the message 34 | message = 'redirect by Python content is ' + str(smsContent) 35 | 36 | params = urlencode([('action', action), ('token', token), ('callToNumber', number), ('message252121', message)]) 37 | data = urlopen('%s?%s' % (base_url, params)).read() 38 | 39 | print 'data is ' 40 | print data 41 | #return t.RenderJson() 42 | return "receive SMS successfully" 43 | 44 | run_itty(server='wsgiref', host='192.168.26.1', port=8042) 45 | #run_itty(config='sample_conf') -------------------------------------------------------------------------------- /samples/appengine/GoogleS3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This software code is made available "AS IS" without warranties of any 4 | # kind. You may copy, display, modify and redistribute the software 5 | # code either by itself or as incorporated into your code; provided that 6 | # you do not remove any proprietary notices. Your use of this software 7 | # code is at your own risk and you waive any claim against Amazon 8 | # Digital Services, Inc. or its affiliates with respect to your use of 9 | # this software code. (c) 2006-2007 Amazon Digital Services, Inc. or its 10 | # affiliates. 11 | 12 | # edited to work with Google App Engine - Maciej Ceglowski 13 | 14 | import base64 15 | import hmac 16 | import httplib 17 | import re 18 | import sha 19 | import sys 20 | import time 21 | import urllib 22 | import urlparse 23 | import xml.sax 24 | from google.appengine.api.urlfetch import * 25 | 26 | DEFAULT_HOST = 's3.amazonaws.com' 27 | PORTS_BY_SECURITY = { True: 443, False: 80 } 28 | METADATA_PREFIX = 'x-amz-meta-' 29 | AMAZON_HEADER_PREFIX = 'x-amz-' 30 | 31 | # generates the aws canonical string for the given parameters 32 | def canonical_string(method, bucket="", key="", query_args={}, headers={}, expires=None): 33 | interesting_headers = {} 34 | for header_key in headers: 35 | lk = header_key.lower() 36 | if lk in ['content-md5', 'content-type', 'date'] or lk.startswith(AMAZON_HEADER_PREFIX): 37 | interesting_headers[lk] = headers[header_key].strip() 38 | 39 | # these keys get empty strings if they don't exist 40 | if not interesting_headers.has_key('content-type'): 41 | interesting_headers['content-type'] = '' 42 | if not interesting_headers.has_key('content-md5'): 43 | interesting_headers['content-md5'] = '' 44 | 45 | # just in case someone used this. it's not necessary in this lib. 46 | if interesting_headers.has_key('x-amz-date'): 47 | interesting_headers['date'] = '' 48 | 49 | # if you're using expires for query string auth, then it trumps date 50 | # (and x-amz-date) 51 | if expires: 52 | interesting_headers['date'] = str(expires) 53 | 54 | sorted_header_keys = interesting_headers.keys() 55 | sorted_header_keys.sort() 56 | 57 | buf = "%s\n" % method 58 | for header_key in sorted_header_keys: 59 | if header_key.startswith(AMAZON_HEADER_PREFIX): 60 | buf += "%s:%s\n" % (header_key, interesting_headers[header_key]) 61 | else: 62 | buf += "%s\n" % interesting_headers[header_key] 63 | 64 | # append the bucket if it exists 65 | if bucket != "": 66 | buf += "/%s" % bucket 67 | 68 | # add the key. even if it doesn't exist, add the slash 69 | buf += "/%s" % urllib.quote_plus(key) 70 | 71 | # handle special query string arguments 72 | 73 | if query_args.has_key("acl"): 74 | buf += "?acl" 75 | elif query_args.has_key("torrent"): 76 | buf += "?torrent" 77 | elif query_args.has_key("logging"): 78 | buf += "?logging" 79 | elif query_args.has_key("location"): 80 | buf += "?location" 81 | 82 | return buf 83 | 84 | # computes the base64'ed hmac-sha hash of the canonical string and the secret 85 | # access key, optionally urlencoding the result 86 | def encode(aws_secret_access_key, str, urlencode=False): 87 | b64_hmac = base64.encodestring(hmac.new(aws_secret_access_key, str, sha).digest()).strip() 88 | if urlencode: 89 | return urllib.quote_plus(b64_hmac) 90 | else: 91 | return b64_hmac 92 | 93 | def merge_meta(headers, metadata): 94 | final_headers = headers.copy() 95 | for k in metadata.keys(): 96 | final_headers[METADATA_PREFIX + k] = metadata[k] 97 | 98 | return final_headers 99 | 100 | # builds the query arg string 101 | def query_args_hash_to_string(query_args): 102 | query_string = "" 103 | pairs = [] 104 | for k, v in query_args.items(): 105 | piece = k 106 | if v != None: 107 | piece += "=%s" % urllib.quote_plus(str(v)) 108 | pairs.append(piece) 109 | 110 | return '&'.join(pairs) 111 | 112 | 113 | class CallingFormat: 114 | PATH = 1 115 | SUBDOMAIN = 2 116 | VANITY = 3 117 | 118 | def build_url_base(protocol, server, port, bucket, calling_format): 119 | url_base = '%s://' % protocol 120 | 121 | if bucket == '': 122 | url_base += server 123 | elif calling_format == CallingFormat.SUBDOMAIN: 124 | url_base += "%s.%s" % (bucket, server) 125 | elif calling_format == CallingFormat.VANITY: 126 | url_base += bucket 127 | else: 128 | url_base += server 129 | 130 | url_base += ":%s" % port 131 | 132 | if (bucket != '') and (calling_format == CallingFormat.PATH): 133 | url_base += "/%s" % bucket 134 | 135 | return url_base 136 | 137 | build_url_base = staticmethod(build_url_base) 138 | 139 | 140 | 141 | class Location: 142 | DEFAULT = None 143 | EU = 'EU' 144 | 145 | 146 | 147 | class AWSAuthConnection: 148 | def __init__(self, aws_access_key_id, aws_secret_access_key, is_secure=True, 149 | server=DEFAULT_HOST, port=None, calling_format=CallingFormat.SUBDOMAIN): 150 | 151 | if not port: 152 | port = PORTS_BY_SECURITY[is_secure] 153 | 154 | self.aws_access_key_id = aws_access_key_id 155 | self.aws_secret_access_key = aws_secret_access_key 156 | self.is_secure = is_secure 157 | self.server = server 158 | self.port = port 159 | self.calling_format = calling_format 160 | 161 | def create_bucket(self, bucket, headers={}): 162 | return Response(self._make_request('PUT', bucket, '', {}, headers)) 163 | 164 | def create_located_bucket(self, bucket, location=Location.DEFAULT, headers={}): 165 | if location == Location.DEFAULT: 166 | body = "" 167 | else: 168 | body = "" + \ 169 | location + \ 170 | "" 171 | return Response(self._make_request('PUT', bucket, '', {}, headers, body)) 172 | 173 | def check_bucket_exists(self, bucket): 174 | return self._make_request('HEAD', bucket, '', {}, {}) 175 | 176 | def list_bucket(self, bucket, options={}, headers={}): 177 | return ListBucketResponse(self._make_request('GET', bucket, '', options, headers)) 178 | 179 | def delete_bucket(self, bucket, headers={}): 180 | return Response(self._make_request('DELETE', bucket, '', {}, headers)) 181 | 182 | def put(self, bucket, key, object, headers={}): 183 | if not isinstance(object, S3Object): 184 | object = S3Object(object) 185 | 186 | return Response( 187 | self._make_request( 188 | 'PUT', 189 | bucket, 190 | key, 191 | {}, 192 | headers, 193 | object.data, 194 | object.metadata)) 195 | 196 | def get(self, bucket, key, headers={}): 197 | return GetResponse( 198 | self._make_request('GET', bucket, key, {}, headers)) 199 | 200 | def delete(self, bucket, key, headers={}): 201 | return Response( 202 | self._make_request('DELETE', bucket, key, {}, headers)) 203 | 204 | def get_bucket_logging(self, bucket, headers={}): 205 | return GetResponse(self._make_request('GET', bucket, '', { 'logging': None }, headers)) 206 | 207 | def put_bucket_logging(self, bucket, logging_xml_doc, headers={}): 208 | return Response(self._make_request('PUT', bucket, '', { 'logging': None }, headers, logging_xml_doc)) 209 | 210 | def get_bucket_acl(self, bucket, headers={}): 211 | return self.get_acl(bucket, '', headers) 212 | 213 | def get_acl(self, bucket, key, headers={}): 214 | return GetResponse( 215 | self._make_request('GET', bucket, key, { 'acl': None }, headers)) 216 | 217 | def put_bucket_acl(self, bucket, acl_xml_document, headers={}): 218 | return self.put_acl(bucket, '', acl_xml_document, headers) 219 | 220 | def put_acl(self, bucket, key, acl_xml_document, headers={}): 221 | return Response( 222 | self._make_request( 223 | 'PUT', 224 | bucket, 225 | key, 226 | { 'acl': None }, 227 | headers, 228 | acl_xml_document)) 229 | 230 | def list_all_my_buckets(self, headers={}): 231 | return ListAllMyBucketsResponse(self._make_request('GET', '', '', {}, headers)) 232 | 233 | def get_bucket_location(self, bucket): 234 | return LocationResponse(self._make_request('GET', bucket, '', {'location' : None})) 235 | 236 | # end public methods 237 | 238 | 239 | def _make_request(self, method, bucket='', key='', query_args={}, headers={}, data='', metadata={}): 240 | 241 | server = '' 242 | if bucket == '': 243 | server = self.server 244 | elif self.calling_format == CallingFormat.SUBDOMAIN: 245 | server = "%s.%s" % (bucket, self.server) 246 | elif self.calling_format == CallingFormat.VANITY: 247 | server = bucket 248 | else: 249 | server = self.server 250 | 251 | path = '' 252 | 253 | if (bucket != '') and (self.calling_format == CallingFormat.PATH): 254 | path += "/%s" % bucket 255 | 256 | # add the slash after the bucket regardless 257 | # the key will be appended if it is non-empty 258 | path += "/%s" % urllib.quote_plus(key) 259 | 260 | 261 | # build the path_argument string 262 | # add the ? in all cases since 263 | # signature and credentials follow path args 264 | if len(query_args): 265 | path += "?" + query_args_hash_to_string(query_args) 266 | 267 | is_secure = self.is_secure 268 | host = "%s:%d" % (server, self.port) 269 | while True: 270 | 271 | final_headers = merge_meta(headers, metadata); 272 | # add auth header 273 | self._add_aws_auth_header(final_headers, method, bucket, key, query_args) 274 | resp = fetch("https://" + host + path,data,method,final_headers) 275 | 276 | 277 | if resp.status_code < 300 or resp.status_code >= 400: 278 | return resp 279 | # handle redirect 280 | try: 281 | location = resp.headers['location'] 282 | except: 283 | return resp 284 | # (close connection) 285 | # resp.read() 286 | scheme, host, path, params, query, fragment \ 287 | = urlparse.urlparse(location) 288 | if scheme == "http": is_secure = True 289 | elif scheme == "https": is_secure = False 290 | else: raise invalidURL("Not http/https: " + location) 291 | if query: path += "?" + query 292 | # retry with redirect 293 | 294 | def _xmake_request(self, method, bucket='', key='', query_args={}, headers={}, data='', metadata={}): 295 | 296 | server = '' 297 | if bucket == '': 298 | server = self.server 299 | elif self.calling_format == CallingFormat.SUBDOMAIN: 300 | server = "%s.%s" % (bucket, self.server) 301 | elif self.calling_format == CallingFormat.VANITY: 302 | server = bucket 303 | else: 304 | server = self.server 305 | 306 | path = '' 307 | 308 | if (bucket != '') and (self.calling_format == CallingFormat.PATH): 309 | path += "/%s" % bucket 310 | 311 | # add the slash after the bucket regardless 312 | # the key will be appended if it is non-empty 313 | path += "/%s" % urllib.quote_plus(key) 314 | 315 | 316 | # build the path_argument string 317 | # add the ? in all cases since 318 | # signature and credentials follow path args 319 | if len(query_args): 320 | path += "?" + query_args_hash_to_string(query_args) 321 | 322 | is_secure = self.is_secure 323 | host = "%s:%d" % (server, self.port) 324 | while True: 325 | if (is_secure): 326 | connection = httplib.HTTPSConnection(host) 327 | else: 328 | connection = httplib.HTTPConnection(host) 329 | 330 | final_headers = merge_meta(headers, metadata); 331 | # add auth header 332 | self._add_aws_auth_header(final_headers, method, bucket, key, query_args) 333 | 334 | connection.request(method, path, data, final_headers) 335 | resp = connection.getresponse() 336 | if resp.status < 300 or resp.status >= 400: 337 | return resp 338 | # handle redirect 339 | location = resp.getheader('location') 340 | if not location: 341 | return resp 342 | # (close connection) 343 | resp.read() 344 | scheme, host, path, params, query, fragment \ 345 | = urlparse.urlparse(location) 346 | if scheme == "http": is_secure = True 347 | elif scheme == "https": is_secure = False 348 | else: raise invalidURL("Not http/https: " + location) 349 | if query: path += "?" + query 350 | # retry with redirect 351 | 352 | def _add_aws_auth_header(self, headers, method, bucket, key, query_args): 353 | if not headers.has_key('Date'): 354 | headers['Date'] = time.strftime("%a, %d %b %Y %X GMT", time.gmtime()) 355 | 356 | c_string = canonical_string(method, bucket, key, query_args, headers) 357 | headers['Authorization'] = \ 358 | "AWS %s:%s" % (self.aws_access_key_id, encode(self.aws_secret_access_key, c_string)) 359 | 360 | 361 | class QueryStringAuthGenerator: 362 | # by default, expire in 1 minute 363 | DEFAULT_EXPIRES_IN = 60 364 | 365 | def __init__(self, aws_access_key_id, aws_secret_access_key, is_secure=True, 366 | server=DEFAULT_HOST, port=None, calling_format=CallingFormat.SUBDOMAIN): 367 | 368 | if not port: 369 | port = PORTS_BY_SECURITY[is_secure] 370 | 371 | self.aws_access_key_id = aws_access_key_id 372 | self.aws_secret_access_key = aws_secret_access_key 373 | if (is_secure): 374 | self.protocol = 'https' 375 | else: 376 | self.protocol = 'http' 377 | 378 | self.is_secure = is_secure 379 | self.server = server 380 | self.port = port 381 | self.calling_format = calling_format 382 | self.__expires_in = QueryStringAuthGenerator.DEFAULT_EXPIRES_IN 383 | self.__expires = None 384 | 385 | # for backwards compatibility with older versions 386 | self.server_name = "%s:%s" % (self.server, self.port) 387 | 388 | def set_expires_in(self, expires_in): 389 | self.__expires_in = expires_in 390 | self.__expires = None 391 | 392 | def set_expires(self, expires): 393 | self.__expires = expires 394 | self.__expires_in = None 395 | 396 | def create_bucket(self, bucket, headers={}): 397 | return self.generate_url('PUT', bucket, '', {}, headers) 398 | 399 | def list_bucket(self, bucket, options={}, headers={}): 400 | return self.generate_url('GET', bucket, '', options, headers) 401 | 402 | def delete_bucket(self, bucket, headers={}): 403 | return self.generate_url('DELETE', bucket, '', {}, headers) 404 | 405 | def put(self, bucket, key, object, headers={}): 406 | if not isinstance(object, S3Object): 407 | object = S3Object(object) 408 | 409 | return self.generate_url( 410 | 'PUT', 411 | bucket, 412 | key, 413 | {}, 414 | merge_meta(headers, object.metadata)) 415 | 416 | def get(self, bucket, key, headers={}): 417 | return self.generate_url('GET', bucket, key, {}, headers) 418 | 419 | def delete(self, bucket, key, headers={}): 420 | return self.generate_url('DELETE', bucket, key, {}, headers) 421 | 422 | def get_bucket_logging(self, bucket, headers={}): 423 | return self.generate_url('GET', bucket, '', { 'logging': None }, headers) 424 | 425 | def put_bucket_logging(self, bucket, logging_xml_doc, headers={}): 426 | return self.generate_url('PUT', bucket, '', { 'logging': None }, headers) 427 | 428 | def get_bucket_acl(self, bucket, headers={}): 429 | return self.get_acl(bucket, '', headers) 430 | 431 | def get_acl(self, bucket, key='', headers={}): 432 | return self.generate_url('GET', bucket, key, { 'acl': None }, headers) 433 | 434 | def put_bucket_acl(self, bucket, acl_xml_document, headers={}): 435 | return self.put_acl(bucket, '', acl_xml_document, headers) 436 | 437 | # don't really care what the doc is here. 438 | def put_acl(self, bucket, key, acl_xml_document, headers={}): 439 | return self.generate_url('PUT', bucket, key, { 'acl': None }, headers) 440 | 441 | def list_all_my_buckets(self, headers={}): 442 | return self.generate_url('GET', '', '', {}, headers) 443 | 444 | def make_bare_url(self, bucket, key=''): 445 | full_url = self.generate_url(self, bucket, key) 446 | return full_url[:full_url.index('?')] 447 | 448 | def generate_url(self, method, bucket='', key='', query_args={}, headers={}): 449 | expires = 0 450 | if self.__expires_in != None: 451 | expires = int(time.time() + self.__expires_in) 452 | elif self.__expires != None: 453 | expires = int(self.__expires) 454 | else: 455 | raise "Invalid expires state" 456 | 457 | canonical_str = canonical_string(method, bucket, key, query_args, headers, expires) 458 | encoded_canonical = encode(self.aws_secret_access_key, canonical_str) 459 | 460 | url = CallingFormat.build_url_base(self.protocol, self.server, self.port, bucket, self.calling_format) 461 | 462 | url += "/%s" % urllib.quote_plus(key) 463 | 464 | query_args['Signature'] = encoded_canonical 465 | query_args['Expires'] = expires 466 | query_args['AWSAccessKeyId'] = self.aws_access_key_id 467 | 468 | url += "?%s" % query_args_hash_to_string(query_args) 469 | 470 | return url 471 | 472 | 473 | class S3Object: 474 | def __init__(self, data, metadata={}): 475 | self.data = data 476 | self.metadata = metadata 477 | 478 | class Owner: 479 | def __init__(self, id='', display_name=''): 480 | self.id = id 481 | self.display_name = display_name 482 | 483 | class ListEntry: 484 | def __init__(self, key='', last_modified=None, etag='', size=0, storage_class='', owner=None): 485 | self.key = key 486 | self.last_modified = last_modified 487 | self.etag = etag 488 | self.size = size 489 | self.storage_class = storage_class 490 | self.owner = owner 491 | 492 | class CommonPrefixEntry: 493 | def __init(self, prefix=''): 494 | self.prefix = prefix 495 | 496 | class Bucket: 497 | def __init__(self, name='', creation_date=''): 498 | self.name = name 499 | self.creation_date = creation_date 500 | 501 | class Response: 502 | def __init__(self, http_response): 503 | self.http_response = http_response 504 | # you have to do this read, even if you don't expect a body. 505 | # otherwise, the next request fails. 506 | self.body = http_response.content 507 | if http_response.status_code >= 300 and self.body: 508 | self.message = self.body 509 | else: 510 | self.message = "%03d" % (http_response.status_code) 511 | 512 | 513 | 514 | class ListBucketResponse(Response): 515 | def __init__(self, http_response): 516 | Response.__init__(self, http_response) 517 | if http_response.status_code < 300: 518 | # if http_response.status < 300: 519 | handler = ListBucketHandler() 520 | xml.sax.parseString(self.body, handler) 521 | self.entries = handler.entries 522 | self.common_prefixes = handler.common_prefixes 523 | self.name = handler.name 524 | self.marker = handler.marker 525 | self.prefix = handler.prefix 526 | self.is_truncated = handler.is_truncated 527 | self.delimiter = handler.delimiter 528 | self.max_keys = handler.max_keys 529 | self.next_marker = handler.next_marker 530 | else: 531 | self.entries = [] 532 | 533 | class ListAllMyBucketsResponse(Response): 534 | def __init__(self, http_response): 535 | Response.__init__(self, http_response) 536 | if http_response.status < 300: 537 | handler = ListAllMyBucketsHandler() 538 | xml.sax.parseString(self.body, handler) 539 | self.entries = handler.entries 540 | else: 541 | self.entries = [] 542 | 543 | class GetResponse(Response): 544 | def __init__(self, http_response): 545 | Response.__init__(self, http_response) 546 | response_headers = http_response.msg # older pythons don't have getheaders 547 | metadata = self.get_aws_metadata(response_headers) 548 | self.object = S3Object(self.body, metadata) 549 | 550 | def get_aws_metadata(self, headers): 551 | metadata = {} 552 | for hkey in headers.keys(): 553 | if hkey.lower().startswith(METADATA_PREFIX): 554 | metadata[hkey[len(METADATA_PREFIX):]] = headers[hkey] 555 | del headers[hkey] 556 | 557 | return metadata 558 | 559 | class LocationResponse(Response): 560 | def __init__(self, http_response): 561 | Response.__init__(self, http_response) 562 | if http_response.status < 300: 563 | handler = LocationHandler() 564 | xml.sax.parseString(self.body, handler) 565 | self.location = handler.location 566 | 567 | class ListBucketHandler(xml.sax.ContentHandler): 568 | def __init__(self): 569 | self.entries = [] 570 | self.curr_entry = None 571 | self.curr_text = '' 572 | self.common_prefixes = [] 573 | self.curr_common_prefix = None 574 | self.name = '' 575 | self.marker = '' 576 | self.prefix = '' 577 | self.is_truncated = False 578 | self.delimiter = '' 579 | self.max_keys = 0 580 | self.next_marker = '' 581 | self.is_echoed_prefix_set = False 582 | 583 | def startElement(self, name, attrs): 584 | if name == 'Contents': 585 | self.curr_entry = ListEntry() 586 | elif name == 'Owner': 587 | self.curr_entry.owner = Owner() 588 | elif name == 'CommonPrefixes': 589 | self.curr_common_prefix = CommonPrefixEntry() 590 | 591 | 592 | def endElement(self, name): 593 | if name == 'Contents': 594 | self.entries.append(self.curr_entry) 595 | elif name == 'CommonPrefixes': 596 | self.common_prefixes.append(self.curr_common_prefix) 597 | elif name == 'Key': 598 | self.curr_entry.key = self.curr_text 599 | elif name == 'LastModified': 600 | self.curr_entry.last_modified = self.curr_text 601 | elif name == 'ETag': 602 | self.curr_entry.etag = self.curr_text 603 | elif name == 'Size': 604 | self.curr_entry.size = int(self.curr_text) 605 | elif name == 'ID': 606 | self.curr_entry.owner.id = self.curr_text 607 | elif name == 'DisplayName': 608 | self.curr_entry.owner.display_name = self.curr_text 609 | elif name == 'StorageClass': 610 | self.curr_entry.storage_class = self.curr_text 611 | elif name == 'Name': 612 | self.name = self.curr_text 613 | elif name == 'Prefix' and self.is_echoed_prefix_set: 614 | self.curr_common_prefix.prefix = self.curr_text 615 | elif name == 'Prefix': 616 | self.prefix = self.curr_text 617 | self.is_echoed_prefix_set = True 618 | elif name == 'Marker': 619 | self.marker = self.curr_text 620 | elif name == 'IsTruncated': 621 | self.is_truncated = self.curr_text == 'true' 622 | elif name == 'Delimiter': 623 | self.delimiter = self.curr_text 624 | elif name == 'MaxKeys': 625 | self.max_keys = int(self.curr_text) 626 | elif name == 'NextMarker': 627 | self.next_marker = self.curr_text 628 | 629 | self.curr_text = '' 630 | 631 | def characters(self, content): 632 | self.curr_text += content 633 | 634 | 635 | class ListAllMyBucketsHandler(xml.sax.ContentHandler): 636 | def __init__(self): 637 | self.entries = [] 638 | self.curr_entry = None 639 | self.curr_text = '' 640 | 641 | def startElement(self, name, attrs): 642 | if name == 'Bucket': 643 | self.curr_entry = Bucket() 644 | 645 | def endElement(self, name): 646 | if name == 'Name': 647 | self.curr_entry.name = self.curr_text 648 | elif name == 'CreationDate': 649 | self.curr_entry.creation_date = self.curr_text 650 | elif name == 'Bucket': 651 | self.entries.append(self.curr_entry) 652 | 653 | def characters(self, content): 654 | self.curr_text = content 655 | 656 | 657 | class LocationHandler(xml.sax.ContentHandler): 658 | def __init__(self): 659 | self.location = None 660 | self.state = 'init' 661 | 662 | def startElement(self, name, attrs): 663 | if self.state == 'init': 664 | if name == 'LocationConstraint': 665 | self.state = 'tag_location' 666 | self.location = '' 667 | else: self.state = 'bad' 668 | else: self.state = 'bad' 669 | 670 | def endElement(self, name): 671 | if self.state == 'tag_location' and name == 'LocationConstraint': 672 | self.state = 'done' 673 | else: self.state = 'bad' 674 | 675 | def characters(self, content): 676 | if self.state == 'tag_location': 677 | self.location += content 678 | -------------------------------------------------------------------------------- /samples/appengine/app.yaml: -------------------------------------------------------------------------------- 1 | application: egilchri1 2 | version: 1 3 | runtime: python 4 | api_version: 1 5 | 6 | handlers: 7 | - url: .* 8 | script: main.py 9 | -------------------------------------------------------------------------------- /samples/appengine/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | This script is intended to be used with Google Appengine. It contains 3 | a number of demos that illustrate the Tropo Web API for Python. 4 | """ 5 | 6 | from google.appengine.ext import webapp 7 | from google.appengine.ext.webapp import util 8 | import cgi 9 | import logging 10 | import tropo 11 | import GoogleS3 12 | from xml.dom import minidom 13 | from google.appengine.api import urlfetch 14 | from xml.etree import ElementTree 15 | from setup import * 16 | 17 | 18 | 19 | def HelloWorld(handler, t): 20 | """ 21 | This is the traditional "Hello, World" function. The idiom is used throughout the API. We construct a Tropo object, and then flesh out that object by calling "action" functions (in this case, tropo.say). Then call tropo.Render, which translates the Tropo object into JSON format. Finally, we write the JSON object to the standard output, so that it will get POSTed back to the API. 22 | """ 23 | t.say (["Hello, World", "How ya doing?"]) 24 | json = t.RenderJson() 25 | logging.info ("HelloWorld json: %s" % json) 26 | handler.response.out.write(json) 27 | 28 | def WeatherDemo(handler, t): 29 | """ 30 | """ 31 | choices = tropo.Choices("[5 DIGITS]") 32 | 33 | t.ask(choices, 34 | say="Please enter your 5 digit zip code.", 35 | attempts=3, bargein=True, name="zip", timeout=5, voice="dave") 36 | 37 | t.on(event="continue", 38 | next="/weather.py?uri=end", 39 | say="Please hold.") 40 | 41 | t.on(event="error", 42 | next="/weather.py?uri=error", 43 | say="Ann error occurred.") 44 | 45 | json = t.RenderJson() 46 | logging.info ("Json result: %s " % json) 47 | logging.info ("WeatherDemo json: %s" % json) 48 | 49 | handler.response.out.write(json) 50 | 51 | def RecordDemo(handler, t): 52 | 53 | url = "%s/receive_recording.py" % THIS_URL 54 | choices_obj = tropo.Choices("", terminator="#").json 55 | t.record(say="Tell us about yourself", url=url, 56 | choices=choices_obj) 57 | json = t.RenderJson() 58 | logging.info ("Json result: %s " % json) 59 | handler.response.out.write(json) 60 | 61 | def SMSDemo(handler, t): 62 | 63 | t.message("Hello World", MY_PHONE, channel='TEXT', network='SMS', timeout=5) 64 | json = t.RenderJson() 65 | logging.info ("Json result: %s " % json) 66 | handler.response.out.write(json) 67 | 68 | 69 | def RecordHelloWorld(handler, t): 70 | """ 71 | Demonstration of recording a message. 72 | """ 73 | url = "%s/receive_recording.py" % THIS_URL 74 | t.startRecording(url) 75 | t.say ("Hello, World.") 76 | t.stopRecording() 77 | json = t.RenderJson() 78 | logging.info ("RecordHelloWorld json: %s" % json) 79 | handler.response.out.write(json) 80 | 81 | def RedirectDemo(handler, t): 82 | """ 83 | Demonstration of redirecting to another number. 84 | """ 85 | # t.say ("One moment please.") 86 | t.redirect(SIP_PHONE) 87 | json = t.RenderJson() 88 | logging.info ("RedirectDemo json: %s" % json) 89 | handler.response.out.write(json) 90 | 91 | def TransferDemo(handler, t): 92 | """ 93 | Demonstration of transfering to another number 94 | """ 95 | t.say ("One moment please.") 96 | t.transfer(MY_PHONE) 97 | t.say("Hi. I am a robot") 98 | json = t.RenderJson() 99 | logging.info ("TransferDemo json: %s" % json) 100 | handler.response.out.write(json) 101 | 102 | 103 | 104 | def CallDemo(handler, t): 105 | t.call(THEIR_PHONE) 106 | json = t.RenderJson() 107 | logging.info ("CallDemo json: %s " % json) 108 | handler.response.out.write(json) 109 | 110 | def ConferenceDemo(handler, t): 111 | t.say ("Have some of your friends launch this demo. You'll be on the world's simplest conference call.") 112 | t.conference("partyline", terminator="#", name="Family Meeting") 113 | json = t.RenderJson() 114 | logging.info ("ConferenceDemo json: %s " % json) 115 | handler.response.out.write(json) 116 | 117 | 118 | 119 | 120 | # List of Demos 121 | DEMOS = { 122 | '1' : ('Hello World', HelloWorld), 123 | '2' : ('Weather Demo', WeatherDemo), 124 | '3' : ('Record Demo', RecordDemo), 125 | '4' : ('SMS Demo', SMSDemo), 126 | '5' : ('Record Conversation Demo', RecordHelloWorld), 127 | '6' : ('Redirect Demo', RedirectDemo), 128 | '7' : ('Transfer Demo', TransferDemo), 129 | '8' : ('Call Demo', CallDemo), 130 | '9' : ('Conference Demo', ConferenceDemo) 131 | } 132 | 133 | class TropoDemo(webapp.RequestHandler): 134 | """ 135 | This class is the entry point to the Tropo Web API for Python demos. Note that it's only method is a POST method, since this is how Tropo kicks off. 136 | 137 | A bundle of information about the call, such as who is calling, is passed in via the POST data. 138 | """ 139 | def post(self): 140 | t = tropo.Tropo() 141 | t.say ("Welcome to the Tropo web API demo") 142 | 143 | request = "Please press" 144 | choices_string = "" 145 | choices_counter = 1 146 | for key in sorted(DEMOS.iterkeys()): 147 | if (len(choices_string) > 0): 148 | choices_string = "%s,%s" % (choices_string, choices_counter) 149 | else: 150 | choices_string = "%s" % (choices_counter) 151 | demo_name = DEMOS[key][0] 152 | demo = DEMOS[key][1] 153 | request = "%s %s for %s," % (request, key, demo_name) 154 | choices_counter += 1 155 | choices = tropo.Choices(choices_string) 156 | 157 | t.ask(choices, say=request, attempts=3, bargein=True, name="zip", timeout=5, voice="dave") 158 | 159 | t.on(event="continue", 160 | next="/demo_continue.py", 161 | say="Please hold.") 162 | 163 | t.on(event="error", 164 | next="/demo_continue.py", 165 | say="An error occurred.") 166 | 167 | json = t.RenderJson() 168 | logging.info ("Json result: %s " % json) 169 | self.response.out.write(json) 170 | 171 | 172 | class TropoDemoContinue(webapp.RequestHandler): 173 | """ 174 | This class implements all the top-level demo functions. Data is POSTed to the application, to start tings off. After retrieving the result value, which is a digit indicating the user's choice of demo function, the POST method dispatches to the chosen demo. 175 | """ 176 | def post (self): 177 | json = self.request.body 178 | logging.info ("json: %s" % json) 179 | t = tropo.Tropo() 180 | result = tropo.Result(json) 181 | choice = result.getValue() 182 | logging.info ("Choice of demo is: %s" % choice) 183 | 184 | for key in DEMOS: 185 | if (choice == key): 186 | demo_name = DEMOS[key][0] 187 | demo = DEMOS[key][1] 188 | demo(self, t) 189 | break 190 | 191 | class Weather(webapp.RequestHandler): 192 | def post (self): 193 | json = self.request.body 194 | logging.info ("json: %s" % json) 195 | 196 | uri = self.request.get ('uri') 197 | logging.info ("uri: %s" % uri) 198 | 199 | t = tropo.Tropo() 200 | 201 | if (uri == "error"): 202 | t.say ("Oops. There was some kind of error") 203 | json = t.RenderJson() 204 | self.response.out.write(json) 205 | return 206 | 207 | result = tropo.Result(json); 208 | zip = result.getValue() 209 | google_weather_url = "%s?weather=%s&hl=en" % (GOOGLE_WEATHER_API_URL, zip) 210 | resp = urlfetch.fetch(google_weather_url) 211 | 212 | logging.info ("weather url: %s " % google_weather_url) 213 | if (resp.status_code == 200): 214 | xml = resp.content 215 | logging.info ("weather xml: %s " % xml) 216 | doc = ElementTree.fromstring(xml) 217 | logging.info ("doc: %s " % doc) 218 | condition = doc.find("weather/current_conditions/condition").attrib['data'] 219 | temp_f = doc.find("weather/current_conditions/temp_f").attrib['data'] 220 | wind_condition = doc.find("weather/current_conditions/wind_condition").attrib['data'] 221 | city = doc.find("weather/forecast_information/city").attrib['data'] 222 | logging.info ("condition: %s temp_f: %s wind_condition: %s city: %s" % (condition, temp_f, wind_condition, city)) 223 | t = tropo.Tropo() 224 | # condition: Partly Cloudy temp_f: 73 wind_condition: Wind: NW at 10 mph city: Portsmouth, NH 225 | temp = "%s degrees" % temp_f 226 | wind = self.english_expand (wind_condition) 227 | t.say("Current city is %s . Weather conditions are %s. Temperature is %s. %s ." % (city, condition, temp, wind)) 228 | json = t.RenderJson() 229 | 230 | self.response.out.write(json) 231 | 232 | 233 | # Wind: N at 0 mph 234 | 235 | def english_expand(self, expr): 236 | logging.info ("expr is : %s" % expr) 237 | expr = expr.replace("Wind: NW", "Wind is from the North West") 238 | expr = expr.replace("Wind: NE", "Wind is from the North East") 239 | expr = expr.replace("Wind: N", "Wind is from the North") 240 | expr = expr.replace("Wind: SW", "Wind is from the South West") 241 | expr = expr.replace("Wind: SE", "Wind is from the South East") 242 | expr = expr.replace("Wind: S", "Wind is from the South") 243 | expr = expr.replace("mph", "miles per hour") 244 | return expr 245 | 246 | 247 | class ReceiveRecording(webapp.RequestHandler): 248 | def post(self): 249 | logging.info ("I just received a post recording") 250 | # wav = self.request.body 251 | wav = self.request.get ('filename') 252 | logging.info ("Just got the wav as %s" % wav) 253 | self.put_in_s3(wav) 254 | logging.info ("I just put the wav in s3") 255 | 256 | def put_in_s3 (self, wav): 257 | 258 | conn = GoogleS3.AWSAuthConnection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) 259 | key_name = "testing.wav" 260 | logging.info ("Putting content in %s in %s bucket" % (key_name, S3_BUCKET_NAME)) 261 | responsedict={} 262 | logging.info ("really putting stuff in %s %s" % (S3_BUCKET_NAME, key_name)) 263 | audio_type = 'audio/wav' 264 | 265 | response = conn.put( 266 | S3_BUCKET_NAME, 267 | key_name, 268 | GoogleS3.S3Object(wav), 269 | {'Content-Type' : audio_type, 270 | 'x-amz-acl' : 'public-read'}) 271 | responsedict["response"] = response 272 | responsedict["url"] = "%s/%s/%s" % (AMAZON_S3_URL, S3_BUCKET_NAME, key_name) 273 | return responsedict 274 | 275 | 276 | 277 | class CallWorld(webapp.RequestHandler): 278 | def post(self): 279 | t = tropo.Tropo() 280 | t.call(MY_PHONE, channel='TEXT', network='SMS', answerOnMedia='True') 281 | t.say ("Wish you were here") 282 | json = t.RenderJson() 283 | logging.info ("Json result: %s " % json) 284 | self.response.out.write(json) 285 | 286 | 287 | 288 | class MainHandler(webapp.RequestHandler): 289 | def get(self): 290 | self.response.out.write('Hello world!') 291 | 292 | 293 | def main(): 294 | application = webapp.WSGIApplication([('/', MainHandler), 295 | ('/hello_tropo.py', TropoDemo), 296 | ('/weather.py', Weather), 297 | ('/receive_recording.py', ReceiveRecording), 298 | ('/demo_continue.py', TropoDemoContinue), 299 | # ('/tropo_web_api.html', ShowDoc) 300 | 301 | ], 302 | debug=True) 303 | util.run_wsgi_app(application) 304 | 305 | if __name__ == '__main__': 306 | main() 307 | -------------------------------------------------------------------------------- /samples/appengine/setup.py: -------------------------------------------------------------------------------- 1 | AWS_ACCESS_KEY_ID = 'Your Amazons S3 Access Key' 2 | AWS_SECRET_ACCESS_KEY = 'You Amazon S3 Secret Key' 3 | S3_BUCKET_NAME = "Your Amazon S3 Bucket Name" 4 | MY_PHONE = "Your Cell Phone" 5 | THEIR_PHONE = "Another Cell Phone" 6 | THIS_URL = "http://YOUR_APP_ENGINE_APP.appspot.com" 7 | 8 | # For storing audio files 9 | AMAZON_S3_URL = "http://s3.amazonaws.com" 10 | # Bare bones weather API 11 | GOOGLE_WEATHER_API_URL = "http://www.google.com/ig/api" 12 | SIP_PHONE = "A SIP Phone Address" 13 | -------------------------------------------------------------------------------- /samples/fire-app-with-token.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Hello world script for Session API ( https://www.tropo.com/docs/webapi/sessionapi.htm ) 4 | 5 | Upon launch, it will trigger a message to be sent via Jabber to the addess specified in 6 | 'number'. 7 | """ 8 | 9 | # Sample application using the itty-bitty python web framework from: 10 | # http://github.com/toastdriven/itty 11 | 12 | from itty import * 13 | from tropo import Tropo, Session, JoinPrompt, LeavePrompt 14 | from urllib import urlencode 15 | from urllib2 import urlopen 16 | 17 | @post('/index.json') 18 | def index(request): 19 | session = Session(request.body) 20 | print 'request.body begin' 21 | print request.body 22 | print 'request.body end' 23 | t = Tropo() 24 | #t.call(to=session.parameters['callToNumber'], network='SIP') 25 | dhhm = session.parameters['callToNumber'] 26 | say_obj = session.parameters['message252121'] 27 | #t.message(say_obj, to=dhhm, network="SMS", _from="+17754641173", channel = "TEXT") 28 | #t.call(dhhm, network="SMS", _from="+17754641173", channel = "TEXT") 29 | t.call(dhhm) 30 | t.say(say_obj) 31 | print t.RenderJson() 32 | return t.RenderJson() 33 | 34 | run_itty(server='wsgiref', host='192.168.26.1', port=8043) 35 | #run_itty(config='sample_conf') -------------------------------------------------------------------------------- /samples/gh-12-test_voice.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-12 . Need a way to set the default voice 2 | # for a Tropo object. 3 | # added a new method "setVoice" on the Tropo object 4 | 5 | # These examples show precdence of setVoice vs using "voice="..." in 6 | # the method call. 7 | 8 | # Sample application using the itty-bitty python web framework from: 9 | # http://github.com/toastdriven/itty 10 | 11 | from itty import * 12 | from tropo import Tropo, Session, TropoAction, Choices 13 | 14 | @post('/index.json') 15 | def index(request): 16 | s = Session(request.body) 17 | t = Tropo() 18 | t.setVoice('dave') 19 | # we use what has been set in Tropo object 20 | t.say(['hello world!']) 21 | # we use what is set in the method call 22 | t.say(['hello world!'], voice="allison") 23 | 24 | # we use the voice that has been set in Tropo object 25 | choices = Choices("[5 digits]").obj 26 | t.ask(choices, 27 | say="Please enter your 5 digit zip code.", 28 | attempts=3, bargein=True, name="zip", timeout=5) 29 | 30 | # we use the voice passed in the method call. 31 | choices = Choices("[5 digits]").obj 32 | t.ask(choices, 33 | say="Please enter your 5 digit zip code.", 34 | attempts=3, bargein=True, name="zip", timeout=5, voice="allison") 35 | 36 | 37 | json = t.RenderJson() 38 | print json 39 | return json 40 | 41 | 42 | run_itty() 43 | 44 | -------------------------------------------------------------------------------- /samples/gh-14.test_call.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-14 for "from" parameter in the "call" function. 2 | # Proposed convention is to use "_from" as the parameter 3 | # so as not to conflict with "from" Python reserved word. 4 | 5 | # _from arg works 6 | 7 | # Invoke using a token 8 | 9 | # Sample application using the itty-bitty python web framework from: 10 | # http://github.com/toastdriven/itty 11 | 12 | from itty import * 13 | from tropo import Tropo, Session 14 | 15 | TO_NUMBER = '1xxxxxxxxxx' 16 | FROM_NUMBER = '1yyyyyyyyyy' 17 | 18 | 19 | @post('/index.json') 20 | def index(request): 21 | s = Session(request.body) 22 | t = Tropo() 23 | t.call(to=' ' + TO_NUMBER, _from=' ' + FROM_NUMBER, label='xiangwyujianghu', voice='Tian-tian', callbackUrl='http://192.168.26.88:8080/FileUpload/receiveJson', promptLogSecurity='suppress') 24 | t.say('This is your mother. Did you brush your teeth today?') 25 | json = t.RenderJson() 26 | print json 27 | return json 28 | 29 | 30 | run_itty() 31 | 32 | -------------------------------------------------------------------------------- /samples/gh-14.test_call_MMS.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-14 for "from" parameter in the "call" function. 2 | # Proposed convention is to use "_from" as the parameter 3 | # so as not to conflict with "from" Python reserved word. 4 | 5 | # _from arg works 6 | 7 | # Invoke using a token 8 | 9 | # Sample application using the itty-bitty python web framework from: 10 | # http://github.com/toastdriven/itty 11 | 12 | import sys 13 | sys.path = ['..'] + sys.path 14 | 15 | from itty import * 16 | from tropo import Tropo, Session 17 | 18 | TO_NUMBER = '13072238293' 19 | FROM_NUMBER = '14076021088' 20 | 21 | 22 | @post('/index.json') 23 | def index(request): 24 | #s = Session(request.body) 25 | t = Tropo() 26 | t.call(to=' ' + TO_NUMBER, _from=' ' + FROM_NUMBER, label='xiangwyujianghu', network = 'MMS') 27 | mediaa = ['http://www.gstatic.com/webp/gallery/1.jpg', 'macbook eclipse', 'http://artifacts.voxeolabs.net.s3.amazonaws.com/test/test.png', 1234567890, '0987654321', 'https://www.travelchinaguide.com/images/photogallery/2012/beijing-tiananmen-tower.jpg'] 28 | t.say('This is your mother. Did you brush your teeth today?', media = mediaa) 29 | json = t.RenderJson() 30 | print json 31 | return json 32 | 33 | 34 | run_itty(server='wsgiref', host='127.0.0.1', port=8083) 35 | 36 | -------------------------------------------------------------------------------- /samples/gh-14.test_message.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-14 for "_from" parameter in the "message" function. 2 | # Proposed convention is to use "_from" as the parameter 3 | # so as not to conflict with "from" Python reserved word. 4 | 5 | # _from arg works 6 | 7 | # Invoke using a token 8 | 9 | # Sample application using the itty-bitty python web framework from: 10 | # http://github.com/toastdriven/itty 11 | 12 | from itty import * 13 | from tropo import Tropo, Session 14 | 15 | TO_NUMBER = 'sip:frank@172.16.22.128:5678' 16 | FROM_NUMBER = 'sip:xiangjun_yu@192.168.26.21:5678' 17 | 18 | 19 | @post('/index.json') 20 | def index(request): 21 | t = Tropo() 22 | t.message("Hello World from tylor", TO_NUMBER, channel='VOICE', _from='' + FROM_NUMBER, promptLogSecurity='sss') 23 | json = t.RenderJson() 24 | print json 25 | return json 26 | #retest 27 | 28 | 29 | run_itty(config='sample_conf') 30 | 31 | -------------------------------------------------------------------------------- /samples/gh-14.test_say.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-14 for "_as" parameter in the "say" function. 2 | # Proposed convention is to use "_from" as the parameter 3 | # so as not to conflict with "from" Python reserved word. 4 | 5 | 6 | 7 | 8 | # Sample application using the itty-bitty python web framework from: 9 | # http://github.com/toastdriven/itty 10 | 11 | import sys 12 | sys.path = ['..'] + sys.path 13 | 14 | from itty import * 15 | from tropo import Tropo, Session 16 | 17 | @post('/index.json') 18 | def index(request): 19 | t = Tropo() 20 | t.say('12345', _as='DIGITS', voice='dave', promptLogSecurity='suppress') 21 | t.say('s s s s f f f ', promptLogSecurity='suppress') 22 | t.say(["Hello, World", "How ya doing?"], promptLogSecurity = "suppredd") 23 | json = t.RenderJson() 24 | print json 25 | print 'sys.path is %s' % sys.path 26 | return json 27 | 28 | 29 | run_itty(server='wsgiref', host='192.168.26.1', port=8083) 30 | 31 | -------------------------------------------------------------------------------- /samples/gh-14.test_transfer.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-14 for "from" parameter in the "transfer" function. 2 | # Proposed convention is to use "_from" as the parameter 3 | # so as not to conflict with "from" Python reserved word. 4 | 5 | # _from arg works 6 | # _from arg works with straight json 7 | 8 | # Invoke by calling up app access number 9 | 10 | # Sample application using the itty-bitty python web framework from: 11 | # http://github.com/toastdriven/itty 12 | 13 | from itty import * 14 | from tropo import Tropo, Session 15 | 16 | TO_NUMBER = '1xxxxxxxxxx' 17 | FROM_NUMBER = '1yyyyyyyyyy' 18 | 19 | 20 | @post('/index.json') 21 | def index(request): 22 | s = Session(request.body) 23 | t = Tropo() 24 | t.say ("One moment please.") 25 | t.transfer(TO_NUMBER, _from="tel:+" + FROM_NUMBER) 26 | t.say("Hi. I am a robot") 27 | json = t.RenderJson() 28 | print json 29 | return json 30 | 31 | 32 | run_itty() 33 | 34 | -------------------------------------------------------------------------------- /samples/gh-14_test.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-14. Proposed convention is to use "_as" as 2 | # the attribute in "say" function, so as not to conflict with "as" 3 | # Python reserved word. 4 | 5 | # Sample application using the itty-bitty python web framework from: 6 | # http://github.com/toastdriven/itty 7 | 8 | from itty import * 9 | from tropo import Tropo, Session 10 | 11 | @post('/index.json') 12 | def index(request): 13 | s = Session(request.body) 14 | t = Tropo() 15 | t.say('12345', _as='DIGITS', voice='allison') 16 | json = t.RenderJson() 17 | print json 18 | return json 19 | 20 | run_itty() 21 | -------------------------------------------------------------------------------- /samples/gh-17.test.py: -------------------------------------------------------------------------------- 1 | from itty import * 2 | from tropo import Tropo, Result 3 | 4 | # Fixes issue gh-17 getValue() should work with "value" property 5 | # and not "interpretation" 6 | 7 | @post('/index.json') 8 | def index(request): 9 | t = Tropo() 10 | t.ask(choices = "yes(yes,y,1), no(no,n,2)", timeout=60, name="reminder", say = "Hey, did you remember to take your pills?") 11 | t.on(event = "continue", next ="/continue") 12 | t.on(event = "incomplete", next ="/incomplete") 13 | json = t.RenderJson() 14 | print json 15 | return json 16 | 17 | @post("/continue") 18 | def index(request): 19 | r = Result(request.body) 20 | t = Tropo() 21 | 22 | answer = r.getValue() 23 | 24 | t.say("You said " + str(answer)) 25 | 26 | if answer == "yes" : 27 | t.say("Ok, just checkin.") 28 | else : 29 | t.say("What are you waiting for?") 30 | 31 | json = t.RenderJson() 32 | print json 33 | return json 34 | 35 | @post("/incomplete") 36 | def index(request): 37 | t = Tropo() 38 | t.say("Sorry, that wasn't on of the options.") 39 | json = t.RenderJson() 40 | print json 41 | return json 42 | 43 | run_itty() 44 | -------------------------------------------------------------------------------- /samples/gh-20-test_ask.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-20 . Extracting out of Result 2 | 3 | # Added a new method on the Result object, called getInterpretation() 4 | 5 | 6 | # Sample application using the itty-bitty python web framework from: 7 | # http://github.com/toastdriven/itty 8 | 9 | import sys 10 | sys.path = ['..'] + sys.path 11 | from itty import * 12 | from tropo import Tropo, Session, Result 13 | 14 | 15 | @post('/index.json') 16 | def index(request): 17 | 18 | t = Tropo() 19 | 20 | t.ask(choices = "yes(yes,y,1), no(no,n,2)", timeout = 15, name = "directory", minConfidence = 1, attempts = 3, say = "Are you trying to reach the sales department??") 21 | 22 | t.ask(choices = "red(red,1), blue(blue,2)", timeout = 15, name = "color", minConfidence = 1, attempts = 3, say = "What color do you like??") 23 | 24 | t.on(event = "continue", next ="/continue") 25 | 26 | json = t.RenderJson() 27 | 28 | print json 29 | return json 30 | 31 | @post("/continue") 32 | def index(request): 33 | 34 | r = Result(request.body) 35 | print "request.body : %s" % request.body 36 | 37 | t = Tropo() 38 | 39 | answer = r.getInterpretation() 40 | value = r.getValue() 41 | 42 | t.say("You said " + answer + ", which is a " + value) 43 | 44 | actions_options_array = ['name', 'attempts', 'disposition', 'confidence', 'interpretation', 'utterance', 'value', 'concept', 'xml', 'uploadStatus'] 45 | actions = r.getActions() 46 | if (type (actions) is list): 47 | for item in actions: 48 | for opt in actions_options_array: 49 | print 'action object filed %(actionfieldname)s\'s value is %(vaalue)s' % {'actionfieldname':opt, "vaalue":item.get(opt,'NoValue')} 50 | print '------------------------------' 51 | 52 | else: 53 | dict = actions 54 | for opt in actions_options_array: 55 | print 'action object filed %(actionfieldname)s\'s value is %(vaalue)s' % {'actionfieldname':opt, "vaalue":dict.get(opt,'NoValue')} 56 | 57 | json = t.RenderJson() 58 | print json 59 | return json 60 | 61 | run_itty(server='wsgiref', host='192.168.26.1', port=8082) 62 | -------------------------------------------------------------------------------- /samples/gh-20-test_ask.testresult: -------------------------------------------------------------------------------- 1 | request.body : {"result":{"sessionId":"d8d42bc7260e88983e72b94b2c5b8a28","callId":"b900196da92c20a4d08423dd180e02d6","state":"ANSWERED","sessionDuration":36,"sequence":1,"complete":true,"error":null,"calledid":"9994806398","actions":[{"name":"directory","attempts":3,"disposition":"SUCCESS","confidence":100,"interpretation":"1","utterance":"1","concept":"yes","value":"yes","xml":"\r\n\r\n \r\n \r\n dtmf-1<\/input>\r\n <\/interpretation>\r\n<\/result>\r\n"},{"name":"color","attempts":3,"disposition":"SUCCESS","confidence":100,"interpretation":"2","utterance":"2","concept":"blue","value":"blue","xml":"\r\n\r\n \r\n \r\n dtmf-2<\/input>\r\n <\/interpretation>\r\n<\/result>\r\n"}]}} 2 | action object filed name's value is directory 3 | action object filed attempts's value is 3 4 | action object filed disposition's value is SUCCESS 5 | action object filed confidence's value is 100 6 | action object filed interpretation's value is 1 7 | action object filed utterance's value is 1 8 | action object filed value's value is yes 9 | action object filed concept's value is yes 10 | action object filed xml's value is 11 | 12 | 13 | 14 | dtmf-1 15 | 16 | 17 | 18 | action object filed uploadStatus's value is NoValue 19 | ------------------------------ 20 | action object filed name's value is color 21 | action object filed attempts's value is 3 22 | action object filed disposition's value is SUCCESS 23 | action object filed confidence's value is 100 24 | action object filed interpretation's value is 2 25 | action object filed utterance's value is 2 26 | action object filed value's value is blue 27 | action object filed concept's value is blue 28 | action object filed xml's value is 29 | 30 | 31 | 32 | dtmf-2 33 | 34 | 35 | 36 | action object filed uploadStatus's value is NoValue 37 | ------------------------------ 38 | {"tropo": [{"say": {"value": "You said 1, which is a yes"}}]} 39 | -------------------------------------------------------------------------------- /samples/gh-21.choices.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-21 . Sorting out syntax of choices for ask. 2 | 3 | # Fixed an error in the way the Ask class init function was 4 | # taking apart the choices argument passed to it. 5 | 6 | # Then, I corrected the original example provided for gh-21. 7 | # Correct way to provide "choices" argument to "ask" is shown in 8 | # the example below. 9 | 10 | 11 | # Sample application using the itty-bitty python web framework from: 12 | # http://github.com/toastdriven/itty 13 | 14 | from itty import * 15 | from tropo import Tropo, Session, Result, Choices 16 | 17 | 18 | @post('/index.json') 19 | def index(request): 20 | 21 | t = Tropo() 22 | 23 | choices = Choices("[4-5 DIGITS]", mode="dtmf", terminator = "#") 24 | t.ask(choices, timeout=15, name="digit", say = "What's your four or five digit pin? Press pound when finished.") 25 | 26 | t.on(event = "continue", next ="/continue") 27 | 28 | json = t.RenderJson() 29 | 30 | print json 31 | return json 32 | 33 | @post("/continue") 34 | def index(request): 35 | 36 | r = Result(request.body) 37 | print "Result : %s" % r 38 | # dump(r) 39 | t = Tropo() 40 | 41 | answer = r.getInterpretation() 42 | 43 | t.say("You said ") 44 | t.say (answer, _as="DIGITS") 45 | 46 | json = t.RenderJson() 47 | print json 48 | return json 49 | 50 | run_itty() 51 | 52 | -------------------------------------------------------------------------------- /samples/gh-22.transfer.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-22 . headers parameter for transfer() 2 | 3 | # Fixes an error whereby we weren't passing 4 | # the "headers" parameter to transfer() 5 | 6 | # Sample below shows how to pass in headers. 7 | 8 | 9 | # Sample application using the itty-bitty python web framework from: 10 | # http://github.com/toastdriven/itty 11 | 12 | 13 | from itty import * 14 | from tropo-webapi-python/tropo import Tropo, Session 15 | 16 | 17 | #TO_NUMBER = '1xxxxxxxxxx' 18 | TO_NUMBER = '16039570051' 19 | 20 | 21 | @post('/index.json') 22 | def index(request): 23 | 24 | s = Session(request.body) 25 | t = Tropo() 26 | 27 | t.say("Hello. , , , Transferring") 28 | # t.transfer(to="sip:9991489767@sip.tropo.com", headers={"x-callername":"Kevin Bond"}) 29 | 30 | t.transfer(TO_NUMBER, headers={"x-callername":"Kevin Bond"}) 31 | 32 | json = t.RenderJson() 33 | print json 34 | return json 35 | 36 | 37 | run_itty() 38 | 39 | 40 | -------------------------------------------------------------------------------- /samples/gh-23.ask_timeout.py: -------------------------------------------------------------------------------- 1 | # tests example clarifying gh-23 . How to use timeout, and nomatch parameters 2 | # in "say" within "ask" 3 | 4 | 5 | from itty import * 6 | from tropo import Tropo, Session, Result 7 | 8 | 9 | @post("/index.json") 10 | def index (request): 11 | t = Tropo() 12 | t.ask(choices = "[4 DIGITs]", 13 | timeout=5, 14 | bargein="true", 15 | name="year", 16 | attempts=3, 17 | required="true", 18 | say = [{'event':'timeout', 19 | 'value':"Sorry, I did not hear anything"}, 20 | {'event':'nomatch:1', 21 | 'value':"Don't think that was a year."}, 22 | {'event':'nomatch:2', 23 | 'value':"Nope, still not a year."}, 24 | {'value': "What is your birth year?"} 25 | ]) 26 | 27 | json = t.RenderJson() 28 | print json 29 | return json 30 | 31 | 32 | 33 | # @post("/index.json") 34 | def index_straight_json (request): 35 | json = """{ 36 | "tropo":[ 37 | { 38 | "ask":{ 39 | "attempts":3, 40 | "say":[ 41 | { 42 | "value":"Sorry, I did not hear anything.", 43 | "event":"timeout" 44 | }, 45 | { 46 | "value":"Don't think that was a year. ", 47 | "event":"nomatch:1" 48 | }, 49 | { 50 | "value":"Nope, still not a year.", 51 | "event":"nomatch:2" 52 | }, 53 | { 54 | "value":"What is your birth year?" 55 | } 56 | ], 57 | "choices":{ 58 | "value":"[4 DIGITS]" 59 | }, 60 | "bargein":true, 61 | "timeout":5, 62 | "name":"year", 63 | "required":true 64 | } 65 | } 66 | ] 67 | 68 | } 69 | """ 70 | print json 71 | return json 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | run_itty() 81 | -------------------------------------------------------------------------------- /samples/gh-24.ask_log.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-21 . Sorting out syntax of choices for ask. 2 | 3 | # Fixed an error in the way the Ask class init function was 4 | # taking apart the choices argument passed to it. 5 | 6 | # Then, I corrected the original example provided for gh-21. 7 | # Correct way to provide "choices" argument to "ask" is shown in 8 | # the example below. 9 | 10 | 11 | # Sample application using the itty-bitty python web framework from: 12 | # http://github.com/toastdriven/itty 13 | 14 | from itty import * 15 | from tropo import Tropo, Session, Result, Choices 16 | 17 | 18 | @post('/index.json') 19 | def index(request): 20 | 21 | t = Tropo() 22 | 23 | choices = Choices("[4-5 DIGITS]", mode="dtmf", terminator = "#") 24 | t.ask(choices, timeout=15, name="digit", say = "What's your four or five digit pin? Press pound when finished.", asrLogSecurity="mask", maskTemplate="XDXD-") 25 | 26 | t.on(event = "continue", next ="/continue") 27 | 28 | json = t.RenderJson() 29 | 30 | print json 31 | return json 32 | 33 | @post("/continue") 34 | def index(request): 35 | 36 | r = Result(request.body) 37 | print "Result : %s" % r 38 | # dump(r) 39 | t = Tropo() 40 | 41 | answer = r.getInterpretation() 42 | 43 | t.say("You said ") 44 | t.say (answer, _as="DIGITS") 45 | 46 | json = t.RenderJson() 47 | print json 48 | return json 49 | 50 | run_itty(config='sample_conf') 51 | 52 | -------------------------------------------------------------------------------- /samples/gh-25.conference.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Hello world script for Session API ( https://www.tropo.com/docs/webapi/sessionapi.htm ) 4 | 5 | Upon launch, it will trigger a message to be sent via Jabber to the addess specified in 6 | 'number'. 7 | """ 8 | 9 | # Sample application using the itty-bitty python web framework from: 10 | # http://github.com/toastdriven/itty 11 | 12 | from itty import * 13 | from tropo import Tropo, Session, JoinPrompt, LeavePrompt 14 | from urllib import urlencode 15 | from urllib2 import urlopen 16 | 17 | @post('/index.json') 18 | def index(request): 19 | session = Session(request.body) 20 | t = Tropo() 21 | #jj = JoinPrompt(value = "who are you who let you come in") 22 | jj = JoinPrompt("who are you who let you come in") 23 | #ll = LeavePrompt(value = "byebye samsung") 24 | ll = LeavePrompt("byebye samsung") 25 | t.call(to=session.parameters['callToNumber'], network='SIP') 26 | t.conference(id='yuxiangj', joinPrompt=jj.json, leavePrompt=ll.json) 27 | t.say(session.parameters['message']) 28 | return t.RenderJsonSDK() 29 | 30 | 31 | #base_url = 'http://api.tropo.com/1.0/sessions' 32 | base_url = 'http://192.168.26.21:8080/gateway/sessions' 33 | token = '7776687947547a6261677359524e665670427145574f544e44616b5a64456d6c526b576265647448516e796c' # Insert your token here 34 | action = 'create' 35 | #number = 'sip:xiangjun_yu@10.140.254.55:5678' # change to the Jabber ID to which you want to send the message 36 | number = 'sip:frank@172.16.22.128:5678' # change to the Jabber ID to which you want to send the message 37 | message = 'hello from the session API!' 38 | 39 | params = urlencode([('action', action), ('token', token), ('callToNumber', number), ('message', message)]) 40 | data = urlopen('%s?%s' % (base_url, params)).read() 41 | 42 | #run_itty(server='wsgiref', host='0.0.0.0', port=8888) 43 | run_itty(config='sample_conf') 44 | 45 | -------------------------------------------------------------------------------- /samples/gh-26.on.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Hello world script for Session API ( https://www.tropo.com/docs/webapi/sessionapi.htm ) 4 | 5 | Upon launch, it will trigger a message to be sent via Jabber to the addess specified in 6 | 'number'. 7 | """ 8 | 9 | # Sample application using the itty-bitty python web framework from: 10 | # http://github.com/toastdriven/itty 11 | 12 | from itty import * 13 | from tropo import Tropo, Session, JoinPrompt, LeavePrompt 14 | from urllib import urlencode 15 | from urllib2 import urlopen 16 | 17 | @post('/continue.json') 18 | def index(request): 19 | t = Tropo() 20 | t.say("I am continue") 21 | json = t.RenderJsonSDK() 22 | print json 23 | return json 24 | 25 | 26 | @post('/error.json') 27 | def index(request): 28 | t = Tropo() 29 | t.say("I am error") 30 | json = t.RenderJsonSDK() 31 | print json 32 | return json 33 | 34 | 35 | @post('/hangup.json') 36 | def index(request): 37 | t = Tropo() 38 | t.say("I am hangup") 39 | json = t.RenderJsonSDK() 40 | print json 41 | return json 42 | 43 | 44 | @post('/incomplete.json') 45 | def index(request): 46 | t = Tropo() 47 | t.say("I am incomplete") 48 | json = t.RenderJsonSDK() 49 | print json 50 | return json 51 | 52 | 53 | @post('/index.json') 54 | def index(request): 55 | t = Tropo() 56 | t.on("continue", next="/continue.json", post = 'http://192.168.26.88:8080/FileUpload/receiveJson', say = "this is say in on function") 57 | t.on("error", next = "/error.json") 58 | t.on("hangup", next = "/hangup.json") 59 | t.on("incomplete", next = "/incomplete.json") 60 | t.ask(say = "Welcome to Tropo. What's your birth year?", name = "year", require = "true", choices = "[4 DIGITS]") 61 | json = t.RenderJsonSDK() 62 | print json 63 | return json 64 | 65 | 66 | run_itty(config='sample_conf') 67 | 68 | -------------------------------------------------------------------------------- /samples/gh-5.hello_cgi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Hello, World CGI script. 4 | # Addresses gh-5. 5 | # Steps: 6 | # 1. edit Apache httpd.conf file 7 | # Alias /tropo/ "/path/to/examples/" 8 | # 9 | # Options +ExecCGI 10 | # SetHandler cgi-script 11 | # Allow from all 12 | # AllowOverride All 13 | # 14 | # 2. Create Web API app in Tropo and assign it the url 15 | # http://my_webserver.com/tropo/g-5.hello_cgi.py 16 | # 3. Place this file in examples folder and chmod it executable 17 | # 4. Dial up Tropo app, and hear "Hello, World ..." 18 | 19 | import cgi 20 | from tropo import Tropo 21 | 22 | def hello(): 23 | t = Tropo() 24 | t.say(['hello world! I am a C G I script.']) 25 | json = t.RenderJson() 26 | print json 27 | return json 28 | 29 | 30 | 31 | print "Content-type: text/json" 32 | print 33 | print 34 | 35 | hello() 36 | 37 | -------------------------------------------------------------------------------- /samples/itty_hello_world.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Sample application using the itty-bitty python web framework from: 4 | # http://github.com/toastdriven/itty 5 | 6 | from itty import * 7 | from tropo import Tropo, Session 8 | 9 | @post('/index.json') 10 | def index(request): 11 | s = Session(request.body) 12 | t = Tropo() 13 | t.say(['hello world!', 'how are you doing?']) 14 | return t.RenderJson() 15 | 16 | run_itty(server='wsgiref', host='0.0.0.0', port=8888) 17 | 18 | -------------------------------------------------------------------------------- /samples/itty_session_api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Hello world script for Session API ( https://www.tropo.com/docs/webapi/sessionapi.htm ) 4 | 5 | Upon launch, it will trigger a message to be sent via Jabber to the addess specified in 6 | 'number'. 7 | """ 8 | 9 | # Sample application using the itty-bitty python web framework from: 10 | # http://github.com/toastdriven/itty 11 | 12 | from itty import * 13 | from tropo import Tropo, Session 14 | from urllib import urlencode 15 | from urllib2 import urlopen 16 | 17 | @post('/index.json') 18 | def index(request): 19 | session = Session(request.body) 20 | t = Tropo() 21 | t.call(to=session.parameters['numberToDial'], network='JABBER') 22 | t.say(session.parameters['message']) 23 | return t.RenderJson() 24 | 25 | 26 | base_url = 'http://api.tropo.com/1.0/sessions' 27 | token = 'xxxxxxxxxx' # Insert your token here 28 | action = 'create' 29 | number = 'username@domain' # change to the Jabber ID to which you want to send the message 30 | message = 'hello from the session API!' 31 | 32 | params = urlencode([('action', action), ('token', token), ('numberToDial', number), ('message', message)]) 33 | data = urlopen('%s?%s' % (base_url, params)).read() 34 | 35 | run_itty(server='wsgiref', host='0.0.0.0', port=8888) 36 | 37 | -------------------------------------------------------------------------------- /samples/recordMultiUrl_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path = ['..'] + sys.path 3 | from itty import * 4 | from tropo import Tropo, Session, Result, Transcription, RecordUrlTuple 5 | 6 | @post('/index') 7 | def index(request): 8 | 9 | t = Tropo() 10 | VOICE = 'Grace' 11 | 12 | transcriptionobj = Transcription(id = "tropo-12123", url = "http://192.168.26.88:8080/FileUpload/uploadFile", language = "English").json 13 | recordURLobj1 = RecordUrlTuple(url = "http://192.168.26.88:8080/FileUpload/uploadFile1", username = "fakename1", password="fakepassword", method="POST").json 14 | recordURLobj2 = RecordUrlTuple(url = "http://192.168.26.88:8080/FileUpload/uploadFile", username = "fakename2", password="fakepassword", method="POST").json 15 | recordURLobj3 = RecordUrlTuple(url = "http://192.168.26.88:8080/FileUpload/uploadFile3", username = "fakename3", password="fakepassword", method="POST").json 16 | recordURLobj4 = RecordUrlTuple(url = "http://192.168.26.88:8080/FileUpload/uploadFile4", username = "fakename4", password="fakepassword", method="POST").json 17 | 18 | t.record(url = [recordURLobj1, recordURLobj2, recordURLobj3, recordURLobj4],transcription = transcriptionobj, name='voicemail.wav', say='Your call is important. Please leave a short message after the tone: ', beep = True, formamt = 'audio/wav', sensitivity = 5.3) 19 | 20 | return t.RenderJson() 21 | 22 | run_itty(server='wsgiref', host='192.168.26.1', port=8888) 23 | -------------------------------------------------------------------------------- /samples/record_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path = ['..'] + sys.path 3 | from itty import * 4 | from tropo import Tropo, Session, Result, Transcription 5 | 6 | @post('/index') 7 | def index(request): 8 | 9 | t = Tropo() 10 | VOICE = 'Grace' 11 | 12 | transcriptionobj = Transcription(id = "tropo-12123", url = "http://192.168.26.88:8080/FileUpload/uploadFile", language = "English").json 13 | 14 | t.record(transcription = transcriptionobj, name='voicemail.wav', say='Your call is important. Please leave a short message after the tone: ', url = 'http://192.168.26.88:8080/FileUpload/uploadFile', beep = True, formamt = 'audio/wav', sensitivity = 5.3) 15 | 16 | return t.RenderJson() 17 | 18 | run_itty(server='wsgiref', host='192.168.26.1', port=8888) 19 | -------------------------------------------------------------------------------- /samples/sample_conf.py: -------------------------------------------------------------------------------- 1 | # itty Config 2 | host = '100.100.100.100' 3 | port = 8080 4 | server = 'wsgiref' 5 | -------------------------------------------------------------------------------- /samples/session_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | https://www.tropo.com/docs/webapi/session 4 | """ 5 | 6 | # Sample application using the itty-bitty python web framework from: 7 | # http://github.com/toastdriven/itty 8 | 9 | from itty import * 10 | from tropo import Tropo, Session, JoinPrompt, LeavePrompt 11 | 12 | @post('/sessiontest.json') 13 | def sessiontest(request): 14 | session = Session(request.body) 15 | print 'request.body is ' + request.body 16 | accountId = session.accountId 17 | callId = session.callId 18 | fromm = session.fromaddress 19 | headers = session.headers 20 | idd = session.id 21 | initialText = session.initialText 22 | if hasattr(session,'parameters'): 23 | parameters = session.parameters 24 | else: 25 | parameters = '' 26 | timestamp = session.timestamp 27 | too = session.to 28 | userType = session.userType 29 | t = Tropo() 30 | t.say('accountId is ' + accountId) 31 | t.say('callId is ' + callId) 32 | 33 | fromid = fromm['id'] 34 | frome164Id = fromm['e164Id'] 35 | fromname = fromm['name'] 36 | fromchannel = fromm['channel'] 37 | fromnetwork = fromm['network'] 38 | 39 | t.say('from id is ' + fromid) 40 | t.say('from e164Id ' + frome164Id) 41 | t.say('from name ' + fromname) 42 | t.say('from channel ' + fromchannel) 43 | t.say('from network ' + fromnetwork) 44 | 45 | t.say('id is ' + idd) 46 | t.say('initialText is ' + str(initialText)) 47 | t.say('headers is ' + str(headers)) 48 | t.say('parameters is ' + parameters) 49 | t.say('timestamp is ' + timestamp) 50 | 51 | tooid = too['id'] 52 | too164Id = too['e164Id'] 53 | tooname = too['name'] 54 | toochannel = too['channel'] 55 | toonetwork = too['network'] 56 | 57 | t.say('to id is ' + tooid) 58 | t.say('to e164Id ' + too164Id) 59 | t.say('to name ' + tooname) 60 | t.say('to channel ' + toochannel) 61 | t.say('to network ' + toonetwork) 62 | 63 | t.say('userType is ' + userType) 64 | 65 | if("frank" in fromname): 66 | t.say('hello frank ') 67 | else: 68 | t.say('sorry you are not frank') 69 | 70 | json = t.RenderJson() 71 | print json 72 | return json 73 | 74 | #run_itty(server='wsgiref', host='0.0.0.0', port=8888) 75 | run_itty(config='sample_conf') 76 | 77 | -------------------------------------------------------------------------------- /samples/session_testMMS.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | https://www.tropo.com/docs/webapi/session 4 | """ 5 | 6 | # Sample application using the itty-bitty python web framework from: 7 | # http://github.com/toastdriven/itty 8 | 9 | import sys 10 | sys.path = ['..'] + sys.path 11 | 12 | from itty import * 13 | from tropo import Tropo, Session, JoinPrompt, LeavePrompt 14 | 15 | @post('/index.json') 16 | def sessiontest(request): 17 | session = Session(request.body) 18 | print 'request.body is ' + request.body 19 | accountId = session.accountId 20 | callId = session.callId 21 | fromm = session.fromaddress 22 | headers = session.headers 23 | idd = session.id 24 | initialText = session.initialText 25 | if hasattr(session,'parameters'): 26 | parameters = session.parameters 27 | else: 28 | parameters = '' 29 | timestamp = session.timestamp 30 | too = session.to 31 | userType = session.userType 32 | 33 | subjectVar = session.subject 34 | if subjectVar: 35 | print subjectVar 36 | 37 | mediaList = session.initialMedia 38 | if mediaList: 39 | for i in range(len(mediaList)): 40 | print("mediaList {}: {}".format(i + 1, mediaList[i])) 41 | 42 | 43 | t = Tropo() 44 | t.say('accountId is ' + accountId) 45 | t.say('callId is ' + callId) 46 | 47 | fromid = fromm['id'] 48 | frome164Id = fromm['e164Id'] 49 | fromname = fromm['name'] 50 | fromchannel = fromm['channel'] 51 | fromnetwork = fromm['network'] 52 | 53 | t.say('from id is ' + fromid) 54 | t.say('from e164Id ' + frome164Id) 55 | t.say('from name ' + str(fromname)) 56 | t.say('from channel ' + fromchannel) 57 | t.say('from network ' + fromnetwork) 58 | 59 | t.say('id is ' + idd) 60 | t.say('initialText is ' + str(initialText)) 61 | t.say('headers is ' + str(headers)) 62 | t.say('parameters is ' + parameters) 63 | t.say('timestamp is ' + timestamp) 64 | 65 | tooid = too['id'] 66 | too164Id = too['e164Id'] 67 | tooname = too['name'] 68 | toochannel = too['channel'] 69 | toonetwork = too['network'] 70 | 71 | t.say('to id is ' + tooid) 72 | t.say('to e164Id ' + too164Id) 73 | t.say('to name ' + str(tooname)) 74 | t.say('to channel ' + toochannel) 75 | t.say('to network ' + toonetwork) 76 | 77 | t.say('userType is ' + userType) 78 | 79 | if("frank" in str(fromname)): 80 | t.say('hello frank ') 81 | else: 82 | t.say('sorry you are not frank') 83 | 84 | json = t.RenderJson() 85 | print json 86 | return json 87 | 88 | #run_itty(server='wsgiref', host='0.0.0.0', port=8888) 89 | run_itty(config='sample_conf') 90 | 91 | -------------------------------------------------------------------------------- /samples/startRecordingMultiUrl_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path = ['..'] + sys.path 3 | from itty import * 4 | from tropo import Tropo, Session, Result, RecordUrlTuple 5 | 6 | @post('/index') 7 | def index(request): 8 | 9 | t = Tropo() 10 | t.say("A B C song") 11 | recordURLobj1 = RecordUrlTuple(url = "http://192.168.26.88:8080/FileUpload/uploadFile1", username = "fakename1", password="fakepassword", method="POST").json 12 | recordURLobj2 = RecordUrlTuple(url = "http://192.168.26.88:8080/FileUpload/uploadFile", username = "fakename2", password="fakepassword", method="POST").json 13 | recordURLobj3 = RecordUrlTuple(url = "http://192.168.26.88:8080/FileUpload/uploadFile3", username = "fakename3", password="fakepassword", method="POST").json 14 | recordURLobj4 = RecordUrlTuple(url = "http://192.168.26.88:8080/FileUpload/uploadFile4", username = "fakename4", password="fakepassword", method="POST").json 15 | 16 | t.startRecording([recordURLobj1, recordURLobj2, recordURLobj3, recordURLobj4], formamt = 'audio/wav', transcriptionID = "20170601startRecording", transcriptionEmailFormat = "plain", transcriptionLanguage = "en-usa", transcriptionOutURI = "http://12b12d1b.ngrok.io/FileUpload/receiveJson") 17 | t.say("a b c d e f g h i j k l m n o p q r s t u v w x y z now you know your a b c start sing with me ") 18 | t.say("Merry Christmas and happy new year") 19 | t.say("1 2 3 4 5 6 7 8 9 0 A B C D E F G") 20 | t.say("today is Thursday 2017-12-25") 21 | t.stopRecording() 22 | t.hangup() 23 | return t.RenderJson() 24 | 25 | run_itty(server='wsgiref', host='192.168.26.1', port=8085) 26 | -------------------------------------------------------------------------------- /samples/startRecording_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path = ['..'] + sys.path 3 | from itty import * 4 | from tropo import Tropo, Session, Result 5 | 6 | @post('/index') 7 | def index(request): 8 | 9 | t = Tropo() 10 | t.call("+8613466549249") 11 | #t.startRecording('http://12b12d1b.ngrok.io/FileUpload/uploadFile', formamt = 'audio/wav', transcriptionID = "20170601startRecording", transcriptionEmailFormat = "plain", transcriptionOutURI = "http://12b12d1b.ngrok.io/FileUpload/receiveJson") 12 | t.startRecording('http://12b12d1b.ngrok.io/FileUpload/uploadFile', formamt = 'audio/wav', transcriptionID = "20170601startRecording", transcriptionEmailFormat = "plain", transcriptionLanguage = "en-usa", transcriptionOutURI = "http://12b12d1b.ngrok.io/FileUpload/receiveJson") 13 | t.say("a b c d e f g h i j k l m n o p q r s t u v w x y z @ # $ % & ") 14 | t.say(" I love my daughter") 15 | t.say("1 2 3 4 5 6 7 8 9 0 A B C D E F G") 16 | t.say("today is Thursday 2017-06-01") 17 | t.stopRecording() 18 | return t.RenderJson() 19 | 20 | run_itty(server='wsgiref', host='192.168.26.1', port=8085) 21 | -------------------------------------------------------------------------------- /samples/test_answer.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path = ['..'] + sys.path 3 | from itty import * 4 | from tropo import Tropo 5 | 6 | 7 | @post('/index.json') 8 | def index(request): 9 | t = Tropo() 10 | t.answer(headers={"P-Header":"value goes here","Remote-Party-ID":"\"John Doe\";party=calling;id-type=subscriber;privacy=full;screen=yes"}) 11 | t.say('This is your mother. Did you brush your teeth today?') 12 | json = t.RenderJson() 13 | print json 14 | return json 15 | 16 | 17 | run_itty(server='wsgiref', host='192.168.26.1', port=8888) 18 | 19 | 20 | -------------------------------------------------------------------------------- /samples/test_generalLogSecurity.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-14 for "_as" parameter in the "say" function. 2 | # Proposed convention is to use "_from" as the parameter 3 | # so as not to conflict with "from" Python reserved word. 4 | 5 | 6 | 7 | 8 | # Sample application using the itty-bitty python web framework from: 9 | # http://github.com/toastdriven/itty 10 | 11 | from itty import * 12 | from tropo import Tropo, Session 13 | 14 | @post('/index.json') 15 | def index(request): 16 | # s = Session(request.body) 17 | t = Tropo() 18 | t.say('12345466974710071', _as='DIGITS') 19 | t.generalLogSecurity('suppress') 20 | t.say('line 20 should be suppressed') 21 | t.generalLogSecurity('none') 22 | t.say('line 22 should be logged') 23 | t.generalLogSecurity('suppress') 24 | t.say('line 24 is not logged') 25 | t.generalLogSecurity('none') 26 | t.say('line 26 will be logged') 27 | json = t.RenderJson() 28 | print json 29 | return json 30 | 31 | 32 | run_itty(config='sample_conf') 33 | 34 | -------------------------------------------------------------------------------- /samples/tropo_11258_transferOnTest.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-14 for "from" parameter in the "transfer" function. 2 | # Proposed convention is to use "_from" as the parameter 3 | # so as not to conflict with "from" Python reserved word. 4 | 5 | # _from arg works 6 | # _from arg works with straight json 7 | 8 | # Invoke by calling up app access number 9 | 10 | # Sample application using the itty-bitty python web framework from: 11 | # http://github.com/toastdriven/itty 12 | 13 | import sys 14 | sys.path = ['..'] + sys.path 15 | from itty import * 16 | from tropo import Tropo, Session, On, TransferOnChoices, Ask, Choices 17 | 18 | TO_NUMBER = 'sip:frank@172.16.22.128:5678' 19 | FROM_NUMBER = 'sip:xiangjun_yu@192.168.26.1:5678' 20 | 21 | 22 | @post('/index.json') 23 | def index(request): 24 | t = Tropo() 25 | t.call("sip:xiangjun_yu@192.168.26.1:5678", say = "ha ha ha ha ha ah ah ah ah") 26 | t.say("ah ah ah ah ah uh uh uh uh ha ha ha") 27 | on1 = On("connect", ask = Ask(Choices("[5 DIGITS]")).json).json 28 | on2 = On("ring", say = "emily2").json 29 | t.transfer(TO_NUMBER, _from= FROM_NUMBER, on=[on1,on2], choices = TransferOnChoices(terminator = '#').json) 30 | t.say("Hi. I am a robot") 31 | json = t.RenderJson() 32 | print json 33 | return json 34 | 35 | 36 | run_itty(config='sample_conf') 37 | 38 | -------------------------------------------------------------------------------- /samples/tropo_11263_multi_ask_multi_actions.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-20 . Extracting out of Result 2 | 3 | # Added a new method on the Result object, called getInterpretation() 4 | 5 | 6 | # Sample application using the itty-bitty python web framework from: 7 | # http://github.com/toastdriven/itty 8 | 9 | import sys 10 | sys.path = ['..'] + sys.path 11 | from itty import * 12 | from tropo import Tropo, Session, Result 13 | 14 | 15 | @post('/index.json') 16 | def index(request): 17 | 18 | t = Tropo() 19 | 20 | t.ask(choices = "yes(yes,y,1), no(no,n,2)", timeout = 15, name = "directory", minConfidence = 1, attempts = 3, say = "Are you trying to reach the sales department??") 21 | 22 | t.ask(choices = "red(red,1), blue(blue,2)", timeout = 15, name = "color", minConfidence = 1, attempts = 3, say = "What color do you like??") 23 | 24 | t.on(event = "continue", next ="/continue") 25 | 26 | json = t.RenderJson() 27 | print json 28 | return json 29 | 30 | @post("/continue") 31 | def index(request): 32 | 33 | r = Result(request.body) 34 | print "request.body : %s" % request.body 35 | 36 | t = Tropo() 37 | 38 | answer = r.getInterpretation() 39 | value = r.getValue() 40 | 41 | answer1 = r.getNamedActionInterpretation("directory") 42 | value1 = r.getNamedActionValue("directory") 43 | t.say("You said " + answer1 + ", which is a " + value1) 44 | 45 | answer2 = r.getNamedActionInterpretation("color") 46 | value2 = r.getNamedActionValue("color") 47 | t.say("You also said " + answer2 + ", which is a " + value2) 48 | 49 | 50 | count= r.getActionsCount() 51 | t.say("actions count is " + str(count)) 52 | for i in range(count): 53 | answer = r.getIndexdedInterpretation(i) 54 | value = r.getIndexedValue(i) 55 | t.say("You said " + answer + ", which is a " + value) 56 | 57 | actions_options_array = ['name', 'attempts', 'disposition', 'confidence', 'interpretation', 'utterance', 'value', 'concept', 'xml', 'uploadStatus'] 58 | actions = r.getActions() 59 | if (type (actions) is list): 60 | for item in actions: 61 | for opt in actions_options_array: 62 | print 'action object filed %(actionfieldname)s\'s value is %(vaalue)s' % {'actionfieldname':opt, "vaalue":item.get(opt,'NoValue')} 63 | print '------------------------------' 64 | 65 | else: 66 | dict = actions 67 | for opt in actions_options_array: 68 | print 'action object filed %(actionfieldname)s\'s value is %(vaalue)s' % {'actionfieldname':opt, "vaalue":dict.get(opt,'NoValue')} 69 | 70 | json = t.RenderJson() 71 | print json 72 | return json 73 | 74 | run_itty(server='wsgiref', host='192.168.26.1', port=8082) 75 | -------------------------------------------------------------------------------- /samples/tropo_11270_transferTest.py: -------------------------------------------------------------------------------- 1 | # tests fix of gh-14 for "from" parameter in the "transfer" function. 2 | # Proposed convention is to use "_from" as the parameter 3 | # so as not to conflict with "from" Python reserved word. 4 | 5 | # _from arg works 6 | # _from arg works with straight json 7 | 8 | # Invoke by calling up app access number 9 | 10 | # Sample application using the itty-bitty python web framework from: 11 | # http://github.com/toastdriven/itty 12 | 13 | import sys 14 | sys.path = ['..'] + sys.path 15 | from itty import * 16 | from tropo import Tropo, Session, On 17 | 18 | TO_NUMBER = 'sip:frank@172.16.22.128:5678' 19 | FROM_NUMBER = 'sip:xiangjun_yu@192.168.26.1:5678' 20 | 21 | 22 | @post('/index.json') 23 | def index(request): 24 | t = Tropo() 25 | t.call("sip:xiangjun_yu@192.168.26.1:5678", say = "ha ha ha ha ha ah ah ah ah") 26 | t.say("a b c d e f g h i j k") 27 | on = On("connect", say = "emily", next = "http://freewavesamples.com/files/Kawai-K5000W-AddSquare-C4.wav", post = "http://192.168.26.88:8080/FileUpload/receiveJson").json 28 | t.transfer(TO_NUMBER, _from= FROM_NUMBER, on=on, callbackUrl="http://192.168.26.88:8080/FileUpload/receiveJson", label="erthnbvc") 29 | t.say("Hi. I am a robot q a z w s x e d c") 30 | json = t.RenderJson() 31 | print json 32 | return json 33 | 34 | 35 | run_itty(server='wsgiref', host='192.168.26.1', port=8084) 36 | 37 | -------------------------------------------------------------------------------- /samples/wait_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path = ['..'] + sys.path 3 | from itty import * 4 | from tropo import Tropo, Session, Result 5 | 6 | @post('/index') 7 | def index(request): 8 | 9 | t = Tropo() 10 | t.call("sip:frank@172.16.22.128:5678") 11 | t.say("tropo status") 12 | t.wait(27222, allowSignals = 'dfghjm') 13 | t.say("today is Friday 2017-06-02") 14 | return t.RenderJson() 15 | 16 | run_itty(server='wsgiref', host='192.168.26.1', port=8086) 17 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup 4 | 5 | setup(name = "ciscotropo-webapi-python", 6 | version = "0.1.4", 7 | url = "http://github.com/tropo/tropo-webapi-python", 8 | maintainer = "Cisco", 9 | maintainer_email = "support@tropo.com", 10 | description = "Python library for building voice/SMS/IM/Twitter apps at Tropo.com", 11 | long_description = "This module implements a set of classes and methods for manipulating the Web API for the Tropo cloud communications service at http://www.tropo.com/", 12 | platforms = ["Platform Independent"], 13 | license = "MIT", 14 | classifiers = [ 15 | "Development Status :: 4 - Beta", 16 | "Intended Audience :: Developers", 17 | "License :: OSI Approved :: MIT License", 18 | "Operating System :: OS Independent", 19 | "Programming Language :: Python" 20 | ], 21 | py_modules = ['tropo'], 22 | ) 23 | 24 | -------------------------------------------------------------------------------- /test/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | try: 5 | import cjson as jsonlib 6 | jsonlib.dumps = jsonlib.encode 7 | jsonlib.loads = jsonlib.decode 8 | except ImportError: 9 | try: 10 | from django.utils import simplejson as jsonlib 11 | except ImportError: 12 | try: 13 | import simplejson as jsonlib 14 | except ImportError: 15 | import json as jsonlib 16 | 17 | import unittest 18 | import sys 19 | sys.path = ['..'] + sys.path 20 | from tropo import Choices, Say, Tropo 21 | 22 | 23 | class TestTropoPython(unittest.TestCase): 24 | """ 25 | Class implementing a set of unit tests for TropoPython. 26 | """ 27 | TO = "8005551212" 28 | MY_PHONE = "6021234567" 29 | RECORDING_URL = "/receive_recording.py" 30 | ID = "foo" 31 | S3_URL = "http://s3.amazonaws.com/xxx_s3_bucket/hello.wav" 32 | 33 | 34 | def test_ask(self): 35 | """ 36 | Test the "ask" Tropo class method. 37 | """ 38 | tropo = Tropo() 39 | tropo.ask("[5 digits]", 40 | say = Say("Please enter a 5 digit zip code").json) 41 | rendered = tropo.RenderJson() 42 | pretty_rendered = tropo.RenderJson(pretty=True) 43 | print "===============test_ask=================" 44 | print "render json: %s" % pretty_rendered 45 | rendered_obj = jsonlib.loads(rendered) 46 | wanted_json = '{"tropo": [{"ask": {"say": {"value": "Please enter a 5 digit zip code"}, "choices": {"value": "[5 digits]"}}}]}' 47 | wanted_obj = jsonlib.loads(wanted_json) 48 | # print "test_ask: %s" % tropo.RenderJson() 49 | self.assertEqual(rendered_obj, wanted_obj) 50 | 51 | def test_call(self): 52 | """ 53 | Test the "call" Tropo class method. 54 | """ 55 | 56 | tropo = Tropo() 57 | tropo.call(self.MY_PHONE, channel='TEXT', network='SMS', label='labelofcall', voice = 'Tian-tian', callbackUrl = 'foourl', promptLogSecurity = 'suppress') 58 | tropo.say ("Wish you were here") 59 | rendered = tropo.RenderJson() 60 | pretty_rendered = tropo.RenderJson(pretty=True) 61 | print ("============test_call=============") 62 | print "render json: %s" % pretty_rendered 63 | 64 | rendered_obj = jsonlib.loads(rendered) 65 | wanted_json = '{"tropo": [{"call": {"to": "%s", "network": "SMS", "channel": "TEXT", "label": "labelofcall", "voice": "Tian-tian", "callbackUrl": "foourl", "promptLogSecurity": "suppress"}}, {"say": {"value": "Wish you were here"}}]}' % self.MY_PHONE 66 | wanted_obj = jsonlib.loads(wanted_json) 67 | # print "test_call: %s" % tropo.RenderJson() 68 | self.assertEqual(rendered_obj, wanted_obj) 69 | 70 | def test_conference(self): 71 | """ 72 | Test the "conference" Tropo class method. 73 | """ 74 | 75 | tropo = Tropo() 76 | tropo.conference(self.ID, playTones=True, mute=False, 77 | name="Staff Meeting") 78 | rendered = tropo.RenderJson() 79 | pretty_rendered = tropo.RenderJson(pretty=True) 80 | print "===============test_conference=================" 81 | print "render json: %s" % pretty_rendered 82 | 83 | rendered_obj = jsonlib.loads(rendered) 84 | wanted_json = '{"tropo": [{"conference": {"playTones": true, "mute": false, "name": "Staff Meeting", "id": "foo"}}]}' 85 | print "wanted_json: %s" % wanted_json 86 | wanted_obj = jsonlib.loads(wanted_json) 87 | # print "test_conference: %s" % tropo.RenderJson() 88 | self.assertEqual(rendered_obj, wanted_obj) 89 | 90 | def test_conference_tropo_11252(self): 91 | """ 92 | Test the "conference" Tropo class method. 93 | """ 94 | 95 | tropo = Tropo() 96 | tropo.conference(self.ID, playTones=True, mute=False, 97 | name="Staff Meeting", promptLogSecurity = "suppress") 98 | rendered = tropo.RenderJson() 99 | pretty_rendered = tropo.RenderJson(pretty=True) 100 | print "===============test_conference tropo-11252=================" 101 | print "render json: %s" % pretty_rendered 102 | 103 | 104 | rendered_obj = jsonlib.loads(rendered) 105 | wanted_json = '{"tropo": [{"conference": {"playTones": true, "mute": false, "promptLogSecurity": "suppress", "name": "Staff Meeting", "id": "foo"}}]}' 106 | print "wanted_json: %s" % wanted_json 107 | wanted_obj = jsonlib.loads(wanted_json) 108 | # print "test_conference: %s" % tropo.RenderJson() 109 | self.assertEqual(rendered_obj, wanted_obj) 110 | 111 | def test_hangup(self): 112 | """ 113 | Test the "hangup" Tropo class method. 114 | """ 115 | 116 | tropo = Tropo() 117 | tropo.hangup() 118 | rendered = tropo.RenderJson() 119 | pretty_rendered = tropo.RenderJson(pretty=True) 120 | print "===============test_hangup=================" 121 | print "render json: %s" % pretty_rendered 122 | 123 | # print "test_hangup: %s" % tropo.RenderJson() 124 | rendered_obj = jsonlib.loads(rendered) 125 | wanted_json = '{"tropo": [{"hangup": null}]}' 126 | wanted_obj = jsonlib.loads(wanted_json) 127 | self.assertEqual(rendered_obj, wanted_obj) 128 | 129 | def test_message(self): 130 | """ 131 | Test the "message" Tropo class method. 132 | """ 133 | 134 | tropo = Tropo() 135 | tropo.message("Hello World", self.MY_PHONE, channel='TEXT', network='SMS', timeout=5, promptLogSecurity = 'suppress') 136 | rendered = tropo.RenderJson() 137 | pretty_rendered = tropo.RenderJson(pretty=True) 138 | print "===============test_message=================" 139 | print "render json: %s" % pretty_rendered 140 | 141 | # print "test_message: %s" % tropo.RenderJson() 142 | rendered_obj = jsonlib.loads(rendered) 143 | wanted_json = ' {"tropo": [{"message": {"to": "%s", "say": {"value": "Hello World"}, "promptLogSecurity": "suppress", "network": "SMS", "timeout": 5, "channel": "TEXT"}}]}' % self.MY_PHONE 144 | wanted_obj = jsonlib.loads(wanted_json) 145 | self.assertEqual(rendered_obj, wanted_obj) 146 | 147 | def test_on(self): 148 | """ 149 | Test the "on" Tropo class method. 150 | """ 151 | 152 | tropo = Tropo() 153 | 154 | tropo.on(event="continue", 155 | next="/weather.py?uri=end", 156 | post="http://foo.com/bar", 157 | say="Please hold.") 158 | rendered = tropo.RenderJson() 159 | pretty_rendered = tropo.RenderJson(pretty=True) 160 | print "===============test_on=================" 161 | print "render json: %s" % pretty_rendered 162 | 163 | # print "test_on: %s" % tropo.RenderJson() 164 | rendered_obj = jsonlib.loads(rendered) 165 | wanted_json = ' {"tropo": [{"on": {"say": {"value": "Please hold."}, "post": "http://foo.com/bar", "event": "continue", "next": "/weather.py?uri=end"}}]}' 166 | wanted_obj = jsonlib.loads(wanted_json) 167 | self.assertEqual(rendered_obj, wanted_obj) 168 | 169 | def test_record(self): 170 | """ 171 | Test the "record" Tropo class method. 172 | """ 173 | 174 | tropo = Tropo() 175 | url = "/receive_recording.py" 176 | choices_obj = Choices("", terminator="#").json 177 | tropo.record(say="Tell us about yourself", url=url, 178 | promptLogSecurity="suppree", 179 | choices=choices_obj) 180 | rendered = tropo.RenderJson() 181 | pretty_rendered = tropo.RenderJson(pretty=True) 182 | print "===============test_record=================" 183 | print "render json: %s" % pretty_rendered 184 | 185 | # print "test_record: %s" % tropo.RenderJson() 186 | rendered_obj = jsonlib.loads(rendered) 187 | wanted_json = ' {"tropo": [{"record": {"url": "/receive_recording.py", "promptLogSecurity": "suppree", "say": {"value": "Tell us about yourself"}, "choices": {"terminator": "#", "value": ""}}}]}' 188 | wanted_obj = jsonlib.loads(wanted_json) 189 | self.assertEqual(rendered_obj, wanted_obj) 190 | 191 | def test_redirect(self): 192 | """ 193 | Test the "redirect" Tropo class method. 194 | """ 195 | 196 | tropo = Tropo() 197 | tropo.redirect(self.MY_PHONE, "fakeredirectname") 198 | rendered = tropo.RenderJson() 199 | pretty_rendered = tropo.RenderJson(pretty=True) 200 | print "===============test_redirect=================" 201 | print "render json: %s" % pretty_rendered 202 | 203 | print "Wanted_Json %s" % tropo.RenderJson() 204 | rendered_obj = jsonlib.loads(rendered) 205 | wanted_json = '{"tropo": [{"redirect": {"name": "fakeredirectname", "to": "%s"}}]}' % self.MY_PHONE 206 | wanted_obj = jsonlib.loads(wanted_json) 207 | # print "test_redirect: %s" % tropo.RenderJson() 208 | self.assertEqual(rendered_obj, wanted_obj) 209 | 210 | def test_reject(self): 211 | """ 212 | Test the "reject" Tropo class method. 213 | """ 214 | 215 | tropo = Tropo() 216 | tropo.reject() 217 | rendered = tropo.RenderJson() 218 | pretty_rendered = tropo.RenderJson(pretty=True) 219 | print "===============test_reject=================" 220 | print "render json: %s" % pretty_rendered 221 | 222 | print "Want %s" % tropo.RenderJson() 223 | rendered_obj = jsonlib.loads(rendered) 224 | wanted_json = '{"tropo": [{"reject": null}]}' 225 | wanted_obj = jsonlib.loads(wanted_json) 226 | # print "test_reject: %s" % tropo.RenderJson() 227 | self.assertEqual(rendered_obj, wanted_obj) 228 | 229 | def test_say(self): 230 | """ 231 | Test the "say" Tropo class method. 232 | """ 233 | 234 | tropo = Tropo() 235 | tropo.say("Hello, World", promptLogSecurity='soppress') 236 | rendered = tropo.RenderJson() 237 | pretty_rendered = tropo.RenderJson(pretty=True) 238 | print "===============test_say=================" 239 | print "render json: %s" % pretty_rendered 240 | 241 | # print "test_say: %s" % tropo.RenderJson() 242 | rendered_obj = jsonlib.loads(rendered) 243 | wanted_json = '{"tropo": [{"say": {"value": "Hello, World", "promptLogSecurity": "soppress"}}]}' 244 | wanted_obj = jsonlib.loads(wanted_json) 245 | self.assertEqual(rendered_obj, wanted_obj) 246 | 247 | def test_list_say(self): 248 | """ 249 | Test the "say" Tropo class method, when a list of Strings is passed to it. 250 | """ 251 | 252 | tropo = Tropo() 253 | tropo.say(["Hello, World", "How ya doing?"], promptLogSecurity = "suppredd") 254 | rendered = tropo.RenderJson() 255 | pretty_rendered = tropo.RenderJson(pretty=True) 256 | print "===============test_list_say=================" 257 | print "render json: %s" % pretty_rendered 258 | 259 | # print "test_say: %s" % tropo.RenderJson() 260 | rendered_obj = jsonlib.loads(rendered) 261 | wanted_json = '{"tropo": [{"say": [{"value": "Hello, World", "promptLogSecurity": "suppredd"}, {"value": "How ya doing?", "promptLogSecurity": "suppredd"}]}]}' 262 | wanted_obj = jsonlib.loads(wanted_json) 263 | self.assertEqual(rendered_obj, wanted_obj) 264 | 265 | def test_startRecording(self): 266 | """ 267 | Test the "startRecording" Tropo class method. 268 | """ 269 | 270 | tropo = Tropo() 271 | tropo.startRecording(self.RECORDING_URL) 272 | rendered = tropo.RenderJson() 273 | pretty_rendered = tropo.RenderJson(pretty=True) 274 | print "===============test_startRecording=================" 275 | print "render json: %s" % pretty_rendered 276 | 277 | # print "test_startRecording: %s" % tropo.RenderJson() 278 | rendered_obj = jsonlib.loads(rendered) 279 | wanted_json = '{"tropo": [{"startRecording": {"url": "/receive_recording.py"}}]}' 280 | wanted_obj = jsonlib.loads(wanted_json) 281 | self.assertEqual(rendered_obj, wanted_obj) 282 | 283 | def test_stopRecording(self): 284 | """ 285 | Test the "stopRecording" Tropo class method. 286 | """ 287 | 288 | tropo = Tropo() 289 | tropo.stopRecording() 290 | rendered = tropo.RenderJson() 291 | pretty_rendered = tropo.RenderJson(pretty=True) 292 | print "===============test_stopRecording=================" 293 | print "render json: %s" % pretty_rendered 294 | 295 | # print "test_stopRecording: %s" % tropo.RenderJson() 296 | rendered_obj = jsonlib.loads(rendered) 297 | wanted_json = ' {"tropo": [{"stopRecording": null}]}' 298 | wanted_obj = jsonlib.loads(wanted_json) 299 | self.assertEqual(rendered_obj, wanted_obj) 300 | 301 | def test_transfer(self): 302 | """ 303 | Test the "transfer" Tropo class method. 304 | """ 305 | 306 | tropo = Tropo() 307 | tropo.say ("One moment please.") 308 | tropo.transfer(self.MY_PHONE, callbackUrl="http://192.168.26.88:8080/FileUpload/receiveJson", label="woshitestlidela") 309 | tropo.say("Hi. I am a robot") 310 | rendered = tropo.RenderJson() 311 | pretty_rendered = tropo.RenderJson(pretty=True) 312 | print "===============test_transfer=================" 313 | print "render json: %s" % pretty_rendered 314 | 315 | # print "test_transfer: %s" % tropo.RenderJson() 316 | rendered_obj = jsonlib.loads(rendered) 317 | wanted_json = '{"tropo": [{"say": {"value": "One moment please."}}, {"transfer": {"to": "6021234567", "callbackUrl": "http://192.168.26.88:8080/FileUpload/receiveJson", "label": "woshitestlidela"}}, {"say": {"value": "Hi. I am a robot"}}]}' 318 | wanted_obj = jsonlib.loads(wanted_json) 319 | self.assertEqual(rendered_obj, wanted_obj) 320 | 321 | 322 | if __name__ == '__main__': 323 | """ 324 | Unit tests. 325 | """ 326 | if (0): 327 | TO = "8005551212" 328 | 329 | ID = "foo" 330 | URL = "http://s3.amazonaws.com/xxx_s3_bucket/hello.wav" 331 | 332 | 333 | 334 | tropo = Tropo() 335 | 336 | tropo.ask("[5 digits]", 337 | say = Say("Please enter a 5 digit zip code").json) 338 | 339 | 340 | tropo.call (TO) 341 | tropo.conference(ID) 342 | tropo.hangup() 343 | tropo.message ("Hello, World", TO) 344 | tropo.on(event="continue", 345 | next="http://example.com/weather.py", 346 | say="Please hold.") 347 | 348 | tropo.record(say="Please say something for posterity", 349 | url=URL, 350 | choices = Choices("", terminator="#").json) 351 | tropo.redirect(ID) 352 | tropo.reject(ID) 353 | tropo.startRecording(URL) 354 | tropo.stopRecording() 355 | tropo.transfer(TO) 356 | 357 | tropo.message("Hello, World", 358 | TO, 359 | channel='TEXT', 360 | network='SMS') 361 | 362 | else: 363 | unittest.main() 364 | 365 | 366 | -------------------------------------------------------------------------------- /test/test1.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path = ['..'] + sys.path 3 | from tropo import Choices, MachineDetection, JoinPrompt, LeavePrompt, On, Ask, Say, Tropo 4 | 5 | t = Tropo() 6 | 7 | #CPA 8 | mc = MachineDetection(introduction="THis is a CPA test", voice="Victor").json 9 | t.call("+14071234321", machineDetection=mc) 10 | 11 | #CPA with Boolean value which will detect CPA with 30 seconds of silence. 12 | t.call("+14071234321", machineDetection=True) 13 | 14 | 15 | #Conference with join/leave prompts 16 | jp = JoinPrompt(value="Someone just joined the conference", voice="Victor").json 17 | lp = LeavePrompt(value="Someone just left the conference", voice="Victor").json 18 | t.conference(id="1234", joinPrompt=jp, leavePrompt=lp) 19 | 20 | 21 | whisper = {} 22 | 23 | c = Choices(value="1", mode="dtmf") 24 | ask = Ask(say="Press 1 to accept this call", choices=c).json 25 | whisper["ask"] = ask 26 | 27 | say = Say("You are now being connected to the call").json 28 | whisper["say"] = say 29 | 30 | say1 = Say("http://www.phono.com/audio/holdmusic.mp3").json 31 | whisper["ring"] = say1 32 | 33 | t.transfer(to="+14071234321", on=whisper) 34 | t.on(event="incomplete", say="You are now being disconnected. Goodbye") 35 | 36 | print t.RenderJson() 37 | 38 | -------------------------------------------------------------------------------- /test/tropoTestMachineDetection.py: -------------------------------------------------------------------------------- 1 | from itty import * 2 | from tropo import Tropo, Result, MachineDetection 3 | 4 | @post('/index.json') 5 | def index(request): 6 | 7 | t = Tropo() 8 | 9 | mc = MachineDetection(introduction="This is a test. Please hold while I determine if you are a Machine or Human. Processing. Finished. THank you for your patience.", voice="Victor").json 10 | t.call(to="+14071234321", machineDetection=mc) 11 | 12 | t.on(event="continue", next="/continue.json") 13 | 14 | return t.RenderJson() 15 | 16 | @post("/continue.json") 17 | def index(request): 18 | 19 | r = Result(request.body) 20 | t = Tropo() 21 | 22 | userType = r.getUserType() 23 | 24 | t.say("You are a " + userType) 25 | 26 | return t.RenderJson() 27 | 28 | run_itty(server='wsgiref', host='0.0.0.0', port=8888) -------------------------------------------------------------------------------- /tropo.py: -------------------------------------------------------------------------------- 1 | """ 2 | The TropoPython module. This module implements a set of classes and methods for manipulating the Voxeo Tropo WebAPI. 3 | 4 | Usage: 5 | 6 | ---- 7 | from tropo import Tropo 8 | 9 | tropo = Tropo() 10 | tropo.say("Hello, World") 11 | json = tropo.RenderJson() 12 | ---- 13 | 14 | You can write this JSON back to standard output to get Tropo to perform 15 | the action. For example, on Google Appengine you might write something like: 16 | 17 | handler.response.out.write(json) 18 | 19 | Much of the time, a you will interact with Tropo by examining the Result 20 | object and communicating back to Tropo via the Tropo class methods, such 21 | as "say". In some cases, you'll want to build a class object directly such as in : 22 | 23 | choices = tropo.Choices("[5 digits]").obj 24 | 25 | tropo.ask(choices, 26 | say="Please enter your 5 digit zip code.", 27 | attempts=3, bargein=True, name="zip", timeout=5, voice="dave") 28 | ... 29 | 30 | NOTE: This module requires python 2.5 or higher. 31 | 32 | """ 33 | 34 | try: 35 | import cjson as jsonlib 36 | jsonlib.dumps = jsonlib.encode 37 | jsonlib.loads = jsonlib.decode 38 | except ImportError: 39 | try: 40 | from django.utils import simplejson as jsonlib 41 | except ImportError: 42 | try: 43 | import simplejson as jsonlib 44 | except ImportError: 45 | import json as jsonlib 46 | 47 | class TropoAction(object): 48 | """ 49 | Class representing the base Tropo action. 50 | Two properties are provided in order to avoid defining the same attributes for every action. 51 | """ 52 | @property 53 | def json(self): 54 | return self._dict 55 | 56 | @property 57 | def obj(self): 58 | return {self.action: self._dict} 59 | 60 | class Ask(TropoAction): 61 | """ 62 | Class representing the "ask" Tropo action. Builds an "ask" JSON object. 63 | Class constructor arg: choices, a Choices object 64 | Convenience function: Tropo.ask() 65 | Class constructor options: attempts, bargein, choices, minConfidence, name, recognizer, required, say, timeout, voice 66 | 67 | Request information from the caller and wait for a response. 68 | (See https://www.tropo.com/docs/webapi/ask) 69 | 70 | { "ask": { 71 | "attempts": Integer, 72 | "allowSignals": String or Array, 73 | "bargein": Boolean, 74 | "choices": Object, #Required 75 | "interdigitTimeout": Float, 76 | "minConfidence": Integer, 77 | "name": String, 78 | "recognizer": String, 79 | "required": Boolean, 80 | "say": Object, 81 | "sensitivity": Float, 82 | "speechCompleteTimeout": Float, 83 | "speechIncompleteTimeout": Float, 84 | "timeout": Float, 85 | "voice": String, 86 | 87 | } } 88 | 89 | """ 90 | action = 'ask' 91 | options_array = ['attempts', 'allowSignals', 'bargein', 'choices', 'interdigitTimeout', 'minConfidence', 'name', 'recognizer', 'required', 'say', 'sensitivity', 'speechCompleteTimeout', 'speechIncompleteTimeout', 'timeout', 'voice', 'promptLogSecurity', 'asrLogSecurity', 'maskTemplate'] 92 | 93 | def __init__(self, choices, **options): 94 | self._dict = {} 95 | if (isinstance(choices, basestring)): 96 | self._dict['choices'] = Choices(choices).json 97 | else: 98 | # self._dict['choices'] = choices['choices'] 99 | self._dict['choices'] = choices.json 100 | for opt in self.options_array: 101 | if opt in options: 102 | if ((opt == 'say') and (isinstance(options['say'], basestring))): 103 | self._dict['say'] = Say(options['say']).json 104 | else: 105 | self._dict[opt] = options[opt] 106 | 107 | class Answer(TropoAction): 108 | """ 109 | Class representing the "answer" Tropo action. Builds a "answer" JSON object. 110 | Class constructor arg: headers, a Object 111 | Class constructor options: headers 112 | Convenience function: Tropo.answer() 113 | 114 | (See https://www.tropo.com/docswebapi/answer) 115 | 116 | { "answer": { 117 | "headers": Object } } 118 | """ 119 | action = 'answer' 120 | options_array = [] 121 | 122 | def __init__(self, headers, **options): 123 | self._dict = {'headers': headers} 124 | for opt in self.options_array: 125 | if opt in options: 126 | self._dict[opt] = options[opt] 127 | 128 | 129 | class Call(TropoAction): 130 | """ 131 | Class representing the "call" Tropo action. Builds a "call" JSON object. 132 | Class constructor arg: to, a String 133 | Class constructor options: answerOnMedia, channel, from, headers, name, network, required, timeout, machineDetection 134 | Convenience function: Tropo.call() 135 | 136 | (See https://www.tropo.com/docswebapi/call) 137 | 138 | { "call": { 139 | "to": String or Array,#Required 140 | "answerOnMedia": Boolean, 141 | "allowSignals": String or Array 142 | "channel": string, 143 | "from": string, 144 | "headers": Object, 145 | "name": String, 146 | "network": String, 147 | "required": Boolean, 148 | "timeout": Float. 149 | "machineDetection: Boolean or Object" 150 | "voice": string default "". 151 | "callbackUrl": string. 152 | "promptLogSecurity": string default none. 153 | "label: string" } } 154 | """ 155 | action = 'call' 156 | options_array = ['answerOnMedia', 'allowSignals', 'channel', '_from', 'headers', 'name', 'network', 'required', 'timeout', 'machineDetection', 'voice', 'callbackUrl', 'promptLogSecurity', 'label'] 157 | 158 | def __init__(self, to, **options): 159 | self._dict = {'to': to} 160 | for opt in self.options_array: 161 | if opt in options: 162 | if (opt == "_from"): 163 | self._dict['from'] = options[opt] 164 | else: 165 | self._dict[opt] = options[opt] 166 | 167 | 168 | 169 | class Choices(TropoAction): 170 | """ 171 | Class representing choice made by a user. Builds a "choices" JSON object. 172 | Class constructor options: terminator, mode 173 | 174 | (See https://www.tropo.com/docs/webapi/ask) 175 | """ 176 | action = 'choices' 177 | options_array = ['terminator', 'mode'] 178 | 179 | def __init__(self, value, **options): 180 | self._dict = {'value': value} 181 | for opt in self.options_array: 182 | if opt in options: 183 | self._dict[opt] = options[opt] 184 | 185 | class TransferOnChoices(TropoAction): 186 | """ 187 | Class representing choice made by a user. Builds a "choices" JSON object. 188 | Class constructor options: terminator, mode 189 | 190 | (See https://www.tropo.com/docs/webapi/ask) 191 | """ 192 | action = 'choices' 193 | options_array = ['terminator'] 194 | 195 | def __init__(self, **options): 196 | self._dict = {} 197 | for opt in self.options_array: 198 | if opt in options: 199 | self._dict[opt] = options[opt] 200 | 201 | class Conference(TropoAction): 202 | """ 203 | Class representing the "conference" Tropo action. Builds a "conference" JSON object. 204 | Class constructor arg: id, a String 205 | Convenience function: Tropo.conference() 206 | Class constructor options: mute, name, playTones, required, terminator 207 | 208 | (See https://www.tropo.com/docs/webapi/conference) 209 | 210 | { "conference": { 211 | "id": String,#Required 212 | "allowSignals": String or Array, 213 | "interdigitTimeout":Integer, 214 | "mute": Boolean, 215 | "name": String, 216 | "playTones": Boolean, 217 | "required": Boolean, 218 | "terminator": String, 219 | "joinPrompt": Object, 220 | "promptLogSecurity": string, 221 | "leavePrompt": Object } } 222 | """ 223 | action = 'conference' 224 | options_array = ['allowSignals', 'interdigitTimeout', 'mute', 'name', 'playTones', 'required', 'terminator', 'joinPrompt', 'leavePrompt', 'promptLogSecurity'] 225 | 226 | def __init__(self, id, **options): 227 | self._dict = {'id': id} 228 | for opt in self.options_array: 229 | if opt in options: 230 | self._dict[opt] = options[opt] 231 | 232 | class Hangup(TropoAction): 233 | """ 234 | Class representing the "hangup" Tropo action. Builds a "hangup" JSON object. 235 | Class constructor arg: 236 | Class constructor options: 237 | Convenience function: Tropo.hangup() 238 | 239 | (See https://www.tropo.com/docs/webapi/hangup) 240 | 241 | { "hangup": { } } 242 | """ 243 | action = 'hangup' 244 | 245 | def __init__(self): 246 | self._dict = None 247 | 248 | class JoinPrompt(TropoAction): 249 | """ 250 | Class representing join prompts for the conference method. Builds a "joinPrompt" JSON object. 251 | Class constructor options: value, voice 252 | 253 | (See https://www.tropo.com/docs/webapi/conference) 254 | """ 255 | action = 'joinPrompt' 256 | options_array = ['voice'] 257 | 258 | def __init__(self, value, **options): 259 | self._dict = {'value': value} 260 | for opt in self.options_array: 261 | if opt in options: 262 | self._dict[opt] = options[opt] 263 | 264 | class LeavePrompt(TropoAction): 265 | """ 266 | Class representing leave prompts for the conference method. Builds a "leavePrompt" JSON object. 267 | Class constructor options: value, voice 268 | 269 | (See https://www.tropo.com/docs/webapi/conference) 270 | """ 271 | action = 'leavePrompt' 272 | options_array = ['voice'] 273 | 274 | def __init__(self, value, **options): 275 | self._dict = {'value': value} 276 | for opt in self.options_array: 277 | if opt in options: 278 | self._dict[opt] = options[opt] 279 | 280 | 281 | class MachineDetection(TropoAction): 282 | """ 283 | Class representing machine detection for the call method. Builds a "machineDetection" JSON object. 284 | Class constructor options: introduction, voice 285 | 286 | (See https://www.tropo.com/docs/webapi/call) 287 | """ 288 | action = 'machineDetection' 289 | options_array = ['introduction', 'voice'] 290 | 291 | def __init__(self, introduction, **options): 292 | self._dict = {'introduction': introduction} 293 | for opt in self.options_array: 294 | if opt in options: 295 | self._dict[opt] = options[opt] 296 | 297 | 298 | class Transcription(TropoAction): 299 | """ 300 | Class representing transcription for the record method. Builds a "transcription" JSON object. 301 | Class constructor options: id, url, emailFormat, language 302 | 303 | (See https://www.tropo.com/docs/webapi/record) 304 | """ 305 | action = 'transcription' 306 | options_array = ['id', 'url', 'emailFormat', 'language'] 307 | 308 | def __init__(self, url, **options): 309 | self._dict = {'url': url} 310 | for opt in self.options_array: 311 | if opt in options: 312 | self._dict[opt] = options[opt] 313 | 314 | 315 | class RecordUrlTuple(TropoAction): 316 | """ 317 | Class representing recordURL for the record method. Builds a "recordURL" JSON object. 318 | Class constructor options: url, username, password, method 319 | 320 | (See https://www.tropo.com/docs/webapi/record) 321 | """ 322 | action = 'url' 323 | options_array = ['url', 'username', 'password', 'method'] 324 | 325 | def __init__(self, url, **options): 326 | self._dict = {'url': url} 327 | for opt in self.options_array: 328 | if opt in options: 329 | self._dict[opt] = options[opt] 330 | 331 | 332 | class Message(TropoAction): 333 | """ 334 | Class representing the "message" Tropo action. Builds a "message" JSON object. 335 | Class constructor arg: say_obj, a Say object 336 | Class constructor arg: to, a String 337 | Class constructor options: answerOnMedia, channel, from, name, network, required, timeout, voice 338 | Convenience function: Tropo.message() 339 | 340 | (See https://www.tropo.com/docs/webapi/message) 341 | { "message": { 342 | "say": Object,#Required 343 | "to": String or Array,#Required 344 | "answerOnMedia": Boolean, 345 | "channel": string, 346 | "from": String, 347 | "name": String, 348 | "network": String, 349 | "required": Boolean, 350 | "timeout": Float, 351 | "promptLogSecurity": String, 352 | "voice": String } } 353 | """ 354 | action = 'message' 355 | options_array = ['answerOnMedia', 'channel', '_from', 'name', 'network', 'required', 'timeout', 'voice', 'promptLogSecurity'] 356 | 357 | def __init__(self, say_obj, to, **options): 358 | self._dict = {'say': say_obj['say'], 'to': to} 359 | for opt in self.options_array: 360 | if opt in options: 361 | if (opt == "_from"): 362 | self._dict['from'] = options[opt] 363 | else: 364 | self._dict[opt] = options[opt] 365 | 366 | 367 | class On(TropoAction): 368 | """ 369 | Class representing the "on" Tropo action. Builds an "on" JSON object. 370 | Class constructor arg: event, a String 371 | Class constructor options: name,next,required,say 372 | Convenience function: Tropo.on() 373 | 374 | (See https://www.tropo.com/docs/webapi/on) 375 | 376 | { "on": { 377 | "event": String,#Required 378 | "next": String, 379 | "say": Object 380 | "post": String } } 381 | """ 382 | action = 'on' 383 | options_array = ['next','say', 'post', 'ask'] 384 | 385 | def __init__(self, event, **options): 386 | self._dict = {} 387 | for opt in self.options_array: 388 | if opt in options: 389 | if ((opt == 'say') and (isinstance(options['say'], basestring))): 390 | if('voice' in options): 391 | self._dict['say'] = Say(options['say'], voice=options['voice']).json 392 | else: 393 | self._dict['say'] = Say(options['say']).json 394 | 395 | elif ((opt == 'ask') and (isinstance(options['ask'], basestring))): 396 | if('voice' in options): 397 | self._dict['ask'] = Ask(options['ask'], voice=options['voice']).json 398 | else: 399 | self._dict['ask'] = Ask(options['ask']).json 400 | else: 401 | self._dict[opt] = options[opt] 402 | 403 | self._dict['event'] = event 404 | 405 | class Record(TropoAction): 406 | """ 407 | Class representing the "record" Tropo action. Builds a "record" JSON object. 408 | Class constructor arg: 409 | Class constructor options: attempts, bargein, beep, choices, format, maxSilence, maxTime, method, minConfidence, name, password, required, say, timeout, transcription, url, username 410 | Convenience function: Tropo.record() 411 | 412 | (See https://www.tropo.com/docs/webapi/record) 413 | 414 | { "record": { 415 | "asyncUpload": Boolean, 416 | "allowSignals": Boolean, 417 | "attempts": Integer, 418 | "bargein": Boolean, 419 | "beep": Boolean, 420 | "choices": Object, 421 | "format": String, 422 | "interdigitTimeout": Float, 423 | "maxSilence": Float, 424 | "maxTime": Float, 425 | "method": String, 426 | "name": String, 427 | "password": String, 428 | "required": Boolean, 429 | "say": Object, 430 | "timeout": Float, 431 | "transcription": Array or Object, 432 | "url": String, #Required 433 | "username": String, 434 | "voice": String, 435 | "promptLogSecurity": String, 436 | "sensitivity": String} } 437 | """ 438 | action = 'record' 439 | options_array = ['asyncUpload', 'attempts', 'bargein', 'beep', 'choices', 'format', 'maxSilence', 'maxTime', 'method', 'name', 'password', 'required', 'say', 'timeout', 'transcription', 'username', 'allowSignals', 'voice', 'interdigitTimeout', 'promptLogSecurity', 'sensitivity'] 440 | def __init__(self, url, **options): 441 | self._dict = {'url': url} 442 | for opt in self.options_array: 443 | if opt in options: 444 | if ((opt == 'say') and (isinstance(options['say'], basestring))): 445 | self._dict['say'] = Say(options['say']).json 446 | else: 447 | self._dict[opt] = options[opt] 448 | 449 | class Redirect(TropoAction): 450 | """ 451 | Class representing the "redirect" Tropo action. Builds a "redirect" JSON object. 452 | Class constructor arg: to, a String; name, a String 453 | Class constructor options: required 454 | Convenience function: Tropo.redirect() 455 | 456 | (See https://www.tropo.com/docs/webapi/redirect) 457 | 458 | { "redirect": { 459 | "to": Object,#Required 460 | "name": String,,#Required 461 | "required": Boolean } } 462 | """ 463 | action = 'redirect' 464 | options_array = ['required'] 465 | 466 | def __init__(self, to, name, **options): 467 | self._dict = {'to': to, 'name': name} 468 | #self._dict = {'name': name} 469 | for opt in self.options_array: 470 | if opt in options: 471 | self._dict[opt] = options[opt] 472 | 473 | class Reject(TropoAction): 474 | """ 475 | Class representing the "reject" Tropo action. Builds a "reject" JSON object. 476 | Class constructor arg: 477 | Class constructor options: 478 | Convenience function: Tropo.reject() 479 | 480 | (See https://www.tropo.com/docs/webapi/reject) 481 | 482 | { "reject": null } 483 | """ 484 | action = 'reject' 485 | 486 | def __init__(self): 487 | self._dict = None 488 | 489 | class Say(TropoAction): 490 | """ 491 | Class representing the "say" Tropo action. Builds a "say" JSON object. 492 | Class constructor arg: message, a String, or a List of Strings 493 | Class constructor options: attempts, bargein, choices, minConfidence, name, recognizer, required, say, timeout, voice 494 | Convenience function: Tropo.say() 495 | 496 | (See https://www.tropo.com/docs/webapi/say) 497 | 498 | { "say": { 499 | "allowSignals": String or Array, 500 | "voice": String, 501 | "as": String, 502 | "name": String, 503 | "required": Boolean, 504 | "value": String #Required, 505 | "promptLogSecurity": String 506 | } } 507 | """ 508 | action = 'say' 509 | # added _as because 'as' is reserved 510 | options_array = ['_as', 'name', 'required', 'voice', 'allowSignals', 'promptLogSecurity', 'media'] 511 | 512 | def __init__(self, message, **options): 513 | dict = {} 514 | for opt in self.options_array: 515 | if opt in options: 516 | if (opt == "_as"): 517 | dict['as'] = options[opt] 518 | else: 519 | dict[opt] = options[opt] 520 | self._list = [] 521 | if (isinstance (message, list)): 522 | for mess in message: 523 | new_dict = dict.copy() 524 | new_dict['value'] = mess 525 | self._list.append(new_dict) 526 | else: 527 | dict['value'] = message 528 | self._list.append(dict) 529 | 530 | @property 531 | def json(self): 532 | return self._list[0] if len(self._list) == 1 else self._list 533 | 534 | @property 535 | def obj(self): 536 | return {self.action: self._list[0]} if len(self._list) == 1 else {self.action: self._list} 537 | 538 | class StartRecording(TropoAction): 539 | """ 540 | Class representing the "startRecording" Tropo action. Builds a "startRecording" JSON object. 541 | Class constructor arg: url, a String 542 | Class constructor options: format, method, username, password 543 | Convenience function: Tropo.startRecording() 544 | 545 | (See https://www.tropo.com/docs/webapi/startrecording) 546 | 547 | { "startRecording": { 548 | "asyncUpload":Boolean, 549 | "format": String, 550 | "method": String, 551 | "url": String,#Required 552 | "username": String, 553 | "password": String, 554 | "transcriptionID": String 555 | "transcriptionEmailFormat":String 556 | "transcriptionLanguage":String 557 | "transcriptionOutURI": String} } 558 | """ 559 | action = 'startRecording' 560 | options_array = ['asyncUpload', 'format', 'method', 'username', 'password', 'transcriptionID', 'transcriptionEmailFormat', 'transcriptionLanguage', 'transcriptionOutURI'] 561 | def __init__(self, url, **options): 562 | self._dict = {'url': url} 563 | for opt in self.options_array: 564 | if opt in options: 565 | self._dict[opt] = options[opt] 566 | 567 | class StopRecording(TropoAction): 568 | """ 569 | Class representing the "stopRecording" Tropo action. Builds a "stopRecording" JSON object. 570 | Class constructor arg: 571 | Class constructor options: 572 | Convenience function: Tropo.stopRecording() 573 | 574 | (See https://www.tropo.com/docs/webapi/stoprecording) 575 | { "stopRecording": null } 576 | """ 577 | action = 'stopRecording' 578 | 579 | def __init__(self): 580 | self._dict = None 581 | 582 | class Transfer(TropoAction): 583 | """ 584 | Class representing the "transfer" Tropo action. Builds a "transfer" JSON object. 585 | Class constructor arg: to, a String, or List 586 | Class constructor options: answerOnMedia, choices, from, name, required, on 587 | Convenience function: Tropo.transfer() 588 | 589 | (See https://www.tropo.com/docs/webapi/transfer) 590 | { "transfer": { 591 | "to": String or Array,#Required 592 | "allowSignals":String or Array, 593 | "answerOnMedia": Boolean, 594 | "choices": Object, 595 | "from": String, 596 | "headers": Object, 597 | "interdigitTimeout":Float, 598 | "machineDetection": Boolean or Object 599 | "name": String, 600 | "playTones":Boolean, 601 | "required": Boolean, 602 | "ringRepeat":Integer, 603 | "on": Onject, 604 | "timeout": Float, 605 | "voice": String, 606 | "callbackUrl": String, 607 | "promptLogSecurity": String, 608 | "label": String 609 | } } 610 | """ 611 | action = 'transfer' 612 | options_array = ['answerOnMedia', 'choices', '_from', 'name', 'on', 'required', 'allowSignals', 'headers', 'interdigitTimeout', 'ringRepeat', 'timeout', 'machineDetection', 'playTones', 'voice', 'callbackUrl', 'promptLogSecurity', 'label'] 613 | def __init__(self, to, **options): 614 | self._dict = {'to': to} 615 | for opt in self.options_array: 616 | if opt in options: 617 | if (opt == '_from'): 618 | self._dict['from'] = options['_from'] 619 | else: 620 | self._dict[opt] = options[opt] 621 | 622 | class Wait(TropoAction): 623 | """ 624 | Class representing the "wait" Tropo action. Builds a "wait" JSON object. 625 | Class constructor arg: milliseconds, an Integer 626 | Class constructor options: allowSignals 627 | Convenience function: Tropo.wait() 628 | 629 | (See https://www.tropo.com/docs/webapi/wait) 630 | { "wait": { 631 | "milliseconds": Integer,#Required 632 | "allowSignals": String or Array 633 | """ 634 | 635 | action = 'wait' 636 | options_array = ['allowSignals'] 637 | 638 | def __init__(self, milliseconds, **options): 639 | self._dict = {'milliseconds': milliseconds} 640 | for opt in self.options_array: 641 | if opt in options: 642 | self._dict[opt] = options[opt] 643 | 644 | class GeneralLogSecurity(TropoAction): 645 | """ 646 | Class representing the "generalLogSecurity" Tropo action. Builds a "generalLogSecurity" JSON object. 647 | Class constructor arg: state, an String 648 | Convenience function: Tropo.generalLogSecurity() 649 | 650 | (See https://www.tropo.com/docs/webapi/generallogsecurity) 651 | { "generallogsecurity": { 652 | "state": String,#Required 653 | """ 654 | # action = '' 655 | # state = '' 656 | 657 | def __init__(self, state): 658 | # self._dict = {'generalLogSecurity': state} 659 | self.state = state 660 | 661 | @property 662 | def obj(self): 663 | return {'generalLogSecurity':self.state} 664 | 665 | 666 | class Result(object): 667 | """ 668 | Returned anytime a request is made to the Tropo Web API. 669 | Method: getValue 670 | (See https://www.tropo.com/docs/webapi/result) 671 | 672 | { "result": { 673 | "actions": Array or Object, 674 | "calledId": String, 675 | "callId": String, 676 | "complete": Boolean, 677 | "connectedDuration": Integer, 678 | "duration": Integer, 679 | "error": String, 680 | "sequence": Integer, 681 | "sessionDuration": Integer, 682 | "sessionId": String, 683 | "state": String, 684 | "userType": String} } 685 | """ 686 | options_array = ['actions','complete','error','sequence', 'sessionDuration', 'sessionId', 'state', 'userType', 'connectedDuration', 'duration', 'calledID', 'callId'] 687 | def __init__(self, result_json): 688 | result_data = jsonlib.loads(result_json) 689 | result_dict = result_data['result'] 690 | 691 | for opt in self.options_array: 692 | if result_dict.get(opt, False): 693 | setattr(self, '_%s' % opt, result_dict[opt]) 694 | 695 | def getActions(self): 696 | actions = self._actions 697 | return actions 698 | 699 | def getActionsCount(self): 700 | actions = self._actions 701 | if actions is None: 702 | count = 0 703 | return count 704 | if (type (actions) is list): 705 | count = len(actions) 706 | else: 707 | count = 1 708 | return count 709 | 710 | 711 | 712 | def getValue(self): 713 | """ 714 | Get the value of the previously POSTed Tropo action. 715 | """ 716 | actions = self._actions 717 | 718 | if (type (actions) is list): 719 | dict = actions[0] 720 | else: 721 | dict = actions 722 | return dict.get('value', 'NoValue') 723 | 724 | def getIndexedValue(self, index): 725 | """ 726 | Get the value of the indexed Tropo action. 727 | """ 728 | actions = self._actions 729 | 730 | if (type (actions) is list): 731 | dict = actions[index] 732 | else: 733 | dict = actions 734 | return dict.get('value', 'NoValue') 735 | 736 | def getNamedActionValue(self, name): 737 | """ 738 | Get the value of the named Tropo action. 739 | """ 740 | actions = self._actions 741 | 742 | if (type (actions) is list): 743 | for a in actions: 744 | if a.get('name', 'NoValue') == name: 745 | dict =a 746 | else: 747 | dict = actions 748 | return dict.get('value', 'NoValue') 749 | 750 | 751 | def getUserType(self): 752 | """ 753 | Get the userType of the previously POSTed Tropo action. 754 | """ 755 | userType = self._userType 756 | return userType 757 | 758 | # # **Tue May 17 07:17:38 2011** -- egilchri 759 | 760 | def getInterpretation(self): 761 | """ 762 | Get the value of the previously POSTed Tropo action. 763 | """ 764 | actions = self._actions 765 | 766 | if (type (actions) is list): 767 | dict = actions[0] 768 | else: 769 | dict = actions 770 | return dict.get('interpretation', 'NoValue') 771 | 772 | def getIndexdedInterpretation(self, index): 773 | """ 774 | Get the value of the indexed Tropo action. 775 | """ 776 | actions = self._actions 777 | 778 | if (type (actions) is list): 779 | dict = actions[index] 780 | else: 781 | dict = actions 782 | return dict.get('interpretation', 'NoValue') 783 | 784 | def getNamedActionInterpretation(self, name): 785 | """ 786 | Get the value of the named Tropo action. 787 | """ 788 | actions = self._actions 789 | 790 | if (type (actions) is list): 791 | for a in actions: 792 | if a.get('name', 'NoValue') == name: 793 | dict =a 794 | else: 795 | dict = actions 796 | return dict.get('interpretation', 'NoValue') 797 | 798 | # # **Tue May 17 07:17:38 2011** -- egilchri 799 | 800 | 801 | class Session(object): 802 | """ 803 | Session is the payload sent as an HTTP POST to your web application when a new session arrives. 804 | (See https://www.tropo.com/docs/webapi/session) 805 | 806 | Because 'from' is a reserved word in Python, the session object's 'from' property is called 807 | fromaddress in the Python library 808 | """ 809 | def __init__(self, session_json): 810 | session_data = jsonlib.loads(session_json) 811 | session_dict = session_data['session'] 812 | for key in session_dict: 813 | val = session_dict[key] 814 | if key == "from": 815 | setattr(self, "fromaddress", val) 816 | else: 817 | setattr(self, key, val) 818 | setattr(self, 'dict', session_dict) 819 | 820 | 821 | class Tropo(object): 822 | """ 823 | This is the top level class for all the Tropo web api actions. 824 | The methods of this class implement individual Tropo actions. 825 | Individual actions are each methods on this class. 826 | 827 | Each method takes one or more required arguments, followed by optional 828 | arguments expressed as key=value pairs. 829 | 830 | The optional arguments for these methods are described here: 831 | https://www.tropo.com/docs/webapi/ 832 | """ 833 | def __init__(self): 834 | self._steps = [] 835 | 836 | # # **Sun May 15 21:05:01 2011** -- egilchri 837 | def setVoice(self, voice): 838 | self.voice = voice 839 | 840 | # # end **Sun May 15 21:05:01 2011** -- egilchri 841 | 842 | def ask(self, choices, **options): 843 | """ 844 | Sends a prompt to the user and optionally waits for a response. 845 | Arguments: "choices" is a Choices object 846 | See https://www.tropo.com/docs/webapi/ask 847 | """ 848 | # # **Sun May 15 21:21:29 2011** -- egilchri 849 | 850 | # Settng the voice in this method call has priority. 851 | # Otherwise, we can pick up the voice from the Tropo object, 852 | # if it is set there. 853 | if hasattr (self, 'voice'): 854 | if (not 'voice' in options): 855 | options['voice'] = self.voice 856 | 857 | # # **Sun May 15 21:21:29 2011** -- egilchri 858 | 859 | self._steps.append(Ask(choices, **options).obj) 860 | 861 | 862 | def answer (self, headers, **options): 863 | """ 864 | Places a call or sends an an IM, Twitter, or SMS message. To start a call, use the Session API headers tell Tropo headers launch your code. 865 | Arguments: headers is a String. 866 | Argument: **options is a set of optional keyword arguments. 867 | See https://www.tropo.com/docs/webapi/answer 868 | """ 869 | self._steps.append(Answer (headers, **options).obj) 870 | 871 | def call (self, to, **options): 872 | """ 873 | Places a call or sends an an IM, Twitter, or SMS message. To start a call, use the Session API to tell Tropo to launch your code. 874 | Arguments: to is a String. 875 | Argument: **options is a set of optional keyword arguments. 876 | See https://www.tropo.com/docs/webapi/call 877 | """ 878 | self._steps.append(Call (to, **options).obj) 879 | 880 | def conference(self, id, **options): 881 | """ 882 | This object allows multiple lines in separate sessions to be conferenced together so that the parties on each line can talk to each other simultaneously. 883 | This is a voice channel only feature. 884 | Argument: "id" is a String 885 | Argument: **options is a set of optional keyword arguments. 886 | See https://www.tropo.com/docs/webapi/conference 887 | """ 888 | self._steps.append(Conference(id, **options).obj) 889 | 890 | def hangup(self): 891 | """ 892 | This method instructs Tropo to "hang-up" or disconnect the session associated with the current session. 893 | See https://www.tropo.com/docs/webapi/hangup 894 | """ 895 | self._steps.append(Hangup().obj) 896 | 897 | def message (self, say_obj, to, **options): 898 | """ 899 | A shortcut method to create a session, say something, and hang up, all in one step. This is particularly useful for sending out a quick SMS or IM. 900 | Argument: "say_obj" is a Say object 901 | Argument: "to" is a String 902 | Argument: **options is a set of optional keyword arguments. 903 | See https://www.tropo.com/docs/webapi/message 904 | """ 905 | if isinstance(say_obj, basestring): 906 | say = Say(say_obj).obj 907 | else: 908 | say = say_obj 909 | self._steps.append(Message(say, to, **options).obj) 910 | 911 | def on(self, event, **options): 912 | """ 913 | Adds an event callback so that your application may be notified when a particular event occurs. 914 | Possible events are: "continue", "error", "incomplete" and "hangup". 915 | Argument: event is an event 916 | Argument: **options is a set of optional keyword arguments. 917 | See https://www.tropo.com/docs/webapi/on 918 | """ 919 | if hasattr (self, 'voice'): 920 | if (not 'voice' in options): 921 | options['voice'] = self.voice 922 | 923 | 924 | self._steps.append(On(event, **options).obj) 925 | 926 | def record(self, **options): 927 | """ 928 | Plays a prompt (audio file or text to speech) and optionally waits for a response from the caller that is recorded. 929 | Argument: **options is a set of optional keyword arguments. 930 | See https://www.tropo.com/docs/webapi/record 931 | """ 932 | self._steps.append(Record(**options).obj) 933 | 934 | def redirect(self, id, name, **options): 935 | """ 936 | Forwards an incoming call to another destination / phone number before answering it. 937 | Argument: id is a String 938 | Argument: name is a String 939 | Argument: **options is a set of optional keyword arguments. 940 | See https://www.tropo.com/docs/webapi/redirect 941 | """ 942 | self._steps.append(Redirect(id, name, **options).obj) 943 | 944 | def reject(self): 945 | """ 946 | Allows Tropo applications to reject incoming sessions before they are answered. 947 | See https://www.tropo.com/docs/webapi/reject 948 | """ 949 | self._steps.append(Reject().obj) 950 | 951 | def say(self, message, **options): 952 | """ 953 | When the current session is a voice channel this key will either play a message or an audio file from a URL. 954 | In the case of an text channel it will send the text back to the user via i nstant messaging or SMS. 955 | Argument: message is a string 956 | Argument: **options is a set of optional keyword arguments. 957 | See https://www.tropo.com/docs/webapi/say 958 | """ 959 | #voice = self.voice 960 | # # **Sun May 15 21:21:29 2011** -- egilchri 961 | 962 | # Settng the voice in this method call has priority. 963 | # Otherwise, we can pick up the voice from the Tropo object, 964 | # if it is set there. 965 | if hasattr (self, 'voice'): 966 | if (not 'voice' in options): 967 | options['voice'] = self.voice 968 | # # **Sun May 15 21:21:29 2011** -- egilchri 969 | 970 | self._steps.append(Say(message, **options).obj) 971 | 972 | def startRecording(self, url, **options): 973 | """ 974 | Allows Tropo applications to begin recording the current session. 975 | Argument: url is a string 976 | Argument: **options is a set of optional keyword arguments. 977 | See https://www.tropo.com/docs/webapi/startrecording 978 | """ 979 | self._steps.append(StartRecording(url, **options).obj) 980 | 981 | def stopRecording(self): 982 | """ 983 | Stops a previously started recording. 984 | See https://www.tropo.com/docs/webapi/stoprecording 985 | """ 986 | self._steps.append(StopRecording().obj) 987 | 988 | def transfer(self, to, **options): 989 | """ 990 | Transfers an already answered call to another destination / phone number. 991 | Argument: to is a string 992 | Argument: **options is a set of optional keyword arguments. 993 | See https://www.tropo.com/docs/webapi/transfer 994 | """ 995 | self._steps.append(Transfer(to, **options).obj) 996 | 997 | def wait(self, milliseconds, **options): 998 | """ 999 | Allows the thread to sleep for a given amount of time in milliseconds 1000 | Argument: milliseconds is an Integer 1001 | Argument: **options is a set of optional keyword arguments. 1002 | See https://www.tropo.com/docs/webapi/wait 1003 | """ 1004 | self._steps.append(Wait(milliseconds, **options).obj) 1005 | 1006 | def generalLogSecurity(self, state, **options): 1007 | """ 1008 | Turn on/off all logging on the Tropo platform 1009 | Argument: state is a String 1010 | See https://www.tropo.com/docs/webapi/generallogsecurity 1011 | """ 1012 | self._steps.append(GeneralLogSecurity(state).obj) 1013 | 1014 | 1015 | 1016 | def RenderJson(self, pretty=False): 1017 | """ 1018 | Render a Tropo object into a Json string. 1019 | """ 1020 | steps = self._steps 1021 | topdict = {} 1022 | topdict['tropo'] = steps 1023 | if pretty: 1024 | try: 1025 | json = jsonlib.dumps(topdict, indent=4, sort_keys=False) 1026 | except TypeError: 1027 | json = jsonlib.dumps(topdict) 1028 | else: 1029 | json = jsonlib.dumps(topdict) 1030 | return json 1031 | 1032 | if __name__ == '__main__': 1033 | print """ 1034 | 1035 | This is the Python web API for http://www.tropo.com/ 1036 | 1037 | To run the test suite, please run: 1038 | 1039 | cd test 1040 | python test.py 1041 | 1042 | 1043 | """ 1044 | 1045 | 1046 | --------------------------------------------------------------------------------