Skip to content
Open
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
27 changes: 27 additions & 0 deletions plivo/1.0.0/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Base our app image off of the WALKOFF App SDK image
FROM frikky/shuffle:app_sdk as base

# We're going to stage away all of the bloat from the build tools so lets create a builder stage
FROM base as builder

# Install all alpine build tools needed for our pip installs
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev

# Install all of our pip packages in a single directory that we can copy to our base image later
RUN mkdir /install
WORKDIR /install
COPY requirements.txt /requirements.txt
RUN pip install --prefix="/install" -r /requirements.txt

# Switch back to our base image and copy in all of our built packages and source code
FROM base
COPY --from=builder /install /usr/local
COPY src /app
RUN apk add curl

# Install any binary dependencies needed in our final image
# RUN apk --no-cache add --update my_binary_dependency

# Finally, lets run our app!
WORKDIR /app
CMD python app.py --log-level DEBUG
22 changes: 22 additions & 0 deletions plivo/1.0.0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Plivo

An app for sending SMS through the Plivo Messages API. Use it in a workflow to text an on-call responder when a monitor fires, a case is created, or an alert changes state.

## Requirements
A Plivo account with an Auth ID and Auth Token, and a sender (a Plivo phone number, short code, or where allowed an alphanumeric sender). Find your Auth ID and Auth Token in the Plivo console at https://cx.plivo.com.

## Authentication
1. url - the Messages API URL for your account, `https://api.plivo.com/v1/Account/YOUR_AUTH_ID/Message/`
2. username - your Plivo Auth ID
3. password - your Plivo Auth Token

## Actions
1. Send_SMS - sends an SMS through the Plivo Messages API.
- From - the sender ID
- To - one receiver, or a comma separated list of receivers sent as a single request
- body - the message text

## Notes
- A successful call means the message is queued, not yet delivered. Delivery status arrives on a separate callback URL if you configure one.
- Each call sends and bills a new message, so guard against accidental retries in a loop.
- The action returns a summary. The raw Plivo response, including message_uuid, is under results[0].body.
108 changes: 108 additions & 0 deletions plivo/1.0.0/api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
walkoff_version: 1.0.0
app_version: 1.0.0
name: plivo
description: Send SMS from Shuffle through Plivo.
tags:
- Communication
- SMS
categories:
- Communication
contact_info:
name: "Sarvesh Patil"
url: https://github.com/sarveshpatil-plivo
email: "sarvesh.patil@plivo.com"
authentication:
required: true
parameters:
- name: url
description: Plivo Messages API URL for your account.
example: "https://api.plivo.com/v1/Account/YOUR_AUTH_ID/Message/"
required: true
schema:
type: string
- name: username
description: Your Plivo Auth ID
multiline: false
required: true
example: "Auth ID"
schema:
type: string
- name: password
description: Your Plivo Auth Token
multiline: false
required: true
example: "*****"
schema:
type: string
- name: headers
description: Headers to use
multiline: true
required: false
example: "Content-Type: application/json"
schema:
type: string
actions:
- name: Send_SMS
description: Sends an SMS through the Plivo Messages API. Each call sends and bills a new message.
parameters:
- name: url
description: Plivo Messages API URL for your account
multiline: false
example: "https://api.plivo.com/v1/Account/YOUR_AUTH_ID/Message/"
required: true
schema:
type: string
- name: headers
description: Headers to use
multiline: true
required: false
example: "Content-Type: application/json"
schema:
type: string
- name: username
description: Your Plivo Auth ID
multiline: false
required: true
example: "Auth ID"
schema:
type: string
- name: password
description: Your Plivo Auth Token
multiline: false
required: true
example: "*****"
schema:
type: string
- name: timeout
description: Add a timeout (in seconds) for the request
multiline: false
required: false
example: "10"
schema:
type: string
- name: body
description: The message to send.
multiline: true
example: "Your monitored service is back up."
required: true
schema:
type: string
- name: From
description: The sender ID, a Plivo phone number, short code, or where allowed an alphanumeric text sender.
multiline: false
example: "+14150000002"
required: true
schema:
type: string
- name: To
description: The receiver phone number, or a comma separated list of numbers, sent as one Plivo request.
multiline: false
example: "+14150000001,+14150000003"
required: true
schema:
type: string
returns:
schema:
type: string
example: '{"success": true, "status": "200", "url": "https://api.plivo.com/v1/Account/YOUR_AUTH_ID/Message/", "results": [{"success": true, "status": 202, "url": "https://api.plivo.com/v1/Account/YOUR_AUTH_ID/Message/", "headers": {}, "body": {"message": "message(s) queued", "message_uuid": ["db3ce55a-7f1d-11e1-8ea7-1231380bc196"], "api_id": "db342550-7f1d-11e1-8ea7-1231380bc196"}, "cookies": {}}]}'
large_image: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABmJLR0QA/wD/AP+gvaeTAAAFLUlEQVR4nO3be4hUVRzA8e+uq2aZEUJGplhhJJRgFGlZgpJWaoUEvahIKKJ/JIiSHhD91T8R9qD3w4L6J3rZy6gMekgmFEW0ZtIDK60kpNXSdXf648yw4+w5Z+/szo47y/cDF3TOOb9779zfPefec2ZBkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJUvO0NXl/64GNwGPAjiHEGQ9sBg6v+bwbOA/4o/z/NuBG4CqgF3gOWDuE/WqIeoESsI9wMWYPMs6kcpzYdnJVvbsi5asGuU8N0RjiF+wTYDn19UZFEqAd2B0p/32I56FBGkf6opWALYS7c0KBWEUS4OhEeQ9hCFGTTSCfAJVtB3APMDkTq+gQ8EOk/MsGnY/qNJFiCVDZ/gWeB06JxCqaAEuAvVVl/wDnNva0VNRY4CvqS4IScAB4GZhbFatoAgDMBG4HbgVOaPxpqR5thLvyPepPhF5gTjlOPQkwkowHTgUWA8uA04GjDukRNdF1hLuxYjbwOKGrL5oEl5XbFk2Aj4FtNduWquNYGynfBnxI+q1kLGEuI9bu6USbxcArQFfkeLuBDcD1QEeifcubAHQCj0TKphAe+nbR+ATYm6gzv1x+XybOfOKWZtrcXVN3OuHiFk3w74FzEvttWVOAN+nrxlcT7qJaEwmzdp00LwFOJLwWxurEkhXgxUT9/8rnWjGPMCNZ71C3D1iZ2HfLmQf8Rv+T/IiDv6xqCxNthiMBIExPx+r8Sf9EPYJ4N14CnqyqdxKDu/iVrQe4NPH9tJxJwEv0jXc30X98bSfMBn5Gc4cAgBWZWBfVHOc1mbqVae0xhLmGwV78yrab9E3SctoJizf313w+HrgW+I5iX8pwJEAHsD1R74Wa4303UW99VZ2VA5zDRuAO4BbgNdJDUAl4glHkSmBG+d+TCQ9MO6nvrlhebt/IBAC4N1Gvi9DtAxxD6MFi9S6oivV55thiC1GLSA8re6v2PypMA9aQPuHc9inhQREanwDTCBNOsbpXlOusSpR30jekTaVvxbN2yy1D35w5nxWZdi0n94Qf23qBdwgPhtUanQAAbyTqvl4u35Qov6EqxqLMcZ2R+V4OI31T3Jlp13KKvOuXCOPiOuDMRJzhSIDU+/1+4Czid/YuDv5RytWZ45pI3teJdg8P0G5I2ocz+CB0AQ8S5uyXA180cd/vAj9HPh9LeBiMzQw+Skiyiq5M/EkD7D81JZyLOWQjJQF2Ep6MpxPG2l8OwTH0AE8lymZGPusmJEC1XzPxl2bKTiOce8z2TLuWkxoCltUZZziGAIBjCV1+kWEq9lDXAfyVqL+9HL/WOMLEWGo/s5LfQgOMlB6g+1AfQNkOwsNgEQ9FPjtAWPSJmUqY6LqEvhnGucAHwIJEm28IcySjRqoHWFJnnOHqAQDOz8SubBsy7Wcw8Apnd4E6JeDi7LfQACOlBxhJ3ge2DlDngUzZT4TVzZwOwqtfzqsU741aRiv0AAC3ZeJvZeAbpw14NhNjoG0zTZoBtAeIe4awvBuzhjAnkFMirAmsLlC31jrChNKeOtu1hNQT8uI64xyZiFPi4Fe2PYk6RX50EVv3/5uBJ3RqnU2xH4V0ApfT5L/Wavafhi0gLKpU6wXeJjwU1eNC+l+M/YQ7qHLXLaT/z8t7gLcIP7zIOY7+ifIjoXsejFmE1905wPGEZeOdwLeE899UPjZJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJo9b/qNt0VLkK4aMAAAAASUVORK5CYII=
1 change: 1 addition & 0 deletions plivo/1.0.0/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
requests==2.32.4
181 changes: 181 additions & 0 deletions plivo/1.0.0/src/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import json
import requests

from walkoff_app_sdk.app_base import AppBase

class PLIVO(AppBase):
__version__ = "1.0.0"
app_name = "plivo"

def __init__(self, redis, logger, console_logger=None):
print("INIT")
"""
Each app should have this __init__ to set up Redis and logging.
:param redis:
:param logger:
:param console_logger:
"""
super().__init__(redis, logger, console_logger)

def splitheaders(self, headers):
parsed_headers = {}
if headers:
split_headers = headers.split("\n")
self.logger.info(split_headers)
for header in split_headers:
if ": " in header:
splititem = ": "
elif ":" in header:
splititem = ":"
elif "= " in header:
splititem = "= "
elif "=" in header:
splititem = "="
else:
self.logger.info("Skipping header %s as its invalid" % header)
continue

splitheader = header.split(splititem)
if len(splitheader) == 2:
parsed_headers[splitheader[0]] = splitheader[1]
else:
self.logger.info("Skipping header %s with split %s cus only one item" % (header, splititem))
continue

return parsed_headers

def fix_url(self, url):
# Random bugs seen by users
if "hhttp" in url:
url = url.replace("hhttp", "http")

if "http:/" in url and not "http://" in url:
url = url.replace("http:/", "http://", -1)
if "https:/" in url and not "https://" in url:
url = url.replace("https:/", "https://", -1)
if "http:///" in url:
url = url.replace("http:///", "http://", -1)
if "https:///" in url:
url = url.replace("https:///", "https://", -1)
if not "http://" in url and not "http" in url:
url = f"http://{url}"

return url

def return_file(self, requestdata):
filedata = {
"filename": "response.txt",
"data": requestdata,
}
fileret = self.set_files([filedata])
if len(fileret) == 1:
return {"success": True, "file_id": fileret[0]}

return fileret

def prepare_response(self, request):
try:
parsedheaders = {}
for key, value in request.headers.items():
parsedheaders[key] = value

cookies = {}
if request.cookies:
for key, value in request.cookies.items():
cookies[key] = value


jsondata = request.text
try:
jsondata = json.loads(jsondata)
except (ValueError, TypeError):
pass

return {
"success": 200 <= int(request.status_code) < 300,
"status": request.status_code,
"url": request.url,
"headers": parsedheaders,
"body": jsondata,
"cookies":cookies,
}
except Exception as e:
print(f"[WARNING] Failed in request: {e}")
return {
"success": False,
"status": "XXX",
"error": request.text
}


def summarize_responses(self, one_response, summary):
summary["results"].append(one_response)

if not one_response["success"]:
summary["success"] = False

summary["status"] = one_response["status"]

return summary


def Send_SMS(self, url, headers="", username="", password="", body="", From="", To="", timeout=5):
url = self.fix_url(url)

parsed_headers = self.splitheaders(headers)
parsed_headers["User-Agent"] = "Shuffle Automation"

auth = None
if (username or password) and "Authorization" not in parsed_headers:
auth = requests.auth.HTTPBasicAuth(username, password)

if not timeout:
timeout = 5
else:
try:
timeout = int(timeout)
except (ValueError, TypeError):
timeout = 5
if timeout <= 0:
timeout = 5

summary = {
"success": True,
"status": None,
"url": url,
"results": []
}

# Plivo takes every receiver in one request, joined with "<"
dst = "<".join(receiver.strip() for receiver in To.split(",") if receiver.strip())
if not dst:
empty = {"success": False, "status": "400", "error": "No valid recipient in 'To'"}
return json.dumps(self.summarize_responses(empty, summary))

payload = {"src": From, "dst": dst, "text": body, "type": "sms"}

try:
request = requests.post(url, headers=parsed_headers, auth=auth, json=payload, timeout=timeout)
response = self.prepare_response(request)
except Exception as e:
response = {"success": False, "status": "XXX", "error": str(e)}
summary = self.summarize_responses(response, summary)

return json.dumps(summary)


# Run the actual thing after we've checked params
def run(request):
print("Starting cloud!")
action = request.get_json()
print(action)
print(type(action))

if action and "name" in action and "app_name" in action:
PLIVO.run(action)
return f'Attempting to execute function {action["name"]} in app {action["app_name"]}'
else:
return f'Invalid action'

if __name__ == "__main__":
PLIVO.run()