Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/venv
__pycache__
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2022 Bandwidth Samples

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
74 changes: 73 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,73 @@
# messaging-auto-respond-python
# Auto-Respond to SMS

<a href="http://dev.bandwidth.com/docs/messaging/quickStart">
<img src="./icon-messaging.svg" title="Messaging Quick Start Guide" alt="Messaging Quick Start Guide"/>
</a>

# Table of Contents

* [Description](#description)
* [Pre-Requisites](#pre-requisites)
* [Running the Application](#running-the-application)
* [Environmental Variables](#environmental-variables)
* [Callback URLs](#callback-urls)
* [Ngrok](#ngrok)

# Description

This app automatically responds to texts sent to the associated Bandwidth number.To use this app, you must check the "Use multiple callback URLs" box on the application page in Dashboard. Then in Dashboard, set the INBOUND CALLBACK to `/callbacks/inbound/messaging` and the STATUS CALLBACK to `/callbacks/outbound/messaging/status`. The same can be accomplished via the Dashboard API by setting InboundCallbackUrl and OutboundCallbackUrl respectively.

Inbound callbacks are sent notifying you of a received message on a Bandwidth number, this app sends a custom response to those messages based on their content. Outbound callbacks are status updates for messages sent from a Bandwidth number, this app has a dedicated response for each type of status update.

# Pre-Requisites

In order to use the Bandwidth API users need to set up the appropriate application at the [Bandwidth Dashboard](https://dashboard.bandwidth.com/) and create API tokens.

To create an application log into the [Bandwidth Dashboard](https://dashboard.bandwidth.com/) and navigate to the `Applications` tab. Fill out the **New Application** form selecting the service (Messaging or Voice) that the application will be used for. All Bandwidth services require publicly accessible Callback URLs, for more information on how to set one up see [Callback URLs](#callback-urls).

For more information about API credentials see our [Account Credentials](https://dev.bandwidth.com/docs/account/credentials) page.

# Running the Application

To install the required packages for this app, run the command:

```sh
pip install -r requirements.txt
```

Use the following command to run the application:

```sh
uvicorn main:app --reload # app will automatically reload upon changes.
```

# Environmental Variables

The sample app uses the below environmental variables.

```sh
BW_ACCOUNT_ID # Your Bandwidth Account Id
BW_USERNAME # Your Bandwidth API Username
BW_PASSWORD # Your Bandwidth API Password
BW_NUMBER # The Bandwidth phone number involved with this application
BW_MESSAGING_APPLICATION_ID # Your Messaging Application Id created in the dashboard
```

# Callback URLs

For a detailed introduction, check out our [Bandwidth Messaging Callbacks](https://dev.bandwidth.com/docs/messaging/webhooks) page.

Below are the callback paths:
* `/callbacks/outbound/messaging/status` For Outbound Status Callbacks
* `/callbacks/inbound/messaging` For Inbound Message Callbacks

## Ngrok

A simple way to set up a local callback URL for testing is to use the free tool [ngrok](https://ngrok.com/).
After you have downloaded and installed `ngrok` run the following command to open a public tunnel to your port (`8000`). `8000` is the default for FastAPI.

```cmd
ngrok http 8000
```

You can view your public URL at `http://127.0.0.1:4040` after ngrok is running. You can also view the status of the tunnel and requests/responses here.
1 change: 1 addition & 0 deletions icon-messaging.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
108 changes: 108 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import os
import re

# TODO: Needs to be changed when the SDK becomes python package.
import sys
sys.path.insert(0, 'C:/Users/ckoegel/Documents/sdks/bandwidth_python')
import bandwidth_python
from bandwidth_python.api.messages_api import MessagesApi
from bandwidth_python.model.message_request import MessageRequest
from bandwidth_python.model.bandwidth_callback_message import BandwidthCallbackMessage
# ---------------------------------------------------

from fastapi import FastAPI, Request
from pydantic import BaseModel


BW_ACCOUNT_ID = os.environ.get('BW_ACCOUNT_ID')
BW_USERNAME = os.environ.get('BW_USERNAME')
BW_PASSWORD = os.environ.get('BW_PASSWORD')
BW_NUMBER = os.environ.get('BW_NUMBER')
BW_MESSAGING_APPLICATION_ID = os.environ.get('BW_MESSAGING_APPLICATION_ID')


class CreateBody(BaseModel): # model for the received json body to create a message
to: str
text: str


configuration = bandwidth_python.Configuration( # TODO: # Configure HTTP basic authorization: httpBasic
username=BW_USERNAME,
password=BW_PASSWORD
)


api_client = bandwidth_python.ApiClient(configuration) # TODO: package name
messages_api_instance = MessagesApi(api_client) # TODO: package name


app = FastAPI()

@app.post('/callbacks/outbound/messaging/status') # This URL handles outbound message status callbacks.
async def handle_outbound_status(request: Request):
status_body_array = await request.json()
status_body = status_body_array[0]
if status_body['type'] == "message-sending":
print("message-sending type is only for MMS.")
elif status_body['type'] == "message-delivered":
print("Your message has been handed off to the Bandwidth's MMSC network, but has not been confirmed at the downstream carrier.")
elif status_body['type'] == "message-failed":
print("For MMS and Group Messages, you will only receive this callback if you have enabled delivery receipts on MMS.")
else:
print("Message type does not match endpoint. This endpoint is used for message status callbacks only.")

return 200


@app.post('/callbacks/inbound/messaging') # This URL handles inbound message callbacks.
async def handle_inbound(request: Request):
inbound_body_array = await request.json()
inbound_body = BandwidthCallbackMessage._new_from_openapi_data(inbound_body_array[0])
print(inbound_body.description)
if inbound_body.type == "message-received":
print(f"To: {inbound_body.message.to[0]}\nFrom: {inbound_body.message._from}\nText: {inbound_body.message.text}")

auto_reponse_message = auto_response(inbound_body.message.text)

message_body = MessageRequest(
to=[inbound_body.message._from],
_from=BW_NUMBER,
application_id=BW_MESSAGING_APPLICATION_ID,
text=auto_reponse_message
)
response = messages_api_instance.create_message(
account_id=BW_ACCOUNT_ID,
message_request=message_body,
_return_http_data_only=False
)

print("\nSending Auto Response")
print(f"To: {inbound_body.message._from}\nFrom: {inbound_body.message.to[0]}\nText: {auto_reponse_message}")
else:
print("Message type does not match endpoint. This endpoint is used for inbound messages only.\nOutbound message status callbacks should be sent to /callbacks/outbound/messaging/status.")

return 200


response_map = {
"stop": "STOP: OK, you'll no longer receive messages from us.",
"quit": "QUIT: OK, you'll no longer receive messages from us.",
"help": "Valid words are: STOP, QUIT, HELP, and INFO. Reply STOP or QUIT to opt out.",
"info": "INFO: This is the test responder service. Reply STOP or QUIT to opt out.",
"default": "Please respond with a valid word. Reply HELP for help."
}


def auto_response(inbound_text):
command = re.search(r"^(.\S+)$", inbound_text).group(1).lower()
if command in response_map.keys():
map_val = response_map[command]
else:
map_val = response_map['default']

response = "[Auto Response] " + map_val
return response


if __name__ == '__main__':
app.run(host='0.0.0.0')