Repository: acheong08/EdgeGPT
Branch: main
Commit: 9aed44b641f8
Files: 38
Total size: 117.1 KB
Directory structure:
gitextract_qfdnm_l6/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ ├── custom.md
│ │ └── feature_request.yml
│ └── workflows/
│ ├── GHCR.yml
│ ├── PyPi.yml
│ └── tests.yml
├── .gitignore
├── .pre-commit-config.yaml
├── Dockerfile
├── LICENSE
├── Makefile
├── docs/
│ ├── CODE_OF_CONDUCT.md
│ ├── README_es.md
│ ├── README_ja.md
│ ├── README_zh-cn.md
│ ├── README_zh-tw.md
│ └── wiki/
│ ├── EdgeGPT.md
│ ├── Home.md
│ └── ImageGen.md
├── example.env
├── requirements.txt
├── setup.cfg
├── setup.py
├── src/
│ └── EdgeGPT/
│ ├── EdgeGPT.py
│ ├── EdgeUtils.py
│ ├── ImageGen.py
│ ├── __init__.py
│ ├── chathub.py
│ ├── constants.py
│ ├── conversation.py
│ ├── conversation_style.py
│ ├── exceptions.py
│ ├── locale.py
│ ├── main.py
│ ├── request.py
│ └── utilities.py
└── test_base.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
description: DO NOT OPEN A DUPLICATE
title: "[Bug]: "
body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered, and that it hasn't been fixed in a recent build/commit.
options:
- label: I have searched the existing issues and checked the recent builds/commits
required: true
- type: markdown
attributes:
value: |
*Please fill this form with as much information as possible, don't forget to fill "What OS..." and "What browsers" and *provide screenshots if possible**
- type: textarea
id: what-did
attributes:
label: What happened?
description: Tell us what happened in a very clear and simple way
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce the problem
description: Please provide us with precise step by step information on how to reproduce the bug
value: |
1. Go to ....
2. Press ....
3. ...
validations:
required: true
- type: textarea
id: what-should
attributes:
label: What should have happened?
description: Tell what you think the normal behavior should be
validations:
required: true
- type: textarea
id: version
attributes:
label: Version where the problem happens
description: "`python3 -m pip show revChatGPT`"
validations:
required: true
- type: input
id: python-version
attributes:
label: What Python version are you running this with?
description: "`python3 -V`"
- type: dropdown
id: platforms
attributes:
label: What is your operating system ?
multiple: true
options:
- Windows
- Linux
- MacOS
- iOS
- Android
- Other/Cloud
- type: textarea
id: cmdargs
attributes:
label: Command Line Arguments
description: Are you using any launching parameters/command line arguments (modified webui-user .bat/.sh) ? If yes, please write them below. Write "No" otherwise.
render: Shell
validations:
required: true
- type: textarea
id: logs
attributes:
label: Console logs
description: Please provide **full** cmd/terminal logs from the moment you started UI to the end of it, after your bug happened. If it's very long, provide a link to pastebin or similar service.
render: Shell
validations:
required: true
- type: textarea
id: misc
attributes:
label: Additional information
description: Please provide us with any relevant additional info or context.
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Discussions, questions, and niche issues in Github
url: https://github.com/acheong08/EdgeGPT/discussions/new/choose
about: For anything that is not an explicit bug
- name: Discuss in the Discord
url: https://discord.gg/9K2BvbXEHT
about: 'Personal server: Chill discussions and more detailed updates'
- name: Communicate in the Discord
url: https://discord.gg/WMNtbDUjUv
about: 'Public server: Large technical community with many high profile members like @transitive-bullshit'
================================================
FILE: .github/ISSUE_TEMPLATE/custom.md
================================================
---
name: "Are you sure you are not opening a duplicate? From now on, I will not be reading all issues. Use 👍 reaction on an issue to upvote it. I will be reading the top 5 issues each day."
about: "DO NOT PRESS THIS"
title: ''
labels: ''
assignees: ''
---
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: Feature request
description: Suggest an idea for this project
title: "[Feature Request]: "
body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the feature you want, and that it's not implemented in a recent build/commit.
options:
- label: I have searched the existing issues and checked the recent builds/commits
required: true
- type: markdown
attributes:
value: |
*Please fill this form with as much information as possible, provide screenshots and/or illustrations of the feature if possible*
- type: textarea
id: feature
attributes:
label: What would your feature do ?
description: Tell us about your feature in a very clear and simple way, and what problem it would solve
validations:
required: true
- type: textarea
id: workflow
attributes:
label: Proposed workflow
description: Please provide us with step by step information on how you'd like the feature to be accessed and used
value: |
1. Go to ....
2. Press ....
3. ...
validations:
required: true
- type: textarea
id: misc
attributes:
label: Additional information
description: Add any other context or screenshots about the feature request here.
================================================
FILE: .github/workflows/GHCR.yml
================================================
name: Publish Docker 🐳 images 📦 to GitHub Container Registry
on:
release:
types: [published]
jobs:
build-release-and-publish-to-ghcr:
if: github.event_name == 'release'
# Explicitly grant the `secrets.GITHUB_TOKEN` permissions.
permissions:
# Grant the ability to write to GitHub Packages (push Docker images to
# GitHub Container Registry).
packages: write
name: Build and publish Release Docker 🐳 images 📦 to GitHub Container Registry
runs-on: ubuntu-latest
steps:
- name: Checkout main
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Get actual patch version
id: actual_patch_version
run: echo ::set-output name=ACTUAL_PATCH_VERSION::$(echo $GITHUB_REF | cut -d / -f 3 )
- name: Get actual minor version
id: actual_minor_version
run: echo ::set-output name=ACTUAL_MINOR_VERSION::$(echo $GITHUB_REF | cut -d / -f 3 | cut -d "." -f 1,2)
- name: Get actual major version
id: actual_major_version
run: echo ::set-output name=ACTUAL_MAJOR_VERSION::$(echo $GITHUB_REF | cut -d / -f 3 | cut -d "." -f 1)
- name: Docker compliant registry for image pushes
id: registry
run: echo "REGISTRY=$(echo 'ghcr.io/${{github.repository }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ghcr.io/${{ github.repository }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
platforms: 'arm64'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
# This is the user that triggered the Workflow. In this case, it will
# either be the user whom created the Release or manually triggered
# the workflow_dispatch.
username: ${{ github.actor }}
# `secrets.GITHUB_TOKEN` is a secret that's automatically generated by
# GitHub Actions at the start of a workflow run to identify the job.
# This is used to authenticate against GitHub Container Registry.
# See https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret
# for more detailed information.
password: ${{ secrets.GH_TOKEN }}
- name: Build and push alpine based image
uses: docker/build-push-action@v4
with:
file: Dockerfile
context: .
push: true # push the image to ghcr
tags: |
${{ env.REGISTRY }}:latest
${{ env.REGISTRY }}:${{ steps.actual_patch_version.outputs.ACTUAL_PATCH_VERSION }},
${{ env.REGISTRY }}:${{ steps.actual_minor_version.outputs.ACTUAL_MINOR_VERSION }}
${{ env.REGISTRY }}:${{ steps.actual_major_version.outputs.ACTUAL_MAJOR_VERSION }}
${{ env.REGISTRY }}:${{github.sha}}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
================================================
FILE: .github/workflows/PyPi.yml
================================================
name: Upload Python Package
on:
release:
types: [published]
permissions:
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
if: github.event_name == 'release'
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11.0'
- name: Install dependencies
run: make
- name: Build package
run: make build
- name: Publish package
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
================================================
FILE: .github/workflows/tests.yml
================================================
name: tests
on:
push:
pull_request:
pull_request_target:
types: [ ready_for_review, review_requested, edited]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.11 ]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
env:
EDGE_COOKIES: ${{ secrets.EDGE_COOKIES }}
run: |
python -m pip install pytest pytest-asyncio
python -m pip install .
- name: Test with pytest
run: |
pytest . -s
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
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/
# 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
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.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/
cookies.json
output/*
README.md
*.json
# Pycharm
.idea/*
================================================
FILE: .pre-commit-config.yaml
================================================
repos:
- repo: https://github.com/asottile/reorder_python_imports
rev: v3.9.0
hooks:
- id: reorder-python-imports
args: [--py37-plus]
- repo: https://github.com/asottile/add-trailing-comma
rev: v2.3.0
hooks:
- id: add-trailing-comma
args: [--py36-plus]
- repo: https://github.com/asottile/pyupgrade
rev: v3.3.1
hooks:
- id: pyupgrade
args: [--py37-plus]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: debug-statements
- id: double-quote-string-fixer
- id: name-tests-test
- id: requirements-txt-fixer
- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
- id: black
================================================
FILE: Dockerfile
================================================
FROM docker.io/library/python:3.11.3-alpine3.16@sha256:0ba61d06b14e5438aa3428ee46c7ccdc8df5b63483bc91ae050411407eb5cbf4 AS builder
WORKDIR /EdgeGPT
COPY requirements.txt requirements.txt
RUN pip3 install --no-cache-dir -r requirements.txt
COPY . .
RUN apk add --no-cache make && \
make init && make build && make ci && apk del make && \
rm -Rf /root/.cache/pip
ENTRYPOINT ["python3", "-m" , "EdgeGPT.EdgeGPT"]
================================================
FILE: LICENSE
================================================
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>
================================================
FILE: Makefile
================================================
.PHONY: docs
init:
python -m pip install --upgrade pip
python -m pip install -r ./requirements.txt --upgrade
python -m pip install build setuptools wheel flake8 --upgrade
build:
python -m build
ci:
python -m flake8 src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
python -m flake8 src --count --select=E9,F63,F7,F82 --show-source --statistics
python setup.py install
================================================
FILE: docs/CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
acheong@student.dalat.org.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: docs/README_es.md
================================================
<div align="center">
<img src="https://socialify.git.ci/acheong08/EdgeGPT/image?font=Inter&language=1&logo=https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F9%2F9c%2FBing_Fluent_Logo.svg&owner=1&pattern=Floating%20Cogs&theme=Auto" alt="EdgeGPT" width="640" height="320" />
# Edge GPT
_Ingeniería inversa al nuevo chat integrado en Bing_
<a href="./README.md">English</a> -
<a href="./README_zh-cn.md">简体中文</a> -
<a href="./README_zh-tw.md">繁體中文</a> -
<a>Español</a> -
<a href="./README_ja.md">日本語</a>
</div>
<p align="center">
<a href="https://github.com/acheong08/EdgeGPT">
<img alt="PyPI version" src="https://img.shields.io/pypi/v/EdgeGPT">
</a>
<img alt="Python version" src="https://img.shields.io/badge/python-3.8+-blue.svg">
<img alt="Total downloads" src="https://static.pepy.tech/badge/edgegpt">
</p>
<details open>
<summary>
# Configuración
</summary>
## Instalación
```bash
python3 -m pip install EdgeGPT --upgrade
```
## Requisitos
- python 3.8+
- Una cuenta de Microsoft con acceso a <https://bing.com/chat> (Opcional, dependiendo de tu país)
- Estar localizado en un país con acceso al nuevo Bing (VPN necesaria para usarios de China)
- [Selenium](https://pypi.org/project/selenium/) (para la recolección automática de cookies)
## Autenticación
!!! ES PROBABLE QUE YA NO SEA NECESARIO !!!
**En algunas regiones**, Microsoft ha **abierto** la función de chat para todos,
por lo que es posible que **puedas saltarte este paso**.
Puedes comprobarlo con un navegador (con el user-agent modificado para parecer Edge),
**intentando abrir un chat sin haber iniciado sesión**.
Se ha encontrado que es posible que sea **dependiente de tu dirección IP**.
Por ejemplo, si intentas acceder a la función de chat desde una dirección IP que se conoce
que **pertenece al rango de un centro de datos** (vServers, servidores raíz, VPN, proxies conocidos, ...),
**es posible que tengas que iniciar sesión** y sin embargo puedas acceder sin problemas a las funciones
desde tu casa.
Si recibes el siguiente error, puedes probar **usando cookies** y viendo si funciona:
`Exception: Authentication failed. You have not been accepted into the beta.`
### Recolección de cookies
1. Necesitas un navegador que _parezca_ Microsoft Edge.
* a) (Fácil) Instala la última versión de Microsoft Edge
* b) (Avanzado) De forma alternativa, puedes usar cualquier navegador y
cambiar el user-agent para que parezca que estás usando Edge
(e.g., `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51`).
Puedes hacer esto con extensiones del estilo "User-Agent Switcher and Manager" para [Chrome](https://chrome.google.com/webstore/detail/user-agent-switcher-and-m/bhchdcejhohfmigjafbampogmaanbfkg)
o [Firefox](https://addons.mozilla.org/en-US/firefox/addon/user-agent-string-switcher/).
2. Abrir [bing.com/chat](https://bing.com/chat)
3. Si ves la nueva función de chat, es que puedes continuar...
4. Instala la extensión cookie editor para [Chrome](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) o
[Firefox](https://addons.mozilla.org/en-US/firefox/addon/cookie-editor/)
5. Ve a [bing.com](https://bing.com)
6. Abre la extensión
7. Pulsa en "Export" abajo a la derecha, luego "Export as JSON" (Esto guarda la cookie en el portapapeles)
8. Pega el contenido en un fichero `bing_cookies_*.json`.
* NOTA: Los **ficheros de cookies DEBEN seguir el siguiente formato de nombre `bing_cookies_*.json`**,
de manera que puedan ser reconocidos por los mecanismos internos de procesado de cookies.
### Uso de la cookie en la librería:
```python
cookies = json.loads(open("./path/to/cookies.json", encoding="utf-8").read()) # might omit cookies option
bot = await Chatbot.create(cookies=cookies)
```
</details>
<details open>
<summary>
# Cómo usar Chatbot
</summary>
## Ejecución desde la línea de comandos
```
$ python3 -m EdgeGPT.EdgeGPT -h
EdgeGPT - A demo of reverse engineering the Bing GPT chatbot
Repo: github.com/acheong08/EdgeGPT
By: Antonio Cheong
!help for help
Type !exit to exit
usage: EdgeGPT.py [-h] [--enter-once] [--search-result] [--no-stream] [--rich] [--proxy PROXY] [--wss-link WSS_LINK]
[--style {creative,balanced,precise}] [--prompt PROMPT] [--cookie-file COOKIE_FILE]
[--history-file HISTORY_FILE] [--locale LOCALE]
options:
-h, --help show this help message and exit
--enter-once
--search-result
--no-stream
--rich
--proxy PROXY Proxy URL (e.g. socks5://127.0.0.1:1080)
--wss-link WSS_LINK WSS URL(e.g. wss://sydney.bing.com/sydney/ChatHub)
--style {creative,balanced,precise}
--prompt PROMPT prompt to start with
--cookie-file COOKIE_FILE
path to cookie file
--history-file HISTORY_FILE
path to history file
--locale LOCALE your locale (e.g. en-US, zh-CN, en-IE, en-GB)
```
(China/US/UK/Norway disponen de mejor soporte para la localización)
## Ejecución en Python
### 1. La clase `Chatbot` y la librería `asyncio` para un control más exhaustivo
Usa Async para una mejor experiencia, por ejemplo:
```python
import asyncio, json
from EdgeGPT.EdgeGPT import Chatbot, ConversationStyle
async def main():
bot = await Chatbot.create() # Passing cookies is "optional", as explained above
response = await bot.ask(prompt="Hello world", conversation_style=ConversationStyle.creative, simplify_response=True)
print(json.dumps(response, indent=2)) # Returns
"""
{
"text": str
"author": str
"sources": list[dict]
"sources_text": str
"suggestions": list[str]
"messages_left": int
}
"""
await bot.close()
if __name__ == "__main__":
asyncio.run(main())
```
### 2) Las clases auxiliares `Query` y `Cookie`
Realiza una petición a la IA de Bing Chat (usa el estilo 'precise' de conversación por defecto)
y devuelve la respuesta final sin ver todo el resultado de la API:
Recuerda almacenar las cookies en el formato: `bing_cookies_*.json`.
```python
from EdgeGPT.EdgeUtils import Query, Cookie
q = Query("What are you? Give your answer as Python code")
print(q)
```
También puedes cambiar el estilo de conversación o el fichero de cookie a usar:
```python
q = Query(
"What are you? Give your answer as Python code",
style="creative", # or: 'balanced', 'precise'
cookie_file="./bing_cookies_alternative.json"
)
```
Obtén rápidamente la respuesta, fragmentos de código, lista de fuentes/referencias,
o las preguntas sugeridas usando los siguientes atributos:
```python
q.output
q.code
q.suggestions
q.sources # for the full json output
q.sources_dict # for a dictionary of titles and urls
```
Obtén la pregunta inicial o el estilo de conversación usado:
```python
q.prompt
q.style
repr(q)
```
Accede a la lista de peticiones realizadas:
```python
Query.index # A list of Query objects; updated dynamically
Query.request_count # A tally of requests made using each cookie file
```
Finalmente, la clase `Cookie` admite múltiples ficheros de cookie, de manera que
si has creado ficheros adicionales de cookies usando el formato de nombrado
`bing_cookies_*.json`, las peticiones intentarán usar automáticamente el siguiente
fichero (alfabéticamente) si has sobrepasado el límite diario de peticiones (actualmente limitado a 200).
Principales atributos que tienes a disposición:
```python
Cookie.current_file_index
Cookie.dirpath
Cookie.search_pattern # default is `bing_cookies_*.json`
Cookie.files() # list as files that match .search_pattern
Cookie.current_filepath
Cookie.current_data
Cookie.import_next()
Cookie.image_token
Cookie.ignore_files
```
---
## Ejecución en Docker
Este ejemplo asume que dispones de un fichero `cookies.json` en tu directorio actual
```bash
docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt
```
Puedes añadir argumentos adicionales de la siguiente manera
```bash
docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt --rich --style creative
```
</details>
</details>
<details open>
<summary>
# Cómo usar Image generator
</summary>
## Ejecución desde la línea de comandos
```bash
$ python3 -m ImageGen.ImageGen -h
usage: ImageGen.py [-h] [-U U] [--cookie-file COOKIE_FILE] --prompt PROMPT [--output-dir OUTPUT_DIR] [--quiet] [--asyncio]
optional arguments:
-h, --help show this help message and exit
-U U Auth cookie from browser
--cookie-file COOKIE_FILE
File containing auth cookie
--prompt PROMPT Prompt to generate images for
--output-dir OUTPUT_DIR
Output directory
--quiet Disable pipeline messages
--asyncio Run ImageGen using asyncio
```
## Ejecución en Python
### 1) La clase auxiliar `ImageQuery`
Genera imágenes de acuerdo a la petición y las descarga en el directorio actual
```python
from EdgeGPT.EdgeUtils import ImageQuery
q=ImageQuery("Meerkats at a garden party in Devon")
```
Cambia el directorio de descarga para las demás imágenes que se descarguen durante el resto de sesión
```
Query.image_dirpath = Path("./to_another_folder")
```
### 2) Usa las clases `ImageGen` y `asyncio` para un control más minucioso
```python
from EdgeGPT.ImageGen import ImageGen
import argparse
import json
async def async_image_gen(args) -> None:
async with ImageGenAsync(args.U, args.quiet) as image_generator:
images = await image_generator.get_images(args.prompt)
await image_generator.save_images(images, output_dir=args.output_dir)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-U", help="Auth cookie from browser", type=str)
parser.add_argument("--cookie-file", help="File containing auth cookie", type=str)
parser.add_argument(
"--prompt",
help="Prompt to generate images for",
type=str,
required=True,
)
parser.add_argument(
"--output-dir",
help="Output directory",
type=str,
default="./output",
)
parser.add_argument(
"--quiet", help="Disable pipeline messages", action="store_true"
)
parser.add_argument(
"--asyncio", help="Run ImageGen using asyncio", action="store_true"
)
args = parser.parse_args()
# Load auth cookie
with open(args.cookie_file, encoding="utf-8") as file:
cookie_json = json.load(file)
for cookie in cookie_json:
if cookie.get("name") == "_U":
args.U = cookie.get("value")
break
if args.U is None:
raise Exception("Could not find auth cookie")
if not args.asyncio:
# Create image generator
image_generator = ImageGen(args.U, args.quiet)
image_generator.save_images(
image_generator.get_images(args.prompt),
output_dir=args.output_dir,
)
else:
asyncio.run(async_image_gen(args))
```
</details>
<details open>
<summary>
# Historial de estrellas
</summary>
[](https://star-history.com/#acheong08/EdgeGPT&Date)
</details>
<details open>
<summary>
# Contribuidores
</summary>
Este proyecto existe gracias a todas las personas que apoyan y contribuyen.
<a href="https://github.com/acheong08/EdgeGPT/graphs/contributors">
<img src="https://contrib.rocks/image?repo=acheong08/EdgeGPT" />
</a>
</details>
================================================
FILE: docs/README_ja.md
================================================
<div align="center">
<img src="https://socialify.git.ci/acheong08/EdgeGPT/image?font=Inter&language=1&logo=https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F9%2F9c%2FBing_Fluent_Logo.svg&owner=1&pattern=Floating%20Cogs&theme=Auto" alt="EdgeGPT" width="640" height="320" />
# Edge GPT
_Bing の新バージョンのチャット機能のリバースエンジニアリング_
<a href="./README.md">English</a> -
<a href="./README_zh-cn.md">简体中文</a> -
<a href="./README_zh-tw.md">繁體中文</a> -
<a href="./README_es.md">Español</a> -
<a>日本語</a>
</div>
<p align="center">
<a href="https://github.com/acheong08/EdgeGPT">
<img alt="PyPI version" src="https://img.shields.io/pypi/v/EdgeGPT">
</a>
<img alt="Python version" src="https://img.shields.io/badge/python-3.8+-blue.svg">
<img alt="Total downloads" src="https://static.pepy.tech/badge/edgegpt">
</p>
---
## 設定
### パッケージをインストール
```bash
python3 -m pip install EdgeGPT --upgrade
```
### 要件
- python 3.8+
- <https://bing.com/chat> に早期アクセスできる Microsoft アカウント(必須)
- New Bing のサポート国で必要(中国本土のVPNは必須)
<details>
<summary>
### アクセスの確認 (必須)
</summary>
- Microsoft Edge の最新バージョンをインストール
- また、任意のブラウザを使用し、ユーザーエージェントを Edge を使用しているように設定することもできます(例:`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0 Safari/537.36 Edg/111.0.1661.51`)。[Chrome](https://chrome.google.com/webstore/detail/user-agent-switcher-and-m/bhchdcejhohfmigjafbampogmaanbfkg) や [Firefox](https://addons.mozilla.org/en-US/firefox/addon/user-agent-string-switcher/) の"User-Agent Switcher and Manager"のような拡張機能を使えば、簡単に行えます。
- [bing.com/chat](https://bing.com/chat) を開く
- チャット機能が表示されたら、準備完了
</details>
<details>
<summary>
### 認証の取得 (必須)
</summary>
- [Chrome](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) または [Firefox](https://addons.mozilla.org/en-US/firefox/addon/cookie-editor/) の Cookie エディター拡張機能をインストール
- `bing.com` へ移動
- 拡張機能を開く
- 右下の"エクスポート"から"JSONとしてエクスポート"をクリック(これで Cookie がクリップボードに保存されます)
- クッキーをファイル `cookies.json` に貼り付け
</details>
<details>
<summary>
## チャットボット
</summary>
## 使用方法
### クイックスタート
```
$ python3 -m EdgeGPT.EdgeGPT -h
EdgeGPT - A demo of reverse engineering the Bing GPT chatbot
Repo: github.com/acheong08/EdgeGPT
By: Antonio Cheong
!help for help
Type !exit to exit
Enter twice to send message or set --enter-once to send one line message
usage: EdgeGPT.py [-h] [--enter-once] [--no-stream] [--rich] [--proxy PROXY] [--wss-link WSS_LINK] [--style {creative,balanced,precise}]
[--cookie-file COOKIE_FILE]
options:
-h, --help show this help message and exit
--enter-once
--no-stream
--rich
--proxy PROXY Proxy URL (e.g. socks5://127.0.0.1:1080)
--wss-link WSS_LINK WSS URL(e.g. wss://sydney.bing.com/sydney/ChatHub)
--style {creative,balanced,precise}
--cookie-file COOKIE_FILE
needed if environment variable COOKIE_FILE is not set
```
---
## Docker での実行
これは、現在の作業ディレクトリに cookies.json ファイルがあることを前提としています
``` bash
docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt
```
次のように追加のフラグを追加できます
``` bash
docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt --rich --style creative
```
### 開発者デモ
Cookie を渡す 3 つの方法:
- 環境変数: `export COOKIE_FILE=/path/to/cookies.json` 。
- 引数 `cookie_path` には、次のように `cookies.json` へのパスを指定する:
```python
bot = Chatbot(cookie_path='./cookies.json')
```
- 次のように、引数 `cookies` で直接クッキーを渡します:
```python
with open('./cookies.json', 'r') as f:
cookies = json.load(f)
bot = Chatbot(cookies=cookies)
```
最高のエクスペリエンスを得るには Async を使用してください
より高度な使用例の参照コード:
```python
import asyncio
from EdgeGPT.EdgeGPT import Chatbot, ConversationStyle
async def main():
bot = await Chatbot.create()
print(await bot.ask(prompt="Hello world", conversation_style=ConversationStyle.creative, wss_link="wss://sydney.bing.com/sydney/ChatHub"))
await bot.close()
if __name__ == "__main__":
asyncio.run(main())
```
</details>
<details>
<summary>
## 画像ジェネレーター
</summary>
```bash
$ python3 -m ImageGen.ImageGen -h
usage: ImageGen.py [-h] [-U U] [--cookie-file COOKIE_FILE] --prompt PROMPT [--output-dir OUTPUT_DIR] [--quiet] [--asyncio]
optional arguments:
-h, --help show this help message and exit
-U U Auth cookie from browser
--cookie-file COOKIE_FILE
File containing auth cookie
--prompt PROMPT Prompt to generate images for
--output-dir OUTPUT_DIR
Output directory
--quiet Disable pipeline messages
--asyncio Run ImageGen using asyncio
```
### 開発者デモ
```python
from EdgeGPT.ImageGen import ImageGen
import argparse
import json
async def async_image_gen(args) -> None:
async with ImageGenAsync(args.U, args.quiet) as image_generator:
images = await image_generator.get_images(args.prompt)
await image_generator.save_images(images, output_dir=args.output_dir)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-U", help="Auth cookie from browser", type=str)
parser.add_argument("--cookie-file", help="File containing auth cookie", type=str)
parser.add_argument(
"--prompt",
help="Prompt to generate images for",
type=str,
required=True,
)
parser.add_argument(
"--output-dir",
help="Output directory",
type=str,
default="./output",
)
parser.add_argument(
"--quiet", help="Disable pipeline messages", action="store_true"
)
parser.add_argument(
"--asyncio", help="Run ImageGen using asyncio", action="store_true"
)
args = parser.parse_args()
# 認証クッキーを読み込む
with open(args.cookie_file, encoding="utf-8") as file:
cookie_json = json.load(file)
for cookie in cookie_json:
if cookie.get("name") == "_U":
args.U = cookie.get("value")
break
if args.U is None:
raise Exception("Could not find auth cookie")
if not args.asyncio:
# 画像ジェネレーターの作成
image_generator = ImageGen(args.U, args.quiet)
image_generator.save_images(
image_generator.get_images(args.prompt),
output_dir=args.output_dir,
)
else:
asyncio.run(async_image_gen(args))
```
</details>
## Star ヒストリー
[](https://star-history.com/#acheong08/EdgeGPT&Date)
## コントリビューター
このプロジェクトが存在するのはコントリビュートしてくださるすべての方々のおかげです。
<a href="https://github.com/acheong08/EdgeGPT/graphs/contributors">
<img src="https://contrib.rocks/image?repo=acheong08/EdgeGPT" />
</a>
================================================
FILE: docs/README_zh-cn.md
================================================
<div align="center">
<img src="https://socialify.git.ci/acheong08/EdgeGPT/image?font=Inter&language=1&logo=https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F9%2F9c%2FBing_Fluent_Logo.svg&owner=1&pattern=Floating%20Cogs&theme=Auto" alt="EdgeGPT" width="640" height="320" />
# Edge GPT
_新必应聊天功能的逆向工程_
<a href="./README.md">English</a> -
<a>简体中文</a> -
<a href="./README_zh-tw.md">繁體中文</a> -
<a href="./README_es.md">Español</a> -
<a href="./README_ja.md">日本語</a>
</div>
<p align="center">
<a href="https://github.com/acheong08/EdgeGPT">
<img alt="PyPI version" src="https://img.shields.io/pypi/v/EdgeGPT">
</a>
<img alt="Python version" src="https://img.shields.io/badge/python-3.8+-blue.svg">
<img alt="Total downloads" src="https://static.pepy.tech/badge/edgegpt">
</p>
<details open>
<summary>
# 设置
</summary>
## 安装模块
```bash
python3 -m pip install EdgeGPT --upgrade
```
## 要求
- python 3.8+
- 一个可以访问必应聊天的微软账户 <https://bing.com/chat> (可选,视所在地区而定)
- 需要在 New Bing 支持的国家或地区(中国大陆需使用VPN)
- [Selenium](https://pypi.org/project/selenium/) (对于需要自动配置cookie的情况)
## 身份验证
基本上不需要了。
**在部分地区**,微软已将聊天功能**开放**给所有人,或许可以**省略这一步**了。可以使用浏览器来确认(将 UA 设置为能表示为 Edge 的),**试一下能不能不登录就可以开始聊天**。
可能也得**看当前所在 IP 地址**。例如,如果试图从一个已知**属于数据中心范围**的 IP 来访问聊天功能(虚拟服务器、根服务器、虚拟专网、公共代理等),**可能就需要登录**;但是要是用家里的 IP 地址访问聊天功能,就没有问题。
如果收到这样的错误,可以试试**提供一个 cookie** 看看能不能解决:
`Exception: Authentication failed. You have not been accepted into the beta.`
### 收集 cookie
1. 获取一个看着像 Microsoft Edge 的浏览器。
- a) (简单) 安装最新版本的 Microsoft Edge
- b) (高级) 或者, 您可以使用任何浏览器并将用户代理设置为Edge的用户代理 (例如 `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51`). 您可以使用像 "User-Agent Switcher and Manager" [Chrome](https://chrome.google.com/webstore/detail/user-agent-switcher-and-m/bhchdcejhohfmigjafbampogmaanbfkg) 和 [Firefox](https://addons.mozilla.org/en-US/firefox/addon/user-agent-string-switcher/) 这样的扩展轻松完成此操作。
2. 打开 [bing.com/chat](https://bing.com/chat)
3. 如果您看到聊天功能,就接着下面的步骤...
4. 安装 [Chrome](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) 或 [Firefox](https://addons.mozilla.org/en-US/firefox/addon/cookie-editor/) 的 cookie editor 扩展
5. 移步到 [bing.com](https://bing.com)
6. 打开扩展程序
7. 点击右下角的"导出" ,然后点击"导出为 JSON" (将会把内容保存到你的剪贴板上)
8. 把你剪贴板上的内容粘贴到 `bing_cookies_*.json` 文件中
- 注意:**Cookie 文件名必须遵守正则表达式 `bing_cookies_*.json`**,这样才能让本模块的 cookie 处理程序识别到。
### 在代码中使用 cookie:
```python
cookies = json.loads(open("./path/to/cookies.json", encoding="utf-8").read()) # 可能会忽略 cookie 选项
bot = await Chatbot.create(cookies=cookies)
```
</details>
<details open>
<summary>
# 如何使用聊天机器人
</summary>
## 从命令行运行
```
$ python3 -m EdgeGPT.EdgeGPT -h
EdgeGPT - A demo of reverse engineering the Bing GPT chatbot
Repo: github.com/acheong08/EdgeGPT
By: Antonio Cheong
!help for help
Type !exit to exit
usage: EdgeGPT.py [-h] [--enter-once] [--search-result] [--no-stream] [--rich] [--proxy PROXY] [--wss-link WSS_LINK]
[--style {creative,balanced,precise}] [--prompt PROMPT] [--cookie-file COOKIE_FILE]
[--history-file HISTORY_FILE] [--locale LOCALE]
options:
-h, --help show this help message and exit
--enter-once
--search-result
--no-stream
--rich
--proxy PROXY Proxy URL (e.g. socks5://127.0.0.1:1080)
--wss-link WSS_LINK WSS URL(e.g. wss://sydney.bing.com/sydney/ChatHub)
--style {creative,balanced,precise}
--prompt PROMPT prompt to start with
--cookie-file COOKIE_FILE
path to cookie file
--history-file HISTORY_FILE
path to history file
--locale LOCALE your locale (e.g. en-US, zh-CN, en-IE, en-GB)
```
(中/美/英/挪具有更好的本地化支持)
## 在 Python 运行
### 1. 使用 `Chatbot` 类和 `asyncio` 类以进行更精细的控制
使用 async 获得最佳体验,例如:
```python
import asyncio, json
from EdgeGPT.EdgeGPT import Chatbot, ConversationStyle
async def main():
bot = await Chatbot.create() # 导入 cookie 是“可选”的,如前所述
response = await bot.ask(prompt="Hello world", conversation_style=ConversationStyle.creative, simplify_response=True)
print(json.dumps(response, indent=2)) # 返回下面这些
"""
{
"text": str,
"author": str,
"sources": list[dict],
"sources_text": str,
"suggestions": list[str],
"messages_left": int
}
"""
await bot.close()
if __name__ == "__main__":
asyncio.run(main())
```
### 2) `Query` 和 `Cookie` 助手类
创建一个简易的必应聊天 AI 查询(默认使用精确模式),这样可以只查看主要的文字输出,而不会打出整个 API 响应内容:
注意按照特定格式保存 cookie:`bing_cookies_*.json`.
```python
from EdgeGPT.EdgeUtils import Query, Cookie
q = Query("你是谁?用python代码给出回答")
print(q)
```
保存 cookie 文件的默认目录是 `HOME/bing_cookies`,不过可以这样修改:
```python
Cookie.dir_path = Path(r"...")
```
也可以修改对话模式,或者指定要使用的 cookie 文件:
```python
q = Query(
"你是谁?用python代码给出回答",
style="creative", # 或者平衡模式 'balanced' 或者精确模式 'precise'
cookie_file="./bing_cookies_alternative.json"
)
# 使用 `help(Query)` 查看其他支持的参数。
```
使用以下属性快速提取文本输出、代码片段、来源/参考列表或建议的后续问题:
```python
q.output # 或者: print(q)
q.sources
q.sources_dict
q.suggestions
q.code
q.code_blocks
q.code_block_formatsgiven)
```
获得原始 prompt 和指定的对话模式:
```python
q.prompt
q.ignore_cookies
q.style
q.simplify_response
q.locale
repr(q)
```
通过 import `Query` 获取之前的查询:
```python
Query.index # 一个查询对象的列表;是动态更新的
Query.image_dir_path
```
最后,`Cookie` 类支持多个 Cookie 文件,因此,如果您使用命名约定 `bing_cookies_*.json` 创建其他 Cookie 文件,那么如果超过了每日请求配额(目前设置为 200),查询将自动尝试使用下一个文件(按字母顺序)。
这些是可以访问的主要属性:
```python
Cookie.current_file_index
Cookie.current_file_path
Cookie.current_data
Cookie.dir_path
Cookie.search_pattern
Cookie.files
Cookie.image_token
Cookie.import_next
Cookie.rotate_cookies
Cookie.ignore_files
Cookie.supplied_files
Cookie.request_count
```
---
## 使用 Docker 运行
假设当前工作目录有一个文件 `cookies.json`
```bash
docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt
```
可以像这样添加任意参数
```bash
docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt --rich --style creative
```
</details>
</details>
<details open>
<summary>
# 如何使用图片生成器
</summary>
## 从命令行运行
```bash
$ python3 -m ImageGen.ImageGen -h
usage: ImageGen.py [-h] [-U U] [--cookie-file COOKIE_FILE] --prompt PROMPT [--output-dir OUTPUT_DIR] [--quiet] [--asyncio]
optional arguments:
-h, --help show this help message and exit
-U U Auth cookie from browser
--cookie-file COOKIE_FILE
File containing auth cookie
--prompt PROMPT Prompt to generate images for
--output-dir OUTPUT_DIR
Output directory
--quiet Disable pipeline messages
--asyncio Run ImageGen using asyncio
```
## 在 Python 运行
### 1) `ImageQuery` 助手类
使用一个简易提示生成图像,并下载到当前工作目录:
```python
from EdgeGPT.EdgeUtils import ImageQuery
q=ImageQuery("Meerkats at a garden party in Devon")
```
在此会话中修改所有后续图像的下载目录:
```
Query.image_dirpath = Path("./to_another_folder")
```
### 2) 使用 `ImageGen` 类和 `asyncio` 类以进行更精细的控制
```python
from EdgeGPT.ImageGen import ImageGen
import argparse
import json
async def async_image_gen(args) -> None:
async with ImageGenAsync(args.U, args.quiet) as image_generator:
images = await image_generator.get_images(args.prompt)
await image_generator.save_images(images, output_dir=args.output_dir)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-U", help="从浏览器获取的身份认证 cookie", type=str)
parser.add_argument("--cookie-file", help="包含认证 cookie 的文件", type=str)
parser.add_argument(
"--prompt",
help="用于生成图片的 prompt",
type=str,
required=True,
)
parser.add_argument(
"--output-dir",
help="输出目录",
type=str,
default="./output",
)
parser.add_argument(
"--quiet", help="禁用管道消息", action="store_true"
)
parser.add_argument(
"--asyncio", help="使用 asyncio 运行 ImageGen", action="store_true"
)
args = parser.parse_args()
# 加载认证 cookie
with open(args.cookie_file, encoding="utf-8") as file:
cookie_json = json.load(file)
for cookie in cookie_json:
if cookie.get("name") == "_U":
args.U = cookie.get("value")
break
if args.U is None:
raise Exception("未能找到认证 Cookie")
if not args.asyncio:
# 创建图像生成器
image_generator = ImageGen(args.U, args.quiet)
image_generator.save_images(
image_generator.get_images(args.prompt),
output_dir=args.output_dir,
)
else:
asyncio.run(async_image_gen(args))
```
</details>
<details open>
<summary>
# Star 历史
</summary>
[](https://star-history.com/#acheong08/EdgeGPT&Date)
</details>
<details open>
<summary>
# 贡献者
</summary>
这个项目的存在要归功于所有做出贡献的人。
<a href="https://github.com/acheong08/EdgeGPT/graphs/contributors">
<img src="https://contrib.rocks/image?repo=acheong08/EdgeGPT" />
</a>
</details>
================================================
FILE: docs/README_zh-tw.md
================================================
<div align="center">
<img src="https://socialify.git.ci/acheong08/EdgeGPT/image?font=Inter&language=1&logo=https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F9%2F9c%2FBing_Fluent_Logo.svg&owner=1&pattern=Floating%20Cogs&theme=Auto" alt="EdgeGPT" width="640" height="320" />
# Edge GPT
_新必應聊天功能的逆向工程_
<a href="./README.md">English</a> -
<a href="./README_zh-cn.md">简体中文</a> -
<a>繁體中文</a> -
<a href="./README_es.md">Español</a> -
<a href="./README_ja.md">日本語</a>
</div>
<p align="center">
<a href="https://github.com/acheong08/EdgeGPT">
<img alt="PyPI version" src="https://img.shields.io/pypi/v/EdgeGPT">
</a>
<img alt="Python version" src="https://img.shields.io/badge/python-3.8+-blue.svg">
<img alt="Total downloads" src="https://static.pepy.tech/badge/edgegpt">
</p>
<details open>
<summary>
# 設置
</summary>
## 安裝模組
```bash
python3 -m pip install EdgeGPT --upgrade
```
## 要求
- python 3.8+
- 一個可以訪問必應聊天的微軟帳戶 <https://bing.com/chat> (可選,取決於所在地區)
- 需要在 New Bing 支持的國家或地區(中國大陸需使用VPN)
- [Selenium](https://pypi.org/project/selenium/) (對於需要自動配置cookie的情況)
## 認證
基本上不需要了。
**在某些地區**,微軟已將聊天功能**開放**給所有人,或許可以**省略這一步**了。您可以使用瀏覽器進行檢查(將 UA 設置為能表示為 Edge 的),**嘗試能否在不登錄的情況下開始聊天**。
可能也得**看當前所在 IP 位址**。例如,如果試圖從一個已知**屬於數據中心範圍**的 IP 來訪問聊天功能(虛擬伺服器、根伺服器、虛擬專網、公共代理等),**可能就需要登錄**;但是要是用家裡的 IP 位址訪問聊天功能,就沒有問題。
如果收到這樣的錯誤,可以試試**提供一個 cookie** 看看能不能解決:
`Exception: Authentication failed. You have not been accepted into the beta.`
### 收集 cookie
1. 獲取一個看著像 Microsoft Edge 的瀏覽器。
* a) (簡單) 安裝最新版本的 Microsoft Edge
* b) (高級) 或者, 您可以使用任何瀏覽器並將用戶代理設置為Edge的用戶代理 (例如 `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51`). 您可以使用像 "User-Agent Switcher and Manager" [Chrome](https://chrome.google.com/webstore/detail/user-agent-switcher-and-m/bhchdcejhohfmigjafbampogmaanbfkg) 和 [Firefox](https://addons.mozilla.org/en-US/firefox/addon/user-agent-string-switcher/) 這樣的擴展輕鬆完成此操作.
2. 打開 [bing.com/chat](https://bing.com/chat)
3. 如果您看到聊天功能,就接著下面的步驟...
4. 安裝 [Chrome](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) 或 [Firefox](https://addons.mozilla.org/en-US/firefox/addon/cookie-editor/) 的 cookie editor 擴展
5. 轉到 [bing.com](https://bing.com)
6. 打開擴展程式
7. 單擊右下角的「匯出」,然後按「匯出為 JSON」(這會將您的 cookie 保存到剪貼簿)
8. 將您剪貼簿上的 cookie 粘貼到檔 `bing_cookies_*.json` 中
* 注意:**cookie 檔名必須遵循正則表示式 `bing_cookies_*.json`**,這樣才能讓本模組的 cookie 處理程式識別到。
### 在代碼中使用 cookie:
```python
cookies = json.loads(open("./path/to/cookies.json", encoding="utf-8").read()) # 可能会忽略 cookie 选项
bot = await Chatbot.create(cookies=cookies)
```
</details>
<details open>
<summary>
# 如何使用聊天機器人
</summary>
## 從命令列運行
```
$ python3 -m EdgeGPT.EdgeGPT -h
EdgeGPT - A demo of reverse engineering the Bing GPT chatbot
Repo: github.com/acheong08/EdgeGPT
By: Antonio Cheong
!help for help
Type !exit to exit
usage: EdgeGPT.py [-h] [--enter-once] [--search-result] [--no-stream] [--rich] [--proxy PROXY] [--wss-link WSS_LINK]
[--style {creative,balanced,precise}] [--prompt PROMPT] [--cookie-file COOKIE_FILE]
[--history-file HISTORY_FILE] [--locale LOCALE]
options:
-h, --help show this help message and exit
--enter-once
--search-result
--no-stream
--rich
--proxy PROXY Proxy URL (e.g. socks5://127.0.0.1:1080)
--wss-link WSS_LINK WSS URL(e.g. wss://sydney.bing.com/sydney/ChatHub)
--style {creative,balanced,precise}
--prompt PROMPT prompt to start with
--cookie-file COOKIE_FILE
path to cookie file
--history-file HISTORY_FILE
path to history file
--locale LOCALE your locale (e.g. en-US, zh-CN, en-IE, en-GB)
```
(中/美/英/挪具有更好的本地化支援)
## 在 Python 運行
### 1. 使用 `Chatbot` 類和 `asyncio` 類以進行更精細的控制
使用 async 獲得最佳體驗,例如:
```python
import asyncio, json
from EdgeGPT.EdgeGPT import Chatbot, ConversationStyle
async def main():
bot = await Chatbot.create() # 導入 cookie 是“可選”的,如前所述
response = await bot.ask(prompt="Hello world", conversation_style=ConversationStyle.creative, simplify_response=True)
print(json.dumps(response, indent=2)) # 返回如下
"""
{
"text": str,
"author": str,
"sources": list[dict],
"sources_text": str,
"suggestions": list[str],
"messages_left": int
}
"""
await bot.close()
if __name__ == "__main__":
asyncio.run(main())
```
### 2) `Query` 和 `Cookie` 助手類
創建一個簡單的必應聊天 AI 查詢(預設情況下使用“精確”對話樣式),這樣可以僅查看主要文本輸出,而不是整個 API 回應:
注意按照特定格式儲存 cookie: ```bing_cookies_*.json```。
```python
from EdgeGPT.EdgeUtils import Query, Cookie
q = Query("你是誰?用python代碼給出回答")
print(q)
```
儲存 Cookie 檔的預設目錄是 `HOME/bing_cookies`,但您可以通過以下方式更改它:
```python
Cookie.dir_path = Path(r"...")
```
或者更改要使用的對話風格或 Cookie 檔:
```python
q = Query(
"你是誰?用python代碼給出回答",
style="creative", # 或者平衡模式 'balanced' 或者精確模式 'precise'
cookies="./bing_cookies_alternative.json"
)
# 使用 `help(Query)` 查看其他支持的參數。
```
使用以下屬性快速提取文字輸出、代碼片段、來源/參考清單或建議的後續問題:
```python
q.output # 或者: print(q)
q.sources
q.sources_dict
q.suggestions
q.code
q.code_blocks
q.code_block_formatsgiven)
```
抓取原始 prompt 與您指定的對話風格:
```python
q.prompt
q.ignore_cookies
q.style
q.simplify_response
q.locale
repr(q)
```
通過 import `Query` 獲取進行的先前查詢:
```python
Query.index # 一个查詢物件的串列;是動態更新的
Query.image_dir_path
```
最後,`Cookie` 類支援多個 cookie 檔,因此,如果您使用命名約定 `bing_cookies_*.json` 創建其他 cookie 檔,則如果您的請求數已超出每日配額(當前設置為 200),您的查詢將自動嘗試使用下一個檔(按字母順序)。
以下是您可以獲得的主要屬性:
```python
Cookie.current_file_index
Cookie.current_file_path
Cookie.current_data
Cookie.dir_path
Cookie.search_pattern
Cookie.files
Cookie.image_token
Cookie.import_next
Cookie.rotate_cookies
Cookie.ignore_files
Cookie.supplied_files
Cookie.request_count
```
---
## 使用 Docker 運行
假設在當前工作目錄中有一個檔 `cookies.json`
```bash
docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt
```
可以像這樣添加任意參數
```bash
docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt --rich --style creative
```
</details>
</details>
<details open>
<summary>
# 如何使用圖像生成器
</summary>
## 從命令列運行
```bash
$ python3 -m ImageGen.ImageGen -h
usage: ImageGen.py [-h] [-U U] [--cookie-file COOKIE_FILE] --prompt PROMPT [--output-dir OUTPUT_DIR] [--quiet] [--asyncio]
optional arguments:
-h, --help show this help message and exit
-U U Auth cookie from browser
--cookie-file COOKIE_FILE
File containing auth cookie
--prompt PROMPT Prompt to generate images for
--output-dir OUTPUT_DIR
Output directory
--quiet Disable pipeline messages
--asyncio Run ImageGen using asyncio
```
## 在 Python 運行
### 1) `ImageQuery` 助手類
根據一個簡單的提示產生圖像並下載到目前工作目錄:
```python
from EdgeGPT.EdgeUtils import ImageQuery
q=ImageQuery("Meerkats at a garden party in Devon")
```
在此工作階段中修改所有後續圖像的下載目錄:
```
Query.image_dirpath = Path("./to_another_folder")
```
### 2) 使用 `ImageGen` 類和 `asyncio` 類以進行更精細的控制
```python
from EdgeGPT.ImageGen import ImageGen
import argparse
import json
async def async_image_gen(args) -> None:
async with ImageGenAsync(args.U, args.quiet) as image_generator:
images = await image_generator.get_images(args.prompt)
await image_generator.save_images(images, output_dir=args.output_dir)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-U", help="來自瀏覽器的身份驗證 cookie", type=str)
parser.add_argument("--cookie-file", help="包含身份驗證 cookie 的檔", type=str)
parser.add_argument(
"--prompt",
help="用于產生圖像的 prompt",
type=str,
required=True,
)
parser.add_argument(
"--output-dir",
help="輸出目錄",
type=str,
default="./output",
)
parser.add_argument(
"--quiet", help="禁用管道消息", action="store_true"
)
parser.add_argument(
"--asyncio", help="使用 asyncio 運行 ImageGen", action="store_true"
)
args = parser.parse_args()
# 載入身份驗證 cookie
with open(args.cookie_file, encoding="utf-8") as file:
cookie_json = json.load(file)
for cookie in cookie_json:
if cookie.get("name") == "_U":
args.U = cookie.get("value")
break
if args.U is None:
raise Exception("找不到身份驗證 Cookie")
if not args.asyncio:
# 創建圖片生成器
image_generator = ImageGen(args.U, args.quiet)
image_generator.save_images(
image_generator.get_images(args.prompt),
output_dir=args.output_dir,
)
else:
asyncio.run(async_image_gen(args))
```
</details>
<details open>
<summary>
# Star 歷史
</summary>
[](https://star-history.com/#acheong08/EdgeGPT&Date)
</details>
<details open>
<summary>
# 貢獻者
</summary>
這個專案的存在要歸功於所有做出貢獻的人。
<a href="https://github.com/acheong08/EdgeGPT/graphs/contributors">
<img src="https://contrib.rocks/image?repo=acheong08/EdgeGPT" />
</a>
</details>
================================================
FILE: docs/wiki/EdgeGPT.md
================================================
<a id="EdgeGPT.EdgeGPT"></a>
# EdgeGPT.EdgeGPT
Main.py
<a id="EdgeGPT.EdgeGPT.Chatbot"></a>
## Chatbot Objects
```python
class Chatbot()
```
Combines everything to make it seamless
<a id="EdgeGPT.EdgeGPT.Chatbot.save_conversation"></a>
#### save\_conversation
```python
async def save_conversation(filename: str) -> None
```
Save the conversation to a file
<a id="EdgeGPT.EdgeGPT.Chatbot.load_conversation"></a>
#### load\_conversation
```python
async def load_conversation(filename: str) -> None
```
Load the conversation from a file
<a id="EdgeGPT.EdgeGPT.Chatbot.get_conversation"></a>
#### get\_conversation
```python
async def get_conversation() -> dict
```
Gets the conversation history from conversation_id (requires load_conversation)
<a id="EdgeGPT.EdgeGPT.Chatbot.get_activity"></a>
#### get\_activity
```python
async def get_activity() -> dict
```
Gets the recent activity (requires cookies)
<a id="EdgeGPT.EdgeGPT.Chatbot.ask"></a>
#### ask
```python
async def ask(prompt: str,
wss_link: str = "wss://sydney.bing.com/sydney/ChatHub",
conversation_style: CONVERSATION_STYLE_TYPE = None,
webpage_context: str | None = None,
search_result: bool = False,
locale: str = guess_locale(),
simplify_response: bool = False) -> dict
```
Ask a question to the bot
Response:
{
item (dict):
messages (list[dict]):
adaptiveCards (list[dict]):
body (list[dict]):
text (str): Response
}
To get the response, you can do:
response["item"]["messages"][1]["adaptiveCards"][0]["body"][0]["text"]
<a id="EdgeGPT.EdgeGPT.Chatbot.ask_stream"></a>
#### ask\_stream
```python
async def ask_stream(
prompt: str,
wss_link: str = "wss://sydney.bing.com/sydney/ChatHub",
conversation_style: CONVERSATION_STYLE_TYPE = None,
raw: bool = False,
webpage_context: str | None = None,
search_result: bool = False,
locale: str = guess_locale()
) -> Generator[bool, dict | str, None]
```
Ask a question to the bot
<a id="EdgeGPT.EdgeGPT.Chatbot.close"></a>
#### close
```python
async def close() -> None
```
Close the connection
<a id="EdgeGPT.EdgeGPT.Chatbot.delete_conversation"></a>
#### delete\_conversation
```python
async def delete_conversation(conversation_id: str = None,
conversation_signature: str = None,
client_id: str = None) -> None
```
Delete the chat in the server and close the connection
<a id="EdgeGPT.EdgeGPT.Chatbot.reset"></a>
#### reset
```python
async def reset(delete=False) -> None
```
Reset the conversation
================================================
FILE: docs/wiki/Home.md
================================================
Welcome to the EdgeGPT wiki!
## Languages
- English
- [简体中文](https://github.com/CoolPlayLin/ChatGPT-Wiki/tree/main/docs/EdgeGPT)
================================================
FILE: docs/wiki/ImageGen.md
================================================
<a id="ImageGen"></a>
# ImageGen
<a id="ImageGen.ImageGen"></a>
## ImageGen Objects
```python
class ImageGen()
```
Image generation by Microsoft Bing
**Arguments**:
- `auth_cookie` - str
<a id="ImageGen.ImageGen.get_images"></a>
#### get\_images
```python
def get_images(prompt: str) -> list
```
Fetches image links from Bing
**Arguments**:
- `prompt` - str
<a id="ImageGen.ImageGen.save_images"></a>
#### save\_images
```python
def save_images(links: list, output_dir: str) -> None
```
Saves images to output directory
================================================
FILE: example.env
================================================
export BING_U="<COOKIE_VALUE>"
================================================
FILE: requirements.txt
================================================
aiohttp
BingImageCreator
certifi
httpx
prompt_toolkit
requests
rich
================================================
FILE: setup.cfg
================================================
[metadata]
description_file=README.md
license_files=LICENSE
================================================
FILE: setup.py
================================================
from pathlib import Path
from setuptools import find_packages
from setuptools import setup
DOCS_PATH = Path(__file__).parents[0] / "docs/README.md"
PATH = Path("README.md")
if not PATH.exists():
with Path.open(DOCS_PATH, encoding="utf-8") as f1:
with Path.open(PATH, "w+", encoding="utf-8") as f2:
f2.write(f1.read())
setup(
name="EdgeGPT",
version="0.13.2",
license="The Unlicense",
author="Antonio Cheong",
author_email="acheong@student.dalat.org",
description="Reverse engineered Edge Chat API",
packages=find_packages("src"),
package_dir={"": "src"},
url="https://github.com/acheong08/EdgeGPT",
project_urls={"Bug Report": "https://github.com/acheong08/EdgeGPT/issues/new"},
entry_points={
"console_scripts": [
"edge-gpt = EdgeGPT.main:main",
"edge-gpt-image = EdgeGPT.ImageGen:main",
],
},
install_requires=[
"httpx[socks]>=0.24.0",
"aiohttp",
"websockets",
"rich",
"certifi",
"prompt_toolkit",
"requests",
"BingImageCreator>=0.4.4",
],
long_description=Path.open(PATH, encoding="utf-8").read(),
long_description_content_type="text/markdown",
py_modules=["EdgeGPT", "EdgeUtils", "ImageGen"],
classifiers=[
"License :: OSI Approved :: The Unlicense (Unlicense)",
"Intended Audience :: Developers",
"Topic :: Software Development :: Libraries :: Python Modules",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
],
)
================================================
FILE: src/EdgeGPT/EdgeGPT.py
================================================
"""
Main.py
"""
from __future__ import annotations
import json
from pathlib import Path
from typing import Generator
from .chathub import *
from .conversation import *
from .conversation_style import *
from .request import *
from .utilities import *
class Chatbot:
"""
Combines everything to make it seamless
"""
def __init__(
self,
proxy: str | None = None,
cookies: list[dict] | None = None,
) -> None:
self.proxy: str | None = proxy
self.chat_hub: ChatHub = ChatHub(
Conversation(self.proxy, cookies=cookies),
proxy=self.proxy,
cookies=cookies,
)
@staticmethod
async def create(
proxy: str | None = None,
cookies: list[dict] | None = None,
) -> Chatbot:
self = Chatbot.__new__(Chatbot)
self.proxy = proxy
self.chat_hub = ChatHub(
await Conversation.create(self.proxy, cookies=cookies),
proxy=self.proxy,
cookies=cookies,
)
return self
async def save_conversation(self, filename: str) -> None:
"""
Save the conversation to a file
"""
with open(filename, "w") as f:
conversation_id = self.chat_hub.request.conversation_id
conversation_signature = self.chat_hub.request.conversation_signature
client_id = self.chat_hub.request.client_id
invocation_id = self.chat_hub.request.invocation_id
f.write(
json.dumps(
{
"conversation_id": conversation_id,
"conversation_signature": conversation_signature,
"client_id": client_id,
"invocation_id": invocation_id,
},
),
)
async def load_conversation(self, filename: str) -> None:
"""
Load the conversation from a file
"""
with open(filename) as f:
conversation = json.load(f)
self.chat_hub.request = ChatHubRequest(
conversation_signature=conversation["conversation_signature"],
client_id=conversation["client_id"],
conversation_id=conversation["conversation_id"],
invocation_id=conversation["invocation_id"],
)
async def get_conversation(self) -> dict:
"""
Gets the conversation history from conversation_id (requires load_conversation)
"""
return await self.chat_hub.get_conversation()
async def get_activity(self) -> dict:
"""
Gets the recent activity (requires cookies)
"""
return await self.chat_hub.get_activity()
async def ask(
self,
prompt: str,
wss_link: str = "wss://sydney.bing.com/sydney/ChatHub",
conversation_style: CONVERSATION_STYLE_TYPE = None,
webpage_context: str | None = None,
search_result: bool = False,
locale: str = guess_locale(),
simplify_response: bool = False,
) -> dict:
"""
Ask a question to the bot
Response:
{
item (dict):
messages (list[dict]):
adaptiveCards (list[dict]):
body (list[dict]):
text (str): Response
}
To get the response, you can do:
response["item"]["messages"][1]["adaptiveCards"][0]["body"][0]["text"]
"""
async for final, response in self.chat_hub.ask_stream(
prompt=prompt,
conversation_style=conversation_style,
wss_link=wss_link,
webpage_context=webpage_context,
search_result=search_result,
locale=locale,
):
if final:
if not simplify_response:
return response
messages_left = response["item"]["throttling"][
"maxNumUserMessagesInConversation"
] - response["item"]["throttling"].get(
"numUserMessagesInConversation",
0,
)
if messages_left == 0:
raise Exception("Max messages reached")
message = ""
for msg in reversed(response["item"]["messages"]):
if msg.get("adaptiveCards") and msg["adaptiveCards"][0]["body"][
0
].get("text"):
message = msg
break
if not message:
raise Exception("No message found")
suggestions = [
suggestion["text"]
for suggestion in message.get("suggestedResponses", [])
]
adaptive_cards = message.get("adaptiveCards", [])
adaptive_text = (
adaptive_cards[0]["body"][0].get("text") if adaptive_cards else None
)
sources = (
adaptive_cards[0]["body"][0].get("text") if adaptive_cards else None
)
sources_text = (
adaptive_cards[0]["body"][-1].get("text")
if adaptive_cards
else None
)
return {
"text": message["text"],
"author": message["author"],
"sources": sources,
"sources_text": sources_text,
"suggestions": suggestions,
"messages_left": messages_left,
"max_messages": response["item"]["throttling"][
"maxNumUserMessagesInConversation"
],
"adaptive_text": adaptive_text,
}
return {}
async def ask_stream(
self,
prompt: str,
wss_link: str = "wss://sydney.bing.com/sydney/ChatHub",
conversation_style: CONVERSATION_STYLE_TYPE = None,
raw: bool = False,
webpage_context: str | None = None,
search_result: bool = False,
locale: str = guess_locale(),
) -> Generator[bool, dict | str, None]:
"""
Ask a question to the bot
"""
async for response in self.chat_hub.ask_stream(
prompt=prompt,
conversation_style=conversation_style,
wss_link=wss_link,
raw=raw,
webpage_context=webpage_context,
search_result=search_result,
locale=locale,
):
yield response
async def close(self) -> None:
"""
Close the connection
"""
await self.chat_hub.close()
async def delete_conversation(
self,
conversation_id: str = None,
conversation_signature: str = None,
client_id: str = None,
) -> None:
"""
Delete the chat in the server
"""
await self.chat_hub.delete_conversation(
conversation_id=conversation_id,
conversation_signature=conversation_signature,
client_id=client_id,
)
async def reset(self, delete=False) -> None:
"""
Reset the conversation
"""
if delete:
await self.remove_and_close()
else:
await self.close()
self.chat_hub = ChatHub(
await Conversation.create(self.proxy, cookies=self.chat_hub.cookies),
proxy=self.proxy,
cookies=self.chat_hub.cookies,
)
if __name__ == "__main__":
from .main import main
main()
================================================
FILE: src/EdgeGPT/EdgeUtils.py
================================================
import asyncio
import contextlib
import json
import re
from pathlib import Path
from log2d import Log
from .EdgeGPT import Chatbot
from .EdgeGPT import ConversationStyle
from .ImageGen import ImageGen
Log("BingChat")
log = Log.BingChat.debug # shortcut to create a log entry
class Cookie:
"""
Convenience class for Bing Cookie files, data, and configuration. This Class
is updated dynamically by the Query class to allow cycling through >1
cookie/credentials file e.g. when daily request limits (current 200 per
account per day) are exceeded.
"""
current_file_index = 0
dir_path = Path.home().resolve() / "bing_cookies"
current_file_path = dir_path # Avoid Path errors when no cookie file used
search_pattern = "bing_cookies_*.json"
ignore_files = set()
request_count = {}
supplied_files = set()
rotate_cookies = True
@classmethod
def files(cls: type) -> list[Path]:
"""
Return a sorted list of all cookie files matching .search_pattern in
cls.dir_path, plus any supplied files, minus any ignored files.
"""
all_files = set(Path(cls.dir_path).glob(cls.search_pattern))
if hasattr(cls, "supplied_files"):
supplied_files = {x for x in cls.supplied_files if x.is_file()}
all_files.update(supplied_files)
return sorted(all_files - cls.ignore_files)
@classmethod
def import_data(cls: type) -> None:
"""
Read the active cookie file and populate the following attributes:
.current_file_path
.current_data
.image_token
"""
if not cls.files():
log("No files in Cookie.dir_path")
return
try:
cls.current_file_path = cls.files()[cls.current_file_index]
except IndexError:
log(f"Invalid file index [{cls.current_file_index}]")
log("Files in Cookie.dir_path:")
for file in cls.files():
log(f"{file}")
return
log(f"Importing cookies from: {cls.current_file_path.name}")
with open(cls.current_file_path, encoding="utf-8") as file:
cls.current_data = json.load(file)
cls.image_token = [
x for x in cls.current_data if x.get("name").startswith("_U")
]
cls.image_token = cls.image_token[0].get("value")
@classmethod
def import_next(cls: type, discard: bool = False) -> None:
"""
Cycle through to the next cookies file then import it.
discard (bool): True -Mark the previous file to be ignored for the remainder of the current session. Otherwise cycle through all available
cookie files (sharing the workload and 'resting' when not in use).
"""
if not hasattr(cls, "current_file_path"):
cls.import_data()
return
with contextlib.suppress(AttributeError):
# Will fail on first instantiation because no current_file_path
if discard:
cls.ignore_files.add(cls.current_file_path)
else:
Cookie.current_file_index += 1
if Cookie.current_file_index >= len(cls.files()):
Cookie.current_file_index = 0
Cookie.import_data()
class Query:
"""
A convenience class that wraps around EdgeGPT.Chatbot to encapsulate input,
config, and output all together. Relies on Cookie class for authentication
unless ignore_cookies=True
"""
index = []
image_dir_path = Path.cwd().resolve() / "bing_images"
def __init__(
self,
prompt: str,
style: ConversationStyle = "precise",
content_type: str = "text",
cookie_files: set[Path] = None,
ignore_cookies: bool = False,
echo: bool = True,
echo_prompt: bool = False,
locale: str = "en-GB",
simplify_response: bool = True,
) -> None:
"""
Arguments:
prompt: Text to enter into Bing Chat
style: creative, balanced, or precise
content_type: "text" for Bing Chat; "image" for Dall-e
ignore_cookies (bool): Ignore cookie data altogether
echo: Print something to confirm request made
echo_prompt: Print confirmation of the evaluated prompt
simplify_response: True -> single simplified prompt/response exchange
cookie_files: iterable of Paths or strings of cookie files (json)
Files in Cookie.dir_path will also be used if they exist. This defaults
to the current working directory, so set Cookie.dir_path before
creating a Query if your cookie files are elsewhere.
"""
self.__class__.index += [self]
self.prompt = prompt
self.locale = locale
self.simplify_response = simplify_response
self.ignore_cookies = ignore_cookies
if not ignore_cookies:
if cookie_files:
# Convert singular argument to an iterable:
if isinstance(cookie_files, (str, Path)):
cookie_files = {cookie_files}
# Check all elements exist and are Paths:
cookie_files = {
Path(x).resolve()
for x in cookie_files
if isinstance(x, (str, Path)) and x
}
Cookie.supplied_files = cookie_files
files = Cookie.files() # includes .supplied_files
if Cookie.rotate_cookies:
Cookie.import_next()
else:
Cookie.import_data()
if content_type == "text":
self.style = style
self.log_and_send_query(echo, echo_prompt)
if content_type == "image":
self.create_image()
def log_and_send_query(self, echo: bool, echo_prompt: bool) -> None:
self.response = asyncio.run(self.send_to_bing(echo, echo_prompt))
if not hasattr(Cookie, "current_data"):
name = "<no_cookies>"
else:
name = Cookie.current_file_path.name
if not Cookie.request_count.get(name):
Cookie.request_count[name] = 1
else:
Cookie.request_count[name] += 1
def create_image(self) -> None:
image_generator = ImageGen(Cookie.image_token)
image_generator.save_images(
image_generator.get_images(self.prompt),
output_dir=self.__class__.image_dir_path,
)
async def send_to_bing(self, echo: bool = True, echo_prompt: bool = False) -> str:
"""Creat, submit, then close a Chatbot instance. Return the response"""
retries = len(Cookie.files()) or 1
while retries:
if not hasattr(Cookie, "current_data"):
bot = await Chatbot.create()
else:
bot = await Chatbot.create(cookies=Cookie.current_data)
if echo_prompt:
log(f"{self.prompt=}")
if echo:
log("Waiting for response...")
if self.style.lower() not in "creative balanced precise".split():
self.style = "precise"
try:
return await bot.ask(
prompt=self.prompt,
conversation_style=getattr(ConversationStyle, self.style),
simplify_response=self.simplify_response,
locale=self.locale,
)
except Exception as ex:
log(
f"Exception: [{Cookie.current_file_path.name} may have exceeded the daily limit]\n{ex}",
)
Cookie.import_next(discard=True)
retries -= 1
finally:
await bot.close()
return None
@property
def output(self) -> str:
"""The response from a completed Chatbot request"""
if not self.simplify_response:
return [
x.get("text") or x.get("hiddenText")
for x in self.response["item"]["messages"]
if x["author"] == "bot"
]
try:
return self.response["text"]
except TypeError as te:
raise TypeError(
f"{te}\n(No response received - probably rate throttled...)",
) from te
@property
def sources(self) -> list[list[dict]]:
"""The source names and details parsed from a completed Chatbot request"""
if self.simplify_response:
return self.response["sources_text"]
return [
x.get("sourceAttributions") or []
for x in self.response["item"]["messages"]
if x["author"] == "bot"
]
@property
def sources_dict(self) -> dict[int, str]:
"""The source names and details as a dictionary"""
if self.simplify_response:
text = self.response["sources_text"]
sources = enumerate(re.findall(r"\((http.*?)\)", text))
return {index + 1: value for index, value in sources}
all_sources = []
name = "providerDisplayName"
url = "seeMoreUrl"
for sources in self.sources:
if not sources:
continue
data = {
index + 1: source[url]
for index, source in enumerate(sources)
if name in source and url in source
}
all_sources += [data]
return all_sources
@property
def code_block_formats(self) -> list[str]:
"""
Extract a list of programming languages/formats used in code blocks
"""
regex = r"``` *(\b\w+\b\+*) *"
if self.simplify_response:
return re.findall(regex, self.output)
return re.findall(regex, "\n".join(self.output))
@property
def code_blocks(self) -> list[str]:
"""
Return a list of code blocks (```) or snippets (`) as strings.
If the response contains a mix of snippets and code blocks, return the
code blocks only.
This method is not suitable if the main text response includes either of
the delimiters but not as part of an actual snippet or code block.
For example:
'In Markdown, the back-tick (`) is used to denote a code snippet'
"""
final_blocks = []
if isinstance(self.output, str): # I.e. simplify_response is True
separator = "```" if "```" in self.output else "`"
code_blocks = self.output.split(separator)[1:-1:2]
if separator == "`":
return code_blocks
else:
code_blocks = []
for response in self.output:
separator = "```" if "```" in response else "`"
code_blocks.extend(response.split(separator)[1:-1:2])
code_blocks = [x for x in code_blocks if x]
# Remove language name if present:
for block in code_blocks:
lines = block.splitlines()
code = lines[1:] if re.match(" *\\w+ *", lines[0]) else lines
final_blocks += ["\n".join(code).removeprefix(separator)]
return [x for x in final_blocks if x]
@property
def code(self) -> str:
"""
Extract and join any snippets of code or formatted data in the response
"""
return "\n\n".join(self.code_blocks)
@property
def suggestions(self) -> list[str]:
"""Follow-on questions suggested by the Chatbot"""
if self.simplify_response:
return self.response["suggestions"]
try:
return [
x["text"]
for x in self.response["item"]["messages"][1]["suggestedResponses"]
]
except KeyError:
return None
def __repr__(self) -> str:
return f"<EdgeGPT.Query: {self.prompt}>"
def __str__(self) -> str:
return self.output if self.simplify_response else "\n\n".join(self.output)
class ImageQuery(Query):
def __init__(self, prompt: str, **kwargs) -> None:
kwargs["content_type"] = "image"
super().__init__(prompt, **kwargs)
def __repr__(self) -> str:
return f"<EdgeGPT.ImageQuery: {self.prompt}>"
def test_cookie_rotation() -> None:
for i in range(1, 50):
q = Query(
f"What is {i} in Roman numerals? Give the answer in JSON",
style="precise",
)
log(f"{i}: {Cookie.current_file_path.name}")
log(q.code)
log(f"Cookie count: {Cookie.request_count.get(Cookie.current_file_path.name)}")
def test_features() -> Query:
try:
q = Query(
f"What is {i} in Roman numerals? Give the answer in JSON",
style="precise",
)
log(f"{i}: {Cookie.current_file_path.name}")
print(f"{Cookie.current_file_index=}")
print(f"{Cookie.current_file_path=}")
print(f"{Cookie.current_data=}")
print(f"{Cookie.dir_path=}")
print(f"{Cookie.search_pattern=}")
print(f"{Cookie.files()=}")
print(f"{Cookie.image_token=}")
print(f"{Cookie.import_next(discard=True)=}")
print(f"{Cookie.rotate_cookies=}")
print(f"{Cookie.files()=}")
print(f"{Cookie.ignore_files=}")
print(f"{Cookie.supplied_files=}")
print(
f"{Cookie.request_count=}"
) # Keeps a tally of requests made in using each cookie file during this session
print(f"{q=}")
print(f"{q.prompt=}")
print(f"{q.ignore_cookies=}")
print(f"{q.style=}")
print(f"{q.simplify_response=}")
print(f"{q.locale=}")
print(f"{q.output=}")
print(q)
print(f"{q.sources=}")
print(f"{q.sources_dict=}")
print(f"{q.suggestions=}")
print(f"{q.code=}") # All code as a single string
print(f"{q.code_blocks=}") # Individual code blocks
print(
f"{q.code_block_formats=}"
) # The language/format of each code block (if given)
print(f"{Query.index=}") # Keeps an index of Query objects created
print(f"{Query.image_dir_path=}")
except Exception as E:
raise Exception(E) from E
finally:
return q
================================================
FILE: src/EdgeGPT/ImageGen.py
================================================
# Open pull requests and issues at https://github.com/acheong08/BingImageCreator
import BingImageCreator
ImageGen = BingImageCreator.ImageGen
ImageGenAsync = BingImageCreator.ImageGenAsync
main = BingImageCreator.main
if __name__ == "__main__":
main()
================================================
FILE: src/EdgeGPT/__init__.py
================================================
================================================
FILE: src/EdgeGPT/chathub.py
================================================
import asyncio
import json
import os
import ssl
import sys
from time import time
from typing import Generator
from typing import List
from typing import Union
import aiohttp
import certifi
import httpx
from BingImageCreator import ImageGenAsync
from .constants import DELIMITER
from .constants import HEADERS
from .constants import HEADERS_INIT_CONVER
from .conversation import Conversation
from .conversation_style import CONVERSATION_STYLE_TYPE
from .request import ChatHubRequest
from .utilities import append_identifier
from .utilities import get_ran_hex
from .utilities import guess_locale
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations(certifi.where())
class ChatHub:
def __init__(
self,
conversation: Conversation,
proxy: str = None,
cookies: Union[List[dict], None] = None,
) -> None:
self.aio_session = None
self.request: ChatHubRequest
self.loop: bool
self.task: asyncio.Task
self.request = ChatHubRequest(
conversation_signature=conversation.struct["conversationSignature"],
client_id=conversation.struct["clientId"],
conversation_id=conversation.struct["conversationId"],
)
self.cookies = cookies
self.proxy: str = proxy
proxy = (
proxy
or os.environ.get("all_proxy")
or os.environ.get("ALL_PROXY")
or os.environ.get("https_proxy")
or os.environ.get("HTTPS_PROXY")
or None
)
if proxy is not None and proxy.startswith("socks5h://"):
proxy = "socks5://" + proxy[len("socks5h://") :]
self.session = httpx.AsyncClient(
proxies=proxy,
timeout=900,
headers=HEADERS_INIT_CONVER,
)
async def get_conversation(
self,
conversation_id: str = None,
conversation_signature: str = None,
client_id: str = None,
) -> dict:
conversation_id = conversation_id or self.request.conversation_id
conversation_signature = (
conversation_signature or self.request.conversation_signature
)
client_id = client_id or self.request.client_id
url = f"https://sydney.bing.com/sydney/GetConversation?conversationId={conversation_id}&source=cib&participantId={client_id}&conversationSignature={conversation_signature}&traceId={get_ran_hex()}"
response = await self.session.get(url)
return response.json()
async def get_activity(self) -> dict:
url = "https://www.bing.com/turing/conversation/chats"
headers = HEADERS_INIT_CONVER.copy()
if self.cookies is not None:
for cookie in self.cookies:
if cookie["name"] == "_U":
headers["Cookie"] = f"SUID=A; _U={cookie['value']};"
break
response = await self.session.get(url, headers=headers)
return response.json()
async def ask_stream(
self,
prompt: str,
wss_link: str = None,
conversation_style: CONVERSATION_STYLE_TYPE = None,
raw: bool = False,
webpage_context: Union[str, None] = None,
search_result: bool = False,
locale: str = guess_locale(),
) -> Generator[bool, Union[dict, str], None]:
""" """
cookies = {}
if self.cookies is not None:
for cookie in self.cookies:
cookies[cookie["name"]] = cookie["value"]
self.aio_session = aiohttp.ClientSession(cookies=cookies)
# Check if websocket is closed
wss = await self.aio_session.ws_connect(
wss_link or "wss://sydney.bing.com/sydney/ChatHub",
ssl=ssl_context,
headers=HEADERS,
proxy=self.proxy,
)
await self._initial_handshake(wss)
# Construct a ChatHub request
self.request.update(
prompt=prompt,
conversation_style=conversation_style,
webpage_context=webpage_context,
search_result=search_result,
locale=locale,
)
# Send request
await wss.send_str(append_identifier(self.request.struct))
draw = False
resp_txt = ""
result_text = ""
resp_txt_no_link = ""
retry_count = 5
while not wss.closed:
msg = await wss.receive_str()
if not msg:
retry_count -= 1
if retry_count == 0:
raise Exception("No response from server")
continue
if isinstance(msg, str):
objects = msg.split(DELIMITER)
else:
continue
for obj in objects:
if int(time()) % 6 == 0:
await wss.send_str(append_identifier({"type": 6}))
if obj is None or not obj:
continue
response = json.loads(obj)
# print(response)
if response.get("type") == 1 and response["arguments"][0].get(
"messages",
):
if not draw:
if (
response["arguments"][0]["messages"][0].get(
"messageType",
)
== "GenerateContentQuery"
):
try:
async with ImageGenAsync(
all_cookies=self.cookies,
) as image_generator:
images = await image_generator.get_images(
response["arguments"][0]["messages"][0]["text"],
)
for i, image in enumerate(images):
resp_txt = f"{resp_txt}\n"
draw = True
except Exception as e:
print(e)
continue
if (
(
response["arguments"][0]["messages"][0]["contentOrigin"]
!= "Apology"
)
and not draw
and not raw
):
resp_txt = result_text + response["arguments"][0][
"messages"
][0]["adaptiveCards"][0]["body"][0].get("text", "")
resp_txt_no_link = result_text + response["arguments"][0][
"messages"
][0].get("text", "")
if response["arguments"][0]["messages"][0].get(
"messageType",
):
resp_txt = (
resp_txt
+ response["arguments"][0]["messages"][0][
"adaptiveCards"
][0]["body"][0]["inlines"][0].get("text")
+ "\n"
)
result_text = (
result_text
+ response["arguments"][0]["messages"][0][
"adaptiveCards"
][0]["body"][0]["inlines"][0].get("text")
+ "\n"
)
if not raw:
yield False, resp_txt
elif response.get("type") == 2:
if response["item"]["result"].get("error"):
await self.close()
raise Exception(
f"{response['item']['result']['value']}: {response['item']['result']['message']}",
)
if draw:
cache = response["item"]["messages"][1]["adaptiveCards"][0][
"body"
][0]["text"]
response["item"]["messages"][1]["adaptiveCards"][0]["body"][0][
"text"
] = (cache + resp_txt)
if (
response["item"]["messages"][-1]["contentOrigin"] == "Apology"
and resp_txt
):
response["item"]["messages"][-1]["text"] = resp_txt_no_link
response["item"]["messages"][-1]["adaptiveCards"][0]["body"][0][
"text"
] = resp_txt
print(
"Preserved the message from being deleted",
file=sys.stderr,
)
await wss.close()
if not self.aio_session.closed:
await self.aio_session.close()
yield True, response
return
if response.get("type") != 2:
if response.get("type") == 6:
await wss.send_str(append_identifier({"type": 6}))
elif response.get("type") == 7:
await wss.send_str(append_identifier({"type": 7}))
elif raw:
yield False, response
async def _initial_handshake(self, wss) -> None:
await wss.send_str(append_identifier({"protocol": "json", "version": 1}))
await wss.receive_str()
await wss.send_str(append_identifier({"type": 6}))
async def delete_conversation(
self,
conversation_id: str = None,
conversation_signature: str = None,
client_id: str = None,
) -> None:
conversation_id = conversation_id or self.request.conversation_id
conversation_signature = (
conversation_signature or self.request.conversation_signature
)
client_id = client_id or self.request.client_id
url = "https://sydney.bing.com/sydney/DeleteSingleConversation"
await self.session.post(
url,
json={
"conversationId": conversation_id,
"conversationSignature": conversation_signature,
"participant": {"id": client_id},
"source": "cib",
"optionsSets": ["autosave"],
},
)
async def close(self) -> None:
await self.session.aclose()
================================================
FILE: src/EdgeGPT/constants.py
================================================
import socket
import uuid
take_ip_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
take_ip_socket.connect(("8.8.8.8", 80))
FORWARDED_IP: str = take_ip_socket.getsockname()[0]
take_ip_socket.close()
DELIMITER = "\x1e"
HEADERS = {
"accept": "application/json",
"accept-language": "en-US;q=0.9",
"accept-encoding": "gzip, deflate, br, zsdch",
"content-type": "application/json",
"sec-ch-ua": '"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"',
"sec-ch-ua-arch": '"x86"',
"sec-ch-ua-bitness": '"64"',
"sec-ch-ua-full-version": '"115.0.1901.188"',
"sec-ch-ua-full-version-list": '"Not/A)Brand";v="99.0.0.0", "Microsoft Edge";v="115.0.1901.188", "Chromium";v="115.0.5790.114"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-model": "",
"sec-ch-ua-platform": '"Windows"',
"sec-ch-ua-platform-version": '"15.0.0"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"sec-ms-gec-version": "1-115.0.1901.188",
"x-ms-client-request-id": str(uuid.uuid4()),
"x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.3 OS/Windows",
"Referer": "https://www.bing.com/search?",
"Referrer-Policy": "origin-when-cross-origin",
"x-forwarded-for": FORWARDED_IP,
}
HEADERS_INIT_CONVER = {
"authority": "www.bing.com",
"accept": "application/json",
"accept-language": "en-US;q=0.9",
"cache-control": "max-age=0",
"sec-ch-ua": '"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"',
"sec-ch-ua-arch": '"x86"',
"sec-ch-ua-bitness": '"64"',
"sec-ch-ua-full-version": '"115.0.1901.188"',
"sec-ch-ua-full-version-list": '"Not/A)Brand";v="99.0.0.0", "Microsoft Edge";v="115.0.1901.188", "Chromium";v="115.0.5790.114"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-model": '""',
"sec-ch-ua-platform": '"Windows"',
"sec-ch-ua-platform-version": '"15.0.0"',
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.188",
"x-edge-shopping-flag": "1",
"x-forwarded-for": FORWARDED_IP,
}
================================================
FILE: src/EdgeGPT/conversation.py
================================================
import json
import os
from typing import List
from typing import Union
import httpx
from .constants import HEADERS_INIT_CONVER
from .exceptions import NotAllowedToAccess
class Conversation:
def __init__(
self,
proxy: Union[str, None] = None,
async_mode: bool = False,
cookies: Union[List[dict], None] = None,
) -> None:
if async_mode:
return
self.struct: dict = {
"conversationId": None,
"clientId": None,
"conversationSignature": None,
"result": {"value": "Success", "message": None},
}
self.proxy = proxy
proxy = (
proxy
or os.environ.get("all_proxy")
or os.environ.get("ALL_PROXY")
or os.environ.get("https_proxy")
or os.environ.get("HTTPS_PROXY")
or None
)
if proxy is not None and proxy.startswith("socks5h://"):
proxy = "socks5://" + proxy[len("socks5h://") :]
self.session = httpx.Client(
proxies=proxy,
timeout=900,
headers=HEADERS_INIT_CONVER,
)
if cookies:
for cookie in cookies:
self.session.cookies.set(cookie["name"], cookie["value"])
# Send GET request
response = self.session.get(
url=os.environ.get("BING_PROXY_URL")
or "https://edgeservices.bing.com/edgesvc/turing/conversation/create",
)
if response.status_code != 200:
print(f"Status code: {response.status_code}")
print(response.text)
print(response.url)
raise Exception("Authentication failed")
try:
self.struct = response.json()
except (json.decoder.JSONDecodeError, NotAllowedToAccess) as exc:
raise Exception(
"Authentication failed. You have not been accepted into the beta.",
) from exc
if self.struct["result"]["value"] == "UnauthorizedRequest":
raise NotAllowedToAccess(self.struct["result"]["message"])
@staticmethod
async def create(
proxy: Union[str, None] = None,
cookies: Union[List[dict], None] = None,
) -> "Conversation":
self = Conversation(async_mode=True)
self.struct = {
"conversationId": None,
"clientId": None,
"conversationSignature": None,
"result": {"value": "Success", "message": None},
}
self.proxy = proxy
proxy = (
proxy
or os.environ.get("all_proxy")
or os.environ.get("ALL_PROXY")
or os.environ.get("https_proxy")
or os.environ.get("HTTPS_PROXY")
or None
)
if proxy is not None and proxy.startswith("socks5h://"):
proxy = "socks5://" + proxy[len("socks5h://") :]
transport = httpx.AsyncHTTPTransport(retries=900)
# Convert cookie format to httpx format
formatted_cookies = None
if cookies:
formatted_cookies = httpx.Cookies()
for cookie in cookies:
formatted_cookies.set(cookie["name"], cookie["value"])
async with httpx.AsyncClient(
proxies=proxy,
timeout=30,
headers=HEADERS_INIT_CONVER,
transport=transport,
cookies=formatted_cookies,
) as client:
# Send GET request
response = await client.get(
url=os.environ.get("BING_PROXY_URL")
or "https://www.bing.com/turing/conversation/create",
follow_redirects=True,
)
if response.status_code != 200:
print(f"Status code: {response.status_code}")
print(response.text)
print(response.url)
raise Exception("Authentication failed")
try:
self.struct = response.json()
except (json.decoder.JSONDecodeError, NotAllowedToAccess) as exc:
print(response.text)
raise Exception(
"Authentication failed. You have not been accepted into the beta.",
) from exc
if self.struct["result"]["value"] == "UnauthorizedRequest":
raise NotAllowedToAccess(self.struct["result"]["message"])
return self
================================================
FILE: src/EdgeGPT/conversation_style.py
================================================
from enum import Enum
try:
from typing import Literal, Union
except ImportError:
from typing_extensions import Literal
from typing import Optional
class ConversationStyle(Enum):
creative = [
"nlu_direct_response_filter",
"deepleo",
"disable_emoji_spoken_text",
"responsible_ai_policy_235",
"enablemm",
"h3imaginative",
"objopinion",
"dsblhlthcrd",
"dv3sugg",
"autosave",
"clgalileo",
"gencontentv3",
]
balanced = [
"nlu_direct_response_filter",
"deepleo",
"disable_emoji_spoken_text",
"responsible_ai_policy_235",
"enablemm",
"galileo",
"saharagenconv5",
"objopinion",
"dsblhlthcrd",
"dv3sugg",
"autosave",
]
precise = [
"nlu_direct_response_filter",
"deepleo",
"disable_emoji_spoken_text",
"responsible_ai_policy_235",
"enablemm",
"h3precise",
"objopinion",
"dsblhlthcrd",
"dv3sugg",
"autosave",
"clgalileo",
"gencontentv3",
]
CONVERSATION_STYLE_TYPE = Optional[
Union[ConversationStyle, Literal["creative", "balanced", "precise"]]
]
================================================
FILE: src/EdgeGPT/exceptions.py
================================================
class NotAllowedToAccess(Exception):
pass
================================================
FILE: src/EdgeGPT/locale.py
================================================
from enum import Enum
try:
from typing import Literal, Union
except ImportError:
from typing_extensions import Literal
from typing import Optional
class LocationHint(Enum):
USA = {
"locale": "en-US",
"LocationHint": [
{
"country": "United States",
"state": "California",
"city": "Los Angeles",
"timezoneoffset": 8,
"countryConfidence": 8,
"Center": {
"Latitude": 34.0536909,
"Longitude": -118.242766,
},
"RegionType": 2,
"SourceType": 1,
},
],
}
CHINA = {
"locale": "zh-CN",
"LocationHint": [
{
"country": "China",
"state": "",
"city": "Beijing",
"timezoneoffset": 8,
"countryConfidence": 8,
"Center": {
"Latitude": 39.9042,
"Longitude": 116.4074,
},
"RegionType": 2,
"SourceType": 1,
},
],
}
EU = {
"locale": "en-IE",
"LocationHint": [
{
"country": "Norway",
"state": "",
"city": "Oslo",
"timezoneoffset": 1,
"countryConfidence": 8,
"Center": {
"Latitude": 59.9139,
"Longitude": 10.7522,
},
"RegionType": 2,
"SourceType": 1,
},
],
}
UK = {
"locale": "en-GB",
"LocationHint": [
{
"country": "United Kingdom",
"state": "",
"city": "London",
"timezoneoffset": 0,
"countryConfidence": 8,
"Center": {
"Latitude": 51.5074,
"Longitude": -0.1278,
},
"RegionType": 2,
"SourceType": 1,
},
],
}
LOCATION_HINT_TYPES = Optional[Union[LocationHint, Literal["USA", "CHINA", "EU", "UK"]]]
================================================
FILE: src/EdgeGPT/main.py
================================================
import argparse
import asyncio
import json
import re
import sys
from pathlib import Path
from EdgeGPT.EdgeGPT import Chatbot
from prompt_toolkit import PromptSession
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.history import InMemoryHistory
from prompt_toolkit.key_binding import KeyBindings
from rich.live import Live
from rich.markdown import Markdown
def create_session() -> PromptSession:
kb = KeyBindings()
@kb.add("enter")
def _(event) -> None:
buffer_text = event.current_buffer.text
if buffer_text.startswith("!"):
event.current_buffer.validate_and_handle()
else:
event.current_buffer.insert_text("\n")
@kb.add("escape")
def _(event) -> None:
if event.current_buffer.complete_state:
# event.current_buffer.cancel_completion()
event.current_buffer.text = ""
return PromptSession(key_bindings=kb, history=InMemoryHistory())
def create_completer(commands: list, pattern_str: str = "$") -> WordCompleter:
return WordCompleter(words=commands, pattern=re.compile(pattern_str))
def _create_history_logger(f) -> callable:
def logger(*args, **kwargs) -> None:
tmp = sys.stdout
sys.stdout = f
print(*args, **kwargs, flush=True)
sys.stdout = tmp
return logger
async def get_input_async(
session: PromptSession = None,
completer: WordCompleter = None,
) -> str:
"""
Multiline input function.
"""
return await session.prompt_async(
completer=completer,
multiline=True,
auto_suggest=AutoSuggestFromHistory(),
)
async def async_main(args: argparse.Namespace) -> None:
"""
Main function
"""
print("Initializing...")
print("Enter `alt+enter` or `escape+enter` to send a message")
# Read and parse cookies
cookies = None
if args.cookie_file:
file_path = Path(args.cookie_file)
if file_path.exists():
with file_path.open("r", encoding="utf-8") as f:
cookies = json.load(f)
bot = await Chatbot.create(proxy=args.proxy, cookies=cookies)
session = create_session()
completer = create_completer(["!help", "!exit", "!reset"])
initial_prompt = args.prompt
# Log chat history
def p_hist(*args, **kwargs) -> None:
pass
if args.history_file:
history_file_path = Path(args.history_file)
f = history_file_path.open("a+", encoding="utf-8")
p_hist = _create_history_logger(f)
while True:
print("\nYou:")
p_hist("\nYou:")
if initial_prompt:
question = initial_prompt
print(question)
initial_prompt = None
else:
question = (
input()
if args.enter_once
else await get_input_async(session=session, completer=completer)
)
print()
p_hist(question + "\n")
if question == "!exit":
await bot.close()
break
if question == "!help":
print(
"""
!help - Show this help message
!exit - Exit the program
!reset - Reset the conversation
""",
)
continue
if question == "!reset":
await bot.reset()
continue
print("Bot:")
p_hist("Bot:")
if args.no_stream:
response = (
await bot.ask(
prompt=question,
conversation_style=args.style,
wss_link=args.wss_link,
search_result=args.search_result,
locale=args.locale,
)
)["item"]["messages"][-1]["adaptiveCards"][0]["body"][0]["text"]
print(response)
p_hist(response)
else:
wrote = 0
if args.rich:
md = Markdown("")
with Live(md, auto_refresh=False) as live:
async for final, response in bot.ask_stream(
prompt=question,
conversation_style=args.style,
wss_link=args.wss_link,
search_result=args.search_result,
locale=args.locale,
):
if not final:
if not wrote:
p_hist(response, end="")
else:
p_hist(response[wrote:], end="")
if wrote > len(response):
print(md)
print(Markdown("***Bing revoked the response.***"))
wrote = len(response)
md = Markdown(response)
live.update(md, refresh=True)
else:
async for final, response in bot.ask_stream(
prompt=question,
conversation_style=args.style,
wss_link=args.wss_link,
search_result=args.search_result,
locale=args.locale,
):
if not final:
if not wrote:
print(response, end="", flush=True)
p_hist(response, end="")
else:
print(response[wrote:], end="", flush=True)
p_hist(response[wrote:], end="")
wrote = len(response)
print()
p_hist()
if args.history_file:
f.close()
await bot.close()
def main() -> None:
print(
"""
EdgeGPT - A demo of reverse engineering the Bing GPT chatbot
Repo: github.com/acheong08/EdgeGPT
By: Antonio Cheong
!help for help
Type !exit to exit
""",
)
parser = argparse.ArgumentParser()
parser.add_argument("--enter-once", action="store_true")
parser.add_argument("--search-result", action="store_true")
parser.add_argument("--no-stream", action="store_true")
parser.add_argument("--rich", action="store_true")
parser.add_argument(
"--proxy",
help="Proxy URL (e.g. socks5://127.0.0.1:1080)",
type=str,
)
parser.add_argument(
"--wss-link",
help="WSS URL(e.g. wss://sydney.bing.com/sydney/ChatHub)",
type=str,
default="wss://sydney.bing.com/sydney/ChatHub",
)
parser.add_argument(
"--style",
choices=["creative", "balanced", "precise"],
default="balanced",
)
parser.add_argument(
"--prompt",
type=str,
default="",
required=False,
help="prompt to start with",
)
parser.add_argument(
"--cookie-file",
type=str,
default="",
required=False,
help="path to cookie file",
)
parser.add_argument(
"--history-file",
type=str,
default="",
required=False,
help="path to history file",
)
parser.add_argument(
"--locale",
type=str,
default="en-US",
required=False,
help="your locale",
)
args = parser.parse_args()
asyncio.run(async_main(args))
================================================
FILE: src/EdgeGPT/request.py
================================================
import uuid
from datetime import datetime
from typing import Union
from .conversation_style import CONVERSATION_STYLE_TYPE
from .conversation_style import ConversationStyle
from .utilities import get_location_hint_from_locale
from .utilities import get_ran_hex
from .utilities import guess_locale
class ChatHubRequest:
def __init__(
self,
conversation_signature: str,
client_id: str,
conversation_id: str,
invocation_id: int = 3,
) -> None:
self.struct: dict = {}
self.client_id: str = client_id
self.conversation_id: str = conversation_id
self.conversation_signature: str = conversation_signature
self.invocation_id: int = invocation_id
def update(
self,
prompt: str,
conversation_style: CONVERSATION_STYLE_TYPE,
webpage_context: Union[str, None] = None,
search_result: bool = False,
locale: str = guess_locale(),
) -> None:
options = [
"deepleo",
"enable_debug_commands",
"disable_emoji_spoken_text",
"enablemm",
]
if conversation_style:
if not isinstance(conversation_style, ConversationStyle):
conversation_style = getattr(ConversationStyle, conversation_style)
options = conversation_style.value
message_id = str(uuid.uuid4())
# Get the current local time
now_local = datetime.now()
# Get the current UTC time
now_utc = datetime.utcnow()
# Calculate the time difference between local and UTC time
timezone_offset = now_local - now_utc
# Get the offset in hours and minutes
offset_hours = int(timezone_offset.total_seconds() // 3600)
offset_minutes = int((timezone_offset.total_seconds() % 3600) // 60)
# Format the offset as a string
offset_string = f"{offset_hours:+03d}:{offset_minutes:02d}"
# Get current time
timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + offset_string
self.struct = {
"arguments": [
{
"source": "cib",
"optionsSets": options,
"allowedMessageTypes": [
"ActionRequest",
"Chat",
"Context",
"InternalSearchQuery",
"InternalSearchResult",
"Disengaged",
"InternalLoaderMessage",
"Progress",
"RenderCardRequest",
"AdsQuery",
"SemanticSerp",
"GenerateContentQuery",
"SearchQuery",
],
"sliceIds": [
"winmuid1tf",
"styleoff",
"ccadesk",
"smsrpsuppv4cf",
"ssrrcache",
"contansperf",
"crchatrev",
"winstmsg2tf",
"creatgoglt",
"creatorv2t",
"sydconfigoptt",
"adssqovroff",
"530pstho",
"517opinion",
"418dhlth",
"512sprtic1s0",
"emsgpr",
"525ptrcps0",
"529rweas0",
"515oscfing2s0",
"524vidansgs0",
],
"verbosity": "verbose",
"traceId": get_ran_hex(32),
"isStartOfSession": self.invocation_id == 3,
"message": {
"locale": locale,
"market": locale,
"region": locale[-2:], # en-US -> US
"locationHints": get_location_hint_from_locale(locale),
"timestamp": timestamp,
"author": "user",
"inputMethod": "Keyboard",
"text": prompt,
"messageType": "Chat",
"messageId": message_id,
"requestId": message_id,
},
"tone": conversation_style.name.capitalize(), # Make first letter uppercase
"requestId": message_id,
"conversationSignature": self.conversation_signature,
"participant": {
"id": self.client_id,
},
"conversationId": self.conversation_id,
},
],
"invocationId": str(self.invocation_id),
"target": "chat",
"type": 4,
}
if search_result:
have_search_result = [
"InternalSearchQuery",
"InternalSearchResult",
"InternalLoaderMessage",
"RenderCardRequest",
]
self.struct["arguments"][0]["allowedMessageTypes"] += have_search_result
if webpage_context:
self.struct["arguments"][0]["previousMessages"] = [
{
"author": "user",
"description": webpage_context,
"contextType": "WebPage",
"messageType": "Context",
"messageId": "discover-web--page-ping-mriduna-----",
},
]
self.invocation_id += 1
# print(timestamp)
================================================
FILE: src/EdgeGPT/utilities.py
================================================
import json
import locale
import random
import sys
from typing import Union
from .constants import DELIMITER
from .locale import LocationHint
def append_identifier(msg: dict) -> str:
# Convert dict to json string
return json.dumps(msg, ensure_ascii=False) + DELIMITER
def get_ran_hex(length: int = 32) -> str:
return "".join(random.choice("0123456789abcdef") for _ in range(length))
def get_location_hint_from_locale(locale: str) -> Union[dict, None]:
locale = locale.lower()
if locale == "en-gb":
hint = LocationHint.UK.value
elif locale == "en-ie":
hint = LocationHint.EU.value
elif locale == "zh-cn":
hint = LocationHint.CHINA.value
else:
hint = LocationHint.USA.value
return hint.get("LocationHint")
def guess_locale() -> str:
if sys.platform.startswith("win"):
return "en-us"
loc, _ = locale.getlocale()
return loc.replace("_", "-") if loc else "en-us"
================================================
FILE: test_base.py
================================================
import json
import pytest
from EdgeGPT.EdgeGPT import Chatbot
from EdgeGPT.EdgeGPT import ConversationStyle
pytest_plugins = ("pytest_asyncio",)
from os import getenv
@pytest.mark.asyncio()
async def test_ask() -> None:
bot = await Chatbot.create(cookies=getenv("EDGE_COOKIES"))
response = await bot.ask(
prompt="find me some information about the new ai released by meta.",
conversation_style=ConversationStyle.balanced,
simplify_response=True,
)
await bot.close()
print(json.dumps(response, indent=2))
assert response
gitextract_qfdnm_l6/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── custom.md │ │ └── feature_request.yml │ └── workflows/ │ ├── GHCR.yml │ ├── PyPi.yml │ └── tests.yml ├── .gitignore ├── .pre-commit-config.yaml ├── Dockerfile ├── LICENSE ├── Makefile ├── docs/ │ ├── CODE_OF_CONDUCT.md │ ├── README_es.md │ ├── README_ja.md │ ├── README_zh-cn.md │ ├── README_zh-tw.md │ └── wiki/ │ ├── EdgeGPT.md │ ├── Home.md │ └── ImageGen.md ├── example.env ├── requirements.txt ├── setup.cfg ├── setup.py ├── src/ │ └── EdgeGPT/ │ ├── EdgeGPT.py │ ├── EdgeUtils.py │ ├── ImageGen.py │ ├── __init__.py │ ├── chathub.py │ ├── constants.py │ ├── conversation.py │ ├── conversation_style.py │ ├── exceptions.py │ ├── locale.py │ ├── main.py │ ├── request.py │ └── utilities.py └── test_base.py
SYMBOL INDEX (63 symbols across 11 files)
FILE: src/EdgeGPT/EdgeGPT.py
class Chatbot (line 17) | class Chatbot:
method __init__ (line 22) | def __init__(
method create (line 35) | async def create(
method save_conversation (line 48) | async def save_conversation(self, filename: str) -> None:
method load_conversation (line 68) | async def load_conversation(self, filename: str) -> None:
method get_conversation (line 81) | async def get_conversation(self) -> dict:
method get_activity (line 87) | async def get_activity(self) -> dict:
method ask (line 93) | async def ask(
method ask_stream (line 174) | async def ask_stream(
method close (line 198) | async def close(self) -> None:
method delete_conversation (line 204) | async def delete_conversation(
method reset (line 219) | async def reset(self, delete=False) -> None:
FILE: src/EdgeGPT/EdgeUtils.py
class Cookie (line 17) | class Cookie:
method files (line 35) | def files(cls: type) -> list[Path]:
method import_data (line 47) | def import_data(cls: type) -> None:
method import_next (line 75) | def import_next(cls: type, discard: bool = False) -> None:
class Query (line 96) | class Query:
method __init__ (line 106) | def __init__(
method log_and_send_query (line 162) | def log_and_send_query(self, echo: bool, echo_prompt: bool) -> None:
method create_image (line 173) | def create_image(self) -> None:
method send_to_bing (line 180) | async def send_to_bing(self, echo: bool = True, echo_prompt: bool = Fa...
method output (line 212) | def output(self) -> str:
method sources (line 228) | def sources(self) -> list[list[dict]]:
method sources_dict (line 239) | def sources_dict(self) -> dict[int, str]:
method code_block_formats (line 260) | def code_block_formats(self) -> list[str]:
method code_blocks (line 270) | def code_blocks(self) -> list[str]:
method code (line 305) | def code(self) -> str:
method suggestions (line 312) | def suggestions(self) -> list[str]:
method __repr__ (line 324) | def __repr__(self) -> str:
method __str__ (line 327) | def __str__(self) -> str:
class ImageQuery (line 331) | class ImageQuery(Query):
method __init__ (line 332) | def __init__(self, prompt: str, **kwargs) -> None:
method __repr__ (line 336) | def __repr__(self) -> str:
function test_cookie_rotation (line 340) | def test_cookie_rotation() -> None:
function test_features (line 351) | def test_features() -> Query:
FILE: src/EdgeGPT/chathub.py
class ChatHub (line 30) | class ChatHub:
method __init__ (line 31) | def __init__(
method get_conversation (line 64) | async def get_conversation(
method get_activity (line 79) | async def get_activity(self) -> dict:
method ask_stream (line 90) | async def ask_stream(
method _initial_handshake (line 242) | async def _initial_handshake(self, wss) -> None:
method delete_conversation (line 247) | async def delete_conversation(
method close (line 270) | async def close(self) -> None:
FILE: src/EdgeGPT/conversation.py
class Conversation (line 12) | class Conversation:
method __init__ (line 13) | def __init__(
method create (line 66) | async def create(
FILE: src/EdgeGPT/conversation_style.py
class ConversationStyle (line 10) | class ConversationStyle(Enum):
FILE: src/EdgeGPT/exceptions.py
class NotAllowedToAccess (line 1) | class NotAllowedToAccess(Exception):
FILE: src/EdgeGPT/locale.py
class LocationHint (line 10) | class LocationHint(Enum):
FILE: src/EdgeGPT/main.py
function create_session (line 18) | def create_session() -> PromptSession:
function create_completer (line 38) | def create_completer(commands: list, pattern_str: str = "$") -> WordComp...
function _create_history_logger (line 42) | def _create_history_logger(f) -> callable:
function get_input_async (line 52) | async def get_input_async(
function async_main (line 66) | async def async_main(args: argparse.Namespace) -> None:
function main (line 183) | def main() -> None:
FILE: src/EdgeGPT/request.py
class ChatHubRequest (line 12) | class ChatHubRequest:
method __init__ (line 13) | def __init__(
method update (line 27) | def update(
FILE: src/EdgeGPT/utilities.py
function append_identifier (line 11) | def append_identifier(msg: dict) -> str:
function get_ran_hex (line 16) | def get_ran_hex(length: int = 32) -> str:
function get_location_hint_from_locale (line 20) | def get_location_hint_from_locale(locale: str) -> Union[dict, None]:
function guess_locale (line 33) | def guess_locale() -> str:
FILE: test_base.py
function test_ask (line 13) | async def test_ask() -> None:
Condensed preview — 38 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (128K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yml",
"chars": 2751,
"preview": "name: Bug Report\ndescription: DO NOT OPEN A DUPLICATE\ntitle: \"[Bug]: \"\n\nbody:\n - type: checkboxes\n attributes:\n "
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 558,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Discussions, questions, and niche issues in Github\n url: https:/"
},
{
"path": ".github/ISSUE_TEMPLATE/custom.md",
"chars": 258,
"preview": "---\nname: \"Are you sure you are not opening a duplicate? From now on, I will not be reading all issues. Use 👍 reaction o"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.yml",
"chars": 1388,
"preview": "name: Feature request\ndescription: Suggest an idea for this project\ntitle: \"[Feature Request]: \"\n\nbody:\n - type: checkb"
},
{
"path": ".github/workflows/GHCR.yml",
"chars": 3292,
"preview": "name: Publish Docker 🐳 images 📦 to GitHub Container Registry\n\non:\n release:\n types: [published]\n\njobs:\n build-relea"
},
{
"path": ".github/workflows/PyPi.yml",
"chars": 621,
"preview": "name: Upload Python Package\n\non:\n release:\n types: [published]\n\npermissions:\n contents: read\n\njobs:\n deploy:\n r"
},
{
"path": ".github/workflows/tests.yml",
"chars": 684,
"preview": "name: tests\n\non:\n push:\n pull_request:\n pull_request_target:\n types: [ ready_for_review, review_requested, edited]"
},
{
"path": ".gitignore",
"chars": 1857,
"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": ".pre-commit-config.yaml",
"chars": 811,
"preview": "repos:\n - repo: https://github.com/asottile/reorder_python_imports\n rev: v3.9.0\n hooks:\n - id: reorder-pytho"
},
{
"path": "Dockerfile",
"chars": 417,
"preview": "FROM docker.io/library/python:3.11.3-alpine3.16@sha256:0ba61d06b14e5438aa3428ee46c7ccdc8df5b63483bc91ae050411407eb5cbf4 "
},
{
"path": "LICENSE",
"chars": 1211,
"preview": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, c"
},
{
"path": "Makefile",
"chars": 404,
"preview": ".PHONY: docs\ninit:\n\tpython -m pip install --upgrade pip\n\tpython -m pip install -r ./requirements.txt --upgrade\n\tpython -"
},
{
"path": "docs/CODE_OF_CONDUCT.md",
"chars": 5227,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "docs/README_es.md",
"chars": 11774,
"preview": "<div align=\"center\">\n <img src=\"https://socialify.git.ci/acheong08/EdgeGPT/image?font=Inter&language=1&logo=https%3A%2F"
},
{
"path": "docs/README_ja.md",
"chars": 6879,
"preview": "<div align=\"center\">\n <img src=\"https://socialify.git.ci/acheong08/EdgeGPT/image?font=Inter&language=1&logo=https%3A%2F"
},
{
"path": "docs/README_zh-cn.md",
"chars": 9188,
"preview": "<div align=\"center\">\n <img src=\"https://socialify.git.ci/acheong08/EdgeGPT/image?font=Inter&language=1&logo=https%3A%2F"
},
{
"path": "docs/README_zh-tw.md",
"chars": 9205,
"preview": "<div align=\"center\">\n <img src=\"https://socialify.git.ci/acheong08/EdgeGPT/image?font=Inter&language=1&logo=https%3A%2F"
},
{
"path": "docs/wiki/EdgeGPT.md",
"chars": 2714,
"preview": "<a id=\"EdgeGPT.EdgeGPT\"></a>\n\n# EdgeGPT.EdgeGPT\n\nMain.py\n\n<a id=\"EdgeGPT.EdgeGPT.Chatbot\"></a>\n\n## Chatbot Objects\n\n```p"
},
{
"path": "docs/wiki/Home.md",
"chars": 131,
"preview": "Welcome to the EdgeGPT wiki!\n\n## Languages\n\n- English\n- [简体中文](https://github.com/CoolPlayLin/ChatGPT-Wiki/tree/main/doc"
},
{
"path": "docs/wiki/ImageGen.md",
"chars": 537,
"preview": "<a id=\"ImageGen\"></a>\n\n# ImageGen\n\n<a id=\"ImageGen.ImageGen\"></a>\n\n## ImageGen Objects\n\n```python\nclass ImageGen()\n```\n\n"
},
{
"path": "example.env",
"chars": 31,
"preview": "export BING_U=\"<COOKIE_VALUE>\"\n"
},
{
"path": "requirements.txt",
"chars": 68,
"preview": "aiohttp\nBingImageCreator\ncertifi\nhttpx\nprompt_toolkit\nrequests\nrich\n"
},
{
"path": "setup.cfg",
"chars": 60,
"preview": "[metadata]\ndescription_file=README.md\nlicense_files=LICENSE\n"
},
{
"path": "setup.py",
"chars": 1700,
"preview": "from pathlib import Path\n\nfrom setuptools import find_packages\nfrom setuptools import setup\n\nDOCS_PATH = Path(__file__)."
},
{
"path": "src/EdgeGPT/EdgeGPT.py",
"chars": 7773,
"preview": "\"\"\"\nMain.py\n\"\"\"\nfrom __future__ import annotations\n\nimport json\nfrom pathlib import Path\nfrom typing import Generator\n\nf"
},
{
"path": "src/EdgeGPT/EdgeUtils.py",
"chars": 14262,
"preview": "import asyncio\nimport contextlib\nimport json\nimport re\nfrom pathlib import Path\n\nfrom log2d import Log\n\nfrom .EdgeGPT im"
},
{
"path": "src/EdgeGPT/ImageGen.py",
"chars": 260,
"preview": "# Open pull requests and issues at https://github.com/acheong08/BingImageCreator\nimport BingImageCreator\n\nImageGen = Bin"
},
{
"path": "src/EdgeGPT/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "src/EdgeGPT/chathub.py",
"chars": 10915,
"preview": "import asyncio\nimport json\nimport os\nimport ssl\nimport sys\nfrom time import time\nfrom typing import Generator\nfrom typin"
},
{
"path": "src/EdgeGPT/constants.py",
"chars": 2217,
"preview": "import socket\nimport uuid\n\ntake_ip_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\ntake_ip_socket.connect((\"8."
},
{
"path": "src/EdgeGPT/conversation.py",
"chars": 4362,
"preview": "import json\nimport os\nfrom typing import List\nfrom typing import Union\n\nimport httpx\n\nfrom .constants import HEADERS_INI"
},
{
"path": "src/EdgeGPT/conversation_style.py",
"chars": 1256,
"preview": "from enum import Enum\n\ntry:\n from typing import Literal, Union\nexcept ImportError:\n from typing_extensions import "
},
{
"path": "src/EdgeGPT/exceptions.py",
"chars": 46,
"preview": "class NotAllowedToAccess(Exception):\n pass\n"
},
{
"path": "src/EdgeGPT/locale.py",
"chars": 2247,
"preview": "from enum import Enum\n\ntry:\n from typing import Literal, Union\nexcept ImportError:\n from typing_extensions import "
},
{
"path": "src/EdgeGPT/main.py",
"chars": 7500,
"preview": "import argparse\nimport asyncio\nimport json\nimport re\nimport sys\nfrom pathlib import Path\n\nfrom EdgeGPT.EdgeGPT import Ch"
},
{
"path": "src/EdgeGPT/request.py",
"chars": 5748,
"preview": "import uuid\nfrom datetime import datetime\nfrom typing import Union\n\nfrom .conversation_style import CONVERSATION_STYLE_T"
},
{
"path": "src/EdgeGPT/utilities.py",
"chars": 955,
"preview": "import json\nimport locale\nimport random\nimport sys\nfrom typing import Union\n\nfrom .constants import DELIMITER\nfrom .loca"
},
{
"path": "test_base.py",
"chars": 573,
"preview": "import json\n\nimport pytest\nfrom EdgeGPT.EdgeGPT import Chatbot\nfrom EdgeGPT.EdgeGPT import ConversationStyle\n\npytest_plu"
}
]
About this extraction
This page contains the full source code of the acheong08/EdgeGPT GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 38 files (117.1 KB), approximately 30.3k tokens, and a symbol index with 63 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.