metaai-sdk 2.0.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.
- metaai_api/__init__.py +20 -0
- metaai_api/api_server.py +256 -0
- metaai_api/client.py +140 -0
- metaai_api/exceptions.py +6 -0
- metaai_api/main.py +534 -0
- metaai_api/utils.py +291 -0
- metaai_api/video_generation.py +679 -0
- metaai_sdk-2.0.0.dist-info/METADATA +846 -0
- metaai_sdk-2.0.0.dist-info/RECORD +12 -0
- metaai_sdk-2.0.0.dist-info/WHEEL +5 -0
- metaai_sdk-2.0.0.dist-info/licenses/LICENSE +28 -0
- metaai_sdk-2.0.0.dist-info/top_level.txt +1 -0
metaai_api/main.py
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import time
|
|
4
|
+
import urllib.parse
|
|
5
|
+
import uuid
|
|
6
|
+
from typing import Dict, List, Generator, Iterator, Optional, Union
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
from requests_html import HTMLSession
|
|
10
|
+
|
|
11
|
+
from metaai_api.utils import (
|
|
12
|
+
generate_offline_threading_id,
|
|
13
|
+
extract_value,
|
|
14
|
+
format_response,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from metaai_api.utils import get_fb_session, get_session
|
|
18
|
+
|
|
19
|
+
from metaai_api.exceptions import FacebookRegionBlocked
|
|
20
|
+
|
|
21
|
+
MAX_RETRIES = 3
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class MetaAI:
|
|
25
|
+
"""
|
|
26
|
+
A class to interact with the Meta AI API to obtain and use access tokens for sending
|
|
27
|
+
and receiving messages from the Meta AI Chat API.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self, fb_email: Optional[str] = None, fb_password: Optional[str] = None, cookies: Optional[dict] = None, proxy: Optional[dict] = None
|
|
32
|
+
):
|
|
33
|
+
self.session = get_session()
|
|
34
|
+
self.session.headers.update(
|
|
35
|
+
{
|
|
36
|
+
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
|
|
37
|
+
"(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
self.access_token = None
|
|
41
|
+
self.fb_email = fb_email
|
|
42
|
+
self.fb_password = fb_password
|
|
43
|
+
self.proxy = proxy
|
|
44
|
+
|
|
45
|
+
self.is_authed = (fb_password is not None and fb_email is not None) or cookies is not None
|
|
46
|
+
|
|
47
|
+
if cookies is not None:
|
|
48
|
+
self.cookies = cookies
|
|
49
|
+
# Auto-fetch lsd and fb_dtsg if not present in cookies
|
|
50
|
+
if "lsd" not in self.cookies or "fb_dtsg" not in self.cookies:
|
|
51
|
+
self._fetch_missing_tokens()
|
|
52
|
+
else:
|
|
53
|
+
self.cookies = self.get_cookies()
|
|
54
|
+
|
|
55
|
+
self.external_conversation_id = None
|
|
56
|
+
self.offline_threading_id = None
|
|
57
|
+
|
|
58
|
+
def _fetch_missing_tokens(self):
|
|
59
|
+
"""
|
|
60
|
+
Fetch lsd and fb_dtsg tokens if they're missing from cookies.
|
|
61
|
+
"""
|
|
62
|
+
try:
|
|
63
|
+
cookies_str = "; ".join([f"{k}={v}" for k, v in self.cookies.items() if v])
|
|
64
|
+
|
|
65
|
+
session = HTMLSession()
|
|
66
|
+
headers = {"cookie": cookies_str}
|
|
67
|
+
response = session.get("https://www.meta.ai/", headers=headers)
|
|
68
|
+
|
|
69
|
+
if "lsd" not in self.cookies:
|
|
70
|
+
lsd = extract_value(response.text, start_str='"LSD",[],{"token":"', end_str='"')
|
|
71
|
+
if lsd:
|
|
72
|
+
self.cookies["lsd"] = lsd
|
|
73
|
+
|
|
74
|
+
if "fb_dtsg" not in self.cookies:
|
|
75
|
+
fb_dtsg = extract_value(response.text, start_str='DTSGInitData",[],{"token":"', end_str='"')
|
|
76
|
+
if fb_dtsg:
|
|
77
|
+
self.cookies["fb_dtsg"] = fb_dtsg
|
|
78
|
+
except Exception as e:
|
|
79
|
+
pass # Silent fail, features may not work without tokens
|
|
80
|
+
|
|
81
|
+
def get_access_token(self) -> str:
|
|
82
|
+
"""
|
|
83
|
+
Retrieves an access token using Meta's authentication API.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
str: A valid access token.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
if self.access_token:
|
|
90
|
+
return self.access_token
|
|
91
|
+
|
|
92
|
+
url = "https://www.meta.ai/api/graphql/"
|
|
93
|
+
payload = {
|
|
94
|
+
"lsd": self.cookies["lsd"],
|
|
95
|
+
"fb_api_caller_class": "RelayModern",
|
|
96
|
+
"fb_api_req_friendly_name": "useAbraAcceptTOSForTempUserMutation",
|
|
97
|
+
"variables": {
|
|
98
|
+
"dob": "1999-01-01",
|
|
99
|
+
"icebreaker_type": "TEXT",
|
|
100
|
+
"__relay_internal__pv__WebPixelRatiorelayprovider": 1,
|
|
101
|
+
},
|
|
102
|
+
"doc_id": "7604648749596940",
|
|
103
|
+
}
|
|
104
|
+
payload = urllib.parse.urlencode(payload) # noqa
|
|
105
|
+
headers = {
|
|
106
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
107
|
+
"cookie": f'_js_datr={self.cookies["_js_datr"]}; '
|
|
108
|
+
f'abra_csrf={self.cookies["abra_csrf"]}; datr={self.cookies["datr"]};',
|
|
109
|
+
"sec-fetch-site": "same-origin",
|
|
110
|
+
"x-fb-friendly-name": "useAbraAcceptTOSForTempUserMutation",
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
response = self.session.post(url, headers=headers, data=payload)
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
auth_json = response.json()
|
|
117
|
+
except json.JSONDecodeError:
|
|
118
|
+
raise FacebookRegionBlocked(
|
|
119
|
+
"Unable to receive a valid response from Meta AI. This is likely due to your region being blocked. "
|
|
120
|
+
"Try manually accessing https://www.meta.ai/ to confirm."
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
access_token = auth_json["data"]["xab_abra_accept_terms_of_service"][
|
|
124
|
+
"new_temp_user_auth"
|
|
125
|
+
]["access_token"]
|
|
126
|
+
|
|
127
|
+
# Need to sleep for a bit, for some reason the API doesn't like it when we send request too quickly
|
|
128
|
+
# (maybe Meta needs to register Cookies on their side?)
|
|
129
|
+
time.sleep(1)
|
|
130
|
+
|
|
131
|
+
return access_token
|
|
132
|
+
|
|
133
|
+
def prompt(
|
|
134
|
+
self,
|
|
135
|
+
message: str,
|
|
136
|
+
stream: bool = False,
|
|
137
|
+
attempts: int = 0,
|
|
138
|
+
new_conversation: bool = False,
|
|
139
|
+
images: Optional[list] = None,
|
|
140
|
+
) -> Union[Dict, Generator[Dict, None, None]]:
|
|
141
|
+
"""
|
|
142
|
+
Sends a message to the Meta AI and returns the response.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
message (str): The message to send.
|
|
146
|
+
stream (bool): Whether to stream the response or not. Defaults to False.
|
|
147
|
+
attempts (int): The number of attempts to retry if an error occurs. Defaults to 0.
|
|
148
|
+
new_conversation (bool): Whether to start a new conversation or not. Defaults to False.
|
|
149
|
+
images (list): List of image URLs to animate (for video generation). Defaults to None.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
dict: A dictionary containing the response message and sources.
|
|
153
|
+
|
|
154
|
+
Raises:
|
|
155
|
+
Exception: If unable to obtain a valid response after several attempts.
|
|
156
|
+
"""
|
|
157
|
+
if not self.is_authed:
|
|
158
|
+
self.access_token = self.get_access_token()
|
|
159
|
+
auth_payload = {"access_token": self.access_token}
|
|
160
|
+
url = "https://graph.meta.ai/graphql?locale=user"
|
|
161
|
+
|
|
162
|
+
else:
|
|
163
|
+
auth_payload = {"fb_dtsg": self.cookies["fb_dtsg"]}
|
|
164
|
+
url = "https://www.meta.ai/api/graphql/"
|
|
165
|
+
|
|
166
|
+
if not self.external_conversation_id or new_conversation:
|
|
167
|
+
external_id = str(uuid.uuid4())
|
|
168
|
+
self.external_conversation_id = external_id
|
|
169
|
+
|
|
170
|
+
# Handle video generation with images
|
|
171
|
+
flash_video_input = {"images": []}
|
|
172
|
+
if images:
|
|
173
|
+
flash_video_input = {"images": images}
|
|
174
|
+
|
|
175
|
+
payload = {
|
|
176
|
+
**auth_payload,
|
|
177
|
+
"fb_api_caller_class": "RelayModern",
|
|
178
|
+
"fb_api_req_friendly_name": "useAbraSendMessageMutation",
|
|
179
|
+
"variables": json.dumps(
|
|
180
|
+
{
|
|
181
|
+
"message": {"sensitive_string_value": message},
|
|
182
|
+
"externalConversationId": self.external_conversation_id,
|
|
183
|
+
"offlineThreadingId": generate_offline_threading_id(),
|
|
184
|
+
"suggestedPromptIndex": None,
|
|
185
|
+
"flashVideoRecapInput": flash_video_input,
|
|
186
|
+
"flashPreviewInput": None,
|
|
187
|
+
"promptPrefix": None,
|
|
188
|
+
"entrypoint": "ABRA__CHAT__TEXT",
|
|
189
|
+
"icebreaker_type": "TEXT",
|
|
190
|
+
"__relay_internal__pv__AbraDebugDevOnlyrelayprovider": False,
|
|
191
|
+
"__relay_internal__pv__WebPixelRatiorelayprovider": 1,
|
|
192
|
+
}
|
|
193
|
+
),
|
|
194
|
+
"server_timestamps": "true",
|
|
195
|
+
"doc_id": "7783822248314888",
|
|
196
|
+
}
|
|
197
|
+
payload = urllib.parse.urlencode(payload) # noqa
|
|
198
|
+
headers = {
|
|
199
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
200
|
+
"x-fb-friendly-name": "useAbraSendMessageMutation",
|
|
201
|
+
}
|
|
202
|
+
if self.is_authed:
|
|
203
|
+
headers["cookie"] = f'abra_sess={self.cookies["abra_sess"]}'
|
|
204
|
+
# Recreate the session to avoid cookie leakage when user is authenticated
|
|
205
|
+
self.session = requests.Session()
|
|
206
|
+
if self.proxy:
|
|
207
|
+
self.session.proxies = self.proxy
|
|
208
|
+
|
|
209
|
+
response = self.session.post(url, headers=headers, data=payload, stream=stream)
|
|
210
|
+
if not stream:
|
|
211
|
+
raw_response = response.text
|
|
212
|
+
last_streamed_response = self.extract_last_response(raw_response)
|
|
213
|
+
if not last_streamed_response:
|
|
214
|
+
return self.retry(message, stream=stream, attempts=attempts)
|
|
215
|
+
|
|
216
|
+
extracted_data = self.extract_data(last_streamed_response)
|
|
217
|
+
return extracted_data
|
|
218
|
+
|
|
219
|
+
else:
|
|
220
|
+
lines = response.iter_lines()
|
|
221
|
+
is_error = json.loads(next(lines))
|
|
222
|
+
if len(is_error.get("errors", [])) > 0:
|
|
223
|
+
return self.retry(message, stream=stream, attempts=attempts)
|
|
224
|
+
return self.stream_response(lines)
|
|
225
|
+
|
|
226
|
+
def retry(self, message: str, stream: bool = False, attempts: int = 0):
|
|
227
|
+
"""
|
|
228
|
+
Retries the prompt function if an error occurs.
|
|
229
|
+
"""
|
|
230
|
+
if attempts <= MAX_RETRIES:
|
|
231
|
+
logging.warning(
|
|
232
|
+
f"Was unable to obtain a valid response from Meta AI. Retrying... Attempt {attempts + 1}/{MAX_RETRIES}."
|
|
233
|
+
)
|
|
234
|
+
time.sleep(3)
|
|
235
|
+
return self.prompt(message, stream=stream, attempts=attempts + 1)
|
|
236
|
+
else:
|
|
237
|
+
raise Exception(
|
|
238
|
+
"Unable to obtain a valid response from Meta AI. Try again later."
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
def extract_last_response(self, response: str) -> Optional[Dict]:
|
|
242
|
+
"""
|
|
243
|
+
Extracts the last response from the Meta AI API.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
response (str): The response to extract the last response from.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
dict: A dictionary containing the last response.
|
|
250
|
+
"""
|
|
251
|
+
last_streamed_response = None
|
|
252
|
+
for line in response.split("\n"):
|
|
253
|
+
try:
|
|
254
|
+
json_line = json.loads(line)
|
|
255
|
+
except json.JSONDecodeError:
|
|
256
|
+
continue
|
|
257
|
+
|
|
258
|
+
bot_response_message = (
|
|
259
|
+
json_line.get("data", {})
|
|
260
|
+
.get("node", {})
|
|
261
|
+
.get("bot_response_message", {})
|
|
262
|
+
)
|
|
263
|
+
chat_id = bot_response_message.get("id")
|
|
264
|
+
if chat_id:
|
|
265
|
+
external_conversation_id, offline_threading_id, _ = chat_id.split("_")
|
|
266
|
+
self.external_conversation_id = external_conversation_id
|
|
267
|
+
self.offline_threading_id = offline_threading_id
|
|
268
|
+
|
|
269
|
+
streaming_state = bot_response_message.get("streaming_state")
|
|
270
|
+
if streaming_state == "OVERALL_DONE":
|
|
271
|
+
last_streamed_response = json_line
|
|
272
|
+
|
|
273
|
+
return last_streamed_response
|
|
274
|
+
|
|
275
|
+
def stream_response(self, lines: Iterator[str]):
|
|
276
|
+
"""
|
|
277
|
+
Streams the response from the Meta AI API.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
lines (Iterator[str]): The lines to stream.
|
|
281
|
+
|
|
282
|
+
Yields:
|
|
283
|
+
dict: A dictionary containing the response message and sources.
|
|
284
|
+
"""
|
|
285
|
+
for line in lines:
|
|
286
|
+
if line:
|
|
287
|
+
json_line = json.loads(line)
|
|
288
|
+
extracted_data = self.extract_data(json_line)
|
|
289
|
+
if not extracted_data.get("message"):
|
|
290
|
+
continue
|
|
291
|
+
yield extracted_data
|
|
292
|
+
|
|
293
|
+
def extract_data(self, json_line: dict):
|
|
294
|
+
"""
|
|
295
|
+
Extract data and sources from a parsed JSON line.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
json_line (dict): Parsed JSON line.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
Tuple (str, list): Response message and list of sources.
|
|
302
|
+
"""
|
|
303
|
+
bot_response_message = (
|
|
304
|
+
json_line.get("data", {}).get("node", {}).get("bot_response_message", {})
|
|
305
|
+
)
|
|
306
|
+
response = format_response(response=json_line)
|
|
307
|
+
fetch_id = bot_response_message.get("fetch_id")
|
|
308
|
+
sources = self.fetch_sources(fetch_id) if fetch_id else []
|
|
309
|
+
medias = self.extract_media(bot_response_message)
|
|
310
|
+
return {"message": response, "sources": sources, "media": medias}
|
|
311
|
+
|
|
312
|
+
@staticmethod
|
|
313
|
+
def extract_media(json_line: dict) -> List[Dict]:
|
|
314
|
+
"""
|
|
315
|
+
Extract media from a parsed JSON line.
|
|
316
|
+
Supports images from imagine_card and videos from various fields.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
json_line (dict): Parsed JSON line.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
list: A list of dictionaries containing the extracted media.
|
|
323
|
+
"""
|
|
324
|
+
medias = []
|
|
325
|
+
|
|
326
|
+
# Extract images from imagine_card (standard image generation)
|
|
327
|
+
imagine_card = json_line.get("imagine_card", {})
|
|
328
|
+
session = imagine_card.get("session", {}) if imagine_card else {}
|
|
329
|
+
media_sets = (
|
|
330
|
+
(json_line.get("imagine_card", {}).get("session", {}).get("media_sets", []))
|
|
331
|
+
if imagine_card and session
|
|
332
|
+
else []
|
|
333
|
+
)
|
|
334
|
+
for media_set in media_sets:
|
|
335
|
+
imagine_media = media_set.get("imagine_media", [])
|
|
336
|
+
for media in imagine_media:
|
|
337
|
+
medias.append(
|
|
338
|
+
{
|
|
339
|
+
"url": media.get("uri"),
|
|
340
|
+
"type": media.get("media_type"),
|
|
341
|
+
"prompt": media.get("prompt"),
|
|
342
|
+
}
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
# Extract from image_attachments (may contain both images and videos)
|
|
346
|
+
image_attachments = json_line.get("image_attachments", [])
|
|
347
|
+
if isinstance(image_attachments, list):
|
|
348
|
+
for attachment in image_attachments:
|
|
349
|
+
if isinstance(attachment, dict):
|
|
350
|
+
# Check for video URLs
|
|
351
|
+
uri = attachment.get("uri") or attachment.get("url")
|
|
352
|
+
if uri:
|
|
353
|
+
media_type = "VIDEO" if ".mp4" in uri.lower() or ".m4v" in uri.lower() else "IMAGE"
|
|
354
|
+
medias.append(
|
|
355
|
+
{
|
|
356
|
+
"url": uri,
|
|
357
|
+
"type": media_type,
|
|
358
|
+
"prompt": attachment.get("prompt"),
|
|
359
|
+
}
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
# Extract videos from video_generation field (if present)
|
|
363
|
+
video_generation = json_line.get("video_generation", {})
|
|
364
|
+
if isinstance(video_generation, dict):
|
|
365
|
+
video_media_sets = video_generation.get("media_sets", [])
|
|
366
|
+
for media_set in video_media_sets:
|
|
367
|
+
video_media = media_set.get("video_media", [])
|
|
368
|
+
for media in video_media:
|
|
369
|
+
uri = media.get("uri")
|
|
370
|
+
if uri: # Only add if URI is not null
|
|
371
|
+
medias.append(
|
|
372
|
+
{
|
|
373
|
+
"url": uri,
|
|
374
|
+
"type": "VIDEO",
|
|
375
|
+
"prompt": media.get("prompt"),
|
|
376
|
+
}
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
# Extract from direct video fields
|
|
380
|
+
for possible_video_field in ["video_media", "generated_video", "reels"]:
|
|
381
|
+
field_data = json_line.get(possible_video_field)
|
|
382
|
+
if field_data:
|
|
383
|
+
if isinstance(field_data, list):
|
|
384
|
+
for item in field_data:
|
|
385
|
+
if isinstance(item, dict) and ("uri" in item or "url" in item):
|
|
386
|
+
url = item.get("uri") or item.get("url")
|
|
387
|
+
if url: # Only add if URL is not null
|
|
388
|
+
medias.append(
|
|
389
|
+
{
|
|
390
|
+
"url": url,
|
|
391
|
+
"type": "VIDEO",
|
|
392
|
+
"prompt": item.get("prompt"),
|
|
393
|
+
}
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
return medias
|
|
397
|
+
|
|
398
|
+
def get_cookies(self) -> dict:
|
|
399
|
+
"""
|
|
400
|
+
Extracts necessary cookies from the Meta AI main page.
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
dict: A dictionary containing essential cookies.
|
|
404
|
+
"""
|
|
405
|
+
session = HTMLSession()
|
|
406
|
+
headers = {}
|
|
407
|
+
fb_session = None
|
|
408
|
+
if self.fb_email is not None and self.fb_password is not None:
|
|
409
|
+
fb_session = get_fb_session(self.fb_email, self.fb_password)
|
|
410
|
+
headers = {"cookie": f"abra_sess={fb_session['abra_sess']}"}
|
|
411
|
+
response = session.get(
|
|
412
|
+
"https://www.meta.ai/",
|
|
413
|
+
headers=headers,
|
|
414
|
+
)
|
|
415
|
+
cookies = {
|
|
416
|
+
"_js_datr": extract_value(
|
|
417
|
+
response.text, start_str='_js_datr":{"value":"', end_str='",'
|
|
418
|
+
),
|
|
419
|
+
"datr": extract_value(
|
|
420
|
+
response.text, start_str='datr":{"value":"', end_str='",'
|
|
421
|
+
),
|
|
422
|
+
"lsd": extract_value(
|
|
423
|
+
response.text, start_str='"LSD",[],{"token":"', end_str='"}'
|
|
424
|
+
),
|
|
425
|
+
"fb_dtsg": extract_value(
|
|
426
|
+
response.text, start_str='DTSGInitData",[],{"token":"', end_str='"'
|
|
427
|
+
),
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if len(headers) > 0 and fb_session is not None:
|
|
431
|
+
cookies["abra_sess"] = fb_session["abra_sess"]
|
|
432
|
+
else:
|
|
433
|
+
cookies["abra_csrf"] = extract_value(
|
|
434
|
+
response.text, start_str='abra_csrf":{"value":"', end_str='",'
|
|
435
|
+
)
|
|
436
|
+
return cookies
|
|
437
|
+
|
|
438
|
+
def fetch_sources(self, fetch_id: str) -> List[Dict]:
|
|
439
|
+
"""
|
|
440
|
+
Fetches sources from the Meta AI API based on the given query.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
fetch_id (str): The fetch ID to use for the query.
|
|
444
|
+
|
|
445
|
+
Returns:
|
|
446
|
+
list: A list of dictionaries containing the fetched sources.
|
|
447
|
+
"""
|
|
448
|
+
|
|
449
|
+
url = "https://graph.meta.ai/graphql?locale=user"
|
|
450
|
+
payload = {
|
|
451
|
+
"access_token": self.access_token,
|
|
452
|
+
"fb_api_caller_class": "RelayModern",
|
|
453
|
+
"fb_api_req_friendly_name": "AbraSearchPluginDialogQuery",
|
|
454
|
+
"variables": json.dumps({"abraMessageFetchID": fetch_id}),
|
|
455
|
+
"server_timestamps": "true",
|
|
456
|
+
"doc_id": "6946734308765963",
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
payload = urllib.parse.urlencode(payload) # noqa
|
|
460
|
+
|
|
461
|
+
headers = {
|
|
462
|
+
"authority": "graph.meta.ai",
|
|
463
|
+
"accept-language": "en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7",
|
|
464
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
465
|
+
"cookie": f'dpr=2; abra_csrf={self.cookies.get("abra_csrf")}; datr={self.cookies.get("datr")}; ps_n=1; ps_l=1',
|
|
466
|
+
"x-fb-friendly-name": "AbraSearchPluginDialogQuery",
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
response = self.session.post(url, headers=headers, data=payload)
|
|
470
|
+
response_json = response.json()
|
|
471
|
+
message = response_json.get("data", {}).get("message", {})
|
|
472
|
+
search_results = (
|
|
473
|
+
(response_json.get("data", {}).get("message", {}).get("searchResults"))
|
|
474
|
+
if message
|
|
475
|
+
else None
|
|
476
|
+
)
|
|
477
|
+
if search_results is None:
|
|
478
|
+
return []
|
|
479
|
+
|
|
480
|
+
references = search_results["references"]
|
|
481
|
+
return references
|
|
482
|
+
|
|
483
|
+
def generate_video(
|
|
484
|
+
self,
|
|
485
|
+
prompt: str,
|
|
486
|
+
wait_before_poll: int = 10,
|
|
487
|
+
max_attempts: int = 30,
|
|
488
|
+
wait_seconds: int = 5,
|
|
489
|
+
verbose: bool = True
|
|
490
|
+
) -> Dict:
|
|
491
|
+
"""
|
|
492
|
+
Generate a video from a text prompt using Meta AI.
|
|
493
|
+
Automatically fetches lsd and fb_dtsg tokens from cookies.
|
|
494
|
+
|
|
495
|
+
Args:
|
|
496
|
+
prompt: Text prompt for video generation
|
|
497
|
+
wait_before_poll: Seconds to wait before starting to poll (default: 10)
|
|
498
|
+
max_attempts: Maximum polling attempts (default: 30)
|
|
499
|
+
wait_seconds: Seconds between polling attempts (default: 5)
|
|
500
|
+
verbose: Whether to print status messages (default: True)
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
Dictionary with success status, conversation_id, prompt, video_urls, and timestamp
|
|
504
|
+
|
|
505
|
+
Example:
|
|
506
|
+
ai = MetaAI(cookies={"datr": "...", "abra_sess": "..."})
|
|
507
|
+
result = ai.generate_video("Generate a video of a sunset")
|
|
508
|
+
if result["success"]:
|
|
509
|
+
print(f"Video URLs: {result['video_urls']}")
|
|
510
|
+
"""
|
|
511
|
+
from metaai_api.video_generation import VideoGenerator
|
|
512
|
+
|
|
513
|
+
# Convert cookies dict to string format if needed
|
|
514
|
+
if isinstance(self.cookies, dict):
|
|
515
|
+
cookies_str = "; ".join([f"{k}={v}" for k, v in self.cookies.items() if v])
|
|
516
|
+
else:
|
|
517
|
+
cookies_str = str(self.cookies)
|
|
518
|
+
|
|
519
|
+
# Use VideoGenerator for video generation
|
|
520
|
+
video_gen = VideoGenerator(cookies_str=cookies_str)
|
|
521
|
+
|
|
522
|
+
return video_gen.generate_video(
|
|
523
|
+
prompt=prompt,
|
|
524
|
+
wait_before_poll=wait_before_poll,
|
|
525
|
+
max_attempts=max_attempts,
|
|
526
|
+
wait_seconds=wait_seconds,
|
|
527
|
+
verbose=verbose
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
if __name__ == "__main__":
|
|
532
|
+
meta = MetaAI()
|
|
533
|
+
resp = meta.prompt("What was the Warriors score last game?", stream=False)
|
|
534
|
+
print(resp)
|