diff --git a/README.md b/README.md index eb76945..431d8fb 100644 --- a/README.md +++ b/README.md @@ -10,33 +10,33 @@ HuggingChat Python API Leave a star :) -> When you use this project, it means that you have agreed to the following two requirements of the HuggingChat: +> When you use this project, it means that you have agreed to the following two requirements of the HuggingChat: > -> 1. AI is an area of active research with known problems such as biased generation and misinformation. Do not use this application for high-stakes decisions or advice. +> 1. AI is an area of active research with known problems such as biased generation and misinformation. Do not use this application for high-stakes decisions or advice. > 2. Your conversations will be shared with model authors. -**Server resources are precious, it is not recommended to request this API in a high frequency.** +**Server resources are precious, it is not recommended to request this API in a high frequency.** (`Hugging Face's CTO🤗` just liked the suggestion)
- - ## Authentication (Required Now) -### Cookies +### Get Cookies -
-How to Get Cookies ? +```python +from hugchat.login import Login -- Install the `Cookie-Editor` extension for [Chrome](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) or [Firefox](https://addons.mozilla.org/en-US/firefox/addon/cookie-editor/) -- Go to [HuggingChat](https://huggingface.co/chat) and **login** -- Open the extension -- Click `Export` on the bottom right, then `Export as JSON`(This saves your cookies to the clipboard) -- Paste your cookies into a file `cookies.json` +# login +sign = Login(email, passwd) +cookies = sign.login() +sign.saveCookies() -
+# load cookies from usercookies/.json +sign = login(email, None) +cookies = sign.loadCookies() # This will detect if the JSON file exists, return cookies if it does and raise an Exception if it's not. +``` ## Usage @@ -48,7 +48,17 @@ pip install hugchat ```py from hugchat import hugchat -chatbot = hugchat.ChatBot(cookie_path="cookies.json") # or cookies=[...] +from hugchat.login import Login + +# Log in to huggingface and grant authorization to huggingchat +sign = Login(email, passwd) +cookies = sign.login() + +# Save cookies to usercookies/.json +sign.saveCookies() + +# Create a ChatBot +chatbot = hugchat.ChatBot(cookies=cookies.get_dict()) # or cookie_path="usercookies/.json" print(chatbot.chat("HI")) # Create a new conversation @@ -69,7 +79,7 @@ The `chat()` function receives these parameters: - `truncate`: Optional[int]. Default is 1024 - `watermark`: Optional[bool]. Default is False - `max_new_tokens`: Optional[int]. Default is 1024 -- `stop`: Optional[list]. Default is [""] +- `stop`: Optional[list]. Default is ["``"] - `return_full_text`: Optional[bool]. Default is False - `stream`: Optional[bool]. Default is True - `use_cache`: Optional[bool]. Default is False @@ -96,4 +106,3 @@ Commands in cli mode: ## Disclaimers This is not an official [Hugging Face](https://huggingface.co/) product. This is a **personal project** and is not affiliated with [Hugging Face](https://huggingface.co/) in any way. Don't sue us. - diff --git a/README_cn.md b/README_cn.md index a1eb2ef..76e02c2 100644 --- a/README_cn.md +++ b/README_cn.md @@ -12,18 +12,21 @@ HuggingChat Python API ## 鉴权 (必需) -### Cookies +### 获取Cookies -
-如何提取Cookies +```python +from hugchat.login import Login -- 安装 [Chrome](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) 或 [Firefox](https://addons.mozilla.org/en-US/firefox/addon/cookie-editor/) 的 cookie editor 扩展 -- 访问 [HuggingChat](https://huggingface.co/chat) 并 **登录** -- 打开扩展程序 -- 点击右下角的"导出" (将会把内容保存到你的剪贴板上) -- 把你剪贴板上的内容粘贴到 `cookies.json` 文件中 +# 登录 +sign = Login(email, passwd) +cookies = sign.login() +sign.saveCookies() -
+# 从 usercookies/.json 中加载已保存的cookies +sign = login(email, None) +cookies = sign.loadCookies() # 这个方法会查询保存cookies的文件是否存在,存在就返回cookies,不存在就报错 + +``` ## 使用方式 @@ -35,14 +38,24 @@ pip install hugchat ```py from hugchat import hugchat -chatbot = hugchat.ChatBot(cookie_path="cookies.json") # 或者 cookies=[...] -print(chatbot.chat("Hello!")) +from hugchat.login import Login + +# 登入huggingface授权huggingchat +sign = Login(email, passwd) +cookies = sign.login() + +# 保存cookies至 usercookies/.json +sign.saveCookies() + +# 创建一个 ChatBot +chatbot = hugchat.ChatBot(cookies=cookies.get_dict()) # or cookie_path="usercookies/.json" +print(chatbot.chat("HI")) -# 创建一个新的会话 +# 创建一个新会话 id = chatbot.new_conversation() chatbot.change_conversation(id) -# 获取会话列表 +# 获取对话列表 conversation_list = chatbot.get_conversation_list() ``` @@ -56,7 +69,7 @@ conversation_list = chatbot.get_conversation_list() - `truncate`: Optional[int]. Default is 1024 - `watermark`: Optional[bool]. Default is False - `max_new_tokens`: Optional[int]. Default is 1024 -- `stop`: Optional[list]. Default is [""] +- `stop`: Optional[list]. Default is ["``"] - `return_full_text`: Optional[bool]. Default is False - `stream`: Optional[bool]. Default is True - `use_cache`: Optional[bool]. Default is False diff --git a/src/hugchat/cli.py b/src/hugchat/cli.py index 17c26b6..16faf09 100644 --- a/src/hugchat/cli.py +++ b/src/hugchat/cli.py @@ -7,7 +7,112 @@ to start cli. """ -from .hugchat import cli +from .hugchat import ChatBot +from .login import Login +import getpass +import argparse + +EMAIL = "" +PASSWD = "" +CHECK_BEFORE_PASSWORD = True + +def cli(): + global EMAIL + global PASSWD + global CHECK_BEFORE_PASSWORD + print("-------HuggingChat-------") + print("Official Site: https://huggingface.co/chat") + print("1. AI is an area of active research with known problems such as biased generation and misinformation. Do not use this application for high-stakes decisions or advice.\n2. Your conversations will be shared with model authors.\nContinuing to use means that you accept the above points") + parser = argparse.ArgumentParser() + parser.add_argument( + "-u", + type=str, + help="Your huggingface account's email" + ) + parser.add_argument( + "-p", + action="store_true", + help="Require Password to login" + ) + args = parser.parse_args() + email = args.u + inputpass = args.p + cookies = None + if CHECK_BEFORE_PASSWORD: + if not email: + email = EMAIL + try: + cookies = Login(email, None).loadCookies() + except Exception as e: + pass + if not cookies or inputpass: + if not email: + if EMAIL: + if not PASSWD or inputpass: + PASSWD = getpass.getpass("Password: ") + email = EMAIL + passwd = PASSWD + else: + raise Exception("No email specified. Please use '-u' or set it in cli.py") + else: + if inputpass or not PASSWD: + passwd = getpass.getpass("Password: ") + else: + passwd = PASSWD + + print(f"Sign in as :{email}") + sign = Login(email, passwd) + try: + cookies = sign.loadCookies() + except Exception as e: + print(e) + print("Logging in...") + cookies = sign.login() + sign.saveCookies() + + chatbot = ChatBot(cookies=cookies) + running = True + while running: + question = input("> ") + if question == "/new": + cid = chatbot.new_conversation() + print("The new conversation ID is: " + cid) + chatbot.change_conversation(cid) + print("Conversation changed successfully.") + continue + + elif question.startswith("/switch"): + try: + conversations = chatbot.get_conversation_list() + conversation_id = str(question.split(" ")[1] if len(question.split(" ")) > 1 else "") + if conversation_id not in conversations: + print("# Please enter a valid ID number.") + print(f"# Sessions include: {conversations}") + else: + chatbot.change_conversation(conversation_id) + print(f"# Conversation switched successfully to {conversation_id}") + except ValueError: + print("# Please enter a valid ID number\n") + + + + elif question == "/ids": + id_list = list(chatbot.get_conversation_list()) + [print(f"{id_list.index(i)+1} : {i}{' ' if chatbot.current_conversation == i else ''}") for i in id_list] + + elif question in ["/exit", "/quit","/close"]: + running = False + + elif question.startswith("/"): + print("# Invalid command") + + elif question == "": + pass + + else: + res = chatbot.chat(question) + print("< " + res) + if __name__ == '__main__': cli() diff --git a/src/hugchat/hugchat.py b/src/hugchat/hugchat.py index 7fe7f9a..fb7494d 100644 --- a/src/hugchat/hugchat.py +++ b/src/hugchat/hugchat.py @@ -1,9 +1,11 @@ from requests import Session +import requests import json import os import uuid import logging - +import re +import getpass class ChatBot: @@ -44,6 +46,7 @@ def __init__( self.accepted_welcome_modal = False # Only when accepted, it can create a new conversation. self.current_conversation = self.new_conversation() + def get_hc_session(self) -> Session: session = Session() # set cookies @@ -261,52 +264,6 @@ def chat( raise Exception(obj["error"]) return res_text -def cli(): - print("-------HuggingChat-------") - print("Official Site: https://huggingface.co/chat") - print("1. AI is an area of active research with known problems such as biased generation and misinformation. Do not use this application for high-stakes decisions or advice.\n2. Your conversations will be shared with model authors.\nContinuing to use means that you accept the above points") - chatbot = ChatBot(cookie_path="cookies.json") - running = True - while running: - question = input("> ") - if question == "/new": - cid = chatbot.new_conversation() - print("The new conversation ID is: " + cid) - chatbot.change_conversation(cid) - print("Conversation changed successfully.") - continue - - elif question.startswith("/switch"): - try: - conversations = chatbot.get_conversation_list() - conversation_id = str(question.split(" ")[1] if len(question.split(" ")) > 1 else "") - if conversation_id not in conversations: - print("# Please enter a valid ID number.") - print(f"# Sessions include: {conversations}") - else: - chatbot.change_conversation(conversation_id) - print(f"# Conversation switched successfully to {conversation_id}") - except ValueError: - print("# Please enter a valid ID number\n") - - - - elif question == "/ids": - id_list = list(chatbot.get_conversation_list()) - [print(f"{id_list.index(i)+1} : {i}{' ' if chatbot.current_conversation == i else ''}") for i in id_list] - - elif question in ["/exit", "/quit","/close"]: - running = False - - elif question.startswith("/"): - print("# Invalid command") - - elif question == "": - pass - - else: - res = chatbot.chat(question) - print("< " + res) if __name__ == "__main__": @@ -318,4 +275,3 @@ def cli(): sharelink = bot.share_conversation() print(sharelink) - cli() \ No newline at end of file diff --git a/src/hugchat/login.py b/src/hugchat/login.py new file mode 100644 index 0000000..00bb881 --- /dev/null +++ b/src/hugchat/login.py @@ -0,0 +1,143 @@ +import requests +import os +import json +import logging +import re + + +class Login: + def __init__(self, email: str, passwd: str) -> None: + self.COOKIE_DIR = os.path.dirname(os.path.abspath(__file__)) + "/usercookies" + self.COOKIE_PATH = self.COOKIE_DIR + f"/{email}.json" + if not os.path.exists(self.COOKIE_DIR): + logging.debug("Cookie directory not found, creating...") + os.makedirs(self.COOKIE_DIR) + logging.debug(f"Cookie store path: {self.COOKIE_DIR}") + + self.email: str = email + self.passwd: str = passwd + self.headers = { + "Referer": "https://huggingface.co/", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.64", + } + self.cookies = requests.sessions.RequestsCookieJar() + + def requestsGet(self, url:str, params=None, allow_redirects=True) -> requests.Response: + res = requests.get( + url, + params=params, + headers=self.headers, + cookies=self.cookies, + allow_redirects=allow_redirects, + ) + self.refreshCookies(res.cookies) + return res + + def requestsPost(self, url:str, headers=None, params=None, data=None, stream=False, allow_redirects=True) -> requests.Response: + res = requests.post( + url, + stream=stream, + params=params, + data=data, + headers=self.headers if headers == None else headers, + cookies=self.cookies, + allow_redirects=allow_redirects + ) + self.refreshCookies(res.cookies) + return res + + def refreshCookies(self, cookies:requests.sessions.RequestsCookieJar): + dic = cookies.get_dict() + for i in dic: + self.cookies.set(i, dic[i]) + + def SigninWithEmail(self): + """ + Login through your email and password. + PS: I found that it doesn't have any type of encrytion till now, + which could expose your password to the internet. + """ + url = "https://huggingface.co/login" + data = { + "username": self.email, + "password": self.passwd, + } + res = self.requestsPost(url=url, data=data, allow_redirects=False) + if res.status_code == 400: + raise Exception("wrong username or password") + + def getAuthURL(self): + url = "https://huggingface.co/chat/login" + headers = { + "Referer": "https://huggingface.co/chat/login", + "User-Agent": self.headers["User-Agent"], + "Content-Type": "application/x-www-form-urlencoded" + } + res = self.requestsPost(url, headers=headers, allow_redirects=False) + if res.status_code == 200: + # location = res.headers.get("Location", None) + location = res.json()["location"] + if location: + return location + else: + raise Exception("No authorize url found, please check your email or password.") + elif res.status_code == 303: + location = res.headers.get("Location") + if location: + return location + else: + raise Exception("No authorize url found, please check your email or password.") + else: + raise Exception("Something went wrong!") + + def grantAuth(self, url: str) -> int: + """ + Grant auth to huggingchat after login process is done. + """ + res = self.requestsGet(url) + if res.status_code != 200: + raise Exception("Grant auth fatal!") + csrf = re.findall('/oauth/authorize.*?name="csrf" value="(.*?)"', res.text) + if len(csrf) == 0: + raise Exception("No csrf found!") + data = { + "csrf":csrf[0] + } + + res = self.requestsPost(url, data=data, allow_redirects=False) + if res.status_code != 303: + raise Exception(f"get hf-chat cookies fatal! - {res.status_code}") + else: + location = res.headers.get("Location") + res = self.requestsGet(location, allow_redirects=False) + if res.status_code != 302: + raise Exception(f"get hf-chat cookie fatal! - {res.status_code}") + else: + return 1 + + def login(self) -> requests.sessions.RequestsCookieJar: + self.SigninWithEmail() + location = self.getAuthURL() + if self.grantAuth(location): + return self.cookies + else: + raise Exception(f"Grant auth fatal, please check your email or password\ncookies gained: \n{self.cookies}") + + def saveCookies(self) -> str: + with open(self.COOKIE_PATH, "w", encoding="utf-8") as f: + f.write(json.dumps(self.cookies.get_dict(), ensure_ascii=False)) + return self.COOKIE_PATH + + def loadCookies(self) -> requests.sessions.RequestsCookieJar: + if os.path.exists(self.COOKIE_PATH): + with open(self.COOKIE_PATH, "r", encoding="utf-8") as f: + try: + js:dict = json.loads(f.read()) + for i in js.keys(): + self.cookies.set(i, js[i]) + logging.debug(f"{i} loaded") + return self.cookies + except: + raise Exception("Load cookies from json file fatal. Error while parsing json file") + else: + raise Exception(f"{self.COOKIE_PATH} doesn't seem to exist")