tosend 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tosend/__init__.py ADDED
@@ -0,0 +1,34 @@
1
+ """
2
+ ToSend Python SDK
3
+
4
+ Official Python SDK for the ToSend email API.
5
+ """
6
+
7
+ from .client import ToSend
8
+ from .exceptions import ToSendError
9
+ from .types import (
10
+ Address,
11
+ Attachment,
12
+ SendEmailRequest,
13
+ SendEmailResponse,
14
+ BatchEmailResult,
15
+ BatchEmailResponse,
16
+ Domain,
17
+ Account,
18
+ AccountInfo,
19
+ )
20
+
21
+ __version__ = "1.0.0"
22
+ __all__ = [
23
+ "ToSend",
24
+ "ToSendError",
25
+ "Address",
26
+ "Attachment",
27
+ "SendEmailRequest",
28
+ "SendEmailResponse",
29
+ "BatchEmailResult",
30
+ "BatchEmailResponse",
31
+ "Domain",
32
+ "Account",
33
+ "AccountInfo",
34
+ ]
tosend/client.py ADDED
@@ -0,0 +1,201 @@
1
+ """ToSend API client."""
2
+
3
+ import json
4
+ from typing import Optional, Dict, Any, List, Union
5
+ from urllib.request import Request, urlopen
6
+ from urllib.error import HTTPError, URLError
7
+
8
+ from .types import (
9
+ Address,
10
+ SendEmailRequest,
11
+ SendEmailResponse,
12
+ BatchEmailResponse,
13
+ AccountInfo,
14
+ )
15
+ from .exceptions import ToSendError
16
+
17
+
18
+ class ToSend:
19
+ """ToSend API client."""
20
+
21
+ DEFAULT_BASE_URL = "https://api.tosend.com"
22
+ DEFAULT_TIMEOUT = 30
23
+
24
+ def __init__(
25
+ self,
26
+ api_key: str,
27
+ base_url: Optional[str] = None,
28
+ timeout: Optional[int] = None,
29
+ ):
30
+ """
31
+ Initialize ToSend client.
32
+
33
+ Args:
34
+ api_key: Your ToSend API key
35
+ base_url: Custom base URL (optional)
36
+ timeout: Request timeout in seconds (optional)
37
+ """
38
+ if not api_key or not api_key.strip():
39
+ raise ToSendError("API key is required", 401, "unauthorized")
40
+
41
+ self.api_key = api_key.strip()
42
+ self.base_url = (base_url or self.DEFAULT_BASE_URL).rstrip("/")
43
+ self.timeout = timeout or self.DEFAULT_TIMEOUT
44
+
45
+ def send(
46
+ self,
47
+ *,
48
+ from_address: Union[Address, Dict[str, str]],
49
+ to: List[Union[Address, Dict[str, str]]],
50
+ subject: str,
51
+ html: Optional[str] = None,
52
+ text: Optional[str] = None,
53
+ cc: Optional[List[Union[Address, Dict[str, str]]]] = None,
54
+ bcc: Optional[List[Union[Address, Dict[str, str]]]] = None,
55
+ reply_to: Optional[Union[Address, Dict[str, str]]] = None,
56
+ headers: Optional[Dict[str, str]] = None,
57
+ attachments: Optional[List[Dict[str, str]]] = None,
58
+ ) -> SendEmailResponse:
59
+ """
60
+ Send a single email.
61
+
62
+ Args:
63
+ from_address: Sender address
64
+ to: List of recipient addresses
65
+ subject: Email subject
66
+ html: HTML content
67
+ text: Plain text content
68
+ cc: CC recipients
69
+ bcc: BCC recipients
70
+ reply_to: Reply-to address
71
+ headers: Custom headers
72
+ attachments: File attachments
73
+
74
+ Returns:
75
+ SendEmailResponse with message_id
76
+ """
77
+ data: Dict[str, Any] = {
78
+ "from": self._to_address_dict(from_address),
79
+ "to": [self._to_address_dict(addr) for addr in to],
80
+ "subject": subject,
81
+ }
82
+
83
+ if html:
84
+ data["html"] = html
85
+ if text:
86
+ data["text"] = text
87
+ if cc:
88
+ data["cc"] = [self._to_address_dict(addr) for addr in cc]
89
+ if bcc:
90
+ data["bcc"] = [self._to_address_dict(addr) for addr in bcc]
91
+ if reply_to:
92
+ data["reply_to"] = self._to_address_dict(reply_to)
93
+ if headers:
94
+ data["headers"] = headers
95
+ if attachments:
96
+ data["attachments"] = attachments
97
+
98
+ response = self._request("POST", "/v2/emails", data)
99
+ return SendEmailResponse.from_dict(response)
100
+
101
+ def batch(
102
+ self,
103
+ emails: List[Dict[str, Any]],
104
+ ) -> BatchEmailResponse:
105
+ """
106
+ Send multiple emails in a single request.
107
+
108
+ Args:
109
+ emails: List of email objects (same format as send())
110
+
111
+ Returns:
112
+ BatchEmailResponse with results for each email
113
+ """
114
+ if not emails:
115
+ raise ToSendError(
116
+ "Emails list is required and cannot be empty",
117
+ 422,
118
+ "validation_error",
119
+ {"emails": {"required": "At least one email is required"}},
120
+ )
121
+
122
+ # Process each email to ensure proper format
123
+ processed_emails = []
124
+ for email in emails:
125
+ processed = {}
126
+ if "from_address" in email:
127
+ processed["from"] = self._to_address_dict(email["from_address"])
128
+ elif "from" in email:
129
+ processed["from"] = self._to_address_dict(email["from"])
130
+
131
+ if "to" in email:
132
+ processed["to"] = [self._to_address_dict(addr) for addr in email["to"]]
133
+
134
+ for key in ("subject", "html", "text", "headers", "attachments"):
135
+ if key in email:
136
+ processed[key] = email[key]
137
+
138
+ for key in ("cc", "bcc"):
139
+ if key in email and email[key]:
140
+ processed[key] = [self._to_address_dict(addr) for addr in email[key]]
141
+
142
+ if "reply_to" in email and email["reply_to"]:
143
+ processed["reply_to"] = self._to_address_dict(email["reply_to"])
144
+
145
+ processed_emails.append(processed)
146
+
147
+ response = self._request("POST", "/v2/emails/batch", {"emails": processed_emails})
148
+ return BatchEmailResponse.from_dict(response)
149
+
150
+ def get_account_info(self) -> AccountInfo:
151
+ """
152
+ Get account information and domains.
153
+
154
+ Returns:
155
+ AccountInfo with account details and domains
156
+ """
157
+ response = self._request("GET", "/v2/info")
158
+ return AccountInfo.from_dict(response)
159
+
160
+ def _to_address_dict(self, address: Union[Address, Dict[str, str]]) -> Dict[str, str]:
161
+ """Convert address to dict format."""
162
+ if isinstance(address, Address):
163
+ return address.to_dict()
164
+ return address
165
+
166
+ def _request(
167
+ self,
168
+ method: str,
169
+ path: str,
170
+ data: Optional[Dict[str, Any]] = None,
171
+ ) -> Dict[str, Any]:
172
+ """Make an HTTP request to the API."""
173
+ url = f"{self.base_url}{path}"
174
+
175
+ headers = {
176
+ "Authorization": f"Bearer {self.api_key}",
177
+ "Content-Type": "application/json",
178
+ "Accept": "application/json",
179
+ }
180
+
181
+ body = json.dumps(data).encode("utf-8") if data else None
182
+
183
+ request = Request(url, data=body, headers=headers, method=method)
184
+
185
+ try:
186
+ with urlopen(request, timeout=self.timeout) as response:
187
+ response_body = response.read().decode("utf-8")
188
+ if response_body:
189
+ return json.loads(response_body)
190
+ return {}
191
+ except HTTPError as e:
192
+ try:
193
+ error_body = e.read().decode("utf-8")
194
+ error_data = json.loads(error_body)
195
+ raise ToSendError.from_response(error_data, e.code)
196
+ except (json.JSONDecodeError, UnicodeDecodeError):
197
+ raise ToSendError(str(e), e.code)
198
+ except URLError as e:
199
+ raise ToSendError(f"Request failed: {e.reason}")
200
+ except Exception as e:
201
+ raise ToSendError(f"Request failed: {str(e)}")
tosend/exceptions.py ADDED
@@ -0,0 +1,47 @@
1
+ """Exceptions for ToSend SDK."""
2
+
3
+ from typing import Optional, Dict, Any
4
+
5
+
6
+ class ToSendError(Exception):
7
+ """ToSend API error."""
8
+
9
+ def __init__(
10
+ self,
11
+ message: str,
12
+ status_code: int = 0,
13
+ error_type: str = "unknown_error",
14
+ errors: Optional[Dict[str, Dict[str, str]]] = None,
15
+ ):
16
+ super().__init__(message)
17
+ self.message = message
18
+ self.status_code = status_code
19
+ self.error_type = error_type
20
+ self.errors = errors or {}
21
+
22
+ @classmethod
23
+ def from_response(cls, data: Dict[str, Any], status_code: int) -> "ToSendError":
24
+ return cls(
25
+ message=data.get("message", "Unknown error"),
26
+ status_code=status_code,
27
+ error_type=data.get("error_type", "unknown_error"),
28
+ errors=data.get("errors"),
29
+ )
30
+
31
+ @property
32
+ def is_validation_error(self) -> bool:
33
+ return self.error_type == "validation_error" or self.status_code == 422
34
+
35
+ @property
36
+ def is_authentication_error(self) -> bool:
37
+ return (
38
+ self.error_type in ("unauthorized", "forbidden")
39
+ or self.status_code in (401, 403)
40
+ )
41
+
42
+ @property
43
+ def is_rate_limit_error(self) -> bool:
44
+ return self.error_type == "rate_limit_exceeded" or self.status_code == 429
45
+
46
+ def __str__(self) -> str:
47
+ return f"ToSendError: {self.message} (status: {self.status_code}, type: {self.error_type})"
tosend/py.typed ADDED
File without changes
tosend/types.py ADDED
@@ -0,0 +1,154 @@
1
+ """Type definitions for ToSend SDK."""
2
+
3
+ from dataclasses import dataclass, field, asdict
4
+ from typing import Optional, List, Dict, Any
5
+
6
+
7
+ @dataclass
8
+ class Address:
9
+ """Email address with optional name."""
10
+ email: str
11
+ name: Optional[str] = None
12
+
13
+ def to_dict(self) -> Dict[str, Any]:
14
+ d = {"email": self.email}
15
+ if self.name:
16
+ d["name"] = self.name
17
+ return d
18
+
19
+
20
+ @dataclass
21
+ class Attachment:
22
+ """Email attachment."""
23
+ type: str # MIME type
24
+ name: str # Filename
25
+ content: str # Base64 encoded content
26
+
27
+ def to_dict(self) -> Dict[str, str]:
28
+ return {"type": self.type, "name": self.name, "content": self.content}
29
+
30
+
31
+ @dataclass
32
+ class SendEmailRequest:
33
+ """Request to send a single email."""
34
+ from_address: Address
35
+ to: List[Address]
36
+ subject: str
37
+ html: Optional[str] = None
38
+ text: Optional[str] = None
39
+ cc: Optional[List[Address]] = None
40
+ bcc: Optional[List[Address]] = None
41
+ reply_to: Optional[Address] = None
42
+ headers: Optional[Dict[str, str]] = None
43
+ attachments: Optional[List[Attachment]] = None
44
+
45
+ def to_dict(self) -> Dict[str, Any]:
46
+ d: Dict[str, Any] = {
47
+ "from": self.from_address.to_dict(),
48
+ "to": [addr.to_dict() for addr in self.to],
49
+ "subject": self.subject,
50
+ }
51
+ if self.html:
52
+ d["html"] = self.html
53
+ if self.text:
54
+ d["text"] = self.text
55
+ if self.cc:
56
+ d["cc"] = [addr.to_dict() for addr in self.cc]
57
+ if self.bcc:
58
+ d["bcc"] = [addr.to_dict() for addr in self.bcc]
59
+ if self.reply_to:
60
+ d["reply_to"] = self.reply_to.to_dict()
61
+ if self.headers:
62
+ d["headers"] = self.headers
63
+ if self.attachments:
64
+ d["attachments"] = [att.to_dict() for att in self.attachments]
65
+ return d
66
+
67
+
68
+ @dataclass
69
+ class SendEmailResponse:
70
+ """Response from sending an email."""
71
+ message_id: str
72
+
73
+ @classmethod
74
+ def from_dict(cls, data: Dict[str, Any]) -> "SendEmailResponse":
75
+ return cls(message_id=data.get("message_id", ""))
76
+
77
+
78
+ @dataclass
79
+ class BatchEmailResult:
80
+ """Result of a single email in a batch."""
81
+ status: str
82
+ message_id: Optional[str] = None
83
+ message: Optional[str] = None
84
+ errors: Optional[Dict[str, Dict[str, str]]] = None
85
+
86
+ @classmethod
87
+ def from_dict(cls, data: Dict[str, Any]) -> "BatchEmailResult":
88
+ return cls(
89
+ status=data.get("status", ""),
90
+ message_id=data.get("message_id"),
91
+ message=data.get("message"),
92
+ errors=data.get("errors"),
93
+ )
94
+
95
+
96
+ @dataclass
97
+ class BatchEmailResponse:
98
+ """Response from sending batch emails."""
99
+ results: List[BatchEmailResult]
100
+
101
+ @classmethod
102
+ def from_dict(cls, data: Dict[str, Any]) -> "BatchEmailResponse":
103
+ results = [BatchEmailResult.from_dict(r) for r in data.get("results", [])]
104
+ return cls(results=results)
105
+
106
+
107
+ @dataclass
108
+ class Domain:
109
+ """Verified domain."""
110
+ domain_name: str
111
+ verification_status: str
112
+ created_at: str
113
+
114
+ @classmethod
115
+ def from_dict(cls, data: Dict[str, Any]) -> "Domain":
116
+ return cls(
117
+ domain_name=data.get("domain_name", ""),
118
+ verification_status=data.get("verification_status", ""),
119
+ created_at=data.get("created_at", ""),
120
+ )
121
+
122
+
123
+ @dataclass
124
+ class Account:
125
+ """Account information."""
126
+ title: str
127
+ plan_type: str
128
+ status: str
129
+ emails_usage_this_month: int
130
+ emails_sent_last_24hrs: int
131
+
132
+ @classmethod
133
+ def from_dict(cls, data: Dict[str, Any]) -> "Account":
134
+ return cls(
135
+ title=data.get("title", ""),
136
+ plan_type=data.get("plan_type", ""),
137
+ status=data.get("status", ""),
138
+ emails_usage_this_month=data.get("emails_usage_this_month", 0),
139
+ emails_sent_last_24hrs=data.get("emails_sent_last_24hrs", 0),
140
+ )
141
+
142
+
143
+ @dataclass
144
+ class AccountInfo:
145
+ """Account info response."""
146
+ account: Account
147
+ domains: List[Domain]
148
+
149
+ @classmethod
150
+ def from_dict(cls, data: Dict[str, Any]) -> "AccountInfo":
151
+ return cls(
152
+ account=Account.from_dict(data.get("account", {})),
153
+ domains=[Domain.from_dict(d) for d in data.get("domains", [])],
154
+ )
@@ -0,0 +1,239 @@
1
+ Metadata-Version: 2.4
2
+ Name: tosend
3
+ Version: 1.0.0
4
+ Summary: Official Python SDK for the ToSend email API
5
+ Author-email: ToSend <support@tosend.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://tosend.com
8
+ Project-URL: Documentation, https://tosend.com/docs
9
+ Project-URL: Repository, https://github.com/tosend/tosend-python
10
+ Project-URL: Issues, https://github.com/tosend/tosend-python/issues
11
+ Keywords: email,api,tosend,transactional,mail
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Communications :: Email
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Dynamic: license-file
28
+
29
+ # ToSend Python SDK
30
+
31
+ Official Python SDK for the [ToSend](https://tosend.com) email API.
32
+
33
+ [![PyPI version](https://badge.fury.io/py/tosend.svg)](https://badge.fury.io/py/tosend)
34
+ [![Python Versions](https://img.shields.io/pypi/pyversions/tosend.svg)](https://pypi.org/project/tosend/)
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install tosend
40
+ ```
41
+
42
+ ## Examples
43
+
44
+ See the [examples](examples) folder for complete working examples:
45
+
46
+ - [Send Email](examples/send_email.py) - Send a single email
47
+ - [Batch Send](examples/batch_send.py) - Send multiple emails in one request
48
+ - [Account Info](examples/account_info.py) - Get account information and domains
49
+
50
+ ```bash
51
+ export TOSEND_API_KEY=tsend_your_api_key
52
+ python examples/send_email.py
53
+ ```
54
+
55
+ ## Quick Start
56
+
57
+ ```python
58
+ from tosend import ToSend
59
+
60
+ client = ToSend("tsend_your_api_key")
61
+
62
+ response = client.send(
63
+ from_address={"email": "hello@yourdomain.com", "name": "Your App"},
64
+ to=[{"email": "user@example.com"}],
65
+ subject="Welcome!",
66
+ html="<h1>Hello!</h1><p>Thanks for signing up.</p>",
67
+ )
68
+
69
+ print(response.message_id)
70
+ ```
71
+
72
+ ## Usage
73
+
74
+ ### Send an Email
75
+
76
+ ```python
77
+ response = client.send(
78
+ from_address={"email": "hello@yourdomain.com", "name": "Your App"},
79
+ to=[
80
+ {"email": "user@example.com"},
81
+ {"email": "another@example.com", "name": "John Doe"},
82
+ ],
83
+ subject="Hello!",
84
+ html="<h1>Welcome</h1>",
85
+ text="Welcome", # optional
86
+ )
87
+ ```
88
+
89
+ ### With CC, BCC, and Reply-To
90
+
91
+ ```python
92
+ response = client.send(
93
+ from_address={"email": "hello@yourdomain.com"},
94
+ to=[{"email": "user@example.com"}],
95
+ cc=[{"email": "cc@example.com"}],
96
+ bcc=[{"email": "bcc@example.com"}],
97
+ reply_to={"email": "support@yourdomain.com", "name": "Support"},
98
+ subject="Hello",
99
+ html="<p>Hello World</p>",
100
+ )
101
+ ```
102
+
103
+ ### With Attachments
104
+
105
+ ```python
106
+ import base64
107
+
108
+ with open("invoice.pdf", "rb") as f:
109
+ content = base64.b64encode(f.read()).decode()
110
+
111
+ response = client.send(
112
+ from_address={"email": "hello@yourdomain.com"},
113
+ to=[{"email": "user@example.com"}],
114
+ subject="Your Invoice",
115
+ html="<p>Please find your invoice attached.</p>",
116
+ attachments=[
117
+ {
118
+ "type": "application/pdf",
119
+ "name": "invoice.pdf",
120
+ "content": content,
121
+ }
122
+ ],
123
+ )
124
+ ```
125
+
126
+ ### Batch Sending
127
+
128
+ Send multiple emails in a single request:
129
+
130
+ ```python
131
+ response = client.batch([
132
+ {
133
+ "from": {"email": "hello@yourdomain.com"},
134
+ "to": [{"email": "user1@example.com"}],
135
+ "subject": "Hello User 1",
136
+ "html": "<p>Welcome!</p>",
137
+ },
138
+ {
139
+ "from": {"email": "hello@yourdomain.com"},
140
+ "to": [{"email": "user2@example.com"}],
141
+ "subject": "Hello User 2",
142
+ "html": "<p>Welcome!</p>",
143
+ },
144
+ ])
145
+
146
+ for result in response.results:
147
+ if result.status == "success":
148
+ print(f"Sent: {result.message_id}")
149
+ else:
150
+ print(f"Failed: {result.message}")
151
+ ```
152
+
153
+ ### Get Account Info
154
+
155
+ ```python
156
+ info = client.get_account_info()
157
+
158
+ print(info.account.title)
159
+ print(info.account.emails_usage_this_month)
160
+ print(info.account.emails_sent_last_24hrs)
161
+
162
+ for domain in info.domains:
163
+ print(f"{domain.domain_name}: {domain.verification_status}")
164
+ ```
165
+
166
+ ## Configuration
167
+
168
+ ### With Options
169
+
170
+ ```python
171
+ client = ToSend(
172
+ api_key="tsend_your_api_key",
173
+ base_url="https://api.tosend.com", # optional
174
+ timeout=60, # optional, in seconds
175
+ )
176
+ ```
177
+
178
+ ## Error Handling
179
+
180
+ ```python
181
+ from tosend import ToSend, ToSendError
182
+
183
+ client = ToSend("tsend_your_api_key")
184
+
185
+ try:
186
+ response = client.send(
187
+ from_address={"email": "hello@yourdomain.com"},
188
+ to=[{"email": "user@example.com"}],
189
+ subject="Hello",
190
+ html="<p>Hello</p>",
191
+ )
192
+ except ToSendError as e:
193
+ print(f"Error: {e.message}")
194
+ print(f"Status: {e.status_code}")
195
+ print(f"Type: {e.error_type}")
196
+ print(f"Details: {e.errors}")
197
+
198
+ if e.is_validation_error:
199
+ # Handle validation error (422)
200
+ pass
201
+
202
+ if e.is_authentication_error:
203
+ # Handle auth error (401/403)
204
+ pass
205
+
206
+ if e.is_rate_limit_error:
207
+ # Handle rate limit (429)
208
+ pass
209
+ ```
210
+
211
+ ## Type Hints
212
+
213
+ The SDK includes full type hints. You can also use the provided dataclasses:
214
+
215
+ ```python
216
+ from tosend import ToSend, Address, SendEmailRequest
217
+
218
+ client = ToSend("tsend_your_api_key")
219
+
220
+ # Using Address dataclass
221
+ from_addr = Address(email="hello@yourdomain.com", name="Your App")
222
+ to_addr = Address(email="user@example.com")
223
+
224
+ response = client.send(
225
+ from_address=from_addr,
226
+ to=[to_addr],
227
+ subject="Hello!",
228
+ html="<h1>Welcome!</h1>",
229
+ )
230
+ ```
231
+
232
+ ## Requirements
233
+
234
+ - Python 3.8 or higher
235
+ - No external dependencies (uses only standard library)
236
+
237
+ ## License
238
+
239
+ MIT
@@ -0,0 +1,10 @@
1
+ tosend/__init__.py,sha256=LE5rGqMteWuW8lPEozNirvAkpAzqbEyyKIN6wHtn304,572
2
+ tosend/client.py,sha256=wWtKUOI95G1vFhqZ0awaVeCBbl9gQul7spCer2xbUQI,6586
3
+ tosend/exceptions.py,sha256=9WB6V2Ny-hR6qFRiFXfzTuFeeGmSHD0jrzTkOvYw9sk,1441
4
+ tosend/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ tosend/types.py,sha256=UXe6npV9CZIpkFNU1tAb2zJ7DrIqdmODAObbWycesS4,4273
6
+ tosend-1.0.0.dist-info/licenses/LICENSE,sha256=KSVUBGoT4K7d_-vdmjkix5VDIv5vJPbnrvIAdhV0rZU,1063
7
+ tosend-1.0.0.dist-info/METADATA,sha256=wi8rdRXN1lRJaq80pebEo1qX5WpCBrpUGHF9-OtbPCU,5775
8
+ tosend-1.0.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
9
+ tosend-1.0.0.dist-info/top_level.txt,sha256=tRqARDnjKno214JLFYAmchYTQPhd1hMgbgP7wbNqw9A,7
10
+ tosend-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 ToSend
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ tosend