python-terminusgps 34.1.0__py3-none-any.whl → 35.1.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.
- {python_terminusgps-34.1.0.dist-info → python_terminusgps-35.1.0.dist-info}/METADATA +1 -1
- {python_terminusgps-34.1.0.dist-info → python_terminusgps-35.1.0.dist-info}/RECORD +5 -5
- terminusgps/aws/notifications.py +128 -124
- {python_terminusgps-34.1.0.dist-info → python_terminusgps-35.1.0.dist-info}/WHEEL +0 -0
- {python_terminusgps-34.1.0.dist-info → python_terminusgps-35.1.0.dist-info}/licenses/COPYING +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-terminusgps
|
|
3
|
-
Version:
|
|
3
|
+
Version: 35.1.0
|
|
4
4
|
Summary: Provides abstractions/utilities for working with Wialon API, Authorize.NET API, AWS API, and more.
|
|
5
5
|
Project-URL: Documentation, https://docs.terminusgps.com
|
|
6
6
|
Project-URL: Repository, https://github.com/terminusgps/python-terminusgps
|
|
@@ -15,7 +15,7 @@ terminusgps/authorizenet/tests/test_auth.py,sha256=sdByYrcv8RKjcJckOHbyHH1z_M4qr
|
|
|
15
15
|
terminusgps/authorizenet/tests/test_profiles.py,sha256=tvLBY0iSAC33RlWiBP_NSnPskUQBjfrf0YHMpR-m22k,7566
|
|
16
16
|
terminusgps/authorizenet/tests/test_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
terminusgps/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
terminusgps/aws/notifications.py,sha256=
|
|
18
|
+
terminusgps/aws/notifications.py,sha256=i6BR4G-uw5m9i4FaHzjmLZb3ufdqSpeFfg3MwTXILVI,7296
|
|
19
19
|
terminusgps/aws/secrets.py,sha256=MxQEmmBLpMUQ2tYAsHdCYf-7RZ84LiogdKcTsACX5dw,361
|
|
20
20
|
terminusgps/aws/utils.py,sha256=68GWcllu4h41qzFiKdUkWOAA4LDsTPqRY446VRMxYsY,116
|
|
21
21
|
terminusgps/aws/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -54,7 +54,7 @@ terminusgps/wialon/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
54
54
|
terminusgps/wialon/tests/test_items.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
55
|
terminusgps/wialon/tests/test_session.py,sha256=9mBlYchMo9NeqRIZsmxYzrM1zBHorqu4ooxqSNppLpI,1336
|
|
56
56
|
terminusgps/wialon/tests/test_utils.py,sha256=SK4PxJQGECFnzx_EQeRAQfsQ5_3FLaVcis2W9u_ibuI,1730
|
|
57
|
-
python_terminusgps-
|
|
58
|
-
python_terminusgps-
|
|
59
|
-
python_terminusgps-
|
|
60
|
-
python_terminusgps-
|
|
57
|
+
python_terminusgps-35.1.0.dist-info/METADATA,sha256=uIku-n8_UI-SA-M-ojn5NpxbNoP9Yjmn6G-bKMJ9WO8,1616
|
|
58
|
+
python_terminusgps-35.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
59
|
+
python_terminusgps-35.1.0.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
60
|
+
python_terminusgps-35.1.0.dist-info/RECORD,,
|
terminusgps/aws/notifications.py
CHANGED
|
@@ -1,46 +1,75 @@
|
|
|
1
|
-
import
|
|
2
|
-
import uuid
|
|
3
|
-
from collections.abc import Sequence
|
|
1
|
+
import os
|
|
4
2
|
from contextlib import AsyncExitStack
|
|
5
3
|
|
|
6
4
|
from aiobotocore.session import AioSession
|
|
7
5
|
|
|
8
|
-
from terminusgps.django.validators import validate_e164_phone_number
|
|
9
|
-
|
|
10
6
|
|
|
11
7
|
class AsyncNotificationManager:
|
|
8
|
+
"""Asyncronously delivers notifications using `AWS End User Messaging <https://docs.aws.amazon.com/end-user-messaging/>`_."""
|
|
9
|
+
|
|
12
10
|
def __init__(
|
|
13
|
-
self,
|
|
11
|
+
self,
|
|
12
|
+
origin_pool_arn: str | None = os.getenv("AWS_MESSAGING_ORIGIN_POOL"),
|
|
13
|
+
configuration_set: str | None = os.getenv("AWS_MESSAGING_CONFIGURATION"),
|
|
14
|
+
max_sms_price: str = "0.20",
|
|
15
|
+
max_voice_price: str = "0.20",
|
|
16
|
+
region_name: str = "us-east-1",
|
|
17
|
+
debug_enabled: bool = False,
|
|
14
18
|
) -> None:
|
|
15
19
|
"""
|
|
16
|
-
Sets
|
|
17
|
-
|
|
20
|
+
Sets attributes on the notification manager.
|
|
21
|
+
|
|
22
|
+
:param origin_pool_arn: A phone pool ARN for notification dispatch. Default is :envvar:`AWS_MESSAGING_ORIGIN_POOL`.
|
|
23
|
+
:type origin_pool_arn: :py:obj:`str` | :py:obj:`None`
|
|
24
|
+
:param configuration_set: An end-user messaging configuration set ARN. Default is :envvar:`AWS_MESSAGING_CONFIGURATION`.
|
|
25
|
+
:type configuration_set: :py:obj:`str` | :py:obj:`None`
|
|
26
|
+
:param max_sms_price: Max price to spend on a single SMS message.
|
|
27
|
+
:type max_sms_price: :py:obj:`str`
|
|
28
|
+
:param max_voice_price: Max price to spend per minute on a single voice message.
|
|
29
|
+
:type max_voice_price: :py:obj:`str`
|
|
30
|
+
:param region_name: An AWS region name used to open an AWS client.
|
|
31
|
+
:type region_name: :py:obj:`str`
|
|
32
|
+
:param debug_enabled: Whether or not to enable debug mode.
|
|
33
|
+
:type debug_enabled: :py:obj:`None`
|
|
34
|
+
:raises ValueError: If ``origin_pool_arn`` wasn't provided and :envvar:`AWS_MESSAGING_ORIGIN_POOL` wasn't set.
|
|
35
|
+
:raises ValueError: If ``configuration_set`` wasn't provided and :envvar:`AWS_MESSAGING_CONFIGURATION` wasn't set.
|
|
18
36
|
:returns: Nothing.
|
|
19
37
|
:rtype: :py:obj:`None`
|
|
20
38
|
|
|
21
39
|
"""
|
|
40
|
+
if origin_pool_arn is None:
|
|
41
|
+
raise ValueError(f"'origin_pool_arn' is required, got '{origin_pool_arn}'.")
|
|
42
|
+
if configuration_set is None:
|
|
43
|
+
raise ValueError(
|
|
44
|
+
f"'configuration_set' is required, got '{configuration_set}'."
|
|
45
|
+
)
|
|
46
|
+
|
|
22
47
|
self._exit_stack = AsyncExitStack()
|
|
23
|
-
self.
|
|
48
|
+
self._pinpoint_client = None
|
|
24
49
|
self._region_name = region_name
|
|
25
|
-
self.
|
|
50
|
+
self._origin_pool_arn = origin_pool_arn
|
|
51
|
+
self._configuration_set = configuration_set
|
|
52
|
+
self._max_sms_price = max_sms_price
|
|
53
|
+
self._max_voice_price = max_voice_price
|
|
54
|
+
self._debug = debug_enabled
|
|
26
55
|
|
|
27
56
|
async def __aenter__(self) -> "AsyncNotificationManager":
|
|
28
57
|
"""
|
|
29
|
-
Creates
|
|
58
|
+
Creates asyncronous clients.
|
|
30
59
|
|
|
31
60
|
:returns: The notification manager.
|
|
32
61
|
:rtype: :py:obj:`~terminusgps.aws.notifications.AsyncNotificationManager`
|
|
33
62
|
|
|
34
63
|
"""
|
|
35
64
|
session = AioSession()
|
|
36
|
-
self.
|
|
37
|
-
session.create_client("
|
|
65
|
+
self._pinpoint_client = await self._exit_stack.enter_async_context(
|
|
66
|
+
session.create_client("pinpoint-sms-voice-v2", region_name=self.region_name)
|
|
38
67
|
)
|
|
39
68
|
return self
|
|
40
69
|
|
|
41
70
|
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
42
71
|
"""
|
|
43
|
-
Destroys
|
|
72
|
+
Destroys asyncronous clients.
|
|
44
73
|
|
|
45
74
|
:param exc_type: Exception type.
|
|
46
75
|
:param exc_val: Exception value.
|
|
@@ -62,132 +91,107 @@ class AsyncNotificationManager:
|
|
|
62
91
|
return self._region_name
|
|
63
92
|
|
|
64
93
|
@property
|
|
65
|
-
def
|
|
66
|
-
|
|
67
|
-
A message group id.
|
|
68
|
-
|
|
69
|
-
:type: :py:obj:`str`
|
|
94
|
+
def configuration_set(self) -> str:
|
|
95
|
+
return self._configuration_set
|
|
70
96
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
@group_id.setter
|
|
75
|
-
def group_id(self, other: str | None) -> None:
|
|
76
|
-
"""
|
|
77
|
-
Sets :py:attr:`group_id` to ``other``.
|
|
78
|
-
|
|
79
|
-
:param other: A message group id.
|
|
80
|
-
:type other: :py:obj:`str` | :py:obj:`None`
|
|
81
|
-
:returns: Nothing.
|
|
82
|
-
:rtype: :py:obj:`None`
|
|
83
|
-
|
|
84
|
-
"""
|
|
85
|
-
if not other:
|
|
86
|
-
other = str(uuid.uuid4())
|
|
87
|
-
self._group_id = other
|
|
88
|
-
|
|
89
|
-
async def send_push_batch(self, target_arns: Sequence[str], message: str) -> None:
|
|
90
|
-
"""
|
|
91
|
-
Sends a push notification to each AWS resource by ARN in ``target_arns``.
|
|
92
|
-
|
|
93
|
-
:param target_arns: A sequence of AWS resource ARNs.
|
|
94
|
-
:type target_arns: :py:obj:`~collections.abc.Sequence`
|
|
95
|
-
:param message: A message body.
|
|
96
|
-
:type message: :py:obj:`str`
|
|
97
|
-
:returns: Nothing.
|
|
98
|
-
:rtype: :py:obj:`None`
|
|
99
|
-
|
|
100
|
-
"""
|
|
101
|
-
await asyncio.gather(
|
|
102
|
-
*[self.send_push(target_arn=arn, message=message) for arn in target_arns]
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
async def send_sms_batch(self, to_numbers: Sequence[str], message: str) -> None:
|
|
106
|
-
"""
|
|
107
|
-
Sends an sms message to each phone number in ``to_numbers``.
|
|
97
|
+
@property
|
|
98
|
+
def origin_pool(self) -> str:
|
|
99
|
+
return self._origin_pool_arn
|
|
108
100
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
:type message: :py:obj:`str`
|
|
113
|
-
:raises ValidationError: If a ``to_number`` wasn't a valid E.164 formatted phone number.
|
|
114
|
-
:returns: Nothing.
|
|
115
|
-
:rtype: :py:obj:`None`
|
|
101
|
+
@property
|
|
102
|
+
def max_sms_price(self) -> str:
|
|
103
|
+
return self._max_sms_price
|
|
116
104
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
105
|
+
@property
|
|
106
|
+
def max_voice_price(self) -> str:
|
|
107
|
+
return self._max_voice_price
|
|
120
108
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
109
|
+
@property
|
|
110
|
+
def debug(self) -> bool:
|
|
111
|
+
return self._debug
|
|
124
112
|
|
|
125
113
|
async def send_sms(
|
|
126
|
-
self,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
:
|
|
132
|
-
|
|
114
|
+
self,
|
|
115
|
+
phone: str,
|
|
116
|
+
message: str,
|
|
117
|
+
ttl: int = 300,
|
|
118
|
+
dry_run: bool = False,
|
|
119
|
+
feedback: bool = False,
|
|
120
|
+
) -> dict:
|
|
121
|
+
"""
|
|
122
|
+
Texts ``message`` to ``phone`` via sms.
|
|
123
|
+
|
|
124
|
+
:param phone: A destination phone number.
|
|
125
|
+
:type phone: :py:obj:`str`
|
|
133
126
|
:param message: A message body.
|
|
134
127
|
:type message: :py:obj:`str`
|
|
135
|
-
:param
|
|
136
|
-
:type
|
|
137
|
-
:
|
|
138
|
-
:
|
|
139
|
-
:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
await self.
|
|
128
|
+
:param ttl: Time to live in ms. Default is ``300``.
|
|
129
|
+
:type ttl: :py:obj:`int`
|
|
130
|
+
:param dry_run: Whether or not to execute the voice message as a dry run. Default is :py:obj:`False`.
|
|
131
|
+
:type dry_run: :py:obj:`bool`
|
|
132
|
+
:param feedback: Whether or not to include message feedback in the response. Default is :py:obj:`False`.
|
|
133
|
+
:type feedback: :py:obj:`bool`
|
|
134
|
+
:raises AssertionError: If :py:attr:`_pinpoint_client` wasn't set.
|
|
135
|
+
:returns: A voice message response.
|
|
136
|
+
:rtype: :py:obj:`dict`
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
assert self._pinpoint_client, "Asyncronous client wasn't set."
|
|
140
|
+
return await self._pinpoint_client.send_text_message(
|
|
148
141
|
**{
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"
|
|
152
|
-
"
|
|
142
|
+
"DestinationPhoneNumber": phone,
|
|
143
|
+
"OriginationIdentity": self.origin_pool,
|
|
144
|
+
"MessageBody": message,
|
|
145
|
+
"MessageType": "TRANSACTIONAL",
|
|
146
|
+
"ConfigurationSetName": self.configuration_set,
|
|
147
|
+
"MaxPrice": self.max_sms_price,
|
|
148
|
+
"TimeToLive": ttl,
|
|
149
|
+
"DryRun": dry_run or self.debug,
|
|
150
|
+
"MessageFeedbackEnabled": feedback,
|
|
153
151
|
}
|
|
154
152
|
)
|
|
155
153
|
|
|
156
|
-
async def
|
|
157
|
-
self,
|
|
158
|
-
|
|
154
|
+
async def send_voice(
|
|
155
|
+
self,
|
|
156
|
+
phone: str,
|
|
157
|
+
message: str,
|
|
158
|
+
ttl: int = 300,
|
|
159
|
+
voice_id: str = "Joanna",
|
|
160
|
+
dry_run: bool = False,
|
|
161
|
+
feedback: bool = False,
|
|
162
|
+
) -> dict:
|
|
159
163
|
"""
|
|
160
|
-
|
|
164
|
+
Calls ``phone`` and reads ``message`` aloud.
|
|
161
165
|
|
|
162
|
-
:param
|
|
163
|
-
:type
|
|
166
|
+
:param phone: A destination phone number.
|
|
167
|
+
:type phone: :py:obj:`str`
|
|
164
168
|
:param message: A message body.
|
|
165
169
|
:type message: :py:obj:`str`
|
|
166
|
-
:param
|
|
167
|
-
:type
|
|
168
|
-
:
|
|
169
|
-
:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
:param ttl: Time to live in ms. Default is ``300``.
|
|
171
|
+
:type ttl: :py:obj:`int`
|
|
172
|
+
:param voice_id: A voice id to use for speech synthesis.
|
|
173
|
+
:type voice_id: :py:obj:`str`
|
|
174
|
+
:param dry_run: Whether or not to execute the voice message as a dry run. Default is :py:obj:`False`.
|
|
175
|
+
:type dry_run: :py:obj:`bool`
|
|
176
|
+
:param feedback: Whether or not to include message feedback in the response. Default is :py:obj:`False`.
|
|
177
|
+
:type feedback: :py:obj:`bool`
|
|
178
|
+
:raises AssertionError: If :py:attr:`_pinpoint_client` wasn't set.
|
|
179
|
+
:returns: A voice message response.
|
|
180
|
+
:rtype: :py:obj:`dict`
|
|
181
|
+
|
|
182
|
+
"""
|
|
183
|
+
assert self._pinpoint_client, "Asyncronous client wasn't set."
|
|
184
|
+
return await self._pinpoint_client.send_voice_message(
|
|
176
185
|
**{
|
|
177
|
-
"
|
|
178
|
-
"
|
|
179
|
-
"
|
|
180
|
-
"
|
|
186
|
+
"DestinationPhoneNumber": phone,
|
|
187
|
+
"OriginationIdentity": self.origin_pool,
|
|
188
|
+
"MessageBody": message,
|
|
189
|
+
"MessageBodyTextType": "TEXT",
|
|
190
|
+
"VoiceId": voice_id.upper(),
|
|
191
|
+
"ConfigurationSetName": self.configuration_set,
|
|
192
|
+
"MaxPricePerMinute": self.max_voice_price,
|
|
193
|
+
"TimeToLive": ttl,
|
|
194
|
+
"DryRun": dry_run or self.debug,
|
|
195
|
+
"MessageFeedbackEnabled": feedback,
|
|
181
196
|
}
|
|
182
197
|
)
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
async def main() -> None:
|
|
186
|
-
async with AsyncNotificationManager(str(uuid.uuid4())) as manager:
|
|
187
|
-
await manager.send_sms_batch(
|
|
188
|
-
["+17133049421", "+18324667085", "+18322835634"], "This is a test message."
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if __name__ == "__main__":
|
|
193
|
-
asyncio.run(main())
|
|
File without changes
|
{python_terminusgps-34.1.0.dist-info → python_terminusgps-35.1.0.dist-info}/licenses/COPYING
RENAMED
|
File without changes
|