chainlit 1.0.401__py3-none-any.whl → 2.0.3__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.
Potentially problematic release.
This version of chainlit might be problematic. Click here for more details.
- chainlit/__init__.py +98 -279
- chainlit/_utils.py +8 -0
- chainlit/action.py +12 -10
- chainlit/{auth.py → auth/__init__.py} +28 -36
- chainlit/auth/cookie.py +122 -0
- chainlit/auth/jwt.py +39 -0
- chainlit/cache.py +4 -6
- chainlit/callbacks.py +362 -0
- chainlit/chat_context.py +64 -0
- chainlit/chat_settings.py +3 -1
- chainlit/cli/__init__.py +77 -8
- chainlit/config.py +181 -101
- chainlit/context.py +42 -13
- chainlit/copilot/dist/index.js +8750 -903
- chainlit/data/__init__.py +101 -416
- chainlit/data/acl.py +6 -2
- chainlit/data/base.py +107 -0
- chainlit/data/chainlit_data_layer.py +608 -0
- chainlit/data/dynamodb.py +590 -0
- chainlit/data/literalai.py +500 -0
- chainlit/data/sql_alchemy.py +721 -0
- chainlit/data/storage_clients/__init__.py +0 -0
- chainlit/data/storage_clients/azure.py +81 -0
- chainlit/data/storage_clients/azure_blob.py +89 -0
- chainlit/data/storage_clients/base.py +26 -0
- chainlit/data/storage_clients/gcs.py +88 -0
- chainlit/data/storage_clients/s3.py +75 -0
- chainlit/data/utils.py +29 -0
- chainlit/discord/__init__.py +6 -0
- chainlit/discord/app.py +354 -0
- chainlit/element.py +91 -33
- chainlit/emitter.py +80 -29
- chainlit/frontend/dist/assets/DailyMotion-C_XC7xJI.js +1 -0
- chainlit/frontend/dist/assets/Dataframe-Cs4l4hA1.js +22 -0
- chainlit/frontend/dist/assets/Facebook-CUeCH7hk.js +1 -0
- chainlit/frontend/dist/assets/FilePlayer-CB-fYkx8.js +1 -0
- chainlit/frontend/dist/assets/Kaltura-YX6qaq72.js +1 -0
- chainlit/frontend/dist/assets/Mixcloud-DGV0ldjP.js +1 -0
- chainlit/frontend/dist/assets/Mux-CmRss5oc.js +1 -0
- chainlit/frontend/dist/assets/Preview-DBVJn7-H.js +1 -0
- chainlit/frontend/dist/assets/SoundCloud-qLUb18oY.js +1 -0
- chainlit/frontend/dist/assets/Streamable-BvYP7bFp.js +1 -0
- chainlit/frontend/dist/assets/Twitch-CTHt-sGZ.js +1 -0
- chainlit/frontend/dist/assets/Vidyard-B-0mCJbm.js +1 -0
- chainlit/frontend/dist/assets/Vimeo-Dnp7ri8q.js +1 -0
- chainlit/frontend/dist/assets/Wistia-DW0x_UBn.js +1 -0
- chainlit/frontend/dist/assets/YouTube--98FipvA.js +1 -0
- chainlit/frontend/dist/assets/index-D71nZ46o.js +8665 -0
- chainlit/frontend/dist/assets/index-g8LTJwwr.css +1 -0
- chainlit/frontend/dist/assets/react-plotly-Cn_BQTQw.js +3484 -0
- chainlit/frontend/dist/index.html +2 -4
- chainlit/haystack/callbacks.py +4 -7
- chainlit/input_widget.py +8 -4
- chainlit/langchain/callbacks.py +103 -68
- chainlit/langflow/__init__.py +1 -0
- chainlit/llama_index/callbacks.py +65 -40
- chainlit/markdown.py +22 -6
- chainlit/message.py +54 -56
- chainlit/mistralai/__init__.py +50 -0
- chainlit/oauth_providers.py +266 -8
- chainlit/openai/__init__.py +10 -18
- chainlit/secret.py +1 -1
- chainlit/server.py +789 -228
- chainlit/session.py +108 -90
- chainlit/slack/__init__.py +6 -0
- chainlit/slack/app.py +397 -0
- chainlit/socket.py +199 -116
- chainlit/step.py +141 -89
- chainlit/sync.py +2 -1
- chainlit/teams/__init__.py +6 -0
- chainlit/teams/app.py +338 -0
- chainlit/translations/bn.json +235 -0
- chainlit/translations/en-US.json +83 -4
- chainlit/translations/gu.json +235 -0
- chainlit/translations/he-IL.json +235 -0
- chainlit/translations/hi.json +235 -0
- chainlit/translations/kn.json +235 -0
- chainlit/translations/ml.json +235 -0
- chainlit/translations/mr.json +235 -0
- chainlit/translations/nl-NL.json +233 -0
- chainlit/translations/ta.json +235 -0
- chainlit/translations/te.json +235 -0
- chainlit/translations/zh-CN.json +233 -0
- chainlit/translations.py +60 -0
- chainlit/types.py +133 -28
- chainlit/user.py +14 -3
- chainlit/user_session.py +6 -3
- chainlit/utils.py +52 -5
- chainlit/version.py +3 -2
- {chainlit-1.0.401.dist-info → chainlit-2.0.3.dist-info}/METADATA +48 -50
- chainlit-2.0.3.dist-info/RECORD +106 -0
- chainlit/cli/utils.py +0 -24
- chainlit/frontend/dist/assets/index-9711593e.js +0 -723
- chainlit/frontend/dist/assets/index-d088547c.css +0 -1
- chainlit/frontend/dist/assets/react-plotly-d8762cc2.js +0 -3602
- chainlit/playground/__init__.py +0 -2
- chainlit/playground/config.py +0 -40
- chainlit/playground/provider.py +0 -108
- chainlit/playground/providers/__init__.py +0 -13
- chainlit/playground/providers/anthropic.py +0 -118
- chainlit/playground/providers/huggingface.py +0 -75
- chainlit/playground/providers/langchain.py +0 -89
- chainlit/playground/providers/openai.py +0 -408
- chainlit/playground/providers/vertexai.py +0 -171
- chainlit/translations/pt-BR.json +0 -155
- chainlit-1.0.401.dist-info/RECORD +0 -66
- /chainlit/copilot/dist/assets/{logo_dark-2a3cf740.svg → logo_dark-IkGJ_IwC.svg} +0 -0
- /chainlit/copilot/dist/assets/{logo_light-b078e7bc.svg → logo_light-Bb_IPh6r.svg} +0 -0
- /chainlit/frontend/dist/assets/{logo_dark-2a3cf740.svg → logo_dark-IkGJ_IwC.svg} +0 -0
- /chainlit/frontend/dist/assets/{logo_light-b078e7bc.svg → logo_light-Bb_IPh6r.svg} +0 -0
- {chainlit-1.0.401.dist-info → chainlit-2.0.3.dist-info}/WHEEL +0 -0
- {chainlit-1.0.401.dist-info → chainlit-2.0.3.dist-info}/entry_points.txt +0 -0
chainlit/slack/app.py
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import uuid
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from functools import partial
|
|
7
|
+
from typing import Dict, List, Optional, Union
|
|
8
|
+
|
|
9
|
+
import httpx
|
|
10
|
+
from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
|
|
11
|
+
from slack_bolt.async_app import AsyncApp
|
|
12
|
+
|
|
13
|
+
from chainlit.config import config
|
|
14
|
+
from chainlit.context import ChainlitContext, HTTPSession, context, context_var
|
|
15
|
+
from chainlit.data import get_data_layer
|
|
16
|
+
from chainlit.element import Element, ElementDict
|
|
17
|
+
from chainlit.emitter import BaseChainlitEmitter
|
|
18
|
+
from chainlit.logger import logger
|
|
19
|
+
from chainlit.message import Message, StepDict
|
|
20
|
+
from chainlit.telemetry import trace
|
|
21
|
+
from chainlit.types import Feedback
|
|
22
|
+
from chainlit.user import PersistedUser, User
|
|
23
|
+
from chainlit.user_session import user_session
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SlackEmitter(BaseChainlitEmitter):
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
session: HTTPSession,
|
|
30
|
+
app: AsyncApp,
|
|
31
|
+
channel_id: str,
|
|
32
|
+
say,
|
|
33
|
+
thread_ts: Optional[str] = None,
|
|
34
|
+
):
|
|
35
|
+
super().__init__(session)
|
|
36
|
+
self.app = app
|
|
37
|
+
self.channel_id = channel_id
|
|
38
|
+
self.say = say
|
|
39
|
+
self.thread_ts = thread_ts
|
|
40
|
+
|
|
41
|
+
async def send_element(self, element_dict: ElementDict):
|
|
42
|
+
if element_dict.get("display") != "inline":
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
persisted_file = self.session.files.get(element_dict.get("chainlitKey") or "")
|
|
46
|
+
file: Optional[Union[bytes, str]] = None
|
|
47
|
+
|
|
48
|
+
if persisted_file:
|
|
49
|
+
file = str(persisted_file["path"])
|
|
50
|
+
elif file_url := element_dict.get("url"):
|
|
51
|
+
async with httpx.AsyncClient() as client:
|
|
52
|
+
response = await client.get(file_url)
|
|
53
|
+
if response.status_code == 200:
|
|
54
|
+
file = response.content
|
|
55
|
+
|
|
56
|
+
if not file:
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
await self.app.client.files_upload_v2(
|
|
60
|
+
channel=self.channel_id,
|
|
61
|
+
thread_ts=self.thread_ts,
|
|
62
|
+
file=file,
|
|
63
|
+
title=element_dict.get("name"),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
async def send_step(self, step_dict: StepDict):
|
|
67
|
+
step_type = step_dict.get("type")
|
|
68
|
+
is_assistant_message = step_type == "assistant_message"
|
|
69
|
+
is_empty_output = not step_dict.get("output")
|
|
70
|
+
|
|
71
|
+
if is_empty_output or not is_assistant_message:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
enable_feedback = get_data_layer()
|
|
75
|
+
blocks: List[Dict] = [
|
|
76
|
+
{
|
|
77
|
+
"type": "section",
|
|
78
|
+
"text": {"type": "mrkdwn", "text": step_dict["output"]},
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
if enable_feedback:
|
|
82
|
+
current_run = context.current_run
|
|
83
|
+
scorable_id = current_run.id if current_run else step_dict.get("id")
|
|
84
|
+
blocks.append(
|
|
85
|
+
{
|
|
86
|
+
"type": "actions",
|
|
87
|
+
"elements": [
|
|
88
|
+
{
|
|
89
|
+
"action_id": "thumbdown",
|
|
90
|
+
"type": "button",
|
|
91
|
+
"text": {
|
|
92
|
+
"type": "plain_text",
|
|
93
|
+
"emoji": True,
|
|
94
|
+
"text": ":thumbsdown:",
|
|
95
|
+
},
|
|
96
|
+
"value": scorable_id,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"action_id": "thumbup",
|
|
100
|
+
"type": "button",
|
|
101
|
+
"text": {
|
|
102
|
+
"type": "plain_text",
|
|
103
|
+
"emoji": True,
|
|
104
|
+
"text": ":thumbsup:",
|
|
105
|
+
},
|
|
106
|
+
"value": scorable_id,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
await self.say(
|
|
112
|
+
text=step_dict["output"], blocks=blocks, thread_ts=self.thread_ts
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
async def update_step(self, step_dict: StepDict):
|
|
116
|
+
is_assistant_message = step_dict["type"] == "assistant_message"
|
|
117
|
+
|
|
118
|
+
if not is_assistant_message:
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
await self.send_step(step_dict)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
slack_app = AsyncApp(
|
|
125
|
+
token=os.environ.get("SLACK_BOT_TOKEN"),
|
|
126
|
+
signing_secret=os.environ.get("SLACK_SIGNING_SECRET"),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@trace
|
|
131
|
+
def init_slack_context(
|
|
132
|
+
session: HTTPSession,
|
|
133
|
+
slack_channel_id: str,
|
|
134
|
+
event,
|
|
135
|
+
say,
|
|
136
|
+
thread_ts: Optional[str] = None,
|
|
137
|
+
) -> ChainlitContext:
|
|
138
|
+
emitter = SlackEmitter(
|
|
139
|
+
session=session,
|
|
140
|
+
app=slack_app,
|
|
141
|
+
channel_id=slack_channel_id,
|
|
142
|
+
say=say,
|
|
143
|
+
thread_ts=thread_ts,
|
|
144
|
+
)
|
|
145
|
+
context = ChainlitContext(session=session, emitter=emitter)
|
|
146
|
+
context_var.set(context)
|
|
147
|
+
user_session.set("slack_event", event)
|
|
148
|
+
user_session.set(
|
|
149
|
+
"fetch_slack_message_history",
|
|
150
|
+
partial(
|
|
151
|
+
fetch_message_history, channel_id=slack_channel_id, thread_ts=thread_ts
|
|
152
|
+
),
|
|
153
|
+
)
|
|
154
|
+
return context
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
slack_app_handler = AsyncSlackRequestHandler(slack_app)
|
|
158
|
+
|
|
159
|
+
users_by_slack_id: Dict[str, Union[User, PersistedUser]] = {}
|
|
160
|
+
|
|
161
|
+
USER_PREFIX = "slack_"
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def clean_content(message: str):
|
|
165
|
+
cleaned_text = re.sub(r"<@[\w]+>", "", message).strip()
|
|
166
|
+
return cleaned_text
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
async def get_user(slack_user_id: str):
|
|
170
|
+
if slack_user_id in users_by_slack_id:
|
|
171
|
+
return users_by_slack_id[slack_user_id]
|
|
172
|
+
|
|
173
|
+
slack_user = await slack_app.client.users_info(user=slack_user_id)
|
|
174
|
+
slack_user_profile = slack_user["user"]["profile"]
|
|
175
|
+
|
|
176
|
+
user_identifier = slack_user_profile.get("email") or slack_user_id
|
|
177
|
+
user = User(identifier=USER_PREFIX + user_identifier, metadata=slack_user_profile)
|
|
178
|
+
|
|
179
|
+
users_by_slack_id[slack_user_id] = user
|
|
180
|
+
|
|
181
|
+
if data_layer := get_data_layer():
|
|
182
|
+
try:
|
|
183
|
+
persisted_user = await data_layer.create_user(user)
|
|
184
|
+
if persisted_user:
|
|
185
|
+
users_by_slack_id[slack_user_id] = persisted_user
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logger.error(f"Error creating user: {e}")
|
|
188
|
+
|
|
189
|
+
return users_by_slack_id[slack_user_id]
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
async def fetch_message_history(
|
|
193
|
+
channel_id: str, thread_ts: Optional[str] = None, limit=30
|
|
194
|
+
):
|
|
195
|
+
if not thread_ts:
|
|
196
|
+
result = await slack_app.client.conversations_history(
|
|
197
|
+
channel=channel_id, limit=limit
|
|
198
|
+
)
|
|
199
|
+
else:
|
|
200
|
+
result = await slack_app.client.conversations_replies(
|
|
201
|
+
channel=channel_id, ts=thread_ts, limit=limit
|
|
202
|
+
)
|
|
203
|
+
if result["ok"]:
|
|
204
|
+
messages = result["messages"]
|
|
205
|
+
return messages
|
|
206
|
+
else:
|
|
207
|
+
raise Exception(f"Failed to fetch messages: {result['error']}")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
async def download_slack_file(url, token):
|
|
211
|
+
headers = {"Authorization": f"Bearer {token}"}
|
|
212
|
+
async with httpx.AsyncClient() as client:
|
|
213
|
+
response = await client.get(url, headers=headers)
|
|
214
|
+
if response.status_code == 200:
|
|
215
|
+
return response.content
|
|
216
|
+
else:
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
async def download_slack_files(session: HTTPSession, files, token):
|
|
221
|
+
download_coros = [
|
|
222
|
+
download_slack_file(file.get("url_private"), token) for file in files
|
|
223
|
+
]
|
|
224
|
+
file_bytes_list = await asyncio.gather(*download_coros)
|
|
225
|
+
file_refs = []
|
|
226
|
+
for idx, file_bytes in enumerate(file_bytes_list):
|
|
227
|
+
if file_bytes:
|
|
228
|
+
name = files[idx].get("name")
|
|
229
|
+
mime_type = files[idx].get("mimetype")
|
|
230
|
+
file_ref = await session.persist_file(
|
|
231
|
+
name=name, mime=mime_type, content=file_bytes
|
|
232
|
+
)
|
|
233
|
+
file_refs.append(file_ref)
|
|
234
|
+
|
|
235
|
+
files_dicts = [
|
|
236
|
+
session.files[file["id"]] for file in file_refs if file["id"] in session.files
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
file_elements = [Element.from_dict(file_dict) for file_dict in files_dicts]
|
|
240
|
+
|
|
241
|
+
return file_elements
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
async def process_slack_message(
|
|
245
|
+
event,
|
|
246
|
+
say,
|
|
247
|
+
thread_id: str,
|
|
248
|
+
thread_name: Optional[str] = None,
|
|
249
|
+
bind_thread_to_user=False,
|
|
250
|
+
thread_ts: Optional[str] = None,
|
|
251
|
+
):
|
|
252
|
+
user = await get_user(event["user"])
|
|
253
|
+
|
|
254
|
+
channel_id = event["channel"]
|
|
255
|
+
|
|
256
|
+
text = event.get("text")
|
|
257
|
+
slack_files = event.get("files", [])
|
|
258
|
+
|
|
259
|
+
session_id = str(uuid.uuid4())
|
|
260
|
+
session = HTTPSession(
|
|
261
|
+
id=session_id,
|
|
262
|
+
thread_id=thread_id,
|
|
263
|
+
user=user,
|
|
264
|
+
client_type="slack",
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
ctx = init_slack_context(
|
|
268
|
+
session=session,
|
|
269
|
+
slack_channel_id=channel_id,
|
|
270
|
+
event=event,
|
|
271
|
+
say=say,
|
|
272
|
+
thread_ts=thread_ts,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
file_elements = await download_slack_files(
|
|
276
|
+
session, slack_files, slack_app.client.token
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
if on_chat_start := config.code.on_chat_start:
|
|
280
|
+
await on_chat_start()
|
|
281
|
+
|
|
282
|
+
msg = Message(
|
|
283
|
+
content=clean_content(text),
|
|
284
|
+
elements=file_elements,
|
|
285
|
+
type="user_message",
|
|
286
|
+
author=user.metadata.get("real_name"),
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
await msg.send()
|
|
290
|
+
|
|
291
|
+
if on_message := config.code.on_message:
|
|
292
|
+
await on_message(msg)
|
|
293
|
+
|
|
294
|
+
if on_chat_end := config.code.on_chat_end:
|
|
295
|
+
await on_chat_end()
|
|
296
|
+
|
|
297
|
+
if data_layer := get_data_layer():
|
|
298
|
+
user_id = None
|
|
299
|
+
if isinstance(user, PersistedUser):
|
|
300
|
+
user_id = user.id if bind_thread_to_user else None
|
|
301
|
+
|
|
302
|
+
try:
|
|
303
|
+
await data_layer.update_thread(
|
|
304
|
+
thread_id=thread_id,
|
|
305
|
+
name=thread_name or msg.content,
|
|
306
|
+
metadata=ctx.session.to_persistable(),
|
|
307
|
+
user_id=user_id,
|
|
308
|
+
)
|
|
309
|
+
except Exception as e:
|
|
310
|
+
logger.error(f"Error updating thread: {e}")
|
|
311
|
+
|
|
312
|
+
ctx.session.delete()
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
@slack_app.event("app_home_opened")
|
|
316
|
+
async def handle_app_home_opened(event, say):
|
|
317
|
+
pass
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
@slack_app.event("app_mention")
|
|
321
|
+
async def handle_app_mentions(event, say):
|
|
322
|
+
thread_ts = event.get("thread_ts", event["ts"])
|
|
323
|
+
thread_id = str(uuid.uuid5(uuid.NAMESPACE_DNS, thread_ts))
|
|
324
|
+
|
|
325
|
+
await process_slack_message(event, say, thread_id=thread_id, thread_ts=thread_ts)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@slack_app.event("message")
|
|
329
|
+
async def handle_message(message, say):
|
|
330
|
+
thread_id = str(
|
|
331
|
+
uuid.uuid5(
|
|
332
|
+
uuid.NAMESPACE_DNS,
|
|
333
|
+
message["channel"] + datetime.today().strftime("%Y-%m-%d"),
|
|
334
|
+
)
|
|
335
|
+
)
|
|
336
|
+
thread_ts = message.get("thread_ts", message["ts"])
|
|
337
|
+
thread_id = str(uuid.uuid5(uuid.NAMESPACE_DNS, thread_ts))
|
|
338
|
+
|
|
339
|
+
await process_slack_message(
|
|
340
|
+
event=message,
|
|
341
|
+
say=say,
|
|
342
|
+
thread_id=thread_id,
|
|
343
|
+
bind_thread_to_user=True,
|
|
344
|
+
thread_ts=thread_ts,
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
@slack_app.block_action("thumbdown")
|
|
349
|
+
async def thumb_down(ack, context, body):
|
|
350
|
+
await ack()
|
|
351
|
+
step_id = body["actions"][0]["value"]
|
|
352
|
+
|
|
353
|
+
if data_layer := get_data_layer():
|
|
354
|
+
feedback = Feedback(forId=step_id, value=0)
|
|
355
|
+
await data_layer.upsert_feedback(feedback)
|
|
356
|
+
|
|
357
|
+
text = body["message"]["text"]
|
|
358
|
+
blocks = body["message"]["blocks"]
|
|
359
|
+
updated_blocks = [block for block in blocks if block["type"] != "actions"]
|
|
360
|
+
updated_blocks.append(
|
|
361
|
+
{
|
|
362
|
+
"type": "section",
|
|
363
|
+
"text": {"type": "mrkdwn", "text": ":thumbsdown: Feedback received."},
|
|
364
|
+
}
|
|
365
|
+
)
|
|
366
|
+
await context.client.chat_update(
|
|
367
|
+
channel=body["channel"]["id"],
|
|
368
|
+
ts=body["container"]["message_ts"],
|
|
369
|
+
text=text,
|
|
370
|
+
blocks=updated_blocks,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
@slack_app.block_action("thumbup")
|
|
375
|
+
async def thumb_up(ack, context, body):
|
|
376
|
+
await ack()
|
|
377
|
+
step_id = body["actions"][0]["value"]
|
|
378
|
+
|
|
379
|
+
if data_layer := get_data_layer():
|
|
380
|
+
feedback = Feedback(forId=step_id, value=1)
|
|
381
|
+
await data_layer.upsert_feedback(feedback)
|
|
382
|
+
|
|
383
|
+
text = body["message"]["text"]
|
|
384
|
+
blocks = body["message"]["blocks"]
|
|
385
|
+
updated_blocks = [block for block in blocks if block["type"] != "actions"]
|
|
386
|
+
updated_blocks.append(
|
|
387
|
+
{
|
|
388
|
+
"type": "section",
|
|
389
|
+
"text": {"type": "mrkdwn", "text": ":thumbsup: Feedback received."},
|
|
390
|
+
}
|
|
391
|
+
)
|
|
392
|
+
await context.client.chat_update(
|
|
393
|
+
channel=body["channel"]["id"],
|
|
394
|
+
ts=body["container"]["message_ts"],
|
|
395
|
+
text=text,
|
|
396
|
+
blocks=updated_blocks,
|
|
397
|
+
)
|