-
Notifications
You must be signed in to change notification settings - Fork 839
/
__init__.py
246 lines (219 loc) · 8.81 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
import copy
import logging
from typing import Optional, Union, Dict, Sequence
from slack_sdk.models.basic_objects import JsonObject, JsonValidator
from slack_sdk.models.blocks import Block, TextObject, PlainTextObject, Option
class View(JsonObject):
"""View object for modals and Home tabs.
https://api.slack.com/reference/surfaces/views
"""
types = ["modal", "home", "workflow_step"]
attributes = {
"type",
"id",
"callback_id",
"external_id",
"team_id",
"bot_id",
"app_id",
"root_view_id",
"previous_view_id",
"title",
"submit",
"close",
"blocks",
"private_metadata",
"state",
"hash",
"clear_on_close",
"notify_on_close",
}
def __init__(
self,
# "modal", "home", and "workflow_step"
type: str, # skipcq: PYL-W0622
id: Optional[str] = None, # skipcq: PYL-W0622
callback_id: Optional[str] = None,
external_id: Optional[str] = None,
team_id: Optional[str] = None,
bot_id: Optional[str] = None,
app_id: Optional[str] = None,
root_view_id: Optional[str] = None,
previous_view_id: Optional[str] = None,
title: Union[str, dict, PlainTextObject] = None,
submit: Optional[Union[str, dict, PlainTextObject]] = None,
close: Optional[Union[str, dict, PlainTextObject]] = None,
blocks: Optional[Sequence[Union[dict, Block]]] = None,
private_metadata: Optional[str] = None,
state: Optional[Union[dict, "ViewState"]] = None,
hash: Optional[str] = None, # skipcq: PYL-W0622
clear_on_close: Optional[bool] = None,
notify_on_close: Optional[bool] = None,
**kwargs,
):
self.type = type
self.id = id
self.callback_id = callback_id
self.external_id = external_id
self.team_id = team_id
self.bot_id = bot_id
self.app_id = app_id
self.root_view_id = root_view_id
self.previous_view_id = previous_view_id
self.title = TextObject.parse(title, default_type=PlainTextObject.type)
self.submit = TextObject.parse(submit, default_type=PlainTextObject.type)
self.close = TextObject.parse(close, default_type=PlainTextObject.type)
self.blocks = Block.parse_all(blocks)
self.private_metadata = private_metadata
self.state = state
if self.state is not None and isinstance(self.state, dict):
self.state = ViewState(**self.state)
self.hash = hash
self.clear_on_close = clear_on_close
self.notify_on_close = notify_on_close
self.additional_attributes = kwargs
title_max_length = 24
blocks_max_length = 100
close_max_length = 24
submit_max_length = 24
private_metadata_max_length = 3000
callback_id_max_length: int = 255
@JsonValidator('type must be either "modal", "home" or "workflow_step"')
def _validate_type(self):
return self.type is not None and self.type in self.types
@JsonValidator(f"title must be between 1 and {title_max_length} characters")
def _validate_title_length(self):
return self.title is None or 1 <= len(self.title.text) <= self.title_max_length
@JsonValidator(f"views must contain between 1 and {blocks_max_length} blocks")
def _validate_blocks_length(self):
return self.blocks is None or 0 < len(self.blocks) <= self.blocks_max_length
@JsonValidator("home view cannot have submit and close")
def _validate_home_tab_structure(self):
return self.type != "home" or (
self.type == "home" and self.close is None and self.submit is None
)
@JsonValidator(f"close cannot exceed {close_max_length} characters")
def _validate_close_length(self):
return self.close is None or len(self.close.text) <= self.close_max_length
@JsonValidator(f"submit cannot exceed {submit_max_length} characters")
def _validate_submit_length(self):
return self.submit is None or len(self.submit.text) <= int(
self.submit_max_length
)
@JsonValidator(
f"private_metadata cannot exceed {private_metadata_max_length} characters"
)
def _validate_private_metadata_max_length(self):
return (
self.private_metadata is None
or len(self.private_metadata) <= self.private_metadata_max_length
)
@JsonValidator(f"callback_id cannot exceed {callback_id_max_length} characters")
def _validate_callback_id_max_length(self):
return (
self.callback_id is None
or len(self.callback_id) <= self.callback_id_max_length
)
def __str__(self):
return str(self.get_non_null_attributes())
def __repr__(self):
return self.__str__()
class ViewState(JsonObject):
attributes = {"values"}
logger = logging.getLogger(__name__)
@classmethod
def _show_warning_about_unknown(cls, value):
c = value.__class__
name = ".".join([c.__module__, c.__name__])
cls.logger.warning(
f"Unknown type for view.state.values detected ({name}) and ViewState skipped to add it"
)
def __init__(
self,
*,
values: Dict[str, Dict[str, Union[dict, "ViewStateValue"]]],
):
value_objects: Dict[str, Dict[str, ViewStateValue]] = {}
new_state_values = copy.copy(values)
if isinstance(new_state_values, dict): # just in case
for block_id, actions in new_state_values.items():
if actions is None: # skipcq: PYL-R1724
continue
elif isinstance(actions, dict):
new_actions = copy.copy(actions)
for action_id, v in actions.items():
if isinstance(v, dict):
d = copy.copy(v)
value_object = ViewStateValue(**d)
elif isinstance(v, ViewStateValue):
value_object = v
else:
self._show_warning_about_unknown(v)
continue
new_actions[action_id] = value_object
value_objects[block_id] = new_actions
else:
self._show_warning_about_unknown(v)
self.values = value_objects
def to_dict(self, *args) -> Dict[str, Dict[str, Dict[str, dict]]]: # type: ignore
self.validate_json()
if self.values is not None:
dict_values: Dict[str, Dict[str, dict]] = {}
for block_id, actions in self.values.items():
if actions:
dict_value: Dict[str, dict] = {
action_id: value.to_dict() # type: ignore
for action_id, value in actions.items() # type: ignore
}
dict_values[block_id] = dict_value
return {"values": dict_values} # type: ignore
else:
return {}
class ViewStateValue(JsonObject):
attributes = {
"type",
"value",
"selected_date",
"selected_conversation",
"selected_channel",
"selected_user",
"selected_option",
"selected_conversations",
"selected_channels",
"selected_users",
"selected_options",
}
def __init__(
self,
*,
type: Optional[str] = None, # skipcq: PYL-W0622
value: Optional[str] = None,
selected_date: Optional[str] = None,
selected_conversation: Optional[str] = None,
selected_channel: Optional[str] = None,
selected_user: Optional[str] = None,
selected_option: Optional[str] = None,
selected_conversations: Optional[Sequence[str]] = None,
selected_channels: Optional[Sequence[str]] = None,
selected_users: Optional[Sequence[str]] = None,
selected_options: Optional[Sequence[Union[dict, Option]]] = None,
):
self.type = type
self.value = value
self.selected_date = selected_date
self.selected_conversation = selected_conversation
self.selected_channel = selected_channel
self.selected_user = selected_user
self.selected_option = selected_option
self.selected_conversations = selected_conversations
self.selected_channels = selected_channels
self.selected_users = selected_users
if isinstance(selected_options, list):
self.selected_options = []
for option in selected_options:
if isinstance(option, Option):
self.selected_options.append(option)
elif isinstance(option, dict):
self.selected_options.append(Option(**option))
else:
self.selected_options = selected_options