dsdanielpark / Bard-API

The unofficial python package that returns response of Google Bard through cookie value.
https://pypi.org/project/bardapi/
MIT License
5.34k stars 528 forks source link

question: how to use a request proxy (not an http proxy) #23

Closed erickythierry closed 1 year ago

erickythierry commented 1 year ago

first of all, congratulations for the excellent project, I did some tests integrating it in a chatbot and it worked incredibly well.

My doubt is: how do I use a request proxy? reason: I'm from a country where bard is not officially available and I don't have access to a secure and fast proxy. I tried to use vpn but for independent scripts it ends up being one more problem to deal with.

my solution was to create a CORS proxy for requests on fly.io that receives a request and retransmits it. but I wanted to know where I can add this in the core of your project? the proxy works by adding the real url in front of the proxy url and it only works for requests. example: https://proxy-request.com/https://bard.google.com/

where could I modify in core.py to make this request like this?

dsdanielpark commented 1 year ago

Thank you for your nice comments. Please try the following code and let me know.

import os
import random
import string
import requests
import json
import re
from urllib.parse import urljoin

class Bard:
    HEADERS = {
        "Host": "bard.google.com",
        "X-Same-Domain": "1",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36",
        "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
        "Origin": "https://bard.google.com",
        "Referer": "https://bard.google.com/",
    }

    def __init__(self, token=None, timeout=20, proxies=None, session=None):
        """
        Initialize Bard

        :param token: (`str`, *optional*)
            __Secure-1PSID value. default to os.getenv("_BARD_API_KEY")
        :param timeout: (`int`, *optional*)
            Timeout in seconds when connecting bard server. The timeout is used on each request.
        :param proxies: (`Dict[str, str]`, *optional*)
            A dictionary of proxy servers to use by protocol or endpoint, e.g., `{'http': 'foo.bar:3128',
            'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.
        :param session: (`requests.Session`, *optional*)
            An existing requests.Session object to be used for making HTTP requests.
        """
        self.token = token or os.getenv("_BARD_API_KEY")
        self.proxies = proxies
        self.timeout = timeout
        self._reqid = int("".join(random.choices(string.digits, k=4)))
        self.conversation_id = ""
        self.response_id = ""
        self.choice_id = ""
        self.session = session or requests.Session()
        self.session.headers = self.HEADERS
        self.session.cookies.set("__Secure-1PSID", self.token)
        self.SNlM0e = self._get_snim0e()

    def _get_snim0e(self):
        if self.proxies:
            if 'https' in self.proxies:
                url = urljoin(self.proxies['https'], "https://bard.google.com/")
            else:
                raise ValueError("HTTPS proxy is required.")
        else:
            url = "https://bard.google.com/"
        resp = self.session.get(url, timeout=self.timeout, proxies=self.proxies)
        if resp.status_code != 200:
            raise Exception(
                f"Response code not 200. Response Status is {resp.status_code}"
            )
        snim0e = re.search(r"SNlM0e\":\"(.*?)\"", resp.text)
        if not snim0e:
            raise Exception(
                "SNlM0e value not found in response. Check __Secure-1PSID value."
            )
        return snim0e.group(1)

    def get_answer(self, input_text: str) -> dict:
        params = {
            "bl": "boq_assistant-bard-web-server_20230419.00_p1",
            "_reqid": str(self._reqid),
            "rt": "c",
        }
        input_text_struct = [
            [input_text],
            None,
            [self.conversation_id, self.response_id, self.choice_id],
        ]
        data = {
            "f.req": json.dumps([None, json.dumps(input_text_struct)]),
            "at": self.SNlM0e,
        }
        if self.proxies:
            if 'https' in self.proxies:
                url = urljoin(self.proxies['https'], "https://bard.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate")
            else:
                raise ValueError("HTTPS proxy is required.")
        else:
            url = "https://bard.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate"
        resp = self.session.post(url, params=params, data=data, timeout=self.timeout, proxies=self.proxies)
        resp_dict = json.loads(resp.content.splitlines()[3])[0][2]

        if not resp_dict:
            return {"content": f"Response Error: {resp.content}."}
        parsed_answer = json.loads(resp_dict)
        bard_answer = {
            "content": parsed_answer[0][0],
            "conversation_id": parsed_answer[1][0],
            "response_id": parsed_answer[1][1],
            "factualityQueries": parsed_answer[3],
            "textQuery": parsed_answer[2][0] if parsed_answer[2] else "",
            "choices": [{"id": i[0], "content": i[1]} for i in parsed_answer[4]],
        }
        self.conversation_id, self.response_id, self.choice_id = (
            bard_answer["conversation_id"],
            bard_answer["response_id"],
            bard_answer["choices"][0]["id"],
        )
        self._reqid += 100000

        return bard_answer

Example

proxies = {
    'http': 'http://proxy.example.com:8080',
    'https': 'https://proxy.example.com:8080'
}
bard = Bard(token="your_token", proxies=proxies)
response = bard.get_answer("Hello, how are you?")
print(response)

Make sure you replace "your_token" with your actual token value, and 'http://proxy.example.com:8080' and 'https://proxy.example.com:8080' with the URLs of your proxy servers.

With these modifications, the Bard class should use the provided proxies for all requests made by the class methods.

Let me know if you have any further questions!

dsdanielpark commented 1 year ago

Further, if you wnat to use FTP proxy, then use this

proxies = { 
              "http"  : http_proxy, 
              "https" : https_proxy, 
              "ftp"   : ftp_proxy
            }

bard = Bard(token="your_token", proxies=proxies)
erickythierry commented 1 year ago

Further, if you wnat to use FTP proxy, then use this

proxies = { 
              "http"  : http_proxy, 
              "https" : https_proxy, 
              "ftp"   : ftp_proxy
            }

bard = Bard(token="your_token", proxies=proxies)

Thanks for the answer, I tested this code and the previous one, but unfortunately it didn't work. But thanks anyway, I ended up finding another solution: I hosted the code as a micro server in a cloud in the USA.

just one last doubt: is there any request limit that I should avoid? to avoid getting banned from google. I'm currently using a chatbot for whatsapp and I'm making a few dozen requests a day, but I'm afraid of receiving some punishment 😅

dsdanielpark commented 1 year ago

Please note that the Python package bardapi is provided as a tool to assist developers with mocking and testing, due to the delay in the development and release of Google Bard's API. It is not a free service. Therefore, we strongly advise against using it for any other purposes.