Skip to content

Commit

Permalink
Merge pull request #67 from F33RNI/next
Browse files Browse the repository at this point in the history
New Bard API + fixed some bugs
  • Loading branch information
F33RNI authored Oct 9, 2023
2 parents 1d9c53a + 30e58e6 commit 54b7401
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 76 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ chats/
users.json
conversations/
EdgeGPT_cookies.json
Bard_cookies.json
data/
Banner.psd
Banner.tif
Binary file modified Banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
94 changes: 66 additions & 28 deletions BardModule.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
"""
import asyncio
import ctypes
import logging
import multiprocessing
import os
import uuid
from typing import List, Dict

import Bard
import requests
from bardapi import Bard

import BotHandler
import UsersHandler
from JSONReaderWriter import load_json, save_json
from RequestResponseContainer import RequestResponseContainer


Expand Down Expand Up @@ -61,14 +61,36 @@ def initialize(self, proxy=None) -> None:
logging.warning("Bard module disabled in config file!")
raise Exception("Bard module disabled in config file!")

# Load cookies and secure_1psid
secure_1psid = None
session = requests.Session()
session_cookies = load_json(self.config["bard"]["cookies_file"], logging_enabled=True)
for i in range(len(session_cookies)):
session.cookies.set(session_cookies[i]["name"],
session_cookies[i]["value"],
domain=session_cookies[i]["domain"],
path=session_cookies[i]["path"])
if secure_1psid is None and session_cookies[i]["name"] == "__Secure-1PSID":
secure_1psid = session_cookies[i]["value"]

# Set headers
session.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.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/",
}

# Initialize chatbot
if proxy:
self._chatbot = Bard.Chatbot(self.config["bard"]["secure_1psid"],
self.config["bard"]["secure_1psidts"],
proxy=proxy)
self._chatbot = Bard(token=secure_1psid, proxies={"https": proxy, "http": proxy}, session=session)
else:
self._chatbot = Bard.Chatbot(self.config["bard"]["secure_1psid"],
self.config["bard"]["secure_1psidts"])
self._chatbot = Bard(token=secure_1psid, session=session)

# Done?
if self._chatbot is not None:
logging.info("Bard module initialized")
Expand Down Expand Up @@ -101,25 +123,32 @@ def process_request(self, request_response: RequestResponseContainer) -> None:
self.processing_flag.value = True

# Get user data
bard_conversation_id = UsersHandler.get_key_or_none(request_response.user, "bard_conversation_id")
conversation_id = UsersHandler.get_key_or_none(request_response.user, "bard_conversation_id")
response_id = UsersHandler.get_key_or_none(request_response.user, "bard_response_id")
choice_id = UsersHandler.get_key_or_none(request_response.user, "bard_choice_id")

# Increment requests_total for statistics
request_response.user["requests_total"] += 1
self.users_handler.save_user(request_response.user)

# Try to load conversation
if bard_conversation_id:
conversation_file = os.path.join(self.config["files"]["conversations_dir"],
bard_conversation_id + ".json")
if os.path.exists(conversation_file):
logging.info("Loading conversation from {}".format(conversation_file))
self._chatbot.load_conversation(conversation_file, bard_conversation_id)
else:
bard_conversation_id = None
if conversation_id and response_id and choice_id:
logging.info("Using conversation_id: {}, response_id: {} and choice_id: {}".format(conversation_id,
response_id,
choice_id))
self._chatbot.conversation_id = conversation_id
self._chatbot.response_id = response_id
self._chatbot.choice_id = choice_id

# Try to download image
image_bytes = None
if request_response.image_url:
logging.info("Downloading user image")
image_bytes = requests.get(request_response.image_url, timeout=120).content

# Ask Bard
logging.info("Asking Bard...")
bard_response = self._chatbot.ask(request_response.request)
bard_response = self._chatbot.get_answer(request_response.request, image=image_bytes)

# Check response
if not bard_response or len(bard_response) < 1 or "content" not in bard_response:
Expand All @@ -130,17 +159,12 @@ def process_request(self, request_response: RequestResponseContainer) -> None:
.format(request_response.user["user_name"], request_response.user["user_id"]))
request_response.response = bard_response["content"]

# Generate new conversation id
if not bard_conversation_id:
bard_conversation_id = str(uuid.uuid4()) + "_bard"

# Save conversation
logging.info("Saving conversation to {}".format(bard_conversation_id))
self._chatbot.save_conversation(os.path.join(self.config["files"]["conversations_dir"],
bard_conversation_id + ".json"), bard_conversation_id)

# Save to user data
request_response.user["bard_conversation_id"] = bard_conversation_id
logging.info("Saving conversation_id as {} and response_id as {} and choice_id as {}".
format(self._chatbot.conversation_id, self._chatbot.response_id, self._chatbot.choice_id))
request_response.user["bard_conversation_id"] = self._chatbot.conversation_id
request_response.user["bard_response_id"] = self._chatbot.response_id
request_response.user["bard_choice_id"] = self._chatbot.choice_id
self.users_handler.save_user(request_response.user)

# Exit requested
Expand All @@ -159,6 +183,18 @@ def process_request(self, request_response: RequestResponseContainer) -> None:
request_response.response = self.messages[lang]["response_error"].replace("\\n", "\n").format(error_text)
request_response.error = True

# Try to save cookies
try:
if self._chatbot and self._chatbot.session and self._chatbot.session.cookies:
session_cookies = load_json(self.config["bard"]["cookies_file"], logging_enabled=True)
for i in range(len(session_cookies)):
session_cookies[i]["value"] = self._chatbot.session.cookies.get(session_cookies[i]["name"],
domain=session_cookies[i]["domain"],
path=session_cookies[i]["path"])
save_json(self.config["bard"]["cookies_file"], session_cookies, True)
except Exception as e:
logging.error("Error saving cookies!", exc_info=e)

# Finish message
BotHandler.async_helper(BotHandler.send_message_async(self.config, self.messages, request_response, end=True))

Expand Down Expand Up @@ -188,6 +224,8 @@ def clear_conversation_for_user(self, user: dict) -> None:

# Reset user data
user["bard_conversation_id"] = None
user["bard_response_id"] = None
user["bard_choice_id"] = None
self.users_handler.save_user(user)

def exit(self):
Expand Down
34 changes: 26 additions & 8 deletions BotHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
BOT_COMMAND_START = "start"
BOT_COMMAND_HELP = "help"
BOT_COMMAND_CHATGPT = "chatgpt"
BOT_COMMAND_EDGEGPT = "edgegpt"
BOT_COMMAND_EDGEGPT = "bing"
BOT_COMMAND_DALLE = "dalle"
BOT_COMMAND_BARD = "bard"
BOT_COMMAND_BING_IMAGEGEN = "bingigen"
Expand Down Expand Up @@ -590,6 +590,7 @@ def start_bot(self):
# Handle requests as messages
if self.config["telegram"]["reply_to_messages"]:
self._application.add_handler(MessageHandler(filters.TEXT & (~filters.COMMAND), self.bot_message))
self._application.add_handler(MessageHandler(filters.PHOTO & (~filters.COMMAND), self.bot_message))

# Admin commands
self._application.add_handler(CommandHandler(BOT_COMMAND_ADMIN_QUEUE, self.bot_command_queue))
Expand Down Expand Up @@ -681,11 +682,13 @@ async def query_callback(self, update: Update, context: ContextTypes.DEFAULT_TYP
# Check if we have the last request
if request:
# Ask
request_image_url = UsersHandler.get_key_or_none(user, "request_last_image_url")
await self.bot_command_or_message_request_raw(request_type,
request,
user,
reply_message_id_last,
context)
context,
request_image_url)

# No or empty request
else:
Expand Down Expand Up @@ -1479,34 +1482,48 @@ async def bot_command_or_message_request(self, request_type: int,
if user["banned"]:
return

# Extract request
if request_type >= 0:
# Check for image
image_url = None
if update.message.photo:
image_file_id = update.message.photo[-1].file_id
image_url = (await (telegram.Bot(self.config["telegram"]["api_key"]).getFile(image_file_id))).file_path

# Extract text request
if update.message.caption:
request_message = update.message.caption.strip()
elif request_type >= 0:
if context.args:
request_message = str(" ".join(context.args)).strip()
else:
request_message = ""
else:
request_message = update.message.text.strip()
if update.message.text:
request_message = update.message.text.strip()
else:
request_message = ""

# Process request
await self.bot_command_or_message_request_raw(request_type,
request_message,
user,
update.message.message_id,
context)
context,
image_url=image_url)

async def bot_command_or_message_request_raw(self, request_type: int,
request_message: str,
user: dict,
reply_message_id: int,
context: ContextTypes.DEFAULT_TYPE):
context: ContextTypes.DEFAULT_TYPE,
image_url=None):
"""
Processes request to module
:param request_type:
:param request_message:
:param user:
:param reply_message_id:
:param context:
:param image_url:
:return:
"""
# Set default user module
Expand Down Expand Up @@ -1544,7 +1561,8 @@ async def bot_command_or_message_request_raw(self, request_type: int,
reply_message_id=reply_message_id,
request=request_message,
request_type=request_type,
request_timestamp=request_timestamp)
request_timestamp=request_timestamp,
image_url=image_url)

# Add request to the queue
logging.info("Adding new request with type {0} from {1} ({2}) to the queue".format(request_type,
Expand Down
19 changes: 19 additions & 0 deletions QueueHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ def _request_processor(config: dict,

# Save request data (for regenerate function)
request_.user["request_last"] = request_.request
request_.user["request_last_image_url"] = request_.image_url
request_.user["reply_message_id_last"] = request_.reply_message_id

# Save user
Expand Down Expand Up @@ -665,6 +666,24 @@ def _collect_data(self, request_response: RequestResponseContainer, log_request=
if log_request:
request_str_to_format = self.config["data_collecting"]["request_format"].replace("\\n", "\n") \
.replace("\\t", "\t").replace("\\r", "\r")

# Log image
try:
if request_response.image_url:
logging.info("Downloading user image")
image_request = base64.b64encode(requests.get(request_response.image_url,
timeout=120).content).decode("utf-8")
log_file.write(request_str_to_format.format(request_response.request_timestamp,
request_response.id,
request_response.user["user_name"],
request_response.user["user_id"],
RequestResponseContainer
.REQUEST_NAMES[request_response.request_type],
image_request))
except Exception as e:
logging.warning("Error logging image request!", exc_info=e)

# Log text
log_file.write(request_str_to_format.format(request_response.request_timestamp,
request_response.id,
request_response.user["user_name"],
Expand Down
39 changes: 34 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# 🤖 GPT-Telegramus
### The best Telegram bot for ChatGPT, EdgeGPT, DALL-E, Bing Image generator and Bard with stream writing, multiple languages, admin control, automatic proxy searcher, data logging and more!
### The best Telegram bot for ChatGPT, EdgeGPT (aka Bing AI), DALL-E, Bing Image generator and Bard with stream writing, requests with images (for Bard only), multiple languages, admin control, automatic proxy searcher, data logging and more!
<div style="width:100%;text-align:center;">
<p align="center">
<img src="https://badges.frapsoft.com/os/v1/open-source.png?v=103" >
Expand All @@ -25,13 +25,15 @@ Support the project by buying and listening to my music 🎵
- 🟦 [Bandcamp](https://f3rni.bandcamp.com)
- 🟧 [SoundCloud](https://soundcloud.com/f3rni)

Or message me if you would like to donate 💰

[![Star History Chart](https://api.star-history.com/svg?repos=F33RNI/GPT-Telegramus&type=Date)](https://star-history.com/#F33RNI/GPT-Telegramus&Date)

----------

## 🤗 Contributors

- 1️⃣ [Sprav04ka](https://github.com/Sprav04ka) - *Tofii'skovyi' language, Testing, Motivation*
- 1️⃣ [Sprav04ka](https://github.com/Sprav04ka) - *Tofii'skovyi' language, Testing, Super beautiful DIY jack o'lantern (for poster), Motivation*
- 2️⃣ [Sergey Krashevich](https://github.com/skrashevich) - *Docker, GitHub Actions*
- 3️⃣ [Wahit Fitriyanto](https://github.com/wahitftry) - *Indonesian language*
- 4️⃣ [Alexander Fadeyev](https://github.com/alfsoft) - *EdgeGPT Fix*
Expand All @@ -46,7 +48,8 @@ Support the project by buying and listening to my music 🎵
- **Bard** (Bard by Google): https://bard.google.com/
- **acheong08/ChatGPT** (API): https://github.com/acheong08/ChatGPT
- **acheong08/EdgeGPT** (API): https://github.com/acheong08/EdgeGPT
- **acheong08/Bard** (API): https://github.com/acheong08/Bard
- **jacobgelling/EdgeGPT** (API): https://github.com/jacobgelling/EdgeGPT
- **dsdanielpark/Bard-API** (API): https://github.com/dsdanielpark/Bard-API
- **acheong08/BingImageCreator** (API): https://github.com/acheong08/BingImageCreator
- **python-telegram-bot** (Telegram bot API): https://github.com/python-telegram-bot/python-telegram-bot

Expand All @@ -63,6 +66,31 @@ Support the project by buying and listening to my music 🎵
7. Type Bot's token into `api_key` in `telegram` in `config.json` file
8. Run main script `python main.py`

- **ChatGPT**
- Browser-like Chat-GPT. Currently, without extensions and image requests (text only) (because I don't have a paid account to test it)
- Stream response support
- Chat history support
- Works better with API type 1 and an access_token for authentication
- Note: Please refer to the `🔗 Chat-GPT Base URL (proxy)` section for correct usage.
- **DALL·E**
- Image generation tool from OpenAI
- Requires an OpenAI account with unexpired credits
- **EdgeGPT (aka Bing AI) (aka Sydney)**
- Supports conversation style `/style`
- Stream response support
- Chat history support
- Web-browsing and sources support
- Unfortunately, it can't accept images as input yet, nor can it generate them. Please use Bing ImageGen to generate images.
- **Bing ImageGen**
- Bing Image Generator. Used as a separate module due to issues with the EdgeGPT module
- Free and unlimited
- **Bard**
- Google's Bard AI
- Stream response support
- Chat history support
- Web-browsing support
- Now supports requests with images (you can send an image with text to it) **NEW**

----------

## 💬 Bot messages
Expand Down Expand Up @@ -194,7 +222,7 @@ Default base URL by acheong08/ChatGPT stopped working. Below is an instruction o
## 🌎 Proxy to bypass geo-blocking
#### NOT TESTED
### NOT TESTED
It is possible to bypass geo-blocking of ChatGPT, EdgeGPT, DALL-E or Bard. GPT-Telegramus includes automatic proxy-list downloading with periodic checks
Expand Down Expand Up @@ -238,7 +266,7 @@ GPT-Telegramus saves chat history for some modules locally (`"conversations_dir"
GPT-Telegramus has a built-in data collecting function (request and response)
- **For ChatGPT, EdgeGPT and Bard** response is saved as plain text
- **For ChatGPT, EdgeGPT (aka Bing AI) and Bard** response is saved as plain text and Base64-encoded images
- **For DALL-E and Bing Image generator** response is saved as Base64-encoded image (in the same text file)
You can enable and configure data collection in config in `data_collecting` block
Expand All @@ -250,6 +278,7 @@ You can enable and configure data collection in config in `data_collecting` bloc
## 📝 TODO
- Add some free GPT-4 model
- Add image input and generation for EdgeGPT (and ChatGPT?)
- Add list of commands
----------
Expand Down
Loading

0 comments on commit 54b7401

Please sign in to comment.