Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Savage Garden Hotline

Savage Garden Hotline

A show and tell of my personal project.
I built a hotline that when called, caller will hear my favorite song from Savage Garden, and my twitter bot will tweet about it.
The project is built using Python 3.6. It uses aiohttp library, Twython library, Twilio service, and deployed to Heroku.

Mariatta
PRO

October 19, 2017
Tweet

More Decks by Mariatta

Other Decks in Programming

Transcript

  1. SAVAGE GARDEN HOTLINE
    Python-powered
    @Mariatta
    Code and Coffee YVR - Oct 2017
    +1-604-706-2558

    View Slide

  2. @Mariatta

    View Slide

  3. View Slide

  4. NATIONAL RICK ASTLEY HOTLINE
    Paul Fenwick’s
    Australia: +61-3-8652-1453
    New Zealand: +64-9-886-0565
    UK: +44-11-7325-7425
    USA: +1-760-706-7425
    https://github.com/pjf/rickastley

    View Slide

  5. https://i.ytimg.com/vi/uxsWjeiQ76s/maxresdefault.jpg

    View Slide

  6. https://i.ytimg.com/vi/uxsWjeiQ76s/maxresdefault.jpg

    View Slide

  7. http://images.eil.com/large_image/SAVAGE_GARDEN_CRASH%2BAND%2BBURN-383118.jpg

    View Slide

  8. http://images.eil.com/large_image/SAVAGE_GARDEN_CRASH%2BAND%2BBURN-383118.jpg
    +1-604-706-2558

    View Slide

  9. Building the Hotline
    • Phone Number
    • MP3
    • Webservice
    • Twitter bot

    View Slide

  10. Building the Hotline
    • Phone Number
    • MP3
    • Webservice
    • Twitter bot
    Python 3.6

    View Slide

  11. Getting the Phone Number
    https://www.twilio.com/

    View Slide

  12. View Slide

  13. https://www.twilio.com/docs/quickstart?filter-product=voice

    View Slide

  14. how it works

    View Slide

  15. how it works
    "

    View Slide

  16. how it works

    "

    View Slide

  17. how it works

    "

    View Slide

  18. how it works

    "

    request

    View Slide

  19. how it works

    "

    request
    response

    View Slide

  20. how it works

    "

    request
    response
    mp3

    View Slide

  21. Twilio Python API
    from twilio.twiml.voice_response import VoiceResponse
    def hello_world():
    resp = VoiceResponse()
    resp.say("Hello world.")
    return str(resp)

    View Slide

  22. TwiML


    Hello world.

    View Slide

  23. aiohttp
    from aiohttp import web
    from twilio.twiml.voice_response import VoiceResponse
    async def handle(request):
    """Respond to incoming requests."""
    response = VoiceResponse()
    response.say("Hello world.")
    return web.Response(text=str(response))
    app = web.Application()
    app.router.add_get('/', handle)
    web.run_app(app)
    http://aiohttp.readthedocs.io/
    server.py

    View Slide

  24. web: python3 -m server
    Procfile

    View Slide

  25. web: python3 -m server
    Procfile

    500

    View Slide

  26. web: python3 -m server
    Web process failed to bind to $PORT within
    60 seconds of launch
    Procfile

    500

    View Slide

  27. make it work
    server.py
    import os
    from aiohttp import web
    from twilio.twiml.voice_response import VoiceResponse
    async def handle(request):
    """Respond to incoming requests."""
    response = VoiceResponse()
    response.say("Hello world.")
    return web.Response(text=str(response))
    app = web.Application()
    app.router.add_get('/', handle)
    port = os.environ.get("PORT")
    if port is not not None:
    port = int(port)
    web.run_app(app, port=port)

    View Slide

  28. make it work
    server.py
    import os
    from aiohttp import web
    from twilio.twiml.voice_response import VoiceResponse
    async def handle(request):
    """Respond to incoming requests."""
    response = VoiceResponse()
    response.say("Hello world.")
    return web.Response(text=str(response))
    app = web.Application()
    app.router.add_get('/', handle)
    port = os.environ.get("PORT")
    if port is not not None:
    port = int(port)
    web.run_app(app, port=port)
    200

    View Slide

  29. MP3

    View Slide

  30. MP3

    View Slide

  31. MP3


    View Slide

  32. MP3



    View Slide

  33. MP3



    View Slide

  34. play the music
    server.py
    import os
    from aiohttp import web
    from twilio.twiml.voice_response import VoiceResponse
    async def handle(request):
    """Respond to incoming requests."""
    response = VoiceResponse()
    response.say("Hello world.")
    return web.Response(text=str(response))
    app = web.Application()
    app.router.add_get('/', handle)
    port = os.environ.get("PORT")
    if port is not not None:
    port = int(port)
    web.run_app(app, port=port)

    View Slide

  35. play the music
    server.py
    import os
    from aiohttp import web
    from twilio.twiml.voice_response import VoiceResponse
    async def handle(request):
    """Respond to incoming requests."""
    response = VoiceResponse()
    response.play("https://s3.ca-central-1.amazonaws.com/strangerelationship/
    Savage+Garden+-+Crash+And+Burn.mp3")
    return web.Response(text=str(response))
    app = web.Application()
    app.router.add_get('/', handle)
    port = os.environ.get("PORT")
    if port is not not None:
    port = int(port)
    web.run_app(app, port=port)

    View Slide

  36. play the music
    server.py
    import os
    from aiohttp import web
    from twilio.twiml.voice_response import VoiceResponse
    async def handle(request):
    """Respond to incoming requests."""
    response = VoiceResponse()
    response.play("https://s3.ca-central-1.amazonaws.com/strangerelationship/
    Savage+Garden+-+Crash+And+Burn.mp3")
    return web.Response(text=str(response))
    app = web.Application()
    app.router.add_get('/', handle)
    port = os.environ.get("PORT")
    if port is not not None:
    port = int(port)
    web.run_app(app, port=port)
    200

    View Slide

  37. play the music
    server.py
    import os
    from aiohttp import web
    from twilio.twiml.voice_response import VoiceResponse
    async def handle(request):
    """Respond to incoming requests."""
    response = VoiceResponse()
    response.play("https://s3.ca-central-1.amazonaws.com/strangerelationship/
    Savage+Garden+-+Crash+And+Burn.mp3")
    return web.Response(text=str(response))
    app = web.Application()
    app.router.add_get('/', handle)
    port = os.environ.get("PORT")
    if port is not not None:
    port = int(port)
    web.run_app(app, port=port)
    200

    View Slide

  38. … looks ok?


    https://s3.ca-central-1.amazonaws.com/
    strangerelationship/Savage+Garden+-
    +Crash+And+Burn.mp3

    View Slide

  39. … looks ok?


    https://s3.ca-central-1.amazonaws.com/
    strangerelationship/Savage+Garden+-
    +Crash+And+Burn.mp3

    Content-Type != xml

    View Slide

  40. make it work (for real)
    server.py
    import os
    from aiohttp import web
    from twilio.twiml.voice_response import VoiceResponse
    async def handle(request):
    """Respond to incoming requests."""
    response = VoiceResponse()
    response.play("https://s3.ca-central-1.amazonaws.com/strangerelationship/
    Savage+Garden+-+Crash+And+Burn.mp3")
    return web.Response(text=str(response), content_type='application/xml')
    app = web.Application()
    app.router.add_get('/', handle)
    port = os.environ.get("PORT")
    if port is not not None:
    port = int(port)
    web.run_app(app, port=port)

    View Slide

  41. make it work (for real)
    server.py
    import os
    from aiohttp import web
    from twilio.twiml.voice_response import VoiceResponse
    async def handle(request):
    """Respond to incoming requests."""
    response = VoiceResponse()
    response.play("https://s3.ca-central-1.amazonaws.com/strangerelationship/
    Savage+Garden+-+Crash+And+Burn.mp3")
    return web.Response(text=str(response), content_type='application/xml')
    app = web.Application()
    app.router.add_get('/', handle)
    port = os.environ.get("PORT")
    if port is not not None:
    port = int(port)
    web.run_app(app, port=port)
    200

    View Slide

  42. make it work (for real)
    server.py
    import os
    from aiohttp import web
    from twilio.twiml.voice_response import VoiceResponse
    async def handle(request):
    """Respond to incoming requests."""
    response = VoiceResponse()
    response.play("https://s3.ca-central-1.amazonaws.com/strangerelationship/
    Savage+Garden+-+Crash+And+Burn.mp3")
    return web.Response(text=str(response), content_type='application/xml')
    app = web.Application()
    app.router.add_get('/', handle)
    port = os.environ.get("PORT")
    if port is not not None:
    port = int(port)
    web.run_app(app, port=port)
    200

    View Slide

  43. the GET request
    at=info method=GET path="/?
    Called=%2B16047062558&ToState=BC&CallerCountry=CA&Direction=inbound&CallerStat
    e=BC&ToZip=&To=%2B16047062558&CallerZip=&ToCountry=CA&ApiVersion=2010-04-01&Ca
    lledZip=&CalledCity=Vancouver&CalledCountry=CA&CallerCity=NEW+WESTMINSTER&From
    Country=CA&ToCity=Vancouver&FromCity=NEW+WESTMINSTER&CalledState=BC&FromZip=&F
    romState=BC" host=crashandburnhotline.herokuapp.com
    request_id=e162d82f-94dc-43be-8b5b-f6d864161bb5 fwd="54.144.213.39" dyno=web.1
    connect=0ms service=269ms status=200 bytes=370 protocol=https

    View Slide

  44. the GET request
    at=info method=GET path="/?
    Called=%2B16047062558&ToState=BC&CallerCountry=CA&Direction=inbound&CallerStat
    e=BC&ToZip=&To=%2B16047062558&CallerZip=&ToCountry=CA&ApiVersion=2010-04-01&Ca
    lledZip=&CalledCity=Vancouver&CalledCountry=CA&CallerCity=NEW+WESTMINSTER&From
    Country=CA&ToCity=Vancouver&FromCity=NEW+WESTMINSTER&CalledState=BC&FromZip=&F
    romState=BC" host=crashandburnhotline.herokuapp.com
    request_id=e162d82f-94dc-43be-8b5b-f6d864161bb5 fwd="54.144.213.39" dyno=web.1
    connect=0ms service=269ms status=200 bytes=370 protocol=https

    View Slide

  45. twitter bot
    Register an APP at https://dev.twitter.com/apps
    Access level: Read and write
    Receive API token & secret

    View Slide

  46. Twython
    server.py
    from twython import Twython
    twitter = Twython(
    os.getenv("TW_API_KEY"),
    os.getenv("TW_API_SECRET"),
    os.getenv("TW_ACCESS_TOKEN"),
    os.getenv("TW_TOKEN_SECRET"))
    )
    async def handle(request):
    """Respond to incoming requests."""
    response = VoiceResponse()
    response.play("https://s3.ca-central-1.amazonaws.com/
    strangerelationship/Savage+Garden+-+Crash+And+Burn.mp3")
    twitter.update_status(status="Thanks for dialing in.")
    return web.Response(text=str(response), content_type='application/xml')
    https://twython.readthedocs.io/

    View Slide

  47. @SG_hotline_bot

    View Slide

  48. THANK YOU!
    @Mariatta
    Code and Coffee YVR - Oct 2017
    Savage Garden Hotline: +1-604-706-2558
    https://github.com/Mariatta/crash_and_burn_hotline/

    View Slide