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/utils.py ADDED
@@ -0,0 +1,291 @@
1
+ import logging
2
+ import random
3
+ import time
4
+ from typing import Dict, Optional
5
+
6
+ from requests_html import HTMLSession
7
+ import requests
8
+ from bs4 import BeautifulSoup
9
+
10
+ from metaai_api.exceptions import FacebookInvalidCredentialsException
11
+
12
+
13
+ def generate_offline_threading_id() -> str:
14
+ """
15
+ Generates an offline threading ID.
16
+
17
+ Returns:
18
+ str: The generated offline threading ID.
19
+ """
20
+ # Maximum value for a 64-bit integer in Python
21
+ max_int = (1 << 64) - 1
22
+ mask22_bits = (1 << 22) - 1
23
+
24
+ # Function to get the current timestamp in milliseconds
25
+ def get_current_timestamp():
26
+ return int(time.time() * 1000)
27
+
28
+ # Function to generate a random 64-bit integer
29
+ def get_random_64bit_int():
30
+ return random.getrandbits(64)
31
+
32
+ # Combine timestamp and random value
33
+ def combine_and_mask(timestamp, random_value):
34
+ shifted_timestamp = timestamp << 22
35
+ masked_random = random_value & mask22_bits
36
+ return (shifted_timestamp | masked_random) & max_int
37
+
38
+ timestamp = get_current_timestamp()
39
+ random_value = get_random_64bit_int()
40
+ threading_id = combine_and_mask(timestamp, random_value)
41
+
42
+ return str(threading_id)
43
+
44
+
45
+ def extract_value(text: str, start_str: str, end_str: str) -> str:
46
+ """
47
+ Helper function to extract a specific value from the given text using a key.
48
+
49
+ Args:
50
+ text (str): The text from which to extract the value.
51
+ start_str (str): The starting key.
52
+ end_str (str): The ending key.
53
+
54
+ Returns:
55
+ str: The extracted value.
56
+ """
57
+ start = text.find(start_str) + len(start_str)
58
+ end = text.find(end_str, start)
59
+ return text[start:end]
60
+
61
+
62
+ def format_response(response: dict) -> str:
63
+ """
64
+ Formats the response from Meta AI to remove unnecessary characters.
65
+
66
+ Args:
67
+ response (dict): The dictionary containing the response to format.
68
+
69
+ Returns:
70
+ str: The formatted response.
71
+ """
72
+ text = ""
73
+ for content in (
74
+ response.get("data", {})
75
+ .get("node", {})
76
+ .get("bot_response_message", {})
77
+ .get("composed_text", {})
78
+ .get("content", [])
79
+ ):
80
+ text += content["text"] + "\n"
81
+ return text
82
+
83
+
84
+ # Function to perform the login
85
+ def get_fb_session(email, password, proxies=None):
86
+ login_url = "https://www.facebook.com/login/?next"
87
+ headers = {
88
+ "authority": "mbasic.facebook.com",
89
+ "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
90
+ "accept-language": "en-US,en;q=0.9",
91
+ "sec-ch-ua": '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"',
92
+ "sec-ch-ua-mobile": "?0",
93
+ "sec-ch-ua-platform": '"macOS"',
94
+ "sec-fetch-dest": "document",
95
+ "sec-fetch-mode": "navigate",
96
+ "sec-fetch-site": "none",
97
+ "sec-fetch-user": "?1",
98
+ "upgrade-insecure-requests": "1",
99
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
100
+ }
101
+ # Send the GET request
102
+ response = requests.get(login_url, headers=headers, proxies=proxies)
103
+ soup = BeautifulSoup(response.text, "html.parser")
104
+
105
+ # Parse necessary parameters from the login form
106
+ lsd = soup.find("input", {"name": "lsd"})["value"]
107
+ jazoest = soup.find("input", {"name": "jazoest"})["value"]
108
+
109
+ # Define the URL and body for the POST request to submit the login form
110
+ post_url = "https://www.facebook.com/login/?next"
111
+ data = {
112
+ "lsd": lsd,
113
+ "jazoest": jazoest,
114
+ "login_source": "comet_headerless_login",
115
+ "email": email,
116
+ "pass": password,
117
+ "login": "1",
118
+ "next": None,
119
+ }
120
+
121
+ headers = {
122
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0",
123
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
124
+ "Accept-Language": "en-US,en;q=0.5",
125
+ "Accept-Encoding": None,
126
+ "Referer": "https://www.facebook.com/",
127
+ "Content-Type": "application/x-www-form-urlencoded",
128
+ "Origin": "https://www.facebook.com",
129
+ "DNT": "1",
130
+ "Sec-GPC": "1",
131
+ "Connection": "keep-alive",
132
+ "cookie": f"datr={response.cookies.get('datr')};",
133
+ "Upgrade-Insecure-Requests": "1",
134
+ "Sec-Fetch-Dest": "document",
135
+ "Sec-Fetch-Mode": "navigate",
136
+ "Sec-Fetch-Site": "same-origin",
137
+ "Sec-Fetch-User": "?1",
138
+ "Priority": "u=0, i",
139
+ }
140
+
141
+ from requests import cookies
142
+
143
+ # Send the POST request
144
+ session = requests.session()
145
+ jar = cookies.RequestsCookieJar()
146
+ session.proxies = proxies
147
+ session.cookies = jar
148
+
149
+ result = session.post(post_url, headers=headers, data=data)
150
+ if "sb" not in jar or "xs" not in jar:
151
+ raise FacebookInvalidCredentialsException(
152
+ "Was not able to login to Facebook. Please check your credentials. "
153
+ "You may also have been rate limited. Try to connect to Facebook manually."
154
+ )
155
+
156
+ cookies = {
157
+ **result.cookies.get_dict(),
158
+ "sb": jar["sb"],
159
+ "xs": jar["xs"],
160
+ "fr": jar["fr"],
161
+ "c_user": jar["c_user"],
162
+ }
163
+
164
+ response_login = {
165
+ "cookies": cookies,
166
+ "headers": result.headers,
167
+ "response": response.text,
168
+ }
169
+ meta_ai_cookies = get_cookies()
170
+
171
+ url = "https://www.meta.ai/state/"
172
+
173
+ payload = f'__a=1&lsd={meta_ai_cookies["lsd"]}'
174
+ headers = {
175
+ "authority": "www.meta.ai",
176
+ "accept": "*/*",
177
+ "accept-language": "en-US,en;q=0.9",
178
+ "cache-control": "no-cache",
179
+ "content-type": "application/x-www-form-urlencoded",
180
+ "cookie": f'ps_n=1; ps_l=1; dpr=2; _js_datr={meta_ai_cookies["_js_datr"]}; abra_csrf={meta_ai_cookies["abra_csrf"]}; datr={meta_ai_cookies["datr"]};; ps_l=1; ps_n=1',
181
+ "origin": "https://www.meta.ai",
182
+ "pragma": "no-cache",
183
+ "referer": "https://www.meta.ai/",
184
+ "sec-fetch-mode": "cors",
185
+ "sec-fetch-site": "same-origin",
186
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
187
+ }
188
+
189
+ response = requests.request(
190
+ "POST", url, headers=headers, data=payload, proxies=proxies
191
+ )
192
+
193
+ state = extract_value(response.text, start_str='"state":"', end_str='"')
194
+
195
+ url = f"https://www.facebook.com/oidc/?app_id=1358015658191005&scope=openid%20linking&response_type=code&redirect_uri=https%3A%2F%2Fwww.meta.ai%2Fauth%2F&no_universal_links=1&deoia=1&state={state}"
196
+ payload = {}
197
+ headers = {
198
+ "authority": "www.facebook.com",
199
+ "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
200
+ "accept-language": "en-US,en;q=0.9",
201
+ "cache-control": "no-cache",
202
+ "cookie": f"datr={response_login['cookies']['datr']}; sb={response_login['cookies']['sb']}; c_user={response_login['cookies']['c_user']}; xs={response_login['cookies']['xs']}; fr={response_login['cookies']['fr']}; abra_csrf={meta_ai_cookies['abra_csrf']};",
203
+ "sec-fetch-dest": "document",
204
+ "sec-fetch-mode": "navigate",
205
+ "sec-fetch-site": "cross-site",
206
+ "sec-fetch-user": "?1",
207
+ "upgrade-insecure-requests": "1",
208
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
209
+ }
210
+ session = requests.session()
211
+ session.proxies = proxies
212
+ response = session.get(url, headers=headers, data=payload, allow_redirects=False)
213
+
214
+ next_url = response.headers["Location"]
215
+
216
+ url = next_url
217
+
218
+ payload = {}
219
+ headers = {
220
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.0",
221
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
222
+ "Accept-Language": "en-US,en;q=0.5",
223
+ "Accept-Encoding": "gzip, deflate, br",
224
+ "Referer": "https://www.meta.ai/",
225
+ "Connection": "keep-alive",
226
+ "Cookie": f'dpr=2; abra_csrf={meta_ai_cookies["abra_csrf"]}; datr={meta_ai_cookies["_js_datr"]}',
227
+ "Upgrade-Insecure-Requests": "1",
228
+ "Sec-Fetch-Dest": "document",
229
+ "Sec-Fetch-Mode": "navigate",
230
+ "Sec-Fetch-Site": "cross-site",
231
+ "Sec-Fetch-User": "?1",
232
+ "TE": "trailers",
233
+ }
234
+ session.get(url, headers=headers, data=payload)
235
+ cookies = session.cookies.get_dict()
236
+ if "abra_sess" not in cookies:
237
+ raise FacebookInvalidCredentialsException(
238
+ "Was not able to login to Facebook. Please check your credentials. "
239
+ "You may also have been rate limited. Try to connect to Facebook manually."
240
+ )
241
+ logging.info("Successfully logged in to Facebook.")
242
+ return cookies
243
+
244
+
245
+ def get_cookies() -> dict:
246
+ """
247
+ Extracts necessary cookies from the Meta AI main page.
248
+
249
+ Returns:
250
+ dict: A dictionary containing essential cookies.
251
+ """
252
+ session = HTMLSession()
253
+ response = session.get("https://www.meta.ai/")
254
+ return {
255
+ "_js_datr": extract_value(
256
+ response.text, start_str='_js_datr":{"value":"', end_str='",'
257
+ ),
258
+ "abra_csrf": extract_value(
259
+ response.text, start_str='abra_csrf":{"value":"', end_str='",'
260
+ ),
261
+ "datr": extract_value(
262
+ response.text, start_str='datr":{"value":"', end_str='",'
263
+ ),
264
+ "lsd": extract_value(
265
+ response.text, start_str='"LSD",[],{"token":"', end_str='"}'
266
+ ),
267
+ }
268
+
269
+
270
+ def get_session(
271
+ proxy: Optional[Dict] = None, test_url: str = "https://api.ipify.org/?format=json"
272
+ ) -> requests.Session:
273
+ """
274
+ Get a session with the proxy set.
275
+
276
+ Args:
277
+ proxy (Dict): The proxy to use
278
+ test_url (str): A test site from which we check that the proxy is installed correctly.
279
+
280
+ Returns:
281
+ requests.Session: A session with the proxy set.
282
+ """
283
+ session = requests.Session()
284
+ if not proxy:
285
+ return session
286
+ response = session.get(test_url, proxies=proxy, timeout=10)
287
+ if response.status_code == 200:
288
+ session.proxies = proxy
289
+ return session
290
+ else:
291
+ raise Exception("Proxy is not working.")