optexity 0.1.2__py3-none-any.whl → 0.1.4__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.
- optexity/cli.py +1 -1
- optexity/examples/__init__.py +0 -0
- optexity/examples/add_example.py +88 -0
- optexity/examples/download_pdf_url.py +29 -0
- optexity/examples/extract_price_stockanalysis.py +44 -0
- optexity/examples/file_upload.py +59 -0
- optexity/examples/i94.py +126 -0
- optexity/examples/i94_travel_history.py +126 -0
- optexity/examples/peachstate_medicaid.py +201 -0
- optexity/examples/supabase_login.py +75 -0
- optexity/inference/__init__.py +0 -0
- optexity/inference/agents/__init__.py +0 -0
- optexity/inference/agents/error_handler/__init__.py +0 -0
- optexity/inference/agents/error_handler/error_handler.py +39 -0
- optexity/inference/agents/error_handler/prompt.py +60 -0
- optexity/inference/agents/index_prediction/__init__.py +0 -0
- optexity/inference/agents/index_prediction/action_prediction_locator_axtree.py +45 -0
- optexity/inference/agents/index_prediction/prompt.py +14 -0
- optexity/inference/agents/select_value_prediction/__init__.py +0 -0
- optexity/inference/agents/select_value_prediction/prompt.py +20 -0
- optexity/inference/agents/select_value_prediction/select_value_prediction.py +39 -0
- optexity/inference/agents/two_fa_extraction/__init__.py +0 -0
- optexity/inference/agents/two_fa_extraction/prompt.py +23 -0
- optexity/inference/agents/two_fa_extraction/two_fa_extraction.py +47 -0
- optexity/inference/child_process.py +251 -0
- optexity/inference/core/__init__.py +0 -0
- optexity/inference/core/interaction/__init__.py +0 -0
- optexity/inference/core/interaction/handle_agentic_task.py +79 -0
- optexity/inference/core/interaction/handle_check.py +57 -0
- optexity/inference/core/interaction/handle_click.py +79 -0
- optexity/inference/core/interaction/handle_command.py +261 -0
- optexity/inference/core/interaction/handle_input.py +76 -0
- optexity/inference/core/interaction/handle_keypress.py +16 -0
- optexity/inference/core/interaction/handle_select.py +109 -0
- optexity/inference/core/interaction/handle_select_utils.py +132 -0
- optexity/inference/core/interaction/handle_upload.py +59 -0
- optexity/inference/core/interaction/utils.py +81 -0
- optexity/inference/core/logging.py +406 -0
- optexity/inference/core/run_assertion.py +55 -0
- optexity/inference/core/run_automation.py +463 -0
- optexity/inference/core/run_extraction.py +240 -0
- optexity/inference/core/run_interaction.py +254 -0
- optexity/inference/core/run_python_script.py +20 -0
- optexity/inference/core/run_two_fa.py +120 -0
- optexity/inference/core/two_factor_auth/__init__.py +0 -0
- optexity/inference/infra/__init__.py +0 -0
- optexity/inference/infra/browser.py +455 -0
- optexity/inference/infra/browser_extension.py +20 -0
- optexity/inference/models/__init__.py +22 -0
- optexity/inference/models/gemini.py +113 -0
- optexity/inference/models/human.py +20 -0
- optexity/inference/models/llm_model.py +210 -0
- optexity/inference/run_local.py +200 -0
- optexity/schema/__init__.py +0 -0
- optexity/schema/actions/__init__.py +0 -0
- optexity/schema/actions/assertion_action.py +66 -0
- optexity/schema/actions/extraction_action.py +143 -0
- optexity/schema/actions/interaction_action.py +330 -0
- optexity/schema/actions/misc_action.py +18 -0
- optexity/schema/actions/prompts.py +27 -0
- optexity/schema/actions/two_fa_action.py +24 -0
- optexity/schema/automation.py +432 -0
- optexity/schema/callback.py +16 -0
- optexity/schema/inference.py +87 -0
- optexity/schema/memory.py +100 -0
- optexity/schema/task.py +212 -0
- optexity/schema/token_usage.py +48 -0
- optexity/utils/__init__.py +0 -0
- optexity/utils/settings.py +54 -0
- optexity/utils/utils.py +76 -0
- {optexity-0.1.2.dist-info → optexity-0.1.4.dist-info}/METADATA +20 -36
- optexity-0.1.4.dist-info/RECORD +80 -0
- optexity-0.1.2.dist-info/RECORD +0 -11
- {optexity-0.1.2.dist-info → optexity-0.1.4.dist-info}/WHEEL +0 -0
- {optexity-0.1.2.dist-info → optexity-0.1.4.dist-info}/entry_points.txt +0 -0
- {optexity-0.1.2.dist-info → optexity-0.1.4.dist-info}/licenses/LICENSE +0 -0
- {optexity-0.1.2.dist-info → optexity-0.1.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
|
|
5
|
+
import aiofiles
|
|
6
|
+
|
|
7
|
+
from optexity.exceptions import AssertLocatorPresenceException
|
|
8
|
+
from optexity.inference.agents.error_handler.error_handler import ErrorHandlerAgent
|
|
9
|
+
from optexity.inference.core.interaction.handle_agentic_task import handle_agentic_task
|
|
10
|
+
from optexity.inference.core.interaction.handle_check import (
|
|
11
|
+
handle_check_element,
|
|
12
|
+
handle_uncheck_element,
|
|
13
|
+
)
|
|
14
|
+
from optexity.inference.core.interaction.handle_click import handle_click_element
|
|
15
|
+
from optexity.inference.core.interaction.handle_input import handle_input_text
|
|
16
|
+
from optexity.inference.core.interaction.handle_keypress import handle_key_press
|
|
17
|
+
from optexity.inference.core.interaction.handle_select import handle_select_option
|
|
18
|
+
from optexity.inference.core.interaction.handle_upload import handle_upload_file
|
|
19
|
+
from optexity.inference.infra.browser import Browser
|
|
20
|
+
from optexity.schema.actions.interaction_action import (
|
|
21
|
+
CloseOverlayPopupAction,
|
|
22
|
+
CloseTabsUntil,
|
|
23
|
+
DownloadUrlAsPdfAction,
|
|
24
|
+
GoBackAction,
|
|
25
|
+
GoToUrlAction,
|
|
26
|
+
InteractionAction,
|
|
27
|
+
)
|
|
28
|
+
from optexity.schema.memory import BrowserState, Memory, OutputData
|
|
29
|
+
from optexity.schema.task import Task
|
|
30
|
+
|
|
31
|
+
error_handler_agent = ErrorHandlerAgent()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def run_interaction_action(
|
|
38
|
+
interaction_action: InteractionAction,
|
|
39
|
+
task: Task,
|
|
40
|
+
memory: Memory,
|
|
41
|
+
browser: Browser,
|
|
42
|
+
retries_left: int,
|
|
43
|
+
):
|
|
44
|
+
if retries_left <= 0:
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
logger.debug(
|
|
48
|
+
f"---------Running interaction action {interaction_action.model_dump_json(exclude_none=True)}---------"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
memory.automation_state.start_2fa_time = datetime.now(timezone.utc)
|
|
53
|
+
if interaction_action.click_element:
|
|
54
|
+
await handle_click_element(
|
|
55
|
+
interaction_action.click_element,
|
|
56
|
+
task,
|
|
57
|
+
memory,
|
|
58
|
+
browser,
|
|
59
|
+
interaction_action.max_timeout_seconds_per_try,
|
|
60
|
+
interaction_action.max_tries,
|
|
61
|
+
)
|
|
62
|
+
elif interaction_action.input_text:
|
|
63
|
+
await handle_input_text(
|
|
64
|
+
interaction_action.input_text,
|
|
65
|
+
task,
|
|
66
|
+
memory,
|
|
67
|
+
browser,
|
|
68
|
+
interaction_action.max_timeout_seconds_per_try,
|
|
69
|
+
interaction_action.max_tries,
|
|
70
|
+
)
|
|
71
|
+
elif interaction_action.select_option:
|
|
72
|
+
await handle_select_option(
|
|
73
|
+
interaction_action.select_option,
|
|
74
|
+
task,
|
|
75
|
+
memory,
|
|
76
|
+
browser,
|
|
77
|
+
interaction_action.max_timeout_seconds_per_try,
|
|
78
|
+
interaction_action.max_tries,
|
|
79
|
+
)
|
|
80
|
+
elif interaction_action.check:
|
|
81
|
+
await handle_check_element(
|
|
82
|
+
interaction_action.check,
|
|
83
|
+
task,
|
|
84
|
+
memory,
|
|
85
|
+
browser,
|
|
86
|
+
interaction_action.max_timeout_seconds_per_try,
|
|
87
|
+
interaction_action.max_tries,
|
|
88
|
+
)
|
|
89
|
+
elif interaction_action.uncheck:
|
|
90
|
+
await handle_uncheck_element(
|
|
91
|
+
interaction_action.uncheck,
|
|
92
|
+
task,
|
|
93
|
+
memory,
|
|
94
|
+
browser,
|
|
95
|
+
interaction_action.max_timeout_seconds_per_try,
|
|
96
|
+
interaction_action.max_tries,
|
|
97
|
+
)
|
|
98
|
+
elif interaction_action.go_back:
|
|
99
|
+
await handle_go_back(interaction_action.go_back, memory, browser)
|
|
100
|
+
elif interaction_action.download_url_as_pdf:
|
|
101
|
+
await handle_download_url_as_pdf(
|
|
102
|
+
interaction_action.download_url_as_pdf, task, memory, browser
|
|
103
|
+
)
|
|
104
|
+
elif interaction_action.agentic_task:
|
|
105
|
+
await handle_agentic_task(
|
|
106
|
+
interaction_action.agentic_task, task, memory, browser
|
|
107
|
+
)
|
|
108
|
+
elif interaction_action.close_overlay_popup:
|
|
109
|
+
await handle_agentic_task(
|
|
110
|
+
interaction_action.close_overlay_popup, task, memory, browser
|
|
111
|
+
)
|
|
112
|
+
elif interaction_action.go_to_url:
|
|
113
|
+
await handle_go_to_url(interaction_action.go_to_url, task, memory, browser)
|
|
114
|
+
elif interaction_action.upload_file:
|
|
115
|
+
await handle_upload_file(
|
|
116
|
+
interaction_action.upload_file,
|
|
117
|
+
task,
|
|
118
|
+
memory,
|
|
119
|
+
browser,
|
|
120
|
+
interaction_action.max_timeout_seconds_per_try,
|
|
121
|
+
interaction_action.max_tries,
|
|
122
|
+
)
|
|
123
|
+
elif interaction_action.close_current_tab:
|
|
124
|
+
await browser.close_current_tab()
|
|
125
|
+
elif interaction_action.switch_tab:
|
|
126
|
+
await browser.switch_tab(interaction_action.switch_tab.tab_index)
|
|
127
|
+
elif interaction_action.close_tabs_until:
|
|
128
|
+
await handle_close_tabs_until(
|
|
129
|
+
interaction_action.close_tabs_until, task, memory, browser
|
|
130
|
+
)
|
|
131
|
+
elif interaction_action.key_press:
|
|
132
|
+
await handle_key_press(interaction_action.key_press, memory, browser)
|
|
133
|
+
except AssertLocatorPresenceException as e:
|
|
134
|
+
await handle_assert_locator_presence_error(
|
|
135
|
+
e, interaction_action, task, memory, browser, retries_left
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
async def handle_close_tabs_until(
|
|
140
|
+
close_tabs_until_action: CloseTabsUntil,
|
|
141
|
+
task: Task,
|
|
142
|
+
memory: Memory,
|
|
143
|
+
browser: Browser,
|
|
144
|
+
):
|
|
145
|
+
|
|
146
|
+
while True:
|
|
147
|
+
page = await browser.get_current_page()
|
|
148
|
+
if page is None:
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
if close_tabs_until_action.matching_url is not None:
|
|
152
|
+
if close_tabs_until_action.matching_url in page.url:
|
|
153
|
+
break
|
|
154
|
+
elif close_tabs_until_action.tab_index is not None:
|
|
155
|
+
if len(browser.context.pages) == close_tabs_until_action.tab_index + 1:
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
await browser.close_current_tab()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
async def handle_go_to_url(
|
|
162
|
+
go_to_url_action: GoToUrlAction, task: Task, memory: Memory, browser: Browser
|
|
163
|
+
):
|
|
164
|
+
await browser.go_to_url(go_to_url_action.url)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async def handle_go_back(
|
|
168
|
+
go_back_action: GoBackAction, memory: Memory, browser: Browser
|
|
169
|
+
):
|
|
170
|
+
page = await browser.get_current_page()
|
|
171
|
+
if page is None:
|
|
172
|
+
return
|
|
173
|
+
await page.go_back()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
async def handle_download_url_as_pdf(
|
|
177
|
+
download_url_as_pdf_action: DownloadUrlAsPdfAction,
|
|
178
|
+
task: Task,
|
|
179
|
+
memory: Memory,
|
|
180
|
+
browser: Browser,
|
|
181
|
+
):
|
|
182
|
+
if download_url_as_pdf_action.url is not None:
|
|
183
|
+
pdf_url = download_url_as_pdf_action.url
|
|
184
|
+
else:
|
|
185
|
+
pdf_url = await browser.get_current_page_url()
|
|
186
|
+
|
|
187
|
+
if pdf_url is None:
|
|
188
|
+
logger.error("No PDF URL found for current page")
|
|
189
|
+
return
|
|
190
|
+
download_path = (
|
|
191
|
+
task.downloads_directory / download_url_as_pdf_action.download_filename
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
resp = await browser.context.request.get(pdf_url)
|
|
195
|
+
|
|
196
|
+
if not resp.ok:
|
|
197
|
+
logger.error(f"Failed to download PDF: {resp.status}")
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
content = await resp.body()
|
|
201
|
+
async with aiofiles.open(download_path, "wb") as f:
|
|
202
|
+
await f.write(content)
|
|
203
|
+
|
|
204
|
+
memory.downloads.append(download_path)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
async def handle_assert_locator_presence_error(
|
|
208
|
+
error: AssertLocatorPresenceException,
|
|
209
|
+
interaction_action: InteractionAction,
|
|
210
|
+
task: Task,
|
|
211
|
+
memory: Memory,
|
|
212
|
+
browser: Browser,
|
|
213
|
+
retries_left: int,
|
|
214
|
+
):
|
|
215
|
+
logger.debug(f"Handling assert locator presence error: {error.command}")
|
|
216
|
+
if retries_left > 1:
|
|
217
|
+
browser_state_summary = await browser.get_browser_state_summary()
|
|
218
|
+
memory.browser_states[-1] = BrowserState(
|
|
219
|
+
url=browser_state_summary.url,
|
|
220
|
+
screenshot=browser_state_summary.screenshot,
|
|
221
|
+
title=browser_state_summary.title,
|
|
222
|
+
axtree=browser_state_summary.dom_state.llm_representation(),
|
|
223
|
+
)
|
|
224
|
+
final_prompt, response, token_usage = error_handler_agent.classify_error(
|
|
225
|
+
error.command, memory.browser_states[-1].screenshot
|
|
226
|
+
)
|
|
227
|
+
memory.token_usage += token_usage
|
|
228
|
+
|
|
229
|
+
if response.error_type == "website_not_loaded":
|
|
230
|
+
await asyncio.sleep(5)
|
|
231
|
+
await run_interaction_action(
|
|
232
|
+
interaction_action, task, memory, browser, retries_left - 1
|
|
233
|
+
)
|
|
234
|
+
elif response.error_type == "overlay_popup_blocking":
|
|
235
|
+
close_overlay_popup_action = CloseOverlayPopupAction()
|
|
236
|
+
await handle_agentic_task(close_overlay_popup_action, task, memory, browser)
|
|
237
|
+
await run_interaction_action(
|
|
238
|
+
interaction_action, task, memory, browser, retries_left - 1
|
|
239
|
+
)
|
|
240
|
+
elif response.error_type == "fatal_error":
|
|
241
|
+
logger.error(
|
|
242
|
+
f"Fatal error running node {memory.automation_state.step_index} after {retries_left} retries: {error.original_error}. Error: {response.detailed_reason}"
|
|
243
|
+
)
|
|
244
|
+
memory.variables.output_data.append(
|
|
245
|
+
OutputData(unique_identifier="error", text=response.detailed_reason)
|
|
246
|
+
)
|
|
247
|
+
raise Exception(
|
|
248
|
+
f"Fatal error running node {memory.automation_state.step_index} after {retries_left} retries: {error.original_error}. Final reason: {response.detailed_reason}"
|
|
249
|
+
)
|
|
250
|
+
else:
|
|
251
|
+
logger.error(
|
|
252
|
+
f"Error running node {memory.automation_state.step_index} after {retries_left} retries: {error.original_error}"
|
|
253
|
+
)
|
|
254
|
+
raise error
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from optexity.inference.infra.browser import Browser
|
|
4
|
+
from optexity.schema.actions.misc_action import PythonScriptAction
|
|
5
|
+
from optexity.schema.memory import Memory
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def run_python_script_action(
|
|
11
|
+
python_script_action: PythonScriptAction, memory: Memory, browser: Browser
|
|
12
|
+
):
|
|
13
|
+
local_vars = {}
|
|
14
|
+
exec(python_script_action.execution_code, {}, local_vars)
|
|
15
|
+
|
|
16
|
+
# Get the function
|
|
17
|
+
code_fn = local_vars["code_fn"]
|
|
18
|
+
|
|
19
|
+
page = await browser.get_current_page()
|
|
20
|
+
await code_fn(page)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from datetime import timedelta
|
|
4
|
+
from urllib.parse import urljoin
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from optexity.inference.agents.two_fa_extraction.two_fa_extraction import (
|
|
9
|
+
TwoFAExtraction,
|
|
10
|
+
)
|
|
11
|
+
from optexity.schema.actions.two_fa_action import (
|
|
12
|
+
EmailTwoFAAction,
|
|
13
|
+
SlackTwoFAAction,
|
|
14
|
+
TwoFAAction,
|
|
15
|
+
)
|
|
16
|
+
from optexity.schema.inference import (
|
|
17
|
+
FetchEmailMessagesRequest,
|
|
18
|
+
FetchMessagesResponse,
|
|
19
|
+
FetchSlackMessagesRequest,
|
|
20
|
+
)
|
|
21
|
+
from optexity.schema.memory import Memory
|
|
22
|
+
from optexity.utils.settings import settings
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
two_fa_extraction_agent = TwoFAExtraction()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def run_two_fa_action(two_fa_action: TwoFAAction, memory: Memory):
|
|
30
|
+
logger.debug(
|
|
31
|
+
f"---------Running 2fa action {two_fa_action.model_dump_json()}---------"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
elapsed = 0
|
|
35
|
+
messages = None
|
|
36
|
+
|
|
37
|
+
while elapsed < two_fa_action.max_wait_time:
|
|
38
|
+
messages = await fetch_messages(
|
|
39
|
+
two_fa_action.action, memory, two_fa_action.max_wait_time
|
|
40
|
+
)
|
|
41
|
+
if messages and len(messages) > 0:
|
|
42
|
+
final_prompt, response, token_usage = two_fa_extraction_agent.extract_code(
|
|
43
|
+
two_fa_action.instructions, messages
|
|
44
|
+
)
|
|
45
|
+
memory.token_usage += token_usage
|
|
46
|
+
code = None
|
|
47
|
+
if response.code is not None:
|
|
48
|
+
if isinstance(response.code, str):
|
|
49
|
+
code = response.code
|
|
50
|
+
elif isinstance(response.code, list):
|
|
51
|
+
if len(response.code) > 0:
|
|
52
|
+
raise ValueError(f"Multiple 2FA codes found, {response.code}")
|
|
53
|
+
else:
|
|
54
|
+
code = response.code[0]
|
|
55
|
+
|
|
56
|
+
if code is not None:
|
|
57
|
+
logger.debug(
|
|
58
|
+
f"2FA code {code} found after {elapsed} seconds from {messages}"
|
|
59
|
+
)
|
|
60
|
+
break
|
|
61
|
+
logger.debug(
|
|
62
|
+
f"No 2FA code found in messages, {messages}, waiting for {two_fa_action.check_interval} seconds"
|
|
63
|
+
)
|
|
64
|
+
else:
|
|
65
|
+
logger.debug(
|
|
66
|
+
f"No messages found for 2FA code after {elapsed} seconds, waiting for {two_fa_action.check_interval} seconds"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
await asyncio.sleep(two_fa_action.check_interval)
|
|
70
|
+
elapsed += two_fa_action.check_interval
|
|
71
|
+
|
|
72
|
+
memory.automation_state.start_2fa_time = None
|
|
73
|
+
if code is None:
|
|
74
|
+
raise ValueError("2FA code not found")
|
|
75
|
+
|
|
76
|
+
memory.variables.generated_variables[two_fa_action.output_variable_name] = [code]
|
|
77
|
+
|
|
78
|
+
return code
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async def fetch_messages(
|
|
82
|
+
action: EmailTwoFAAction | SlackTwoFAAction,
|
|
83
|
+
memory: Memory,
|
|
84
|
+
max_wait_time: float,
|
|
85
|
+
):
|
|
86
|
+
|
|
87
|
+
start_2fa_time = memory.automation_state.start_2fa_time
|
|
88
|
+
end_2fa_time = memory.automation_state.start_2fa_time + timedelta(
|
|
89
|
+
seconds=max_wait_time
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
headers = {"x-api-key": settings.API_KEY}
|
|
93
|
+
|
|
94
|
+
if isinstance(action, EmailTwoFAAction):
|
|
95
|
+
url = urljoin(settings.SERVER_URL, settings.FETCH_EMAIL_MESSAGES_ENDPOINT)
|
|
96
|
+
body = FetchEmailMessagesRequest(
|
|
97
|
+
receiver_email_address=action.receiver_email_address,
|
|
98
|
+
sender_email_address=action.sender_email_address,
|
|
99
|
+
start_2fa_time=start_2fa_time,
|
|
100
|
+
end_2fa_time=end_2fa_time,
|
|
101
|
+
)
|
|
102
|
+
elif isinstance(action, SlackTwoFAAction):
|
|
103
|
+
url = urljoin(settings.SERVER_URL, settings.FETCH_SLACK_MESSAGES_ENDPOINT)
|
|
104
|
+
body = FetchSlackMessagesRequest(
|
|
105
|
+
slack_workspace_domain=action.slack_workspace_domain,
|
|
106
|
+
channel_name=action.channel_name,
|
|
107
|
+
sender_name=action.sender_name,
|
|
108
|
+
start_2fa_time=start_2fa_time,
|
|
109
|
+
end_2fa_time=end_2fa_time,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
113
|
+
|
|
114
|
+
response = await client.post(
|
|
115
|
+
url, json=body.model_dump(mode="json"), headers=headers
|
|
116
|
+
)
|
|
117
|
+
response.raise_for_status()
|
|
118
|
+
response_data = FetchMessagesResponse.model_validate(response.json())
|
|
119
|
+
|
|
120
|
+
return response_data.messages
|
|
File without changes
|
|
File without changes
|