From a9e3ad82b4a2559293628bc973e3810a17c3c99b Mon Sep 17 00:00:00 2001 From: sarveshpatil-plivo Date: Thu, 9 Jul 2026 01:50:32 +0530 Subject: [PATCH 1/2] feat(plivo): add Plivo SMS app Add a Plivo Messages app (plivo/1.0.0) that sends SMS from a workflow, mirroring the structure of the existing twilio app. Contract (Plivo Messages API): - POST https://api.plivo.com/v1/Account/{AUTH_ID}/Message/ - HTTP Basic auth (Auth ID / Auth Token) - JSON body {src, dst, text, type: "sms"} - multiple recipients joined into one dst with "<" - send-only; no inbound webhook, so no signature verification needed Intentional divergences from the twilio app (hardening, not copied bugs): - success is derived from the HTTP status (2xx, including Plivo's 202 on queue) instead of hardcoded True, so a failed send reports success=False - guard against an empty recipient list before calling the API - defensive timeout parse (fall back to the default on bad/zero input) - send the SMS text verbatim (no ast.literal_eval); dropped the now-unused checkbody helper - returns.example documents the summary wrapper (the raw Plivo body is under results[0].body) Co-Authored-By: Claude Opus 4.8 (1M context) --- plivo/1.0.0/Dockerfile | 27 +++++ plivo/1.0.0/README.md | 22 ++++ plivo/1.0.0/api.yaml | 108 ++++++++++++++++++++ plivo/1.0.0/requirements.txt | 1 + plivo/1.0.0/src/app.py | 192 +++++++++++++++++++++++++++++++++++ 5 files changed, 350 insertions(+) create mode 100644 plivo/1.0.0/Dockerfile create mode 100644 plivo/1.0.0/README.md create mode 100644 plivo/1.0.0/api.yaml create mode 100644 plivo/1.0.0/requirements.txt create mode 100644 plivo/1.0.0/src/app.py diff --git a/plivo/1.0.0/Dockerfile b/plivo/1.0.0/Dockerfile new file mode 100644 index 00000000..9bbc5110 --- /dev/null +++ b/plivo/1.0.0/Dockerfile @@ -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 diff --git a/plivo/1.0.0/README.md b/plivo/1.0.0/README.md new file mode 100644 index 00000000..7813d0a2 --- /dev/null +++ b/plivo/1.0.0/README.md @@ -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. diff --git a/plivo/1.0.0/api.yaml b/plivo/1.0.0/api.yaml new file mode 100644 index 00000000..2a496596 --- /dev/null +++ b/plivo/1.0.0/api.yaml @@ -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,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 diff --git a/plivo/1.0.0/requirements.txt b/plivo/1.0.0/requirements.txt new file mode 100644 index 00000000..bd6f2345 --- /dev/null +++ b/plivo/1.0.0/requirements.txt @@ -0,0 +1 @@ +requests==2.32.4 diff --git a/plivo/1.0.0/src/app.py b/plivo/1.0.0/src/app.py new file mode 100644 index 00000000..4107b2da --- /dev/null +++ b/plivo/1.0.0/src/app.py @@ -0,0 +1,192 @@ +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: + 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 ONE request fails, summary is marked as failed + if not one_response["success"]: + summary["success"] = False + + # surface a non-2xx status code on the summary + try: + code = int(one_response["status"]) + except (ValueError, TypeError): + code = 0 + if not (200 <= code < 300): + 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: + # Shouldn't be used if authorization headers exist + if "Authorization" in parsed_headers: + #print("Found authorization - skipping username & pw") + pass + else: + 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": "200", + "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"} + + request = requests.post(url, headers=parsed_headers, auth=auth, json=payload, timeout=timeout) + response = self.prepare_response(request) + 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)) + authorization_key = action.get("authorization") + current_execution_id = action.get("execution_id") + + 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() From ffa3e94e24e6a85db2c70dda07ca5965ea33c7a0 Mon Sep 17 00:00:00 2001 From: sarveshpatil-plivo Date: Sat, 11 Jul 2026 00:29:46 +0530 Subject: [PATCH 2/2] Harden Plivo app and use the current Plivo logo - wrap the send in try/except so network errors are reported, not raised - drop ghost/no-op auth branch and unused run() reads carried from the template - narrow the bare except; reflect the actual HTTP status (Plivo returns 202) - replace the placeholder icon with the current Plivo brand logo --- plivo/1.0.0/api.yaml | 2 +- plivo/1.0.0/src/app.py | 33 +++++++++++---------------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/plivo/1.0.0/api.yaml b/plivo/1.0.0/api.yaml index 2a496596..765b2d3c 100644 --- a/plivo/1.0.0/api.yaml +++ b/plivo/1.0.0/api.yaml @@ -105,4 +105,4 @@ actions: 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,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 +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= diff --git a/plivo/1.0.0/src/app.py b/plivo/1.0.0/src/app.py index 4107b2da..76dc7707 100644 --- a/plivo/1.0.0/src/app.py +++ b/plivo/1.0.0/src/app.py @@ -88,7 +88,7 @@ def prepare_response(self, request): jsondata = request.text try: jsondata = json.loads(jsondata) - except: + except (ValueError, TypeError): pass return { @@ -111,17 +111,10 @@ def prepare_response(self, request): def summarize_responses(self, one_response, summary): summary["results"].append(one_response) - # if ONE request fails, summary is marked as failed if not one_response["success"]: summary["success"] = False - # surface a non-2xx status code on the summary - try: - code = int(one_response["status"]) - except (ValueError, TypeError): - code = 0 - if not (200 <= code < 300): - summary["status"] = one_response["status"] + summary["status"] = one_response["status"] return summary @@ -132,14 +125,9 @@ def Send_SMS(self, url, headers="", username="", password="", body="", From="", parsed_headers = self.splitheaders(headers) parsed_headers["User-Agent"] = "Shuffle Automation" - auth=None - if username or password: - # Shouldn't be used if authorization headers exist - if "Authorization" in parsed_headers: - #print("Found authorization - skipping username & pw") - pass - else: - auth = requests.auth.HTTPBasicAuth(username, password) + auth = None + if (username or password) and "Authorization" not in parsed_headers: + auth = requests.auth.HTTPBasicAuth(username, password) if not timeout: timeout = 5 @@ -153,7 +141,7 @@ def Send_SMS(self, url, headers="", username="", password="", body="", From="", summary = { "success": True, - "status": "200", + "status": None, "url": url, "results": [] } @@ -166,8 +154,11 @@ def Send_SMS(self, url, headers="", username="", password="", body="", From="", payload = {"src": From, "dst": dst, "text": body, "type": "sms"} - request = requests.post(url, headers=parsed_headers, auth=auth, json=payload, timeout=timeout) - response = self.prepare_response(request) + 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) @@ -179,8 +170,6 @@ def run(request): action = request.get_json() print(action) print(type(action)) - authorization_key = action.get("authorization") - current_execution_id = action.get("execution_id") if action and "name" in action and "app_name" in action: PLIVO.run(action)