anymessage-sdk 0.2.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.
- anymessage/__init__.py +132 -0
- anymessage/client.py +383 -0
- anymessage/config.py +27 -0
- anymessage/errors.py +90 -0
- anymessage/http.py +109 -0
- anymessage/methods/__init__.py +57 -0
- anymessage/methods/balance.py +78 -0
- anymessage/methods/email.py +307 -0
- anymessage/methods/longlive.py +276 -0
- anymessage/methods/ratio.py +96 -0
- anymessage/models.py +162 -0
- anymessage/utils.py +58 -0
- anymessage_sdk-0.2.0.dist-info/METADATA +186 -0
- anymessage_sdk-0.2.0.dist-info/RECORD +16 -0
- anymessage_sdk-0.2.0.dist-info/WHEEL +5 -0
- anymessage_sdk-0.2.0.dist-info/top_level.txt +1 -0
anymessage/__init__.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# anymessage/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
anymessage — Python SDK for the service https://api.anymessage.shop
|
|
4
|
+
|
|
5
|
+
The library provides a high-level client `AnyMessageClient`
|
|
6
|
+
and convenient helpers for interacting with the API:
|
|
7
|
+
|
|
8
|
+
Short-term emails:
|
|
9
|
+
- order a temporary email inbox and wait for a message,
|
|
10
|
+
- reorder or cancel an activation,
|
|
11
|
+
- get the balance and available email inventory.
|
|
12
|
+
|
|
13
|
+
Long-term emails:
|
|
14
|
+
- buy persistent mailboxes (bulk up to 1000),
|
|
15
|
+
- fetch recent or all messages via the API,
|
|
16
|
+
- look up a purchased mailbox by address.
|
|
17
|
+
|
|
18
|
+
Activation Rate:
|
|
19
|
+
- get cancel statistics per site+domain pair,
|
|
20
|
+
- enable/disable automatic blocking on low ratio.
|
|
21
|
+
|
|
22
|
+
API docs: https://anymessage.shop/en/docs
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
__version__ = "0.2.0"
|
|
26
|
+
|
|
27
|
+
# Primary client
|
|
28
|
+
from .client import AnyMessageClient
|
|
29
|
+
|
|
30
|
+
# Errors (all inherit from AnyMessageError)
|
|
31
|
+
from .errors import (
|
|
32
|
+
AnyMessageError,
|
|
33
|
+
AuthError,
|
|
34
|
+
ValidationError,
|
|
35
|
+
NotFoundError,
|
|
36
|
+
NoBalanceError,
|
|
37
|
+
NoEmailsError,
|
|
38
|
+
ActivationCanceledError,
|
|
39
|
+
ActivationAlreadyCanceledError,
|
|
40
|
+
EmailBannedError,
|
|
41
|
+
WaitMessageError,
|
|
42
|
+
RatioBlockError,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Data models (API responses)
|
|
46
|
+
from .models import (
|
|
47
|
+
OrderResponse,
|
|
48
|
+
QuantityResponse,
|
|
49
|
+
Message,
|
|
50
|
+
RatioEntry,
|
|
51
|
+
ImapCredentials,
|
|
52
|
+
LongliveEmail,
|
|
53
|
+
LongliveOrderResponse,
|
|
54
|
+
LongliveMessage,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# High-level API wrappers
|
|
58
|
+
from .methods import (
|
|
59
|
+
# Short-term email
|
|
60
|
+
get_balance,
|
|
61
|
+
get_email_quantity,
|
|
62
|
+
order_email,
|
|
63
|
+
reorder_email,
|
|
64
|
+
cancel_email,
|
|
65
|
+
get_message,
|
|
66
|
+
wait_for_message,
|
|
67
|
+
order_wait_and_extract,
|
|
68
|
+
# Activation rate
|
|
69
|
+
get_ratio,
|
|
70
|
+
enable_block_ratio,
|
|
71
|
+
disable_block_ratio,
|
|
72
|
+
# Long-live email
|
|
73
|
+
get_longlive_quantity,
|
|
74
|
+
order_longlive_email,
|
|
75
|
+
get_longlive_history,
|
|
76
|
+
get_longlive_last_messages,
|
|
77
|
+
get_longlive_messages,
|
|
78
|
+
find_longlive_email,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Utilities
|
|
82
|
+
from .utils import extract_with_regex, domain_param
|
|
83
|
+
|
|
84
|
+
# Explicitly define the public package interface
|
|
85
|
+
__all__ = (
|
|
86
|
+
# Client
|
|
87
|
+
"AnyMessageClient",
|
|
88
|
+
# Errors
|
|
89
|
+
"AnyMessageError",
|
|
90
|
+
"AuthError",
|
|
91
|
+
"ValidationError",
|
|
92
|
+
"NotFoundError",
|
|
93
|
+
"NoBalanceError",
|
|
94
|
+
"NoEmailsError",
|
|
95
|
+
"ActivationCanceledError",
|
|
96
|
+
"ActivationAlreadyCanceledError",
|
|
97
|
+
"EmailBannedError",
|
|
98
|
+
"WaitMessageError",
|
|
99
|
+
"RatioBlockError",
|
|
100
|
+
# Models
|
|
101
|
+
"OrderResponse",
|
|
102
|
+
"QuantityResponse",
|
|
103
|
+
"Message",
|
|
104
|
+
"RatioEntry",
|
|
105
|
+
"ImapCredentials",
|
|
106
|
+
"LongliveEmail",
|
|
107
|
+
"LongliveOrderResponse",
|
|
108
|
+
"LongliveMessage",
|
|
109
|
+
# Short-term email methods
|
|
110
|
+
"get_balance",
|
|
111
|
+
"get_email_quantity",
|
|
112
|
+
"order_email",
|
|
113
|
+
"reorder_email",
|
|
114
|
+
"cancel_email",
|
|
115
|
+
"get_message",
|
|
116
|
+
"wait_for_message",
|
|
117
|
+
"order_wait_and_extract",
|
|
118
|
+
# Activation rate methods
|
|
119
|
+
"get_ratio",
|
|
120
|
+
"enable_block_ratio",
|
|
121
|
+
"disable_block_ratio",
|
|
122
|
+
# Long-live email methods
|
|
123
|
+
"get_longlive_quantity",
|
|
124
|
+
"order_longlive_email",
|
|
125
|
+
"get_longlive_history",
|
|
126
|
+
"get_longlive_last_messages",
|
|
127
|
+
"get_longlive_messages",
|
|
128
|
+
"find_longlive_email",
|
|
129
|
+
# Utilities
|
|
130
|
+
"extract_with_regex",
|
|
131
|
+
"domain_param",
|
|
132
|
+
)
|
anymessage/client.py
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any, Dict, List, Optional, Callable, Union
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
from .config import DEFAULT_TIMEOUT
|
|
8
|
+
from .errors import AuthError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AnyMessageClient:
|
|
12
|
+
"""
|
|
13
|
+
High-level client for the AnyMessage API.
|
|
14
|
+
|
|
15
|
+
This client encapsulates:
|
|
16
|
+
• an authorization token,
|
|
17
|
+
• a reusable HTTP session (requests.Session),
|
|
18
|
+
• a default network timeout for API calls.
|
|
19
|
+
|
|
20
|
+
Recommended usage (context manager):
|
|
21
|
+
|
|
22
|
+
>>> from anymessage import AnyMessageClient
|
|
23
|
+
>>> with AnyMessageClient(token="YOUR_TOKEN") as client:
|
|
24
|
+
... balance = client.get_balance()
|
|
25
|
+
... order = client.order_email(site="instagram.com", domain="gmx")
|
|
26
|
+
... msg = client.wait_for_message(id=order.id, timeout=120, poll_interval=2)
|
|
27
|
+
|
|
28
|
+
Thread safety:
|
|
29
|
+
• create a separate client instance per thread or task;
|
|
30
|
+
• the library does not use temporary files — everything runs in memory.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
token: str,
|
|
36
|
+
*,
|
|
37
|
+
session: Optional[requests.Session] = None,
|
|
38
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Initialize the client.
|
|
42
|
+
|
|
43
|
+
:param token: Required API authorization token.
|
|
44
|
+
:param session: Optional external requests.Session. If not provided, a new one is created.
|
|
45
|
+
:param timeout: Default network timeout (in seconds) for all requests.
|
|
46
|
+
:raises AuthError: If the token is missing.
|
|
47
|
+
"""
|
|
48
|
+
if not token:
|
|
49
|
+
raise AuthError("Token must be provided")
|
|
50
|
+
|
|
51
|
+
self.token: str = token
|
|
52
|
+
self.timeout: int = timeout
|
|
53
|
+
|
|
54
|
+
# If no external session is provided, create our own and mark it for cleanup.
|
|
55
|
+
self._own_session: bool = session is None
|
|
56
|
+
self.session: requests.Session = session or requests.Session()
|
|
57
|
+
|
|
58
|
+
# Optionally attach retry adapters for better network stability.
|
|
59
|
+
try:
|
|
60
|
+
from .http import mount_retries # local import to avoid circular dependencies
|
|
61
|
+
mount_retries(self.session)
|
|
62
|
+
except Exception:
|
|
63
|
+
# Fallback: continue without retries if adapters are not available.
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
# ────────────────────────── Context Manager ──────────────────────────
|
|
67
|
+
|
|
68
|
+
def __enter__(self) -> "AnyMessageClient":
|
|
69
|
+
return self
|
|
70
|
+
|
|
71
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
72
|
+
self.close()
|
|
73
|
+
|
|
74
|
+
def close(self) -> None:
|
|
75
|
+
"""Explicitly close the HTTP session if it was created internally."""
|
|
76
|
+
if self._own_session and self.session:
|
|
77
|
+
self.session.close()
|
|
78
|
+
|
|
79
|
+
# ─────────────────────────── High-level API Methods ───────────────────────────
|
|
80
|
+
|
|
81
|
+
def get_balance(self) -> float:
|
|
82
|
+
"""
|
|
83
|
+
Retrieve the current user balance.
|
|
84
|
+
|
|
85
|
+
Endpoint: GET /user/balance
|
|
86
|
+
:return: Balance as a float.
|
|
87
|
+
:raises ApiError/AuthError: On API or authentication errors.
|
|
88
|
+
"""
|
|
89
|
+
from .methods.balance import _get_balance
|
|
90
|
+
return _get_balance(self.session, self.token, self.timeout)
|
|
91
|
+
|
|
92
|
+
def get_email_quantity(self, *, site: str):
|
|
93
|
+
"""
|
|
94
|
+
Get available email data for a specific site.
|
|
95
|
+
|
|
96
|
+
Endpoint: GET /email/quantity
|
|
97
|
+
:param site: Target site (e.g., "instagram.com").
|
|
98
|
+
:return: QuantityResponse
|
|
99
|
+
:raises ValidationError: If `site` is not provided.
|
|
100
|
+
:raises ApiError/AuthError: On API or authentication errors.
|
|
101
|
+
"""
|
|
102
|
+
from .methods.email import _get_email_quantity
|
|
103
|
+
return _get_email_quantity(self.session, self.token, self.timeout, site=site)
|
|
104
|
+
|
|
105
|
+
def order_email(
|
|
106
|
+
self,
|
|
107
|
+
*,
|
|
108
|
+
site: str,
|
|
109
|
+
domain: str, # may include multiple providers: "mailcom,gmx,hotmail"
|
|
110
|
+
regex: Optional[str] = None, # regex for extracting part of the message
|
|
111
|
+
subject: Optional[str] = None, # subject filter for incoming email
|
|
112
|
+
):
|
|
113
|
+
"""
|
|
114
|
+
Order a temporary email address for the given site/domain.
|
|
115
|
+
|
|
116
|
+
Endpoint: GET /email/order
|
|
117
|
+
:return: OrderResponse (contains id, email, etc.)
|
|
118
|
+
:raises ValidationError/NoEmailsError/NoBalanceError/ApiError/AuthError
|
|
119
|
+
"""
|
|
120
|
+
from .methods.email import _order_email
|
|
121
|
+
return _order_email(
|
|
122
|
+
self.session,
|
|
123
|
+
self.token,
|
|
124
|
+
self.timeout,
|
|
125
|
+
site=site,
|
|
126
|
+
domain=domain,
|
|
127
|
+
regex=regex,
|
|
128
|
+
subject=subject,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def reorder_email(
|
|
132
|
+
self,
|
|
133
|
+
*,
|
|
134
|
+
id: Optional[Union[str, int]] = None,
|
|
135
|
+
email: Optional[str] = None,
|
|
136
|
+
site: Optional[str] = None,
|
|
137
|
+
):
|
|
138
|
+
"""
|
|
139
|
+
Reorder email (repeat activation).
|
|
140
|
+
|
|
141
|
+
Endpoint: GET /email/reorder
|
|
142
|
+
Pass either a previous activation `id`, or an `email` + `site` pair.
|
|
143
|
+
|
|
144
|
+
:return: OrderResponse
|
|
145
|
+
:raises ValidationError/ApiError/AuthError
|
|
146
|
+
"""
|
|
147
|
+
from .methods.email import _reorder_email
|
|
148
|
+
return _reorder_email(
|
|
149
|
+
self.session,
|
|
150
|
+
self.token,
|
|
151
|
+
self.timeout,
|
|
152
|
+
id=id,
|
|
153
|
+
email=email,
|
|
154
|
+
site=site,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def cancel_email(self, *, id: Union[str, int]) -> str:
|
|
158
|
+
"""
|
|
159
|
+
Cancel an email activation.
|
|
160
|
+
|
|
161
|
+
Endpoint: GET /email/cancel
|
|
162
|
+
:return: Confirmation text (e.g., "activation canceled").
|
|
163
|
+
:raises ApiError/AuthError
|
|
164
|
+
"""
|
|
165
|
+
from .methods.email import _cancel_email
|
|
166
|
+
return _cancel_email(self.session, self.token, self.timeout, id=id)
|
|
167
|
+
|
|
168
|
+
def get_message(self, *, id: Union[str, int], preview: bool = False):
|
|
169
|
+
"""
|
|
170
|
+
Retrieve an email message for a given activation.
|
|
171
|
+
|
|
172
|
+
Endpoint: GET /email/getmessage
|
|
173
|
+
|
|
174
|
+
API behavior:
|
|
175
|
+
• preview=False (default) — JSON response, HTML in `message` field;
|
|
176
|
+
• preview=True — raw HTML (no JSON wrapper).
|
|
177
|
+
The low-level function normalizes the response into a Message model.
|
|
178
|
+
|
|
179
|
+
:return: Message
|
|
180
|
+
:raises WaitMessageError: If the message is not yet ready (value == "wait message").
|
|
181
|
+
:raises ApiError/AuthError
|
|
182
|
+
"""
|
|
183
|
+
from .methods.email import _get_message
|
|
184
|
+
return _get_message(self.session, self.token, self.timeout, id=id, preview=preview)
|
|
185
|
+
|
|
186
|
+
def wait_for_message(
|
|
187
|
+
self,
|
|
188
|
+
*,
|
|
189
|
+
id: Union[str, int],
|
|
190
|
+
timeout: Union[int, float] = 120,
|
|
191
|
+
poll_interval: Union[int, float] = 2,
|
|
192
|
+
preview: bool = False,
|
|
193
|
+
on_tick: Optional[Callable[[int], None]] = None,
|
|
194
|
+
stop_event: Optional["object"] = None, # e.g., threading.Event; use `object` type to avoid hard dependency
|
|
195
|
+
):
|
|
196
|
+
"""
|
|
197
|
+
Wait for a message to arrive and return it as a Message object.
|
|
198
|
+
|
|
199
|
+
Internally, it repeatedly polls GET /email/getmessage until the message arrives
|
|
200
|
+
or the overall timeout expires. Includes progress callback and cancellation support.
|
|
201
|
+
|
|
202
|
+
:param id: Activation ID.
|
|
203
|
+
:param timeout: Overall waiting limit in seconds.
|
|
204
|
+
:param poll_interval: Polling interval in seconds.
|
|
205
|
+
:param preview: Whether to use preview=1 mode.
|
|
206
|
+
:param on_tick: Optional progress callback: on_tick(iteration: int) -> None.
|
|
207
|
+
:param stop_event: Optional object with `is_set()` method to cancel waiting.
|
|
208
|
+
:return: Message
|
|
209
|
+
:raises WaitMessageError/ApiError/AuthError
|
|
210
|
+
"""
|
|
211
|
+
from .methods.email import _wait_for_message
|
|
212
|
+
return _wait_for_message(
|
|
213
|
+
self.session,
|
|
214
|
+
self.token,
|
|
215
|
+
self.timeout,
|
|
216
|
+
id=id,
|
|
217
|
+
overall_timeout=timeout, # corresponds to parameter name in the low-level function
|
|
218
|
+
poll_interval=poll_interval,
|
|
219
|
+
preview=preview,
|
|
220
|
+
on_tick=on_tick,
|
|
221
|
+
stop_event=stop_event,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
def order_wait_and_extract(
|
|
225
|
+
self,
|
|
226
|
+
*,
|
|
227
|
+
site: str,
|
|
228
|
+
domain: Union[str, list],
|
|
229
|
+
regex: Optional[str] = None,
|
|
230
|
+
subject: Optional[str] = None,
|
|
231
|
+
timeout: Union[int, float] = 180,
|
|
232
|
+
poll_interval: Union[int, float] = 2.0,
|
|
233
|
+
preview: bool = False,
|
|
234
|
+
stop_event: Optional[object] = None,
|
|
235
|
+
):
|
|
236
|
+
"""
|
|
237
|
+
Convenience method: order an email → wait for message → extract via regex.
|
|
238
|
+
|
|
239
|
+
:return: Tuple (activation_id, email, html_or_text, extracted_value_or_None)
|
|
240
|
+
"""
|
|
241
|
+
from .methods.email import order_wait_and_extract as _owe
|
|
242
|
+
return _owe(
|
|
243
|
+
self, site=site, domain=domain, regex=regex, subject=subject,
|
|
244
|
+
timeout=timeout, poll_interval=poll_interval, preview=preview,
|
|
245
|
+
stop_event=stop_event,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# ────────────────────────── Activation Rate ──────────────────────────
|
|
249
|
+
|
|
250
|
+
def get_ratio(self, *, full: bool = False):
|
|
251
|
+
"""
|
|
252
|
+
Get activation rate statistics per site+domain pair.
|
|
253
|
+
|
|
254
|
+
Endpoint: GET /user/getratio
|
|
255
|
+
:param full: If True, include pairs with cancel_percent == 0 (ratio >= 40%).
|
|
256
|
+
:return: List[RatioEntry]
|
|
257
|
+
:raises AuthError: On authentication errors.
|
|
258
|
+
"""
|
|
259
|
+
from .methods.ratio import _get_ratio
|
|
260
|
+
return _get_ratio(self.session, self.token, self.timeout, full=full)
|
|
261
|
+
|
|
262
|
+
def enable_block_ratio(self) -> bool:
|
|
263
|
+
"""
|
|
264
|
+
Enable automatic blocking when activation rate drops below threshold.
|
|
265
|
+
|
|
266
|
+
Endpoint: GET /user/enableBlockRatio
|
|
267
|
+
:return: True on success.
|
|
268
|
+
"""
|
|
269
|
+
from .methods.ratio import _enable_block_ratio
|
|
270
|
+
return _enable_block_ratio(self.session, self.token, self.timeout)
|
|
271
|
+
|
|
272
|
+
def disable_block_ratio(self) -> bool:
|
|
273
|
+
"""
|
|
274
|
+
Disable automatic ratio blocking.
|
|
275
|
+
|
|
276
|
+
Endpoint: GET /user/disableBlockRatio
|
|
277
|
+
:return: True on success.
|
|
278
|
+
"""
|
|
279
|
+
from .methods.ratio import _disable_block_ratio
|
|
280
|
+
return _disable_block_ratio(self.session, self.token, self.timeout)
|
|
281
|
+
|
|
282
|
+
# ────────────────────────── Long-live Emails ──────────────────────────
|
|
283
|
+
|
|
284
|
+
def get_longlive_quantity(self, *, site: str):
|
|
285
|
+
"""
|
|
286
|
+
Get available long-term email domains and counts for a site.
|
|
287
|
+
|
|
288
|
+
Endpoint: GET /longlive-email/quantity
|
|
289
|
+
:param site: Target site (e.g., "instagram.com").
|
|
290
|
+
:return: QuantityResponse — data maps domain → {count, price}.
|
|
291
|
+
:raises ValidationError/AuthError
|
|
292
|
+
"""
|
|
293
|
+
from .methods.longlive import _get_longlive_quantity
|
|
294
|
+
return _get_longlive_quantity(self.session, self.token, self.timeout, site=site)
|
|
295
|
+
|
|
296
|
+
def order_longlive_email(self, *, site: str, domain: str, count: int = 1):
|
|
297
|
+
"""
|
|
298
|
+
Purchase one or more long-term mailboxes (bulk up to 1000).
|
|
299
|
+
|
|
300
|
+
Endpoint: GET /longlive-email/order
|
|
301
|
+
:param site: Target site (e.g., "instagram.com").
|
|
302
|
+
:param domain: Mailbox domain (e.g., "hotmail.com").
|
|
303
|
+
:param count: How many to buy (1–1000).
|
|
304
|
+
:return: LongliveOrderResponse
|
|
305
|
+
:raises ValidationError/NoEmailsError/NoBalanceError/AuthError
|
|
306
|
+
"""
|
|
307
|
+
from .methods.longlive import _order_longlive_email
|
|
308
|
+
return _order_longlive_email(
|
|
309
|
+
self.session, self.token, self.timeout,
|
|
310
|
+
site=site, domain=domain, count=count,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
def get_longlive_history(self, *, offset: int = 1, limit: int = 10) -> Dict[str, Any]:
|
|
314
|
+
"""
|
|
315
|
+
Get long-term email order history.
|
|
316
|
+
|
|
317
|
+
Endpoint: GET /longlive-email/history
|
|
318
|
+
:param offset: Pagination offset (1-based).
|
|
319
|
+
:param limit: Number of records to return.
|
|
320
|
+
:return: Dict keyed by activation ID.
|
|
321
|
+
:raises AuthError
|
|
322
|
+
"""
|
|
323
|
+
from .methods.longlive import _get_longlive_history
|
|
324
|
+
return _get_longlive_history(
|
|
325
|
+
self.session, self.token, self.timeout,
|
|
326
|
+
offset=offset, limit=limit,
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
def get_longlive_last_messages(
|
|
330
|
+
self,
|
|
331
|
+
*,
|
|
332
|
+
id: Union[str, int],
|
|
333
|
+
subject: Optional[str] = None,
|
|
334
|
+
):
|
|
335
|
+
"""
|
|
336
|
+
Get messages received in the last 40 minutes for a long-term activation.
|
|
337
|
+
|
|
338
|
+
Endpoint: GET /longlive-email/getlastmessages
|
|
339
|
+
:param id: Activation ID.
|
|
340
|
+
:param subject: Optional subject filter.
|
|
341
|
+
:return: List[LongliveMessage]
|
|
342
|
+
:raises ValidationError/NotFoundError/AuthError
|
|
343
|
+
"""
|
|
344
|
+
from .methods.longlive import _get_longlive_last_messages
|
|
345
|
+
return _get_longlive_last_messages(
|
|
346
|
+
self.session, self.token, self.timeout,
|
|
347
|
+
id=id, subject=subject,
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
def get_longlive_messages(
|
|
351
|
+
self,
|
|
352
|
+
*,
|
|
353
|
+
id: Union[str, int],
|
|
354
|
+
created_at: Optional[int] = None,
|
|
355
|
+
subject: Optional[str] = None,
|
|
356
|
+
):
|
|
357
|
+
"""
|
|
358
|
+
Get all messages for a long-term activation.
|
|
359
|
+
|
|
360
|
+
Endpoint: GET /longlive-email/getmessages
|
|
361
|
+
:param id: Activation ID.
|
|
362
|
+
:param created_at: Optional Unix timestamp to return only newer messages.
|
|
363
|
+
:param subject: Optional subject filter.
|
|
364
|
+
:return: List[LongliveMessage]
|
|
365
|
+
:raises ValidationError/NotFoundError/AuthError
|
|
366
|
+
"""
|
|
367
|
+
from .methods.longlive import _get_longlive_messages
|
|
368
|
+
return _get_longlive_messages(
|
|
369
|
+
self.session, self.token, self.timeout,
|
|
370
|
+
id=id, created_at=created_at, subject=subject,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
def find_longlive_email(self, *, email: str) -> List[Dict[str, Any]]:
|
|
374
|
+
"""
|
|
375
|
+
Find a previously purchased long-term mailbox by its email address.
|
|
376
|
+
|
|
377
|
+
Endpoint: GET /longlive-email/findemail
|
|
378
|
+
:param email: Full mailbox address (e.g., "example@outlook.com").
|
|
379
|
+
:return: List of activation records; empty list if not found.
|
|
380
|
+
:raises ValidationError/AuthError
|
|
381
|
+
"""
|
|
382
|
+
from .methods.longlive import _find_longlive_email
|
|
383
|
+
return _find_longlive_email(self.session, self.token, self.timeout, email=email)
|
anymessage/config.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
config.py — basic configuration for the anymessage SDK
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Dict
|
|
9
|
+
|
|
10
|
+
BASE_URL: str = "https://api.anymessage.shop"
|
|
11
|
+
DEFAULT_TIMEOUT: int = 30
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class SDKInfo:
|
|
15
|
+
name: str = "anymessage-python"
|
|
16
|
+
version: str = "0.2.0"
|
|
17
|
+
|
|
18
|
+
SDK_INFO = SDKInfo()
|
|
19
|
+
|
|
20
|
+
def get_soft_header() -> Dict[str, str]:
|
|
21
|
+
return {
|
|
22
|
+
"X-Soft-Id": "126",
|
|
23
|
+
"X-SDK-Name": SDK_INFO.name,
|
|
24
|
+
"X-SDK-Version": SDK_INFO.version,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
DEBUG_HTTP: bool = True
|
anymessage/errors.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
errors.py — exception definitions for the anymessage SDK
|
|
4
|
+
|
|
5
|
+
All client exceptions inherit from the base class AnyMessageError.
|
|
6
|
+
Each specific error corresponds to a particular API error value,
|
|
7
|
+
for example: {"status":"error","value":"token"} → AuthError.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AnyMessageError(Exception):
|
|
12
|
+
"""Base exception for all SDK errors."""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# --- Authorization / Parameter Errors ----------------------------------------
|
|
16
|
+
|
|
17
|
+
class AuthError(AnyMessageError):
|
|
18
|
+
"""Invalid or missing API token."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ValidationError(AnyMessageError):
|
|
22
|
+
"""Invalid or missing parameter (e.g., site, domain)."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# --- Activation-related Errors ----------------------------------------------
|
|
26
|
+
|
|
27
|
+
class NotFoundError(AnyMessageError):
|
|
28
|
+
"""Activation or resource not found."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class NoBalanceError(AnyMessageError):
|
|
32
|
+
"""Insufficient balance."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class NoEmailsError(AnyMessageError):
|
|
36
|
+
"""No available emails for the given site or domain."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ActivationCanceledError(AnyMessageError):
|
|
40
|
+
"""Activation has been canceled by the user."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ActivationAlreadyCanceledError(AnyMessageError):
|
|
44
|
+
"""Activation has already been canceled."""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class EmailBannedError(AnyMessageError):
|
|
48
|
+
"""This email address is banned by the service."""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class RatioBlockError(AnyMessageError):
|
|
52
|
+
"""Blocked due to low activation rate for this site+domain pair."""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# --- Message Waiting --------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
class WaitMessageError(AnyMessageError):
|
|
58
|
+
"""Message not yet available (API returned 'wait message')."""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# --- Mapping API 'value' field to exceptions --------------------------------
|
|
62
|
+
|
|
63
|
+
ERROR_VALUE_TO_EXCEPTION = {
|
|
64
|
+
"token": AuthError,
|
|
65
|
+
"site": ValidationError,
|
|
66
|
+
"domain": ValidationError,
|
|
67
|
+
"no activation": NotFoundError,
|
|
68
|
+
"activation canceled": ActivationCanceledError,
|
|
69
|
+
"activation already canceled": ActivationAlreadyCanceledError,
|
|
70
|
+
"no balance": NoBalanceError,
|
|
71
|
+
"no emails": NoEmailsError,
|
|
72
|
+
"email banned": EmailBannedError,
|
|
73
|
+
"wait message": WaitMessageError,
|
|
74
|
+
"ratio block": RatioBlockError,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def map_api_error(value: str) -> AnyMessageError:
|
|
79
|
+
"""
|
|
80
|
+
Map the API "value" field to a corresponding exception class.
|
|
81
|
+
|
|
82
|
+
Example:
|
|
83
|
+
>>> map_api_error("token")
|
|
84
|
+
AuthError('token')
|
|
85
|
+
|
|
86
|
+
:param value: String value from the API response field "value".
|
|
87
|
+
:return: Instance of the corresponding exception.
|
|
88
|
+
"""
|
|
89
|
+
exc_cls = ERROR_VALUE_TO_EXCEPTION.get(value, AnyMessageError)
|
|
90
|
+
return exc_cls(value)
|