aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound')
-rw-r--r--.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/__init__.py13
-rw-r--r--.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/app.py45
-rw-r--r--.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/config.py65
-rw-r--r--.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/config.yml34
-rw-r--r--.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/parse.py100
-rw-r--r--.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/default_data.txt58
-rw-r--r--.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/default_data_with_attachments.txt0
-rw-r--r--.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/raw_data.txt57
-rw-r--r--.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/raw_data_with_attachments.txt298
-rw-r--r--.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/send.py61
10 files changed, 731 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/__init__.py b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/__init__.py
new file mode 100644
index 00000000..85ab4d0b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/__init__.py
@@ -0,0 +1,13 @@
+"""
+Inbound Parse helper
+--------------------
+This is a standalone module to help get you started consuming and processing
+Inbound Parse data. It provides a Flask server to listen for Inbound Parse
+POSTS, and utilities to send sample data to the server.
+
+See README.txt for detailed usage instructions, including quick-start guides
+for local testing and Heroku deployment.
+"""
+
+from .config import * # noqa
+from .parse import * # noqa
diff --git a/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/app.py b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/app.py
new file mode 100644
index 00000000..0d443590
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/app.py
@@ -0,0 +1,45 @@
+"""Receiver module for processing SendGrid Inbound Parse messages.
+
+See README.txt for usage instructions."""
+try:
+ from config import Config
+except:
+ # Python 3+, Travis
+ from sendgrid.helpers.inbound.config import Config
+
+try:
+ from parse import Parse
+except:
+ # Python 3+, Travis
+ from sendgrid.helpers.inbound.parse import Parse
+
+from flask import Flask, request, render_template
+import os
+
+app = Flask(__name__)
+config = Config()
+
+
+@app.route('/', methods=['GET'])
+def index():
+ """Show index page to confirm that server is running."""
+ return render_template('index.html')
+
+
+@app.route(config.endpoint, methods=['POST'])
+def inbound_parse():
+ """Process POST from Inbound Parse and print received data."""
+ parse = Parse(config, request)
+ # Sample processing action
+ print(parse.key_values())
+ # Tell SendGrid's Inbound Parse to stop sending POSTs
+ # Everything is 200 OK :)
+ return "OK"
+
+
+if __name__ == '__main__':
+ # Be sure to set config.debug_mode to False in production
+ port = int(os.environ.get("PORT", config.port))
+ if port != config.port:
+ config.debug = False
+ app.run(host='0.0.0.0', debug=config.debug_mode, port=port)
diff --git a/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/config.py b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/config.py
new file mode 100644
index 00000000..06ca683c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/config.py
@@ -0,0 +1,65 @@
+"""Set up credentials (.env) and application variables (config.yml)"""
+import os
+import yaml
+
+
+class Config(object):
+ """All configuration for this app is loaded here"""
+
+ def __init__(self, **opts):
+ if os.environ.get('ENV') != 'prod': # We are not in Heroku
+ self.init_environment()
+
+ """Allow variables assigned in config.yml available the following variables
+ via properties"""
+ self.path = opts.get(
+ 'path', os.path.abspath(os.path.dirname(__file__))
+ )
+ with open('{0}/config.yml'.format(self.path)) as stream:
+ config = yaml.load(stream, Loader=yaml.FullLoader)
+ self._debug_mode = config['debug_mode']
+ self._endpoint = config['endpoint']
+ self._host = config['host']
+ self._keys = config['keys']
+ self._port = config['port']
+
+ @staticmethod
+ def init_environment():
+ """Allow variables assigned in .env available using
+ os.environ.get('VAR_NAME')"""
+ base_path = os.path.abspath(os.path.dirname(__file__))
+ env_path = '{0}/.env'.format(base_path)
+ if os.path.exists(env_path):
+ with open(env_path) as f:
+ lines = f.readlines()
+ for line in lines:
+ var = line.strip().split('=')
+ if len(var) == 2:
+ os.environ[var[0]] = var[1]
+
+ @property
+ def debug_mode(self):
+ """Flask debug mode - set to False in production."""
+ return self._debug_mode
+
+ @property
+ def endpoint(self):
+ """Endpoint to receive Inbound Parse POSTs."""
+ return self._endpoint
+
+ @property
+ def host(self):
+ """URL that the sender will POST to."""
+ return self._host
+
+ @property
+ def keys(self):
+ """Incoming Parse fields to parse. For reference, see
+ https://sendgrid.com/docs/Classroom/Basics/Inbound_Parse_Webhook/setting_up_the_inbound_parse_webhook.html
+ """
+ return self._keys
+
+ @property
+ def port(self):
+ """Port to listen on."""
+ return self._port
diff --git a/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/config.yml b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/config.yml
new file mode 100644
index 00000000..d1a131ae
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/config.yml
@@ -0,0 +1,34 @@
+# Incoming Parse endpoint
+endpoint: '/inbound'
+
+# Port to listen on
+port: 5000
+
+# Flask debug mode
+# Set this to False in production
+# Reference: http://flask.pocoo.org/docs/0.11/api/#flask.Flask.run
+debug_mode: True
+
+# List all Incoming Parse fields you would like parsed
+# Reference: https://sendgrid.com/docs/Classroom/Basics/Inbound_Parse_Webhook/setting_up_the_inbound_parse_webhook.html
+keys:
+ - from
+ - attachments
+ - headers
+ - text
+ - envelope
+ - to
+ - html
+ - sender_ip
+ - attachment-info
+ - subject
+ - dkim
+ - SPF
+ - charsets
+ - content-ids
+ - spam_report
+ - spam_score
+ - email
+
+# URL that the sender will POST to
+host: 'http://127.0.0.1:5000/inbound'
diff --git a/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/parse.py b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/parse.py
new file mode 100644
index 00000000..49627a12
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/parse.py
@@ -0,0 +1,100 @@
+"""Parse data received from the SendGrid Inbound Parse webhook"""
+import base64
+import email
+import mimetypes
+from six import iteritems
+from werkzeug.utils import secure_filename
+
+
+class Parse(object):
+
+ def __init__(self, config, request):
+ self._keys = config.keys
+ self._request = request
+ request.get_data(as_text=True)
+ self._payload = request.form
+ self._raw_payload = request.data
+
+ def key_values(self):
+ """
+ Return a dictionary of key/values in the payload received from
+ the webhook
+ """
+ key_values = {}
+ for key in self.keys:
+ if key in self.payload:
+ key_values[key] = self.payload[key]
+ return key_values
+
+ def get_raw_email(self):
+ """
+ This only applies to raw payloads:
+ https://sendgrid.com/docs/Classroom/Basics/Inbound_Parse_Webhook/setting_up_the_inbound_parse_webhook.html#-Raw-Parameters
+ """
+ if 'email' in self.payload:
+ raw_email = email.message_from_string(self.payload['email'])
+ return raw_email
+ else:
+ return None
+
+ def attachments(self):
+ """Returns an object with:
+ type = file content type
+ file_name = the name of the file
+ contents = base64 encoded file contents"""
+ attachments = None
+ if 'attachment-info' in self.payload:
+ attachments = self._get_attachments(self.request)
+ # Check if we have a raw message
+ raw_email = self.get_raw_email()
+ if raw_email is not None:
+ attachments = self._get_attachments_raw(raw_email)
+ return attachments
+
+ def _get_attachments(self, request):
+ attachments = []
+ for _, filestorage in iteritems(request.files):
+ attachment = {}
+ if filestorage.filename not in (None, 'fdopen', '<fdopen>'):
+ filename = secure_filename(filestorage.filename)
+ attachment['type'] = filestorage.content_type
+ attachment['file_name'] = filename
+ attachment['contents'] = base64.b64encode(filestorage.read())
+ attachments.append(attachment)
+ return attachments
+
+ def _get_attachments_raw(self, raw_email):
+ attachments = []
+ counter = 1
+ for part in raw_email.walk():
+ attachment = {}
+ if part.get_content_maintype() == 'multipart':
+ continue
+ filename = part.get_filename()
+ if not filename:
+ ext = mimetypes.guess_extension(part.get_content_type())
+ if not ext:
+ ext = '.bin'
+ filename = 'part-%03d%s' % (counter, ext)
+ counter += 1
+ attachment['type'] = part.get_content_type()
+ attachment['file_name'] = filename
+ attachment['contents'] = part.get_payload(decode=False)
+ attachments.append(attachment)
+ return attachments
+
+ @property
+ def keys(self):
+ return self._keys
+
+ @property
+ def request(self):
+ return self._request
+
+ @property
+ def payload(self):
+ return self._payload
+
+ @property
+ def raw_payload(self):
+ return self._raw_payload
diff --git a/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/default_data.txt b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/default_data.txt
new file mode 100644
index 00000000..7c3ce6be
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/default_data.txt
@@ -0,0 +1,58 @@
+--xYzZY
+Content-Disposition: form-data; name="headers"
+
+MIME-Version: 1.0
+Received: by 0.0.0.0 with HTTP; Wed, 10 Aug 2016 18:10:13 -0700 (PDT)
+From: Example User <test@example.com>
+Date: Wed, 10 Aug 2016 18:10:13 -0700
+Subject: Inbound Parse Test Data
+To: inbound@inbound.example.com
+Content-Type: multipart/alternative; boundary=001a113df448cad2d00539c16e89
+
+--xYzZY
+Content-Disposition: form-data; name="dkim"
+
+{@sendgrid.com : pass}
+--xYzZY
+Content-Disposition: form-data; name="to"
+
+inbound@inbound.example.com
+--xYzZY
+Content-Disposition: form-data; name="html"
+
+<html><body><strong>Hello Twilio SendGrid!</body></html>
+
+--xYzZY
+Content-Disposition: form-data; name="from"
+
+Example User <test@example.com>
+--xYzZY
+Content-Disposition: form-data; name="text"
+
+Hello Twilio SendGrid!
+
+--xYzZY
+Content-Disposition: form-data; name="sender_ip"
+
+0.0.0.0
+--xYzZY
+Content-Disposition: form-data; name="envelope"
+
+{"to":["inbound@inbound.example.com"],"from":"test@example.com"}
+--xYzZY
+Content-Disposition: form-data; name="attachments"
+
+0
+--xYzZY
+Content-Disposition: form-data; name="subject"
+
+Testing non-raw
+--xYzZY
+Content-Disposition: form-data; name="charsets"
+
+{"to":"UTF-8","html":"UTF-8","subject":"UTF-8","from":"UTF-8","text":"UTF-8"}
+--xYzZY
+Content-Disposition: form-data; name="SPF"
+
+pass
+--xYzZY-- \ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/default_data_with_attachments.txt b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/default_data_with_attachments.txt
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/default_data_with_attachments.txt
diff --git a/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/raw_data.txt b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/raw_data.txt
new file mode 100644
index 00000000..12f64cb4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/raw_data.txt
@@ -0,0 +1,57 @@
+--xYzZY
+Content-Disposition: form-data; name="dkim"
+
+{@sendgrid.com : pass}
+--xYzZY
+Content-Disposition: form-data; name="email"
+
+MIME-Version: 1.0
+Received: by 0.0.0.0 with HTTP; Wed, 10 Aug 2016 14:44:21 -0700 (PDT)
+From: Example User <test@example.com>
+Date: Wed, 10 Aug 2016 14:44:21 -0700
+Subject: Inbound Parse Test Raw Data
+To: inbound@inbound.inbound.com
+Content-Type: multipart/alternative; boundary=001a113ee97c89842f0539be8e7a
+
+--001a113ee97c89842f0539be8e7a
+Content-Type: text/plain; charset=UTF-8
+
+Hello Twilio SendGrid!
+
+--001a113ee97c89842f0539be8e7a
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+<html><body><strong>Hello Twilio SendGrid!</body></html>
+
+--001a113ee97c89842f0539be8e7a--
+
+--xYzZY
+Content-Disposition: form-data; name="to"
+
+inbound@inbound.inbound.com
+--xYzZY
+Content-Disposition: form-data; name="from"
+
+Example User <test@example.com>
+--xYzZY
+Content-Disposition: form-data; name="sender_ip"
+
+0.0.0.0
+--xYzZY
+Content-Disposition: form-data; name="envelope"
+
+{"to":["inbound@inbound.inbound.com"],"from":"test@example.com"}
+--xYzZY
+Content-Disposition: form-data; name="subject"
+
+Testing with Request.bin
+--xYzZY
+Content-Disposition: form-data; name="charsets"
+
+{"to":"UTF-8","subject":"UTF-8","from":"UTF-8"}
+--xYzZY
+Content-Disposition: form-data; name="SPF"
+
+pass
+--xYzZY-- \ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/raw_data_with_attachments.txt b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/raw_data_with_attachments.txt
new file mode 100644
index 00000000..058fd8a6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/sample_data/raw_data_with_attachments.txt
@@ -0,0 +1,298 @@
+--xYzZY
+Content-Disposition: form-data; name="dkim"
+
+{@sendgrid.com : pass}
+--xYzZY
+Content-Disposition: form-data; name="email"
+
+MIME-Version: 1.0
+Received: by 0.0.0.0 with HTTP; Mon, 15 Aug 2016 13:47:21 -0700 (PDT)
+From: Example User <test@example.com>
+Date: Mon, 15 Aug 2016 13:47:21 -0700
+Subject: Inbound Parse Test Raw Data with Attachment
+To: inbound@inbound.inbound.com
+Content-Type: multipart/mixed; boundary=001a1140ffb6f4fc63053a2257e2
+
+--001a1140ffb6f4fc63053a2257e2
+Content-Type: multipart/alternative; boundary=001a1140ffb6f4fc5f053a2257e0
+
+--001a1140ffb6f4fc5f053a2257e0
+Content-Type: text/plain; charset=UTF-8
+
+Hello Twilio SendGrid!
+
+--001a1140ffb6f4fc5f053a2257e0
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+<html><body><strong>Hello Twilio SendGrid!</body></html>
+
+--001a1140ffb6f4fc5f053a2257e0--
+
+--001a1140ffb6f4fc63053a2257e2
+Content-Type: image/jpeg; name="SendGrid.jpg"
+Content-Disposition: attachment; filename="SendGrid.jpg"
+Content-Transfer-Encoding: base64
+X-Attachment-Id: f_irwihell0
+
+/9j/4AAQSkZJRgABAQABLAEsAAD/4QDKRXhpZgAATU0AKgAAAAgABwESAAMA
+AAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAEx
+AAIAAAARAAAAcgEyAAIAAAAUAAAAhIdpAAQAAAABAAAAmAAAAAAAAAEsAAAA
+AQAAASwAAAABUGl4ZWxtYXRvciAzLjQuNAAAMjAxNjowODoxMSAxNjowODo1
+NwAAA6ABAAMAAAABAAEAAKACAAQAAAABAAACEqADAAQAAAABAAACFQAAAAD/
+4Qn2aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVn
+aW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4
+bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAg
+Q29yZSA1LjQuMCI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53
+My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3Jp
+cHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2Jl
+LmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9l
+bGVtZW50cy8xLjEvIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxNi0wOC0xMVQxNjow
+ODo1NyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBJbWFnZVJlYWR5Ij4gPGRj
+OnN1YmplY3Q+IDxyZGY6QmFnLz4gPC9kYzpzdWJqZWN0PiA8L3JkZjpEZXNj
+cmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ICAgICAgICAgICA8P3hwYWNrZXQgZW5kPSJ3Ij8+AP/tADhQaG90b3Nob3Ag
+My4wADhCSU0EBAAAAAAAADhCSU0EJQAAAAAAENQdjNmPALIE6YAJmOz4Qn7/
+wAARCAIVAhIDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQF
+BgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJx
+FDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdI
+SUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKj
+pKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx
+8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QA
+tREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHB
+CSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldY
+WVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq
+srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6
+/9sAQwAcHBwcHBwwHBwwRDAwMERcRERERFx0XFxcXFx0jHR0dHR0dIyMjIyM
+jIyMqKioqKioxMTExMTc3Nzc3Nzc3Nzc/9sAQwEiJCQ4NDhgNDRg5pyAnObm
+5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm
+5ubm/90ABAAi/9oADAMBAAIRAxEAPwDpKKKKACiiigAooooAKKKKACiiigAo
+oooAKKKKACiiigAooooAKKKKACiiigDEnkkEzgMep71F5sn94/nTp/8AXP8A
+U1FXWloeZJu7H+bJ/eP50ebJ/eP50yinYm7H+bJ/eP50ebJ/eP50yiiwXY/z
+ZP7x/OjzZP7x/OmUUWC7H+bJ/eP50ebJ/eP50yiiwXY/zZP7x/OjzZP7x/Om
+UUWC7H+bJ/eP50ebJ/eP50yiiwXY/wA2T+8fzo82T+8fzplFFgux/myf3j+d
+Hmyf3j+dMoosF2P82T+8fzo82T+8fzplFFgux/myf3j+dHmyf3j+dMoosF2I
+0sufvt+dJ5sv99vzNMbrSVVkbpuxJ5sv99vzNHmy/wB9vzNR0U7Id2SebL/f
+b8zR5sv99vzNR0UWQXZJ5sv99vzNHmy/32/M1HRRZBdknmy/32/M0ebL/fb8
+zUdFFkF2SebL/fb8zR5sv99vzNR0UWQXZJ5sv99vzNHmy/32/M1HRRZBdknm
+y/32/M0ebL/fb8zUdFFkF2SebL/fb8zR5sv99vzNR0UWQXZJ5sv99vzNHmy/
+32/M1HRRZBdknmy/32/M0ebL/fb8zUdFFkF2SebL/fb8zR5sv99vzNR0UWQX
+Z//Q6SiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAo
+oooAwZ/9c/1NRVLP/rn+pqKuxbHly3YUUUUyQooooAKKKKACiiigAooooAKK
+KKACiiigAooooAKKKKACiiigCJutJSt1pKo3WwUUUUDCiiigAooooAKKKKAC
+iiigAooooAKKKKACiiigAooooAKKKKACiiigD//R6SiiigAooooAKKKKACii
+igAooooAKKKKACiiigAooooAKKKKACiiigAooooAwZ/9c/1NRVLP/rn+pqKu
+xbHly3YUUUUyQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig
+CJutJSt1pKo3WwUUUUDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooo
+oAKKKKACiiigD//S6SiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA
+KKKKACiiigAooooAwZ/9c/1NRVLP/rn+pqKuxbHly3YUUUUyQooooAKKKKAC
+iiigAooooAKKKKACiiigAooooAKKKKACiiigCJutJSt1pKo3WwUUUUDCiiig
+AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//T6SiiigAo
+oooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAwZ/9c/1N
+RVLP/rn+pqKuxbHly3YUUUUyQooooAKKKKACiiigAooooAKKKKACiiigAooo
+oAKKKKACiiigCJutJSt1pKo3WwUUUUDCiiigAooooAKKKKACiiigAooooAKK
+KKACiiigAooooAKKKKACiiigD//U6SiiigAooooAKKKKACiiigAooooAKKKK
+ACiiigAooooAKKKKACiiigAooooAwZ/9c/1NRVLP/rn+pqKuxbHly3YUUUUy
+QooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigCJutJSt1pKo3
+WwUUUUDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig
+D//V6SiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAo
+oooAwZ/9c/1NRVLP/rn+pqKuxbHly3YUUUUyQooooAKKKKACiiigAooooAKK
+KKACiiigAooooAKKKKACiiigCJutJSt1pKo3WwUUUUDCiiigAooooAKKKKAC
+iiigAooooAKKKKACiiigAooooAKKKKACiiigD//W6SiiigAooooAKKKKACii
+igAooooAKKKKACiiigAooooAKKKKACiiigAooooAwZ/9c/1NRVLP/rn+pqKu
+xbHly3YUUUUyQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig
+CJutJSt1pKo3WwUUUUDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooo
+oAKKKKACiiigD//X6SiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA
+KKKKACiiigAooooAwZ/9c/1NRVLP/rn+pqKuxbHly3YUUUUyQooooAKKKKAC
+iiigAooooAKKKKACiiigAooooAKKKKACiiigCJutJSt1pKo3WwUUUUDCiiig
+AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q6SiiigAo
+oooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAwZ/9c/1N
+RVLP/rn+pqKuxbHly3YUUUUyQooooAKKKKACiiigAooooAKKKKACiiigAooo
+oAKKKKACiiigCJutJSt1pKo3WwUUUUDCiiigAooooAKKKKACiiigAooooAKK
+KKACiiigAooooAKKKKACiiigD//R6SiiigAooooAKKKKACiiigAooooAKKKK
+ACiiigAooooAKKKKACiiigAooooAwZ/9c/1NRVLP/rn+pqKuxbHly3YUUUUy
+QooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigCJutJSt1pKo3
+WwUUUUDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig
+D//S2i7560m9/WmnqaStbHPdj97+tG9/WmUUWC7H739aN7+tMoosF2P3v60b
+39aZRRYLsfvf1o3v60yiiwXY/e/rRvf1plFFgux+9/Wje/rTKKLBdj97+tG9
+/WmUUWC7H739aN7+tMoosF2P3v60b39aZRRYLsfvf1o3v60yiiwXZlzEmVvr
+UeTT5f8AWt9ajrdbHI9xcmjJpKKYhcmjJpKKAFyaMmkooAXJoyaSigBcmjJp
+KKAFyaMmkooAXJoyaSigBcmjJpKKAFyaMmkooAXJoyaSigBwAPWlwKB0paBX
+YmBRgUtFAXYmBRgUtFAXYmBRgUtFAXYmBRgUtFAXYmBRgUtFAXYmBRgUtFAX
+YmBRgUtFAXYmBRgUtFAXYmBRgUtFAXYmBRgUtFAXYmBRgUtFAXZ//9PXPU0l
+KeppK1OYKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKK
+AMqX/Wt9ajqSX/Wt9ajroRyvcKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFF
+FABRRRQAUUUUAFFFFADx0paQdKWgkKKKKACiiigAooooAKKKKACiiigAoooo
+AKKKKACiiigAooooAKKKKACiiigD/9TXPU0lKeppK1OYKKKKACiiigAooooA
+KKKKACiiigAooooAKKKKACiiigAooooAKKKKAMqX/Wt9ajqSX/Wt9ajroRyv
+cKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFADx0paQ
+dKWgkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACi
+iigD/9XXPU0lKeppK1OYKKKKACiiigAooooAKKKKACiiigAooooAKKKKACii
+igAooooAKKKKAMqX/Wt9ajqSX/Wt9ajroRyvcKKKKBBRRRQAUUUUAFFFFABR
+RRQAUUUUAFFFFABRRRQAUUUUAFFFFADx0paQdKWgkKKKKACiiigAooooAKKK
+KACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9bXPU0lKeppK1OYKKKK
+ACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAMqX/Wt9ajqS
+X/Wt9ajroRyvcKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU
+AFFFFADx0paQdKWgkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigA
+ooooAKKKKACiiigD/9fXPU0lKeppK1OYKKKKACiiigAooooAKKKKACiiigAo
+oooAKKKKACiiigAooooAKKKKAMqX/Wt9ajqSX/Wt9ajroRyvcKKKKBBRRRQA
+UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFADx0paQdKWgkKKKKACi
+iigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9DXPU0l
+KeppK1OYKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKK
+AMqX/Wt9ajqSX/Wt9ajroRyvcKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFF
+FABRRRQAUUUUAFFFFADx0paQdKWgkKKKKACiiigAooooAKKKKACiiigAoooo
+AKKKKACiiigAooooAKKKKACiiigD/9HXPU0lKeppK1OYKKKKACiiigAooooA
+KKKKACiiigAooooAKKKKACiiigAooooAKKKKAMqX/Wt9ajqSX/Wt9ajroRyv
+cKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFADx0paQ
+dKWgkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACi
+iigD/9LXPU0lKeppK1OYKKKKACiiigAooooAKKKKACiiigAooooAKKKKACii
+igAooooAKKKKAMqX/Wt9ajqSX/Wt9ajroRyvcKKKKBBRRRQAUUUUAFFFFABR
+RRQAUUUUAFFFFABRRRQAUUUUAFFFFADx0paQdKWgkKKKKACiiigAooooAKKK
+KACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9PXPU0lKeppK1OYKKKK
+ACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAMqX/Wt9ajqS
+X/Wt9ajroRyvcKKKKBBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU
+AFFFFADx0paQdKWgkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigA
+ooooAKKKKACiiigD/9TXPU0lKeppK1OYKKKKACiiigAooooAKKKKACiiigAo
+oooAKKKKACiiigAooooAKKKKAMqX/Wt9ajqSX/Wt9ajroRyvcKKKKBBRRRQA
+UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFADx0paQdKWgkKKKKACi
+iigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9WV7mUO
+Rnv6U37TN6/pUMn32+pptdyiuxxNssfaZvX9KPtM3r+lV6KOVdguyx9pm9f0
+o+0zev6VXoo5V2C7LH2mb1/Sj7TN6/pVeijlXYLssfaZvX9KPtM3r+lV6KOV
+dguyx9pm9f0o+0zev6VXoo5V2C7LH2mb1/Sj7TN6/pVeijlXYLssfaZvX9KP
+tM3r+lV6KOVdguyx9pm9f0o+0zev6VXoo5V2C7LH2mb1/Sj7TN6/pVeijlXY
+LssfaZvX9KPtM3r+lV6KOVdguzRSFJFEjdTyad9mi96fD/ql+lS1g27lcqK/
+2aL3o+zRe9WKKXMw5V2K/wBmi96Ps0XvViijmYcq7Ff7NF70fZoverFFHMw5
+V2K/2aL3o+zRe9WKKOZhyrsV/s0XvR9mi96sUUczDlXYr/Zovej7NF71Yoo5
+mHKuxX+zRe9H2aL3qxRRzMOVdiv9mi96Ps0XvViijmYcq7Ff7NF70fZoverF
+FHMw5V2K/wBmi96Ps0XvViijmYcq7BHZwlcnP50/7FB6H86ni+4KkqHJ9zdU
+422Kn2KD0P50fYoPQ/nVuilzvuP2cexU+xQeh/Oj7FB6H86t0Uc77h7OPYqf
+YoPQ/nR9ig9D+dW6KOd9w9nHsVPsUHofzo+xQeh/OrdFHO+4ezj2Kn2KD0P5
+0fYoPQ/nVuijnfcPZx7FT7FB6H86PsUHofzq3RRzvuHs49ip9ig9D+dH2KD0
+P51boo533D2cexU+xQeh/Oj7FB6H86t0Uc77h7OPYqfYoPQ/nR9ig9D+dW6K
+Od9w9nHsVPsUHofzo+xQeh/OrdFHO+4ezj2Kn2KD0P50fYoPQ/nVuijnfcPZ
+x7H/1mSffb6mm06T77fU02u9HCwooooAKKKKACiiigAooooAKKKKACiiigAo
+oooAKKKKACiiigAooooA1of9Uv0qWoof9Uv0qWuZ7mqCiiikAUUUUAFFFFAB
+RRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBai+4KkqOL7gqSs3udEdgooo
+pDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9dk
+n32+pptOk++31NNrvRwsKKKKACiiigAooooAKKKKACiiigAooooAKKKKACii
+igAooooAKKKKANaH/VL9KlqKH/VL9Klrme5qgooopAFFFFABRRRQAUUUUAFF
+FFABRRRQAUUUUAFFFFABRRRQAUUUUAWovuCpKji+4KkrN7nRHYKKKKQwoooo
+AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//QZJ99vqab
+TpPvt9TTa70cLCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKK
+ACiiigDWh/1S/Spaih/1S/Spa5nuaoKKKKQBRRRQAUUUUAFFFFABRRRQAUUU
+UAFFFFABRRRQAUUUUAFFFFAFqL7gqSo4vuCpKze50R2CiiikMKKKKACiiigA
+ooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/0WSffb6mm06T77fU
+02u9HCwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA
+1of9Uv0qWoof9Uv0qWuZ7mqCiiikAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQ
+AUUUUAFFFFABRRRQBai+4KkqOL7gqSs3udEdgooopDCiiigAooooAKKKKACi
+iigAooooAKKKKACiiigAooooAKKKKACiiigD/9Jkn32+pptOk++31NNrvRws
+KKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKANaH/VL9
+KlqKH/VL9Klrme5qgooopAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAB
+RRRQAUUUUAWovuCpKji+4KkrN7nRHYKKKKQwooooAKKKKACiiigAooooAKKK
+KACiiigAooooAKKKKACiiigAooooA//TZJ99vqabTpPvt9TTa70cLCiiigAo
+oooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDWh/1S/Spaih/1
+S/Spa5nuaoKKKKQBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFF
+FFAFqL7gqSo4vuCpKze50R2CiiikMKKKKACiiigAooooAKKKKACiiigAoooo
+AKKKKACiiigAooooAKKKKAP/1GSffb6mm06T77fU02u9HCwooooAKKKKACii
+igAooooAKKKKACiiigAooooAKKKKACiiigAooooA1of9Uv0qWoof9Uv0qWuZ
+7mqCiiikAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBai+
+4KkqOL7gqSs3udEdgooopDCiiigAooooAKKKKACiiigAooooAKKKKACiiigA
+ooooAKKKKACiiigD/9Vkn32+pptOk++31NNrvRwsKKKKACiiigAooooAKKKK
+ACiiigAooooAKKKKACiiigAooooAKKKKANaH/VL9KlqKH/VL9Klrme5qgooo
+pAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAWovuCpKji+
+4KkrN7nRHYKKKKQwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACi
+iigAooooA//WZJ99vqabTpPvt9TTa70cLCiiigAooooAKKKKACiiigAooooA
+KKKKACiiigAooooAKKKKACiiigDWh/1S/Spaih/1S/Spa5nuaoKKKKQBRRRQ
+AUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAFqL7gqSo4vuCpKze5
+0R2CiiikMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKK
+KAP/12Sffb6mm06T77fU02u9HCwooooAKKKKACiiigAooooAKKKKACiiigAo
+oooAKKKKACiiigAooooA1of9Uv0qWoof9Uv0qWuZ7mqCiiikAUUUUAFFFFAB
+RRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBai+4KkqOL7gqSs3udEdgooo
+pDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9DU
+OnxMSdx5pP7Oi/vNWjRV+0l3I5I9jO/s6L+81H9nRf3mrRoo9pLuHs49jO/s
+6L+81H9nRf3mrRoo9pLuHs49jO/s6L+81H9nRf3mrRoo9pLuHs49jO/s6L+8
+1H9nRf3mrRoo9pLuHs49jO/s6L+81H9nRf3mrRoo9pLuHs49jO/s6L+81H9n
+Rf3mrRoo9pLuHs49jO/s6L+81H9nRf3mrRoo9pLuHs49jO/s6L+81H9nRf3m
+rRoo9pLuHs49jO/s6L+81H9nRf3mrRoo9pLuHs49jO/s6L+81H9nRf3mrRoo
+9pLuHs49iBLdUUKCeKd5K+pqWip5mPlRF5K+po8lfU1LRRdhyoi8lfU0eSvq
+aloouw5UReSvqaPJX1NS0UXYcqIvJX1NHkr6mpaKLsOVEXkr6mjyV9TUtFF2
+HKiLyV9TR5K+pqWii7DlRF5K+po8lfU1LRRdhyoi8lfU0eSvqaloouw5UReS
+vqaPJX1NS0UXYcqIvJX1NHkr6mpaKLsOVCKu0YFLRRSKCiiigAooooAKKKKA
+CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9k=
+
+--001a1140ffb6f4fc63053a2257e2--
+
+--xYzZY
+Content-Disposition: form-data; name="to"
+
+inbound@inbound.inbound.com
+--xYzZY
+Content-Disposition: form-data; name="from"
+
+Example User <test@example.com>
+--xYzZY
+Content-Disposition: form-data; name="sender_ip"
+
+0.0.0.0
+--xYzZY
+Content-Disposition: form-data; name="envelope"
+
+{"to":["inbound@inbound.inbound.com"],"from":"test@example.com"}
+--xYzZY
+Content-Disposition: form-data; name="subject"
+
+Raw Payload
+--xYzZY
+Content-Disposition: form-data; name="charsets"
+
+{"to":"UTF-8","subject":"UTF-8","from":"UTF-8"}
+--xYzZY
+Content-Disposition: form-data; name="SPF"
+
+pass
+--xYzZY-- \ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/send.py b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/send.py
new file mode 100644
index 00000000..8dbfa68d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sendgrid/helpers/inbound/send.py
@@ -0,0 +1,61 @@
+"""A module for sending test SendGrid Inbound Parse messages.
+Usage: ./send.py [path to file containing test data]"""
+import argparse
+import sys
+from io import open
+try:
+ from config import Config
+except ImportError:
+ # Python 3+, Travis
+ from sendgrid.helpers.inbound.config import Config
+from python_http_client import Client
+
+
+class Send(object):
+
+ def __init__(self, url):
+ """Create a Send object with target `url`."""
+ self._url = url
+
+ def test_payload(self, payload_filepath):
+ """Send a test payload.
+
+ Load a payload from payload_filepath, apply headers, and POST self.url.
+ Return the response object.
+ """
+ headers = {
+ "User-Agent": "SendGrid-Test",
+ "Content-Type": "multipart/form-data; boundary=xYzZY"
+ }
+ client = Client(host=self.url, request_headers=headers)
+ f = open(payload_filepath, 'r', encoding='utf-8')
+ data = f.read()
+ return client.post(request_body=data)
+
+ @property
+ def url(self):
+ """URL to send to."""
+ return self._url
+
+
+def main():
+ config = Config()
+ parser = argparse.ArgumentParser(
+ description='Test data and optional host.')
+ parser.add_argument('data',
+ type=str,
+ help='path to the sample data')
+ parser.add_argument('-host',
+ type=str,
+ help='name of host to send the sample data to',
+ default=config.host, required=False)
+ args = parser.parse_args()
+ send = Send(args.host)
+ response = send.test_payload(sys.argv[1])
+ print(response.status_code)
+ print(response.headers)
+ print(response.body)
+
+
+if __name__ == '__main__':
+ main()