Full Code of Strvm/meta-ai-api for AI

main 89655182ef3e cached
11 files
39.0 KB
10.2k tokens
19 symbols
1 requests
Download .txt
Repository: Strvm/meta-ai-api
Branch: main
Commit: 89655182ef3e
Files: 11
Total size: 39.0 KB

Directory structure:
gitextract_aa7abxek/

├── .github/
│   └── workflows/
│       └── python-publish.yml
├── .gitignore
├── README.md
├── pyproject.toml
├── requirements.txt
├── setup.cfg
├── setup.py
└── src/
    └── meta_ai_api/
        ├── __init__.py
        ├── exceptions.py
        ├── main.py
        └── utils.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/python-publish.yml
================================================
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Upload Python Package

on:
  release:
    types: [published]

permissions:
  contents: read

jobs:
  deploy:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Set up Python
      uses: actions/setup-python@v3
      with:
        python-version: '3.x'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install build
        pip install wheel
        pip install setuptools
    - name: Build package
      run: python3 setup.py sdist bdist_wheel
    - name: Publish package
      uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
      with:
        user: __token__
        password: ${{ secrets.PYPI_API_TOKEN }}

================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.idea
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

================================================
FILE: README.md
================================================
# MetaAI API Wrapper

MetaAI is a Python library designed to interact with Meta's AI APIs that run in the backend of https://www.meta.ai/. It encapsulates the complexities of authentication and communication with the APIs, providing a straightforward interface for sending queries and receiving responses.

With this you can easily prompt the AI with a message and get a response, directly from your Python code. **NO API KEY REQUIRED**

**Meta AI is connected to the internet, so you will be able to get the latest real-time responses from the AI.** (powered by Bing)

Meta AI is running Llama 3 LLM.

## Features
- **Prompt AI**: Send a message to the AI and get a response from Llama 3.
- **Image Generation**: Generate images using the AI. (Only for FB authenticated users)
- **Get Up To Date Information**: Get the latest information from the AI thanks to its connection to the internet.
- **Get Sources**: Get the sources of the information provided by the AI.
- **Streaming**: Stream the AI's response in real-time or get the final response.
- **Follow Conversations**: Start a new conversation or follow up on an existing one.

## Usage
**Download**:

   ```bash
   pip install meta-ai-api
   ```
   
**Initialization**:

```python
from meta_ai_api import MetaAI

ai = MetaAI()
response = ai.prompt(message="Whats the weather in San Francisco today? And what is the date?")
print(response)
 
```
```json
{
   "message":"The weather in San Francisco today is mostly clear to overcast, with no precipitation, a wind speed between 0 and 8 miles per hour and temperatures ranging from 51 to 55 degrees Fahrenheit ¹. The date is Friday, April 19, 2024 ². Please note that the weather forecast is continually changing ³ ⁴ ⁵ ⁶.\n",
   "sources":[
      {
         "link":"https://www.wolframalpha.com/input?i=San+Francisco+weather+today+and+date",
         "title":"WolframAlpha"
      },
      {
         "link":"https://www.timeanddate.com/weather/usa/san-francisco",
         "title":"Weather for San Francisco, California, USA - timeanddate.com"
      },
      {
         "link":"https://www.accuweather.com/en/us/san-francisco/94103/weather-today/347629",
         "title":"Weather Today for San Francisco, CA | AccuWeather"
      },
      {
         "link":"https://www.accuweather.com/en/us/san-francisco/94103/weather-forecast/347629",
         "title":"San Francisco, CA Weather Forecast | AccuWeather"
      },
      {
         "link":"https://forecast.weather.gov/zipcity.php?inputstring=San%20francisco%2CCA",
         "title":"National Weather Service"
      },
      {
         "link":"https://www.wunderground.com/weather/us/ca/san-francisco",
         "title":"San Francisco, CA Weather Conditions | Weather Underground"
      }
   ]
}
```
---
### Follow conversations:
```python
```python
meta = MetaAI()

print(meta.prompt("what is 2 + 2?"))
print(meta.prompt("what was my previous question?"))
```

```
{'message': '2 + 2 = 4\n', 'sources': [], 'media': []}
{'message': 'Your previous question was "what is 2 + 2?"\n', 'sources': [], 'media': []}
```

And to start a new one:
```python
meta = MetaAI()

print(meta.prompt("what is 2 + 2?"))
print(meta.prompt("what was my previous question?", new_conversation=True))
```

```
{'message': '2 + 2 = 4\n', 'sources': [], 'media': []}
{'message': "This is the beginning of our conversation, so I don't have a previous question to refer to. I'm happy to chat with you, though! What's on your mind today?\n", 'sources': [], 'media': []}
```

---
```python
from meta_ai_api import MetaAI

ai = MetaAI()
response = ai.prompt(message="What was the Warriors score last game?")
print(response)
```
```json
{
   "message":"The Golden State Warriors' last game was against the Sacramento Kings, and they lost 118-94 ¹ ². Stephen Curry scored 22 points, and the Kings' win eliminated the Warriors from the playoffs ³. The Warriors finished the season 46-36 and 10th in the Western Conference ⁴ ³.\n",
   "sources":[
      {
         "link":"https://sportradar.com/",
         "title":"Game Info of NBA from sportradar.com"
      },
      {
         "link":"https://www.sofascore.com/team/basketball/golden-state-warriors/3428",
         "title":"Golden State Warriors live scores & schedule - Sofascore"
      },
      {
         "link":"https://www.foxsports.com/nba/golden-state-warriors-team-schedule",
         "title":"Golden State Warriors Schedule & Scores - NBA - FOX Sports"
      },
      {
         "link":"https://en.wikipedia.org/wiki/History_of_the_Golden_State_Warriors",
         "title":"History of the Golden State Warriors"
      }
   ]
}
```

**Using proxy**:

```python
from meta_ai_api import MetaAI

proxy = {
    'http': 'http://proxy_address:port',
    'https': 'https://proxy_address:port'
}

ai = MetaAI(proxy=proxy)
response = ai.prompt(message="How to find out which mushrooms are edible?")
print(response)
```

**Streaming Response**:

```python
from meta_ai_api import MetaAI

ai = MetaAI()
response = ai.prompt(message="What was the Warriors score last game?", stream=True)
for r in response:
    print(r)
```

```
{'message': '\n', 'sources': []}
{'message': 'The\n', 'sources': []}
{'message': 'The Golden\n', 'sources': []}
{'message': 'The Golden State\n', 'sources': []}
{'message': 'The Golden State Warriors\n', 'sources': []}
{'message': "The Golden State Warriors'\n", 'sources': []}
{'message': "The Golden State Warriors' last\n", 'sources': []}
{'message': "The Golden State Warriors' last game\n", 'sources': []}
{'message': "The Golden State Warriors' last game was\n", 'sources': []}
{'message': "The Golden State Warriors' last game was against\n", 'sources': []}
{'message': "The Golden State Warriors' last game was against the\n", 'sources': []}
{'message': "The Golden State Warriors' last game was against the Sacramento\n", 'sources': []}
{'message': "The Golden State Warriors' last game was against the Sacramento Kings\n", 'sources': []}
{'message': "The Golden State Warriors' last game was against the Sacramento Kings on\n", 'sources': []}
{'message': "The Golden State Warriors' last game was against the Sacramento Kings on April\n", 'sources': []}
...
{'message': "The Golden State Warriors' last game was against the Sacramento Kings on April 16, 2024, at the Golden 1 Center in Sacramento, California. The Kings won the game with a score of 118-94, with the Warriors scoring 22 points in the first quarter, 28 in the second, 26 in the third and 18 in the fourth quarter ¹.\n", 'sources': [{'link': 'https://sportradar.com/', 'title': 'Game Info of NBA from sportradar.com'}]}
```

**Generate Image**:

By default image generation is only available for FB authenticated users. If you go on https://www.meta.ai/ , and ask the AI to generate an image, you will be prompted to authenticate with Facebook.
So if you want to generate images using this library, you need to authenticate using your FB credentials.

**Note**: There seems to be higher rate limits for authenticated users. So only authenticate to generate images.

```python
from meta_ai_api import MetaAI
ai = MetaAI(fb_email="your_fb_email", fb_password="your_fb_password")
resp = ai.prompt(message="Generate an image of a tech CEO")
print(resp)
```

```json
{
   "message":"\n",
   "sources":[
      
   ],
   "media":[
      {
         "url":"https://scontent-lax3-1.xx.fbcdn.net/o1/v/t0/f1/m247/4282108942387920518_1946149595_21-04-2024-14-17-48.jpeg?_nc_ht=scontent-lax3-1.xx.fbcdn.net&_nc_cat=103&ccb=9-4&oh=00_AfCnbCX7nl_J5kF6mahnams4d99Rs5WZA780HGS_scfc6A&oe=662771EE&_nc_sid=5b3566",
         "type":"IMAGE",
         "prompt":"a tech CEO"
      },
      {
         "url":"https://scontent-lax3-1.xx.fbcdn.net/o1/v/t0/f1/m247/3356467762794691754_1025991308_21-04-2024-14-17-48.jpeg?_nc_ht=scontent-lax3-1.xx.fbcdn.net&_nc_cat=108&ccb=9-4&oh=00_AfBLmSbTSqshNAL82KIFk8hGXyL8iK_CZLGcMmmddPrxuA&oe=66276EDD&_nc_sid=5b3566",
         "type":"IMAGE",
         "prompt":"a tech CEO"
      },
      {
         "url":"https://scontent-lax3-1.xx.fbcdn.net/o1/v/t0/f1/m247/127487551948523111_2181921077_21-04-2024-14-17-48.jpeg?_nc_ht=scontent-lax3-1.xx.fbcdn.net&_nc_cat=104&ccb=9-4&oh=00_AfAejXKeKPA4vyKXoc6UR0rEirvZwi41P3KiCSQmHRHsEw&oe=66276E45&_nc_sid=5b3566",
         "type":"IMAGE",
         "prompt":"a tech CEO"
      },
      {
         "url":"https://scontent-lax3-1.xx.fbcdn.net/o1/v/t0/f1/m247/3497663176351797954_3954783377_21-04-2024-14-17-47.jpeg?_nc_ht=scontent-lax3-1.xx.fbcdn.net&_nc_cat=110&ccb=9-4&oh=00_AfBp3bAfcuofqtI-z9D4bHw-GuGgCNPH_xhMM0PG_95S9Q&oe=66277AE9&_nc_sid=5b3566",
         "type":"IMAGE",
         "prompt":"a tech CEO"
      }
   ]
}
```
![Tech CEO](https://i.imgur.com/9YR6qHq.jpeg)

# Educational Purpose:
This repository is intended for educational purposes only. It is a tool to demonstrate how to interact with Meta's AI APIs, providing an example for learning and experimentation. Users should adhere to Meta's terms of service and use the library responsibly.


# Copyright:
This program is licensed under the GNU GPL v3. All code has been written by me, Strvm.

# Copyright Notice:
```
Strvm/meta-ai-api: a reverse engineered API wrapper for MetaAI
Copyright (C) 2023 Strvm

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
```

# Meta Copyright:

For more information related to the license tied to Llama, please visit https://www.llama.com/llama3/license/


================================================
FILE: pyproject.toml
================================================
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "meta_ai_api"
version = "0.1.0"
description = "Interact with the Meta AI API"
authors = ["Romeo Phillips"]

[tool.poetry.dependencies]
python = "^3.7"
requests = "2.31.0"
requests-html = "0.10.0"
lxml_html_cleaner = "0.1.1"
bs4 = "0.0.2"

[build]
script = "build.py"

================================================
FILE: requirements.txt
================================================
requests==2.31.0
requests-html==0.10.0
lxml_html_clean==0.1.1
bs4==0.0.2

================================================
FILE: setup.cfg
================================================
[metadata]
version = attr: meta_ai_api.__version__

================================================
FILE: setup.py
================================================
import setuptools

with open("README.md", "r", encoding="utf-8") as fh:
    long_description = fh.read()

setuptools.setup(
    name="meta_ai_api",
    author="Roméo Phillips",
    author_email="phillipsromeo@gmail.com",
    description="Meta AI API Wrapper to interact with the Meta AI API",
    keywords="llm, ai, meta_ai_api",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/tomchen/example_pypi_package",
    project_urls={
        "Documentation": "https://github.com/Strvm/meta-ai-api",
        "Bug Reports": "https://github.com/Strvm/meta-ai-api",
        "Source Code": "https://github.com/Strvm/meta-ai-api",
    },
    package_dir={"": "src"},
    packages=setuptools.find_packages(where="src"),
    classifiers=[
        "Development Status :: 5 - Production/Stable",
        "Intended Audience :: Developers",
        "Topic :: Software Development :: Build Tools",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.6",
        "Programming Language :: Python :: 3.7",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
        "Programming Language :: Python :: 3 :: Only",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires=">=3.6",
    extras_require={
        "dev": ["check-manifest"],
    },
    install_requires=["requests", "requests-html", "lxml_html_clean"],
)


================================================
FILE: src/meta_ai_api/__init__.py
================================================
__version__ = "1.2.5"
from .main import MetaAI  # noqa


================================================
FILE: src/meta_ai_api/exceptions.py
================================================
class FacebookInvalidCredentialsException(Exception):
    pass


class FacebookRegionBlocked(Exception):
    pass


================================================
FILE: src/meta_ai_api/main.py
================================================
import json
import logging
import time
import urllib
import uuid
from typing import Dict, List, Generator, Iterator

import requests
from requests_html import HTMLSession

from meta_ai_api.utils import (
    generate_offline_threading_id,
    extract_value,
    format_response,
)

from meta_ai_api.utils import get_fb_session, get_session

from meta_ai_api.exceptions import FacebookRegionBlocked

MAX_RETRIES = 3


class MetaAI:
    """
    A class to interact with the Meta AI API to obtain and use access tokens for sending
    and receiving messages from the Meta AI Chat API.
    """

    def __init__(
        self, fb_email: str = None, fb_password: str = None, proxy: dict = None
    ):
        self.session = get_session()
        self.session.headers.update(
            {
                "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",
            }
        )
        self.access_token = None
        self.fb_email = fb_email
        self.fb_password = fb_password
        self.proxy = proxy

        self.is_authed = fb_password is not None and fb_email is not None
        self.cookies = self.get_cookies()
        self.external_conversation_id = None
        self.offline_threading_id = None

    def get_access_token(self) -> str:
        """
        Retrieves an access token using Meta's authentication API.

        Returns:
            str: A valid access token.
        """

        if self.access_token:
            return self.access_token

        url = "https://www.meta.ai/api/graphql/"
        payload = {
            "lsd": self.cookies["lsd"],
            "fb_api_caller_class": "RelayModern",
            "fb_api_req_friendly_name": "useAbraAcceptTOSForTempUserMutation",
            "variables": {
                "dob": "1999-01-01",
                "icebreaker_type": "TEXT",
                "__relay_internal__pv__WebPixelRatiorelayprovider": 1,
            },
            "doc_id": "7604648749596940",
        }
        payload = urllib.parse.urlencode(payload)  # noqa
        headers = {
            "content-type": "application/x-www-form-urlencoded",
            "cookie": f'_js_datr={self.cookies["_js_datr"]}; '
            f'abra_csrf={self.cookies["abra_csrf"]}; datr={self.cookies["datr"]};',
            "sec-fetch-site": "same-origin",
            "x-fb-friendly-name": "useAbraAcceptTOSForTempUserMutation",
        }

        response = self.session.post(url, headers=headers, data=payload)

        try:
            auth_json = response.json()
        except json.JSONDecodeError:
            raise FacebookRegionBlocked(
                "Unable to receive a valid response from Meta AI. This is likely due to your region being blocked. "
                "Try manually accessing https://www.meta.ai/ to confirm."
            )

        access_token = auth_json["data"]["xab_abra_accept_terms_of_service"][
            "new_temp_user_auth"
        ]["access_token"]

        # Need to sleep for a bit, for some reason the API doesn't like it when we send request too quickly
        # (maybe Meta needs to register Cookies on their side?)
        time.sleep(1)

        return access_token

    def prompt(
        self,
        message: str,
        stream: bool = False,
        attempts: int = 0,
        new_conversation: bool = False,
    ) -> Dict or Generator[Dict, None, None]:
        """
        Sends a message to the Meta AI and returns the response.

        Args:
            message (str): The message to send.
            stream (bool): Whether to stream the response or not. Defaults to False.
            attempts (int): The number of attempts to retry if an error occurs. Defaults to 0.
            new_conversation (bool): Whether to start a new conversation or not. Defaults to False.

        Returns:
            dict: A dictionary containing the response message and sources.

        Raises:
            Exception: If unable to obtain a valid response after several attempts.
        """
        if not self.is_authed:
            self.access_token = self.get_access_token()
            auth_payload = {"access_token": self.access_token}
            url = "https://graph.meta.ai/graphql?locale=user"

        else:
            auth_payload = {"fb_dtsg": self.cookies["fb_dtsg"]}
            url = "https://www.meta.ai/api/graphql/"

        if not self.external_conversation_id or new_conversation:
            external_id = str(uuid.uuid4())
            self.external_conversation_id = external_id
        payload = {
            **auth_payload,
            "fb_api_caller_class": "RelayModern",
            "fb_api_req_friendly_name": "useAbraSendMessageMutation",
            "variables": json.dumps(
                {
                    "message": {"sensitive_string_value": message},
                    "externalConversationId": self.external_conversation_id,
                    "offlineThreadingId": generate_offline_threading_id(),
                    "suggestedPromptIndex": None,
                    "flashVideoRecapInput": {"images": []},
                    "flashPreviewInput": None,
                    "promptPrefix": None,
                    "entrypoint": "ABRA__CHAT__TEXT",
                    "icebreaker_type": "TEXT",
                    "__relay_internal__pv__AbraDebugDevOnlyrelayprovider": False,
                    "__relay_internal__pv__WebPixelRatiorelayprovider": 1,
                }
            ),
            "server_timestamps": "true",
            "doc_id": "7783822248314888",
        }
        payload = urllib.parse.urlencode(payload)  # noqa
        headers = {
            "content-type": "application/x-www-form-urlencoded",
            "x-fb-friendly-name": "useAbraSendMessageMutation",
        }
        if self.is_authed:
            headers["cookie"] = f'abra_sess={self.cookies["abra_sess"]}'
            # Recreate the session to avoid cookie leakage when user is authenticated
            self.session = requests.Session()
            self.session.proxies = self.proxy

        response = self.session.post(url, headers=headers, data=payload, stream=stream)
        if not stream:
            raw_response = response.text
            last_streamed_response = self.extract_last_response(raw_response)
            if not last_streamed_response:
                return self.retry(message, stream=stream, attempts=attempts)

            extracted_data = self.extract_data(last_streamed_response)
            return extracted_data

        else:
            lines = response.iter_lines()
            is_error = json.loads(next(lines))
            if len(is_error.get("errors", [])) > 0:
                return self.retry(message, stream=stream, attempts=attempts)
            return self.stream_response(lines)

    def retry(self, message: str, stream: bool = False, attempts: int = 0):
        """
        Retries the prompt function if an error occurs.
        """
        if attempts <= MAX_RETRIES:
            logging.warning(
                f"Was unable to obtain a valid response from Meta AI. Retrying... Attempt {attempts + 1}/{MAX_RETRIES}."
            )
            time.sleep(3)
            return self.prompt(message, stream=stream, attempts=attempts + 1)
        else:
            raise Exception(
                "Unable to obtain a valid response from Meta AI. Try again later."
            )

    def extract_last_response(self, response: str) -> Dict:
        """
        Extracts the last response from the Meta AI API.

        Args:
            response (str): The response to extract the last response from.

        Returns:
            dict: A dictionary containing the last response.
        """
        last_streamed_response = None
        for line in response.split("\n"):
            try:
                json_line = json.loads(line)
            except json.JSONDecodeError:
                continue

            bot_response_message = (
                json_line.get("data", {})
                .get("node", {})
                .get("bot_response_message", {})
            )
            chat_id = bot_response_message.get("id")
            if chat_id:
                external_conversation_id, offline_threading_id, _ = chat_id.split("_")
                self.external_conversation_id = external_conversation_id
                self.offline_threading_id = offline_threading_id

            streaming_state = bot_response_message.get("streaming_state")
            if streaming_state == "OVERALL_DONE":
                last_streamed_response = json_line

        return last_streamed_response

    def stream_response(self, lines: Iterator[str]):
        """
        Streams the response from the Meta AI API.

        Args:
            lines (Iterator[str]): The lines to stream.

        Yields:
            dict: A dictionary containing the response message and sources.
        """
        for line in lines:
            if line:
                json_line = json.loads(line)
                extracted_data = self.extract_data(json_line)
                if not extracted_data.get("message"):
                    continue
                yield extracted_data

    def extract_data(self, json_line: dict):
        """
        Extract data and sources from a parsed JSON line.

        Args:
            json_line (dict): Parsed JSON line.

        Returns:
            Tuple (str, list): Response message and list of sources.
        """
        bot_response_message = (
            json_line.get("data", {}).get("node", {}).get("bot_response_message", {})
        )
        response = format_response(response=json_line)
        fetch_id = bot_response_message.get("fetch_id")
        sources = self.fetch_sources(fetch_id) if fetch_id else []
        medias = self.extract_media(bot_response_message)
        return {"message": response, "sources": sources, "media": medias}

    @staticmethod
    def extract_media(json_line: dict) -> List[Dict]:
        """
        Extract media from a parsed JSON line.

        Args:
            json_line (dict): Parsed JSON line.

        Returns:
            list: A list of dictionaries containing the extracted media.
        """
        medias = []
        imagine_card = json_line.get("imagine_card", {})
        session = imagine_card.get("session", {}) if imagine_card else {}
        media_sets = (
            (json_line.get("imagine_card", {}).get("session", {}).get("media_sets", []))
            if imagine_card and session
            else []
        )
        for media_set in media_sets:
            imagine_media = media_set.get("imagine_media", [])
            for media in imagine_media:
                medias.append(
                    {
                        "url": media.get("uri"),
                        "type": media.get("media_type"),
                        "prompt": media.get("prompt"),
                    }
                )
        return medias

    def get_cookies(self) -> dict:
        """
        Extracts necessary cookies from the Meta AI main page.

        Returns:
            dict: A dictionary containing essential cookies.
        """
        session = HTMLSession()
        headers = {}
        if self.fb_email is not None and self.fb_password is not None:
            fb_session = get_fb_session(self.fb_email, self.fb_password)
            headers = {"cookie": f"abra_sess={fb_session['abra_sess']}"}
        response = session.get(
            "https://www.meta.ai/",
            headers=headers,
        )
        cookies = {
            "_js_datr": extract_value(
                response.text, start_str='_js_datr":{"value":"', end_str='",'
            ),
            "datr": extract_value(
                response.text, start_str='datr":{"value":"', end_str='",'
            ),
            "lsd": extract_value(
                response.text, start_str='"LSD",[],{"token":"', end_str='"}'
            ),
            "fb_dtsg": extract_value(
                response.text, start_str='DTSGInitData",[],{"token":"', end_str='"'
            ),
        }

        if len(headers) > 0:
            cookies["abra_sess"] = fb_session["abra_sess"]
        else:
            cookies["abra_csrf"] = extract_value(
                response.text, start_str='abra_csrf":{"value":"', end_str='",'
            )
        return cookies

    def fetch_sources(self, fetch_id: str) -> List[Dict]:
        """
        Fetches sources from the Meta AI API based on the given query.

        Args:
            fetch_id (str): The fetch ID to use for the query.

        Returns:
            list: A list of dictionaries containing the fetched sources.
        """

        url = "https://graph.meta.ai/graphql?locale=user"
        payload = {
            "access_token": self.access_token,
            "fb_api_caller_class": "RelayModern",
            "fb_api_req_friendly_name": "AbraSearchPluginDialogQuery",
            "variables": json.dumps({"abraMessageFetchID": fetch_id}),
            "server_timestamps": "true",
            "doc_id": "6946734308765963",
        }

        payload = urllib.parse.urlencode(payload)  # noqa

        headers = {
            "authority": "graph.meta.ai",
            "accept-language": "en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7",
            "content-type": "application/x-www-form-urlencoded",
            "cookie": f'dpr=2; abra_csrf={self.cookies.get("abra_csrf")}; datr={self.cookies.get("datr")}; ps_n=1; ps_l=1',
            "x-fb-friendly-name": "AbraSearchPluginDialogQuery",
        }

        response = self.session.post(url, headers=headers, data=payload)
        response_json = response.json()
        message = response_json.get("data", {}).get("message", {})
        search_results = (
            (response_json.get("data", {}).get("message", {}).get("searchResults"))
            if message
            else None
        )
        if search_results is None:
            return []

        references = search_results["references"]
        return references


if __name__ == "__main__":
    meta = MetaAI()
    resp = meta.prompt("What was the Warriors score last game?", stream=False)
    print(resp)


================================================
FILE: src/meta_ai_api/utils.py
================================================
import logging
import random
import time
from typing import Dict, Optional

from requests_html import HTMLSession
import requests
from bs4 import BeautifulSoup

from meta_ai_api.exceptions import FacebookInvalidCredentialsException


def generate_offline_threading_id() -> str:
    """
    Generates an offline threading ID.

    Returns:
        str: The generated offline threading ID.
    """
    # Maximum value for a 64-bit integer in Python
    max_int = (1 << 64) - 1
    mask22_bits = (1 << 22) - 1

    # Function to get the current timestamp in milliseconds
    def get_current_timestamp():
        return int(time.time() * 1000)

    # Function to generate a random 64-bit integer
    def get_random_64bit_int():
        return random.getrandbits(64)

    # Combine timestamp and random value
    def combine_and_mask(timestamp, random_value):
        shifted_timestamp = timestamp << 22
        masked_random = random_value & mask22_bits
        return (shifted_timestamp | masked_random) & max_int

    timestamp = get_current_timestamp()
    random_value = get_random_64bit_int()
    threading_id = combine_and_mask(timestamp, random_value)

    return str(threading_id)


def extract_value(text: str, start_str: str, end_str: str) -> str:
    """
    Helper function to extract a specific value from the given text using a key.

    Args:
        text (str): The text from which to extract the value.
        start_str (str): The starting key.
        end_str (str): The ending key.

    Returns:
        str: The extracted value.
    """
    start = text.find(start_str) + len(start_str)
    end = text.find(end_str, start)
    return text[start:end]


def format_response(response: dict) -> str:
    """
    Formats the response from Meta AI to remove unnecessary characters.

    Args:
        response (dict): The dictionnary containing the response to format.

    Returns:
        str: The formatted response.
    """
    text = ""
    for content in (
        response.get("data", {})
        .get("node", {})
        .get("bot_response_message", {})
        .get("composed_text", {})
        .get("content", [])
    ):
        text += content["text"] + "\n"
    return text


# Function to perform the login
def get_fb_session(email, password, proxies=None):
    login_url = "https://www.facebook.com/login/?next"
    headers = {
        "authority": "mbasic.facebook.com",
        "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",
        "accept-language": "en-US,en;q=0.9",
        "sec-ch-ua": '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"',
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": '"macOS"',
        "sec-fetch-dest": "document",
        "sec-fetch-mode": "navigate",
        "sec-fetch-site": "none",
        "sec-fetch-user": "?1",
        "upgrade-insecure-requests": "1",
        "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",
    }
    # Send the GET request
    response = requests.get(login_url, headers=headers, proxies=proxies)
    soup = BeautifulSoup(response.text, "html.parser")

    # Parse necessary parameters from the login form
    lsd = soup.find("input", {"name": "lsd"})["value"]
    jazoest = soup.find("input", {"name": "jazoest"})["value"]

    # Define the URL and body for the POST request to submit the login form
    post_url = "https://www.facebook.com/login/?next"
    data = {
        "lsd": lsd,
        "jazoest": jazoest,
        "login_source": "comet_headerless_login",
        "email": email,
        "pass": password,
        "login": "1",
        "next": None,
    }

    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Accept-Language": "en-US,en;q=0.5",
        "Accept-Encoding": None,
        "Referer": "https://www.facebook.com/",
        "Content-Type": "application/x-www-form-urlencoded",
        "Origin": "https://www.facebook.com",
        "DNT": "1",
        "Sec-GPC": "1",
        "Connection": "keep-alive",
        "cookie": f"datr={response.cookies.get('datr')};",
        "Upgrade-Insecure-Requests": "1",
        "Sec-Fetch-Dest": "document",
        "Sec-Fetch-Mode": "navigate",
        "Sec-Fetch-Site": "same-origin",
        "Sec-Fetch-User": "?1",
        "Priority": "u=0, i",
    }

    from requests import cookies

    # Send the POST request
    session = requests.session()
    jar = cookies.RequestsCookieJar()
    session.proxies = proxies
    session.cookies = jar

    result = session.post(post_url, headers=headers, data=data)
    if "sb" not in jar or "xs" not in jar:
        raise FacebookInvalidCredentialsException(
            "Was not able to login to Facebook. Please check your credentials. "
            "You may also have been rate limited. Try to connect to Facebook manually."
        )

    cookies = {
        **result.cookies.get_dict(),
        "sb": jar["sb"],
        "xs": jar["xs"],
        "fr": jar["fr"],
        "c_user": jar["c_user"],
    }

    response_login = {
        "cookies": cookies,
        "headers": result.headers,
        "response": response.text,
    }
    meta_ai_cookies = get_cookies()

    url = "https://www.meta.ai/state/"

    payload = f'__a=1&lsd={meta_ai_cookies["lsd"]}'
    headers = {
        "authority": "www.meta.ai",
        "accept": "*/*",
        "accept-language": "en-US,en;q=0.9",
        "cache-control": "no-cache",
        "content-type": "application/x-www-form-urlencoded",
        "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',
        "origin": "https://www.meta.ai",
        "pragma": "no-cache",
        "referer": "https://www.meta.ai/",
        "sec-fetch-mode": "cors",
        "sec-fetch-site": "same-origin",
        "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",
    }

    response = requests.request(
        "POST", url, headers=headers, data=payload, proxies=proxies
    )

    state = extract_value(response.text, start_str='"state":"', end_str='"')

    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}"
    payload = {}
    headers = {
        "authority": "www.facebook.com",
        "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",
        "accept-language": "en-US,en;q=0.9",
        "cache-control": "no-cache",
        "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']};",
        "sec-fetch-dest": "document",
        "sec-fetch-mode": "navigate",
        "sec-fetch-site": "cross-site",
        "sec-fetch-user": "?1",
        "upgrade-insecure-requests": "1",
        "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",
    }
    session = requests.session()
    session.proxies = proxies
    response = session.get(url, headers=headers, data=payload, allow_redirects=False)

    next_url = response.headers["Location"]

    url = next_url

    payload = {}
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.0",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
        "Accept-Language": "en-US,en;q=0.5",
        "Accept-Encoding": "gzip, deflate, br",
        "Referer": "https://www.meta.ai/",
        "Connection": "keep-alive",
        "Cookie": f'dpr=2; abra_csrf={meta_ai_cookies["abra_csrf"]}; datr={meta_ai_cookies["_js_datr"]}',
        "Upgrade-Insecure-Requests": "1",
        "Sec-Fetch-Dest": "document",
        "Sec-Fetch-Mode": "navigate",
        "Sec-Fetch-Site": "cross-site",
        "Sec-Fetch-User": "?1",
        "TE": "trailers",
    }
    session.get(url, headers=headers, data=payload)
    cookies = session.cookies.get_dict()
    if "abra_sess" not in cookies:
        raise FacebookInvalidCredentialsException(
            "Was not able to login to Facebook. Please check your credentials. "
            "You may also have been rate limited. Try to connect to Facebook manually."
        )
    logging.info("Successfully logged in to Facebook.")
    return cookies


def get_cookies() -> dict:
    """
    Extracts necessary cookies from the Meta AI main page.

    Returns:
        dict: A dictionary containing essential cookies.
    """
    session = HTMLSession()
    response = session.get("https://www.meta.ai/")
    return {
        "_js_datr": extract_value(
            response.text, start_str='_js_datr":{"value":"', end_str='",'
        ),
        "abra_csrf": extract_value(
            response.text, start_str='abra_csrf":{"value":"', end_str='",'
        ),
        "datr": extract_value(
            response.text, start_str='datr":{"value":"', end_str='",'
        ),
        "lsd": extract_value(
            response.text, start_str='"LSD",[],{"token":"', end_str='"}'
        ),
    }


def get_session(
    proxy: Optional[Dict] = None, test_url: str = "https://api.ipify.org/?format=json"
) -> requests.Session:
    """
    Get a session with the proxy set.

    Args:
        proxy (Dict): The proxy to use
        test_url (str): A test site from which we check that the proxy is installed correctly.

    Returns:
        requests.Session: A session with the proxy set.
    """
    session = requests.Session()
    if not proxy:
        return session
    response = session.get(test_url, proxies=proxy, timeout=10)
    if response.status_code == 200:
        session.proxies = proxy
        return session
    else:
        raise Exception("Proxy is not working.")
Download .txt
gitextract_aa7abxek/

├── .github/
│   └── workflows/
│       └── python-publish.yml
├── .gitignore
├── README.md
├── pyproject.toml
├── requirements.txt
├── setup.cfg
├── setup.py
└── src/
    └── meta_ai_api/
        ├── __init__.py
        ├── exceptions.py
        ├── main.py
        └── utils.py
Download .txt
SYMBOL INDEX (19 symbols across 3 files)

FILE: src/meta_ai_api/exceptions.py
  class FacebookInvalidCredentialsException (line 1) | class FacebookInvalidCredentialsException(Exception):
  class FacebookRegionBlocked (line 5) | class FacebookRegionBlocked(Exception):

FILE: src/meta_ai_api/main.py
  class MetaAI (line 24) | class MetaAI:
    method __init__ (line 30) | def __init__(
    method get_access_token (line 50) | def get_access_token(self) -> str:
    method prompt (line 102) | def prompt(
    method retry (line 186) | def retry(self, message: str, stream: bool = False, attempts: int = 0):
    method extract_last_response (line 201) | def extract_last_response(self, response: str) -> Dict:
    method stream_response (line 235) | def stream_response(self, lines: Iterator[str]):
    method extract_data (line 253) | def extract_data(self, json_line: dict):
    method extract_media (line 273) | def extract_media(json_line: dict) -> List[Dict]:
    method get_cookies (line 303) | def get_cookies(self) -> dict:
    method fetch_sources (line 342) | def fetch_sources(self, fetch_id: str) -> List[Dict]:

FILE: src/meta_ai_api/utils.py
  function generate_offline_threading_id (line 13) | def generate_offline_threading_id() -> str:
  function extract_value (line 45) | def extract_value(text: str, start_str: str, end_str: str) -> str:
  function format_response (line 62) | def format_response(response: dict) -> str:
  function get_fb_session (line 85) | def get_fb_session(email, password, proxies=None):
  function get_cookies (line 245) | def get_cookies() -> dict:
  function get_session (line 270) | def get_session(
Condensed preview — 11 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (43K chars).
[
  {
    "path": ".github/workflows/python-publish.yml",
    "chars": 1159,
    "preview": "# This workflow will upload a Python Package using Twine when a release is created\n# For more information see: https://d"
  },
  {
    "path": ".gitignore",
    "chars": 2040,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
  },
  {
    "path": "README.md",
    "chars": 9945,
    "preview": "# MetaAI API Wrapper\n\nMetaAI is a Python library designed to interact with Meta's AI APIs that run in the backend of htt"
  },
  {
    "path": "pyproject.toml",
    "chars": 379,
    "preview": "[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n\n[tool.poetry]\nname = \"meta_a"
  },
  {
    "path": "requirements.txt",
    "chars": 72,
    "preview": "requests==2.31.0\nrequests-html==0.10.0\nlxml_html_clean==0.1.1\nbs4==0.0.2"
  },
  {
    "path": "setup.cfg",
    "chars": 50,
    "preview": "[metadata]\nversion = attr: meta_ai_api.__version__"
  },
  {
    "path": "setup.py",
    "chars": 1515,
    "preview": "import setuptools\n\nwith open(\"README.md\", \"r\", encoding=\"utf-8\") as fh:\n    long_description = fh.read()\n\nsetuptools.set"
  },
  {
    "path": "src/meta_ai_api/__init__.py",
    "chars": 55,
    "preview": "__version__ = \"1.2.5\"\nfrom .main import MetaAI  # noqa\n"
  },
  {
    "path": "src/meta_ai_api/exceptions.py",
    "chars": 114,
    "preview": "class FacebookInvalidCredentialsException(Exception):\n    pass\n\n\nclass FacebookRegionBlocked(Exception):\n    pass\n"
  },
  {
    "path": "src/meta_ai_api/main.py",
    "chars": 14194,
    "preview": "import json\nimport logging\nimport time\nimport urllib\nimport uuid\nfrom typing import Dict, List, Generator, Iterator\n\nimp"
  },
  {
    "path": "src/meta_ai_api/utils.py",
    "chars": 10395,
    "preview": "import logging\nimport random\nimport time\nfrom typing import Dict, Optional\n\nfrom requests_html import HTMLSession\nimport"
  }
]

About this extraction

This page contains the full source code of the Strvm/meta-ai-api GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 11 files (39.0 KB), approximately 10.2k tokens, and a symbol index with 19 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!