strideutils 0.5.4__tar.gz → 0.6.0__tar.gz
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.
- {strideutils-0.5.4 → strideutils-0.6.0}/PKG-INFO +1 -1
- {strideutils-0.5.4 → strideutils-0.6.0}/pyproject.toml +1 -1
- strideutils-0.6.0/strideutils/slack_connector.py +147 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/stride_alerts.py +9 -10
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/stride_config.py +25 -9
- strideutils-0.6.0/strideutils/twilio_connector.py +49 -0
- strideutils-0.5.4/strideutils/slack_connector.py +0 -74
- strideutils-0.5.4/strideutils/twilio_connector.py +0 -31
- {strideutils-0.5.4 → strideutils-0.6.0}/README.md +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/Makefile +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/__init__.py +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/coingecko.py +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/config_examples/env.local.example +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/config_examples/strideutils_config.yaml.example +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/cryptography.py +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/exec_tx.py +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/invariant_helpers.py +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/run_safely.py +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/sheets_connector.py +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/stride_requests.py +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/stride_upstash.py +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils/types.py +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/__init__.pyi +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/coingecko.pyi +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/cryptography.pyi +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/exec_tx.pyi +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/invariant_helpers.pyi +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/run_safely.pyi +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/sheets_connector.pyi +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/slack_connector.pyi +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/stride_alerts.pyi +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/stride_config.pyi +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/stride_requests.pyi +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/stride_upstash.pyi +0 -0
- {strideutils-0.5.4 → strideutils-0.6.0}/strideutils-stubs/twilio_connector.pyi +0 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import traceback
|
|
3
|
+
from typing import List, Optional, Union
|
|
4
|
+
|
|
5
|
+
from slack_sdk import WebClient
|
|
6
|
+
from slack_sdk.errors import SlackApiError
|
|
7
|
+
|
|
8
|
+
from strideutils.stride_config import config, get_env_or_raise
|
|
9
|
+
from strideutils.stride_config import Environment as e
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SlackClient:
|
|
13
|
+
"""
|
|
14
|
+
Singleton client used to post messages to slack
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
_instance = None
|
|
18
|
+
|
|
19
|
+
def __new__(cls):
|
|
20
|
+
# Creates a new instance if one does not already exist
|
|
21
|
+
if cls._instance is None:
|
|
22
|
+
cls._instance = super(SlackClient, cls).__new__(cls)
|
|
23
|
+
return cls._instance
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
# Prevent re-initialization if a client has already been created
|
|
27
|
+
if hasattr(self, "client"):
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
self.api_token = get_env_or_raise(e.STRIDEBOT_API_TOKEN)
|
|
31
|
+
self.client = WebClient(token=self.api_token)
|
|
32
|
+
|
|
33
|
+
def post_message(
|
|
34
|
+
self,
|
|
35
|
+
message: Union[str, List[str]],
|
|
36
|
+
channel: str,
|
|
37
|
+
botname: Optional[str] = None,
|
|
38
|
+
thread_ts: Optional[str] = None,
|
|
39
|
+
):
|
|
40
|
+
"""
|
|
41
|
+
Posts a slack message to the given channel
|
|
42
|
+
If the message is an array, this will post each element of the array as a thread
|
|
43
|
+
|
|
44
|
+
Returns the thread_ts to allow chaining messages
|
|
45
|
+
"""
|
|
46
|
+
# Allow optional channel overrides via an environment variable
|
|
47
|
+
channel = os.environ.get(e.SLACK_CHANNEL_OVERRIDE, default=channel)
|
|
48
|
+
|
|
49
|
+
# Listify
|
|
50
|
+
messages = [message] if type(message) is str else message
|
|
51
|
+
|
|
52
|
+
# Post each message, threading with the previous
|
|
53
|
+
thread_ts = None or thread_ts
|
|
54
|
+
for msg in messages:
|
|
55
|
+
response = self.client.chat_postMessage(
|
|
56
|
+
channel=channel,
|
|
57
|
+
text=msg,
|
|
58
|
+
thread_ts=thread_ts,
|
|
59
|
+
username=botname,
|
|
60
|
+
)
|
|
61
|
+
if thread_ts is None: # API advises to use parent IDs instead of child's
|
|
62
|
+
thread_ts = response["ts"]
|
|
63
|
+
|
|
64
|
+
return thread_ts
|
|
65
|
+
|
|
66
|
+
def upload_file(self, file_name: str, content: str) -> str:
|
|
67
|
+
"""
|
|
68
|
+
This uploads a file and returns a link that can be used to embed a file in slack
|
|
69
|
+
Ex:
|
|
70
|
+
url = upload_file("test.txt", "Hello World")
|
|
71
|
+
post_msg(f"<{url}|This is a file>", channel="#alerts-debug")
|
|
72
|
+
"""
|
|
73
|
+
slack_file = self.client.files_upload_v2(filename=file_name, content=content)
|
|
74
|
+
file_link = slack_file["file"]["permalink"]
|
|
75
|
+
return file_link
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# TODO: Remove after all launchpad instances are removed
|
|
79
|
+
def post_msg(
|
|
80
|
+
txt: Union[str, List[str]],
|
|
81
|
+
channel: str,
|
|
82
|
+
botname: Optional[str] = None,
|
|
83
|
+
unfurl_links: bool = False,
|
|
84
|
+
unfurl_media: bool = False,
|
|
85
|
+
thread_ts: Optional[str] = None,
|
|
86
|
+
):
|
|
87
|
+
"""
|
|
88
|
+
DEPRECATED: Use slack client instead
|
|
89
|
+
|
|
90
|
+
Posts a slack message to the given channel, e.g.
|
|
91
|
+
|
|
92
|
+
post_msg('hello world', 'general')
|
|
93
|
+
|
|
94
|
+
if txt is an array, this will post each element of the array as a thread
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
username: Rename stridebot
|
|
98
|
+
"""
|
|
99
|
+
token = get_env_or_raise(e.STRIDEBOT_API_TOKEN)
|
|
100
|
+
client = WebClient(token=token)
|
|
101
|
+
channel = config.slack_channels.get(channel.replace('#', ''), channel)
|
|
102
|
+
channel = os.environ.get('SLACK_CHANNEL_OVERRIDE', default=channel)
|
|
103
|
+
|
|
104
|
+
# Listify
|
|
105
|
+
messages = [txt] if type(txt) is str else txt
|
|
106
|
+
|
|
107
|
+
# thread ts keeps track of the latest thread ID so we can keep replying in-thread
|
|
108
|
+
thread_ts = None or thread_ts
|
|
109
|
+
for msg in messages:
|
|
110
|
+
try:
|
|
111
|
+
response = client.chat_postMessage(
|
|
112
|
+
channel=channel,
|
|
113
|
+
text=msg,
|
|
114
|
+
thread_ts=thread_ts,
|
|
115
|
+
username=botname,
|
|
116
|
+
unfurl_links=unfurl_links,
|
|
117
|
+
unfurl_media=unfurl_media,
|
|
118
|
+
)
|
|
119
|
+
if thread_ts is None: # API advises to use parent IDs instead of child's
|
|
120
|
+
thread_ts = response['ts']
|
|
121
|
+
except SlackApiError as error:
|
|
122
|
+
print("Error sending message: ", error)
|
|
123
|
+
raise
|
|
124
|
+
return thread_ts
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def upload_file(file_name: str, content: str) -> str:
|
|
128
|
+
"""
|
|
129
|
+
DEPRECATED: Use slack client instead
|
|
130
|
+
|
|
131
|
+
This uploads a file called "file_name" with the string contents "content"
|
|
132
|
+
|
|
133
|
+
This will return a link that can be used to embed a file in slack.
|
|
134
|
+
For example:
|
|
135
|
+
url = upload_file("test.txt", "Hello World")
|
|
136
|
+
post_msg(f"<{url}|This is a file>", channel="#alerts-debug")
|
|
137
|
+
"""
|
|
138
|
+
token = get_env_or_raise(e.STRIDEBOT_API_TOKEN)
|
|
139
|
+
client = WebClient(token=token)
|
|
140
|
+
try:
|
|
141
|
+
slack_file = client.files_upload_v2(filename=file_name, content=content)
|
|
142
|
+
except Exception as error:
|
|
143
|
+
print(f"Error uploading file to slack: {error}")
|
|
144
|
+
traceback.print_exc()
|
|
145
|
+
raise ValueError(f"Error uploading file to slack: {error}")
|
|
146
|
+
file_link = slack_file['file']['permalink']
|
|
147
|
+
return file_link
|
|
@@ -7,7 +7,7 @@ from typing import Any, Callable, Iterable, List, Optional, Tuple, Union, cast
|
|
|
7
7
|
import pandas as pd
|
|
8
8
|
import tabulate
|
|
9
9
|
|
|
10
|
-
from strideutils import slack_connector
|
|
10
|
+
from strideutils import slack_connector
|
|
11
11
|
from strideutils.stride_config import config
|
|
12
12
|
|
|
13
13
|
DEFAULT_DAILY_UTC_HOUR = 17
|
|
@@ -43,13 +43,12 @@ def notify(
|
|
|
43
43
|
messages: Union[str, List[str]],
|
|
44
44
|
urgency,
|
|
45
45
|
tag: Optional[Union[str, Iterable[str]]] = None,
|
|
46
|
-
call: Optional[Union[str, Iterable[str]]] = None,
|
|
47
46
|
beta_alert: bool = False,
|
|
48
47
|
debug_readme: str = config.alerts_playbook,
|
|
49
48
|
debug_readme_section: str = "",
|
|
50
49
|
) -> None:
|
|
51
50
|
"""
|
|
52
|
-
Post an alert in the proper channel
|
|
51
|
+
Post an alert in the proper channel
|
|
53
52
|
|
|
54
53
|
Args:
|
|
55
54
|
msgs: A string or list of strings to notify on
|
|
@@ -60,7 +59,6 @@ def notify(
|
|
|
60
59
|
urgency = AlertType(urgency)
|
|
61
60
|
|
|
62
61
|
messages_list: List = cast(List, [messages] if type(messages) is str else messages)
|
|
63
|
-
top_level_message = str(messages_list[0])
|
|
64
62
|
|
|
65
63
|
debug_msg = f"Read more: {debug_readme}{debug_readme_section}"
|
|
66
64
|
|
|
@@ -80,16 +78,15 @@ def notify(
|
|
|
80
78
|
if tag:
|
|
81
79
|
messages_list[0] += f"\n{get_tags(tag)}"
|
|
82
80
|
|
|
83
|
-
|
|
81
|
+
messages_list.append(debug_msg)
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
slack_client = slack_connector.SlackClient()
|
|
84
|
+
slack_client.post_message(message=messages_list, channel=channel)
|
|
87
85
|
|
|
88
86
|
|
|
89
87
|
def raise_alert(
|
|
90
88
|
urgency: Union[AlertType, str],
|
|
91
89
|
tag: Optional[Union[str, Iterable[str]]] = None,
|
|
92
|
-
call: Optional[Union[str, Iterable[str]]] = None,
|
|
93
90
|
beta_alert: bool = False,
|
|
94
91
|
debug_readme: str = config.alerts_playbook,
|
|
95
92
|
debug_readme_section: str = "",
|
|
@@ -103,10 +100,13 @@ def raise_alert(
|
|
|
103
100
|
For local testing, set an environment variable ENV=DEV or use
|
|
104
101
|
urgency='debug'
|
|
105
102
|
"""
|
|
103
|
+
# Initialize stride client to make sure environment variables are set
|
|
104
|
+
slack_connector.SlackClient()
|
|
105
|
+
|
|
106
106
|
urgency = AlertType(urgency)
|
|
107
107
|
if urgency == AlertType.DEBUG or os.environ.get('ENV') == 'DEV':
|
|
108
108
|
print("DEV mode: Alert tagging and calling is suppressed. \n" "Slack alerts will appear in #alerts-debug.")
|
|
109
|
-
|
|
109
|
+
tag = []
|
|
110
110
|
urgency = AlertType.DEBUG
|
|
111
111
|
|
|
112
112
|
def _decorator(f: Callable):
|
|
@@ -125,7 +125,6 @@ def raise_alert(
|
|
|
125
125
|
messages=msgs,
|
|
126
126
|
urgency=urgency,
|
|
127
127
|
tag=tag,
|
|
128
|
-
call=call,
|
|
129
128
|
beta_alert=beta_alert,
|
|
130
129
|
debug_readme=debug_readme,
|
|
131
130
|
debug_readme_section=debug_readme_section,
|
|
@@ -17,6 +17,31 @@ if ENV not in ["DEV", "PROD"]:
|
|
|
17
17
|
raise ValueError("Environment variable 'ENV' must be set to either DEV or PROD")
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class Environment:
|
|
22
|
+
"""
|
|
23
|
+
List of all possible environment variables
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
TWILIO_ALERTS_NUMBER = "TWILIO_ALERTS_NUMBER"
|
|
27
|
+
TWILIO_ACCOUNT_ID = "TWILIO_ACCOUNT_ID"
|
|
28
|
+
TWILIO_API_TOKEN = "TWILIO_API_TOKEN"
|
|
29
|
+
|
|
30
|
+
STRIDEBOT_API_TOKEN = "STRIDEBOT_API_TOKEN"
|
|
31
|
+
SLACK_CHANNEL_OVERRIDE = "SLACK_CHANNEL_OVERRIDE"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_env_or_raise(variable_name: str) -> str:
|
|
35
|
+
"""
|
|
36
|
+
Attempts to fetch and return the environment variable, but errors
|
|
37
|
+
if it's not set
|
|
38
|
+
"""
|
|
39
|
+
value = os.getenv(variable_name)
|
|
40
|
+
if not value:
|
|
41
|
+
raise EnvironmentError(f"Environment variable {variable_name} must be set")
|
|
42
|
+
return value
|
|
43
|
+
|
|
44
|
+
|
|
20
45
|
@dataclass
|
|
21
46
|
class ConfigObj:
|
|
22
47
|
"""Raise an error if a config is not set."""
|
|
@@ -168,15 +193,6 @@ class Config(ConfigObj):
|
|
|
168
193
|
ENV: str
|
|
169
194
|
timezone: str = "US/Eastern"
|
|
170
195
|
|
|
171
|
-
# Twilio
|
|
172
|
-
TWILIO_ALERTS_NUMBER: str = ""
|
|
173
|
-
TWILIO_ACCOUNT_ID: str = ""
|
|
174
|
-
TWILIO_API_TOKEN: str = ""
|
|
175
|
-
PHONE_NUMBERS: ConfigDict = field(default_factory=ConfigDict)
|
|
176
|
-
|
|
177
|
-
# Slack
|
|
178
|
-
STRIDEBOT_API_TOKEN: str = ""
|
|
179
|
-
|
|
180
196
|
# Gsheets - loads from str env var in PROD
|
|
181
197
|
PUBLICSHEETS_AUTH: Union[str, ConfigDict] = field(default_factory=ConfigDict)
|
|
182
198
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from typing import Iterable, List, Union
|
|
2
|
+
|
|
3
|
+
from twilio.rest import Client
|
|
4
|
+
|
|
5
|
+
from strideutils.stride_config import config, get_env_or_raise
|
|
6
|
+
from strideutils.stride_config import Environment as e
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TwilioClient:
|
|
10
|
+
"""
|
|
11
|
+
Singleton client used to make phone calls
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
_instance = None
|
|
15
|
+
call_template = """<Response><Say>{}</Say></Response>"""
|
|
16
|
+
|
|
17
|
+
def __new__(cls):
|
|
18
|
+
# Creates a new instance if one does not already exist
|
|
19
|
+
if cls._instance is None:
|
|
20
|
+
cls._instance = super(TwilioClient, cls).__new__(cls)
|
|
21
|
+
return cls._instance
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
# Prevent re-initialization if a client has already been created
|
|
25
|
+
if hasattr(self, "client"):
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
self.account_id = get_env_or_raise(e.TWILIO_ACCOUNT_ID)
|
|
29
|
+
self.api_token = get_env_or_raise(e.TWILIO_API_TOKEN)
|
|
30
|
+
self.alert_numbers = get_env_or_raise(e.TWILIO_ALERTS_NUMBER)
|
|
31
|
+
self.client = Client(self.account_id, self.api_token)
|
|
32
|
+
|
|
33
|
+
def call(self, msg: str, to: Union[str, Iterable[str], List[str]]) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Make a phone call
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
msg: A timl formatted message to read aloud to the recipient
|
|
39
|
+
to: A name or list of names that can be looked up in config.PHONE_NUMBERS
|
|
40
|
+
mapping to a 11 digit phone number, with international prefix
|
|
41
|
+
e.g. config.PHONE_NUMBERS['joe'] = +12223334545
|
|
42
|
+
|
|
43
|
+
Note: SMS not available without opt-in logic for apps for toll-free verification
|
|
44
|
+
"""
|
|
45
|
+
to = [to] if type(to) is str else to
|
|
46
|
+
voice_message = self.call_template.format(msg)
|
|
47
|
+
for destination in to:
|
|
48
|
+
number = config.PHONE_NUMBERS[destination]
|
|
49
|
+
self.client.calls.create(to=number, from_=config.TWILIO_ALERTS_NUMBER, twiml=voice_message)
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import traceback
|
|
3
|
-
from typing import List, Optional, Union
|
|
4
|
-
|
|
5
|
-
from slack_sdk import WebClient
|
|
6
|
-
from slack_sdk.errors import SlackApiError
|
|
7
|
-
|
|
8
|
-
from strideutils.stride_config import config
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def post_msg(
|
|
12
|
-
txt: Union[str, List[str]],
|
|
13
|
-
channel: str,
|
|
14
|
-
token: str = config.STRIDEBOT_API_TOKEN,
|
|
15
|
-
botname: Optional[str] = None,
|
|
16
|
-
unfurl_links: bool = False,
|
|
17
|
-
unfurl_media: bool = False,
|
|
18
|
-
thread_ts: Optional[str] = None,
|
|
19
|
-
):
|
|
20
|
-
"""
|
|
21
|
-
Posts a slack message to the given channel, e.g.
|
|
22
|
-
|
|
23
|
-
post_msg('hello world', 'general')
|
|
24
|
-
|
|
25
|
-
if txt is an array, this will post each element of the array as a thread
|
|
26
|
-
|
|
27
|
-
Args:
|
|
28
|
-
username: Rename stridebot
|
|
29
|
-
"""
|
|
30
|
-
client = WebClient(token=token)
|
|
31
|
-
channel = config.slack_channels.get(channel.replace('#', ''), channel)
|
|
32
|
-
channel = os.environ.get('SLACK_CHANNEL_OVERRIDE', default=channel)
|
|
33
|
-
|
|
34
|
-
# Listify
|
|
35
|
-
messages = [txt] if type(txt) is str else txt
|
|
36
|
-
|
|
37
|
-
# thread ts keeps track of the latest thread ID so we can keep replying in-thread
|
|
38
|
-
thread_ts = None or thread_ts
|
|
39
|
-
for msg in messages:
|
|
40
|
-
try:
|
|
41
|
-
response = client.chat_postMessage(
|
|
42
|
-
channel=channel,
|
|
43
|
-
text=msg,
|
|
44
|
-
thread_ts=thread_ts,
|
|
45
|
-
username=botname,
|
|
46
|
-
unfurl_links=unfurl_links,
|
|
47
|
-
unfurl_media=unfurl_media,
|
|
48
|
-
)
|
|
49
|
-
if thread_ts is None: # API advises to use parent IDs instead of child's
|
|
50
|
-
thread_ts = response['ts']
|
|
51
|
-
except SlackApiError as e:
|
|
52
|
-
print("Error sending message: ", e)
|
|
53
|
-
raise
|
|
54
|
-
return thread_ts
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def upload_file(file_name: str, content: str) -> str:
|
|
58
|
-
'''
|
|
59
|
-
This uploads a file called "file_name" with the string contents "content"
|
|
60
|
-
|
|
61
|
-
This will return a link that can be used to embed a file in slack.
|
|
62
|
-
For example:
|
|
63
|
-
url = upload_file("test.txt", "Hello World")
|
|
64
|
-
post_msg(f"<{url}|This is a file>", channel="#alerts-debug")
|
|
65
|
-
'''
|
|
66
|
-
client = WebClient(token=config.STRIDEBOT_API_TOKEN)
|
|
67
|
-
try:
|
|
68
|
-
slack_file = client.files_upload_v2(filename=file_name, content=content)
|
|
69
|
-
except Exception as e:
|
|
70
|
-
print(f"Error uploading file to slack: {e}")
|
|
71
|
-
traceback.print_exc()
|
|
72
|
-
raise ValueError(f"Error uploading file to slack: {e}")
|
|
73
|
-
file_link = slack_file['file']['permalink']
|
|
74
|
-
return file_link
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
from typing import Iterable, List, Union
|
|
2
|
-
|
|
3
|
-
from twilio.rest import Client
|
|
4
|
-
|
|
5
|
-
from strideutils.stride_config import config
|
|
6
|
-
|
|
7
|
-
# Check for necessary config on import
|
|
8
|
-
_ = config.TWILIO_ALERTS_NUMBER
|
|
9
|
-
CALL_TEMPLATE = """<Response><Say>{}</Say></Response>"""
|
|
10
|
-
|
|
11
|
-
client = Client(config.TWILIO_ACCOUNT_ID, config.TWILIO_API_TOKEN)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# N.B. SMS not available without opt-in logic for apps for toll-free verification
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def send_calls(msg: str, to: Union[str, Iterable[str], List[str]]) -> None:
|
|
18
|
-
"""
|
|
19
|
-
Make a phone call
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
msg: A timl formatted message to read aloud to the recipient
|
|
23
|
-
to: A name or list of names that can be looked up in config.PHONE_NUMBERS
|
|
24
|
-
mapping to a 11 digit phone number, with international prefix
|
|
25
|
-
e.g. config.PHONE_NUMBERS['joe'] = +12223334545
|
|
26
|
-
"""
|
|
27
|
-
to = [to] if type(to) is str else to
|
|
28
|
-
voice_message = CALL_TEMPLATE.format(msg)
|
|
29
|
-
for destination in to:
|
|
30
|
-
number = config.PHONE_NUMBERS[destination]
|
|
31
|
-
client.calls.create(to=number, from_=config.TWILIO_ALERTS_NUMBER, twiml=voice_message)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{strideutils-0.5.4 → strideutils-0.6.0}/strideutils/config_examples/strideutils_config.yaml.example
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|