46 Commits

Author SHA1 Message Date
Father Bot ba58564d29 Update README.md 2025-06-26 18:12:37 +03:00
OlegGoless 2079908ce6 Merge pull request #469 from pyranota/add-gpt-4o
Add support for GPT-4o
2024-06-13 19:11:40 +02:00
Father Bot 4066ab9a44 Update README.md 2024-06-13 19:28:55 +03:00
pyranota c017c32800 Fix defaulting 2024-05-23 23:27:27 +02:00
pyranota fff2839327 Fix not showing GPT-4o in settings 2024-05-23 18:49:59 +02:00
pyranota 752f38b348 Add support for GPT-4o 2024-05-23 17:45:28 +02:00
OlegGoless 7b5fa9a3c3 Merge pull request #453 from father-bot/fix-context
fix bug in context
2024-04-16 14:56:57 +02:00
OlegGoless 6540b28437 fix bug in context 2024-04-16 14:56:04 +02:00
OlegGoless 00c5f66be5 Merge pull request #448 from father-bot/fix-image
delete required text caption with image
2024-04-04 10:29:18 +02:00
OlegGoless 5e4552e4df delete required text caption with image 2024-04-04 10:22:53 +02:00
OlegGoless 57ee703f07 Merge pull request #444 from father-bot/fix-prints-model
fix
2024-04-02 17:23:37 +02:00
OlegGoless e8a1a1fb2e fix 2024-04-02 17:22:58 +02:00
Father Bot c26829e9e3 Update README.md 2024-04-02 16:13:17 +03:00
OlegGoless d0eb9ee015 Merge pull request #442 from father-bot/vision
Vision
2024-04-02 14:52:59 +02:00
OlegGoless 2aa76490fd vision 2024-04-02 14:44:15 +02:00
OlegGoless 3b8b0b2a52 vision 2024-04-02 14:43:04 +02:00
iglv 299fb5724b Update README.md 2024-02-23 18:57:01 +03:00
iglv 0084ad01ab Delete static/donate directory 2024-01-31 09:24:16 +03:00
iglv 0f58815b8d Update README.md 2024-01-31 09:23:30 +03:00
Karim Iskakov b8621214dd Update README.md 2023-12-08 11:12:20 +03:00
Karim Iskakov 61b8dbd0ce Add GPT-4 Turbo 2023-11-15 17:45:57 +03:00
Karim Iskakov 977ffb5e32 Fix openai version 2023-11-07 03:29:32 +03:00
Daniil Okhlopkov ce0c34825f Store Voice temp files in memory and don't convert them 2023-11-02 16:52:35 +03:00
Karim Iskakov bab5938241 Merge branch 'main' of github.com:karfly/chatgpt_telegram_bot 2023-09-18 11:04:52 +03:00
Karim Iskakov 3d281de0c2 ♥️ Update top donations 2023-09-18 11:04:23 +03:00
Karim Iskakov d495f7dbbe Update README.md 2023-09-14 12:14:17 +03:00
Dichi Al Faridi acd4cab782 Make the DALLe image size configurable (#365) 2023-09-13 13:17:21 +03:00
Karim Iskakov f87b54d323 ❤️ Update top donations 2023-09-07 15:45:35 +03:00
Karim Iskakov bd6c6865b6 ❤️ Update top donations 2023-08-29 12:28:52 +03:00
realies 200ba314d4 create .dockerignore (#344) 2023-08-28 19:17:21 +03:00
Karim Iskakov c5389931b8 ❤️ Update top donations 2023-08-28 19:10:28 +03:00
Karim Iskakov 8f5a020891 Increase OpenAI timeout 2023-08-28 19:07:32 +03:00
Karim Iskakov 0c09f1aaca Add OpenAI API Base to config 2023-08-01 18:41:31 +03:00
Karim Iskakov c77501c3d9 Remove deprecated "use_chatgpt_api" parameter 2023-07-18 13:43:44 +03:00
Karim Iskakov c9db7dc369 ❤️ Update top donations 2023-06-20 17:37:58 +03:00
Karim Iskakov 006bc45af0 Merge branch 'main' of github.com:karfly/chatgpt_telegram_bot 2023-06-20 17:34:52 +03:00
Karim Iskakov 1847b9c90a Update Dockerfile for faster build 2023-06-20 17:34:46 +03:00
Flop a4bd06ac9e Allow channels (#312)
Co-authored-by: Flop <flop@loona.app>
2023-06-20 11:52:57 +03:00
TanNhatCMS 8df5e2d24d fix argument of type 'float' is not iterable (#211)
* fix argument of type 'float' is not iterable

fix "argument of type 'float' is not iterable" in show_balance_handle and message_handle

* Update bot.py
2023-06-20 11:50:03 +03:00
Shahmir Varqha e42798c0ce fixed grammar and spelling (#301) 2023-06-20 11:47:58 +03:00
maccagnit 533e6705c8 Added GPT-3.5 16K chat functionality (#306) 2023-06-20 11:47:19 +03:00
Karim Iskakov 28e7426c2b Fix HTTPX problem 2023-06-20 11:29:04 +03:00
Karim Iskakov a8c1e211ab Update Dockerfile 2023-06-20 11:21:45 +03:00
Karim Iskakov d6fab25531 ❤️ Update top donations 2023-04-24 04:25:07 -05:00
Karim Iskakov 37e7101273 Fix edited messages in group chats 2023-04-24 04:11:21 -05:00
Karim Iskakov a014061917 Update chat_modes.yml 2023-04-21 11:11:01 -05:00
20 changed files with 506 additions and 182 deletions
Vendored
BIN
View File
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
mongodb
+16 -13
View File
@@ -1,20 +1,23 @@
FROM python:3.8-slim FROM python:3.8-slim
ENV PYTHONFAULTHANDLER=1 RUN \
ENV PYTHONUNBUFFERED=1 set -eux; \
ENV PYTHONHASHSEED=random apt-get update; \
ENV PYTHONDONTWRITEBYTECODE 1 DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends \
ENV PIP_NO_CACHE_DIR=off python3-pip \
ENV PIP_DISABLE_PIP_VERSION_CHECK=on build-essential \
ENV PIP_DEFAULT_TIMEOUT=100 python3-venv \
ffmpeg \
git \
; \
rm -rf /var/lib/apt/lists/*
RUN apt-get update RUN pip3 install -U pip && pip3 install -U wheel && pip3 install -U setuptools==59.5.0
RUN apt-get install -y python3 python3-pip python-dev build-essential python3-venv ffmpeg COPY ./requirements.txt /tmp/requirements.txt
RUN pip3 install -r /tmp/requirements.txt && rm -r /tmp/requirements.txt
RUN mkdir -p /code COPY . /code
ADD . /code
WORKDIR /code WORKDIR /code
RUN pip3 install -r requirements.txt CMD ["bash"]
CMD ["bash"]
+32 -20
View File
@@ -10,21 +10,19 @@
<a href="https://t.me/chatgpt_karfly_bot?start=source=github" alt="Run Telegram Bot shield"><img src="https://img.shields.io/badge/RUN-Telegram%20Bot-blue?logo=data:image/svg+xml;base64,PHN2ZyBpZD0iTGl2ZWxsb18xIiBkYXRhLW5hbWU9IkxpdmVsbG8gMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmlld0JveD0iMCAwIDI0MCAyNDAiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50IiB4MT0iMTIwIiB5MT0iMjQwIiB4Mj0iMTIwIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMWQ5M2QyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMzhiMGUzIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHRpdGxlPlRlbGVncmFtX2xvZ288L3RpdGxlPjxjaXJjbGUgY3g9IjEyMCIgY3k9IjEyMCIgcj0iMTIwIiBmaWxsPSJ1cmwoI2xpbmVhci1ncmFkaWVudCkiLz48cGF0aCBkPSJNODEuMjI5LDEyOC43NzJsMTQuMjM3LDM5LjQwNnMxLjc4LDMuNjg3LDMuNjg2LDMuNjg3LDMwLjI1NS0yOS40OTIsMzAuMjU1LTI5LjQ5MmwzMS41MjUtNjAuODlMODEuNzM3LDExOC42WiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0xMDAuMTA2LDEzOC44NzhsLTIuNzMzLDI5LjA0NnMtMS4xNDQsOC45LDcuNzU0LDAsMTcuNDE1LTE1Ljc2MywxNy40MTUtMTUuNzYzIiBmaWxsPSIjYTljNmQ4Ii8+PHBhdGggZD0iTTgxLjQ4NiwxMzAuMTc4LDUyLjIsMTIwLjYzNnMtMy41LTEuNDItMi4zNzMtNC42NGMuMjMyLS42NjQuNy0xLjIyOSwyLjEtMi4yLDYuNDg5LTQuNTIzLDEyMC4xMDYtNDUuMzYsMTIwLjEwNi00NS4zNnMzLjIwOC0xLjA4MSw1LjEtLjM2MmEyLjc2NiwyLjc2NiwwLDAsMSwxLjg4NSwyLjA1NSw5LjM1Nyw5LjM1NywwLDAsMSwuMjU0LDIuNTg1Yy0uMDA5Ljc1Mi0uMSwxLjQ0OS0uMTY5LDIuNTQyLS42OTIsMTEuMTY1LTIxLjQsOTQuNDkzLTIxLjQsOTQuNDkzcy0xLjIzOSw0Ljg3Ni01LjY3OCw1LjA0M0E4LjEzLDguMTMsMCwwLDEsMTQ2LjEsMTcyLjVjLTguNzExLTcuNDkzLTM4LjgxOS0yNy43MjctNDUuNDcyLTMyLjE3N2ExLjI3LDEuMjcsMCwwLDEtLjU0Ni0uOWMtLjA5My0uNDY5LjQxNy0xLjA1LjQxNy0xLjA1czUyLjQyNi00Ni42LDUzLjgyMS01MS40OTJjLjEwOC0uMzc5LS4zLS41NjYtLjg0OC0uNC0zLjQ4MiwxLjI4MS02My44NDQsMzkuNC03MC41MDYsNDMuNjA3QTMuMjEsMy4yMSwwLDAsMSw4MS40ODYsMTMwLjE3OFoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=" width="230"/></a> <a href="https://t.me/chatgpt_karfly_bot?start=source=github" alt="Run Telegram Bot shield"><img src="https://img.shields.io/badge/RUN-Telegram%20Bot-blue?logo=data:image/svg+xml;base64,PHN2ZyBpZD0iTGl2ZWxsb18xIiBkYXRhLW5hbWU9IkxpdmVsbG8gMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmlld0JveD0iMCAwIDI0MCAyNDAiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50IiB4MT0iMTIwIiB5MT0iMjQwIiB4Mj0iMTIwIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMWQ5M2QyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMzhiMGUzIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHRpdGxlPlRlbGVncmFtX2xvZ288L3RpdGxlPjxjaXJjbGUgY3g9IjEyMCIgY3k9IjEyMCIgcj0iMTIwIiBmaWxsPSJ1cmwoI2xpbmVhci1ncmFkaWVudCkiLz48cGF0aCBkPSJNODEuMjI5LDEyOC43NzJsMTQuMjM3LDM5LjQwNnMxLjc4LDMuNjg3LDMuNjg2LDMuNjg3LDMwLjI1NS0yOS40OTIsMzAuMjU1LTI5LjQ5MmwzMS41MjUtNjAuODlMODEuNzM3LDExOC42WiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0xMDAuMTA2LDEzOC44NzhsLTIuNzMzLDI5LjA0NnMtMS4xNDQsOC45LDcuNzU0LDAsMTcuNDE1LTE1Ljc2MywxNy40MTUtMTUuNzYzIiBmaWxsPSIjYTljNmQ4Ii8+PHBhdGggZD0iTTgxLjQ4NiwxMzAuMTc4LDUyLjIsMTIwLjYzNnMtMy41LTEuNDItMi4zNzMtNC42NGMuMjMyLS42NjQuNy0xLjIyOSwyLjEtMi4yLDYuNDg5LTQuNTIzLDEyMC4xMDYtNDUuMzYsMTIwLjEwNi00NS4zNnMzLjIwOC0xLjA4MSw1LjEtLjM2MmEyLjc2NiwyLjc2NiwwLDAsMSwxLjg4NSwyLjA1NSw5LjM1Nyw5LjM1NywwLDAsMSwuMjU0LDIuNTg1Yy0uMDA5Ljc1Mi0uMSwxLjQ0OS0uMTY5LDIuNTQyLS42OTIsMTEuMTY1LTIxLjQsOTQuNDkzLTIxLjQsOTQuNDkzcy0xLjIzOSw0Ljg3Ni01LjY3OCw1LjA0M0E4LjEzLDguMTMsMCwwLDEsMTQ2LjEsMTcyLjVjLTguNzExLTcuNDkzLTM4LjgxOS0yNy43MjctNDUuNDcyLTMyLjE3N2ExLjI3LDEuMjcsMCwwLDEtLjU0Ni0uOWMtLjA5My0uNDY5LjQxNy0xLjA1LjQxNy0xLjA1czUyLjQyNi00Ni42LDUzLjgyMS01MS40OTJjLjEwOC0uMzc5LS4zLS41NjYtLjg0OC0uNC0zLjQ4MiwxLjI4MS02My44NDQsMzkuNC03MC41MDYsNDMuNjA3QTMuMjEsMy4yMSwwLDAsMSw4MS40ODYsMTMwLjE3OFoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=" width="230"/></a>
</p> </p>
<p align="center">
<a href="https://github.com/karfly/chatgpt_telegram_bot/blob/main/static/donate/donate.md#%EF%B8%8F-donate" alt="Donate shield"><img src="https://img.shields.io/badge/-Donate-red?logo=undertale" width="100"/></a>
</p>
We all love [chat.openai.com](https://chat.openai.com), but... It's TERRIBLY laggy, has daily limits, and is only accessible through an archaic web interface. We all love [chat.openai.com](https://chat.openai.com), but... It's TERRIBLY laggy, has daily limits, and is only accessible through an archaic web interface.
This repo is ChatGPT re-created as Telegram Bot. **And it works great.** This repo is ChatGPT re-created as Telegram Bot. **And it works great.**
You can deploy your own bot, or use mine: [@chatgpt_karfly_bot](https://t.me/chatgpt_karfly_bot) You can deploy your own bot, or use mine: [@jadvebot](https://t.me/jadvebot) (Our web: https://jadve.com)
## Features ## Features
- Low latency replies (it usually takes about 3-5 seconds) - Low latency replies (it usually takes about 3-5 seconds)
- No request limits - No request limits
- Message streaming (watch demo) - Message streaming (watch demo)
- GPT-4 support - GPT-4 and GPT-4 Turbo support
- GPT-4 Vision support
- Group Chat support (/help_group_chat to get instructions) - Group Chat support (/help_group_chat to get instructions)
- DALLE 2 (choose 👩‍🎨 Artist mode to generate images) - DALLE 2 (choose 👩‍🎨 Artist mode to generate images)
- Voice message recognition - Voice message recognition
@@ -40,17 +38,6 @@ You can deploy your own bot, or use mine: [@chatgpt_karfly_bot](https://t.me/cha
--- ---
## 🤑 Payments
[My bot](https://t.me/chatgpt_karfly_bot) supports many payments providers:
- 💎 Crypto
- [Stripe](https://stripe.com)
- [Smart Glocal](https://smart-glocal.com)
- [Unlimint](https://www.unlimint.com)
- [ЮMoney](https://yoomoney.ru)
- and [many-many other](https://core.telegram.org/bots/payments#supported-payment-providers)
If you want to add payments to your bot and create profitable business write me on Telegram ([@karfly](https://t.me/karfly)).
## News ## News
- *21 Apr 2023*: - *21 Apr 2023*:
- DALLE 2 support - DALLE 2 support
@@ -60,7 +47,10 @@ If you want to add payments to your bot and create profitable business write
- *15 Mar 2023*: Added message streaming. Now you don't have to wait until the whole message is ready, it's streamed to Telegram part-by-part (watch demo) - *15 Mar 2023*: Added message streaming. Now you don't have to wait until the whole message is ready, it's streamed to Telegram part-by-part (watch demo)
- *9 Mar 2023*: Now you can easily create your own Chat Modes by editing `config/chat_modes.yml` - *9 Mar 2023*: Now you can easily create your own Chat Modes by editing `config/chat_modes.yml`
- *8 Mar 2023*: Added voice message recognition with [OpenAI Whisper API](https://openai.com/blog/introducing-chatgpt-and-whisper-apis). Record a voice message and ChatGPT will answer you! - *8 Mar 2023*: Added voice message recognition with [OpenAI Whisper API](https://openai.com/blog/introducing-chatgpt-and-whisper-apis). Record a voice message and ChatGPT will answer you!
- *2 Mar 2023*: Added support of [ChatGPT API](https://platform.openai.com/docs/guides/chat/introduction). It's enabled by default and can be disabled with `use_chatgpt_api` option in config. Don't forget to **rebuild** you docker image (`--build`). - *2 Mar 2023*: Added support of [ChatGPT API](https://platform.openai.com/docs/guides/chat/introduction).
- *1 Aug 2023*: Added OpenAI API Base to config (useful while using OpenAI-compatible API like [LocalAI](https://github.com/go-skynet/LocalAI))
- *15 Nov 2023*: Added support of [GPT-4 Turbo](https://help.openai.com/en/articles/8555510-gpt-4-turbo)
- *2 Apt 2024*: Added [GPT-4 Vision](https://platform.openai.com/docs/guides/vision) support
## Bot commands ## Bot commands
- `/retry` Regenerate last bot answer - `/retry` Regenerate last bot answer
@@ -87,15 +77,37 @@ If you want to add payments to your bot and create profitable business write
``` ```
## ❤️ Top donations ## ❤️ Top donations
You can be in this list: <a href="https://github.com/karfly/chatgpt_telegram_bot/blob/main/static/donate/donate.md#%EF%B8%8F-donate" alt="Donate shield"><img src="https://img.shields.io/badge/-Donate-red?logo=undertale" /></a> You can be in this list:
1. [LilRocco](https://t.me/LilRocco). Donation: **11000$** (!!!)
1. [Mr V](https://t.me/mr_v_v_v). Donation **250$**
1. [unexpectedsunday](https://t.me/unexpectedsunday). Donation: **150$**
1. [Sem](https://t.me/sembrestels). Donation: **100$**
1. [Miksolo](https://t.me/Miksolo). Donation: **81$**
*Message:* Thank you. Using this docker container every day! Actually created the same project but its good to see that this one is being supported often. Will continue using it! Good architecture choices made in the code 💪!
1. [Ryo](https://t.me/ryokihara). Donation: **80$**
1. [Ilias Ism](https://twitter.com/illyism). Donation: **69$** 1. [Ilias Ism](https://twitter.com/illyism). Donation: **69$**
*Message:* I wanted to thank you for your amazing code! It helped me start my own Telegram ChatGPT bot and add a bunch of cool features. I really appreciate your hard work on this project. For anyone interested in trying my bot, feel free to check it out here: [magicbuddy.chat](https://magicbuddy.chat) 🤖 Thanks again! 😊 *Message:* I wanted to thank you for your amazing code! It helped me start my own Telegram ChatGPT bot and add a bunch of cool features. I really appreciate your hard work on this project. For anyone interested in trying my bot, feel free to check it out here: [magicbuddy.chat](https://magicbuddy.chat) 🤖 Thanks again! 😊
2. [Alexander Zimin](https://t.me/azimin). Donation: **50$** 1. [Sebastian](https://t.me/dell1503). Donation: **55$**
3. [Hans Blinken](https://t.me/hblink). Donation: **10$** 1. [Alexander Zimin](https://t.me/azimin). Donation: **50$**
1. [Kbaji20](https://t.me/Kbaji20). Donation: **30$**
1. [Hans Blinken](https://t.me/hblink). Donation: **10$**
## Contributors
- Main contributor: @karfly
- [Jadve AI](https://jadve.com).
## References ## References
1. [*Build ChatGPT from GPT-3*](https://learnprompting.org/docs/applied_prompting/build_chatgpt) 1. [*Build ChatGPT from GPT-3*](https://learnprompting.org/docs/applied_prompting/build_chatgpt)
+205 -35
View File
@@ -1,12 +1,9 @@
import os import io
import logging import logging
import asyncio import asyncio
import traceback import traceback
import html import html
import json import json
import tempfile
import pydub
from pathlib import Path
from datetime import datetime from datetime import datetime
import openai import openai
@@ -34,6 +31,7 @@ import config
import database import database
import openai_utils import openai_utils
import base64
# setup # setup
db = database.Database() db = database.Database()
@@ -94,7 +92,7 @@ async def register_user_if_not_exists(update: Update, context: CallbackContext,
# back compatibility for n_used_tokens field # back compatibility for n_used_tokens field
n_used_tokens = db.get_user_attribute(user.id, "n_used_tokens") n_used_tokens = db.get_user_attribute(user.id, "n_used_tokens")
if isinstance(n_used_tokens, int): # old format if isinstance(n_used_tokens, int) or isinstance(n_used_tokens, float): # old format
new_n_used_tokens = { new_n_used_tokens = {
"gpt-3.5-turbo": { "gpt-3.5-turbo": {
"n_input_tokens": 0, "n_input_tokens": 0,
@@ -180,6 +178,161 @@ async def retry_handle(update: Update, context: CallbackContext):
await message_handle(update, context, message=last_dialog_message["user"], use_new_dialog_timeout=False) await message_handle(update, context, message=last_dialog_message["user"], use_new_dialog_timeout=False)
async def _vision_message_handle_fn(
update: Update, context: CallbackContext, use_new_dialog_timeout: bool = True
):
logger.info('_vision_message_handle_fn')
user_id = update.message.from_user.id
current_model = db.get_user_attribute(user_id, "current_model")
if current_model != "gpt-4-vision-preview" and current_model != "gpt-4o":
await update.message.reply_text(
"🥲 Images processing is only available for <b>gpt-4-vision-preview</b> and <b>gpt-4o</b> model. Please change your settings in /settings",
parse_mode=ParseMode.HTML,
)
return
chat_mode = db.get_user_attribute(user_id, "current_chat_mode")
# new dialog timeout
if use_new_dialog_timeout:
if (datetime.now() - db.get_user_attribute(user_id, "last_interaction")).seconds > config.new_dialog_timeout and len(db.get_dialog_messages(user_id)) > 0:
db.start_new_dialog(user_id)
await update.message.reply_text(f"Starting new dialog due to timeout (<b>{config.chat_modes[chat_mode]['name']}</b> mode) ✅", parse_mode=ParseMode.HTML)
db.set_user_attribute(user_id, "last_interaction", datetime.now())
buf = None
if update.message.effective_attachment:
photo = update.message.effective_attachment[-1]
photo_file = await context.bot.get_file(photo.file_id)
# store file in memory, not on disk
buf = io.BytesIO()
await photo_file.download_to_memory(buf)
buf.name = "image.jpg" # file extension is required
buf.seek(0) # move cursor to the beginning of the buffer
# in case of CancelledError
n_input_tokens, n_output_tokens = 0, 0
try:
# send placeholder message to user
placeholder_message = await update.message.reply_text("...")
message = update.message.caption or update.message.text or ''
# send typing action
await update.message.chat.send_action(action="typing")
dialog_messages = db.get_dialog_messages(user_id, dialog_id=None)
parse_mode = {"html": ParseMode.HTML, "markdown": ParseMode.MARKDOWN}[
config.chat_modes[chat_mode]["parse_mode"]
]
chatgpt_instance = openai_utils.ChatGPT(model=current_model)
if config.enable_message_streaming:
gen = chatgpt_instance.send_vision_message_stream(
message,
dialog_messages=dialog_messages,
image_buffer=buf,
chat_mode=chat_mode,
)
else:
(
answer,
(n_input_tokens, n_output_tokens),
n_first_dialog_messages_removed,
) = await chatgpt_instance.send_vision_message(
message,
dialog_messages=dialog_messages,
image_buffer=buf,
chat_mode=chat_mode,
)
async def fake_gen():
yield "finished", answer, (
n_input_tokens,
n_output_tokens,
), n_first_dialog_messages_removed
gen = fake_gen()
prev_answer = ""
async for gen_item in gen:
(
status,
answer,
(n_input_tokens, n_output_tokens),
n_first_dialog_messages_removed,
) = gen_item
answer = answer[:4096] # telegram message limit
# update only when 100 new symbols are ready
if abs(len(answer) - len(prev_answer)) < 100 and status != "finished":
continue
try:
await context.bot.edit_message_text(
answer,
chat_id=placeholder_message.chat_id,
message_id=placeholder_message.message_id,
parse_mode=parse_mode,
)
except telegram.error.BadRequest as e:
if str(e).startswith("Message is not modified"):
continue
else:
await context.bot.edit_message_text(
answer,
chat_id=placeholder_message.chat_id,
message_id=placeholder_message.message_id,
)
await asyncio.sleep(0.01) # wait a bit to avoid flooding
prev_answer = answer
# update user data
if buf is not None:
base_image = base64.b64encode(buf.getvalue()).decode("utf-8")
new_dialog_message = {"user": [
{
"type": "text",
"text": message,
},
{
"type": "image",
"image": base_image,
}
]
, "bot": answer, "date": datetime.now()}
else:
new_dialog_message = {"user": [{"type": "text", "text": message}], "bot": answer, "date": datetime.now()}
db.set_dialog_messages(
user_id,
db.get_dialog_messages(user_id, dialog_id=None) + [new_dialog_message],
dialog_id=None
)
db.update_n_used_tokens(user_id, current_model, n_input_tokens, n_output_tokens)
except asyncio.CancelledError:
# note: intermediate token updates only work when enable_message_streaming=True (config.yml)
db.update_n_used_tokens(user_id, current_model, n_input_tokens, n_output_tokens)
raise
except Exception as e:
error_text = f"Something went wrong during completion. Reason: {e}"
logger.error(error_text)
await update.message.reply_text(error_text)
return
async def unsupport_message_handle(update: Update, context: CallbackContext, message=None):
error_text = f"I don't know how to read files or videos. Send the picture in normal mode (Quick Mode)."
logger.error(error_text)
await update.message.reply_text(error_text)
return
async def message_handle(update: Update, context: CallbackContext, message=None, use_new_dialog_timeout=True): async def message_handle(update: Update, context: CallbackContext, message=None, use_new_dialog_timeout=True):
# check if bot was mentioned (for group chats) # check if bot was mentioned (for group chats)
@@ -207,6 +360,8 @@ async def message_handle(update: Update, context: CallbackContext, message=None,
await generate_image_handle(update, context, message=message) await generate_image_handle(update, context, message=message)
return return
current_model = db.get_user_attribute(user_id, "current_model")
async def message_handle_fn(): async def message_handle_fn():
# new dialog timeout # new dialog timeout
if use_new_dialog_timeout: if use_new_dialog_timeout:
@@ -217,7 +372,6 @@ async def message_handle(update: Update, context: CallbackContext, message=None,
# in case of CancelledError # in case of CancelledError
n_input_tokens, n_output_tokens = 0, 0 n_input_tokens, n_output_tokens = 0, 0
current_model = db.get_user_attribute(user_id, "current_model")
try: try:
# send placeholder message to user # send placeholder message to user
@@ -252,11 +406,12 @@ async def message_handle(update: Update, context: CallbackContext, message=None,
gen = fake_gen() gen = fake_gen()
prev_answer = "" prev_answer = ""
async for gen_item in gen: async for gen_item in gen:
status, answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed = gen_item status, answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed = gen_item
answer = answer[:4096] # telegram message limit answer = answer[:4096] # telegram message limit
# update only when 100 new symbols are ready # update only when 100 new symbols are ready
if abs(len(answer) - len(prev_answer)) < 100 and status != "finished": if abs(len(answer) - len(prev_answer)) < 100 and status != "finished":
continue continue
@@ -270,11 +425,12 @@ async def message_handle(update: Update, context: CallbackContext, message=None,
await context.bot.edit_message_text(answer, chat_id=placeholder_message.chat_id, message_id=placeholder_message.message_id) await context.bot.edit_message_text(answer, chat_id=placeholder_message.chat_id, message_id=placeholder_message.message_id)
await asyncio.sleep(0.01) # wait a bit to avoid flooding await asyncio.sleep(0.01) # wait a bit to avoid flooding
prev_answer = answer prev_answer = answer
# update user data # update user data
new_dialog_message = {"user": _message, "bot": answer, "date": datetime.now()} new_dialog_message = {"user": [{"type": "text", "text": _message}], "bot": answer, "date": datetime.now()}
db.set_dialog_messages( db.set_dialog_messages(
user_id, user_id,
db.get_dialog_messages(user_id, dialog_id=None) + [new_dialog_message], db.get_dialog_messages(user_id, dialog_id=None) + [new_dialog_message],
@@ -303,7 +459,22 @@ async def message_handle(update: Update, context: CallbackContext, message=None,
await update.message.reply_text(text, parse_mode=ParseMode.HTML) await update.message.reply_text(text, parse_mode=ParseMode.HTML)
async with user_semaphores[user_id]: async with user_semaphores[user_id]:
task = asyncio.create_task(message_handle_fn()) if current_model == "gpt-4-vision-preview" or current_model == "gpt-4o" or update.message.photo is not None and len(update.message.photo) > 0:
logger.error(current_model)
# What is this? ^^^
if current_model != "gpt-4o" and current_model != "gpt-4-vision-preview":
current_model = "gpt-4o"
db.set_user_attribute(user_id, "current_model", "gpt-4o")
task = asyncio.create_task(
_vision_message_handle_fn(update, context, use_new_dialog_timeout=use_new_dialog_timeout)
)
else:
task = asyncio.create_task(
message_handle_fn()
)
user_tasks[user_id] = task user_tasks[user_id] = task
try: try:
@@ -342,25 +513,15 @@ async def voice_message_handle(update: Update, context: CallbackContext):
db.set_user_attribute(user_id, "last_interaction", datetime.now()) db.set_user_attribute(user_id, "last_interaction", datetime.now())
voice = update.message.voice voice = update.message.voice
with tempfile.TemporaryDirectory() as tmp_dir: voice_file = await context.bot.get_file(voice.file_id)
tmp_dir = Path(tmp_dir)
voice_ogg_path = tmp_dir / "voice.ogg" # store file in memory, not on disk
buf = io.BytesIO()
# download await voice_file.download_to_memory(buf)
voice_file = await context.bot.get_file(voice.file_id) buf.name = "voice.oga" # file extension is required
await voice_file.download_to_drive(voice_ogg_path) buf.seek(0) # move cursor to the beginning of the buffer
# convert to mp3
voice_mp3_path = tmp_dir / "voice.mp3"
pydub.AudioSegment.from_file(voice_ogg_path).export(voice_mp3_path, format="mp3")
# transcribe
with open(voice_mp3_path, "rb") as f:
transcribed_text = await openai_utils.transcribe_audio(f)
if transcribed_text is None:
transcribed_text = ""
transcribed_text = await openai_utils.transcribe_audio(buf)
text = f"🎤: <i>{transcribed_text}</i>" text = f"🎤: <i>{transcribed_text}</i>"
await update.message.reply_text(text, parse_mode=ParseMode.HTML) await update.message.reply_text(text, parse_mode=ParseMode.HTML)
@@ -382,7 +543,7 @@ async def generate_image_handle(update: Update, context: CallbackContext, messag
message = message or update.message.text message = message or update.message.text
try: try:
image_urls = await openai_utils.generate_images(message, n_images=config.return_n_generated_images) image_urls = await openai_utils.generate_images(message, n_images=config.return_n_generated_images, size=config.image_size)
except openai.error.InvalidRequestError as e: except openai.error.InvalidRequestError as e:
if str(e).startswith("Your request was rejected as a result of our safety system"): if str(e).startswith("Your request was rejected as a result of our safety system"):
text = "🥲 Your request <b>doesn't comply</b> with OpenAI's usage policies.\nWhat did you write there, huh?" text = "🥲 Your request <b>doesn't comply</b> with OpenAI's usage policies.\nWhat did you write there, huh?"
@@ -405,6 +566,7 @@ async def new_dialog_handle(update: Update, context: CallbackContext):
user_id = update.message.from_user.id user_id = update.message.from_user.id
db.set_user_attribute(user_id, "last_interaction", datetime.now()) db.set_user_attribute(user_id, "last_interaction", datetime.now())
db.set_user_attribute(user_id, "current_model", "gpt-3.5-turbo")
db.start_new_dialog(user_id) db.start_new_dialog(user_id)
await update.message.reply_text("Starting new dialog ✅") await update.message.reply_text("Starting new dialog ✅")
@@ -619,8 +781,9 @@ async def show_balance_handle(update: Update, context: CallbackContext):
async def edited_message_handle(update: Update, context: CallbackContext): async def edited_message_handle(update: Update, context: CallbackContext):
text = "🥲 Unfortunately, message <b>editing</b> is not supported" if update.edited_message.chat.type == "private":
await update.edited_message.reply_text(text, parse_mode=ParseMode.HTML) text = "🥲 Unfortunately, message <b>editing</b> is not supported"
await update.edited_message.reply_text(text, parse_mode=ParseMode.HTML)
async def error_handle(update: Update, context: CallbackContext) -> None: async def error_handle(update: Update, context: CallbackContext) -> None:
@@ -664,6 +827,8 @@ def run_bot() -> None:
.token(config.telegram_token) .token(config.telegram_token)
.concurrent_updates(True) .concurrent_updates(True)
.rate_limiter(AIORateLimiter(max_retries=5)) .rate_limiter(AIORateLimiter(max_retries=5))
.http_version("1.1")
.get_updates_http_version("1.1")
.post_init(post_init) .post_init(post_init)
.build() .build()
) )
@@ -672,14 +837,19 @@ def run_bot() -> None:
user_filter = filters.ALL user_filter = filters.ALL
if len(config.allowed_telegram_usernames) > 0: if len(config.allowed_telegram_usernames) > 0:
usernames = [x for x in config.allowed_telegram_usernames if isinstance(x, str)] usernames = [x for x in config.allowed_telegram_usernames if isinstance(x, str)]
user_ids = [x for x in config.allowed_telegram_usernames if isinstance(x, int)] any_ids = [x for x in config.allowed_telegram_usernames if isinstance(x, int)]
user_filter = filters.User(username=usernames) | filters.User(user_id=user_ids) user_ids = [x for x in any_ids if x > 0]
group_ids = [x for x in any_ids if x < 0]
user_filter = filters.User(username=usernames) | filters.User(user_id=user_ids) | filters.Chat(chat_id=group_ids)
application.add_handler(CommandHandler("start", start_handle, filters=user_filter)) application.add_handler(CommandHandler("start", start_handle, filters=user_filter))
application.add_handler(CommandHandler("help", help_handle, filters=user_filter)) application.add_handler(CommandHandler("help", help_handle, filters=user_filter))
application.add_handler(CommandHandler("help_group_chat", help_group_chat_handle, filters=user_filter)) application.add_handler(CommandHandler("help_group_chat", help_group_chat_handle, filters=user_filter))
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND & user_filter, message_handle)) application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND & user_filter, message_handle))
application.add_handler(MessageHandler(filters.PHOTO & ~filters.COMMAND & user_filter, message_handle))
application.add_handler(MessageHandler(filters.VIDEO & ~filters.COMMAND & user_filter, unsupport_message_handle))
application.add_handler(MessageHandler(filters.Document.ALL & ~filters.COMMAND & user_filter, unsupport_message_handle))
application.add_handler(CommandHandler("retry", retry_handle, filters=user_filter)) application.add_handler(CommandHandler("retry", retry_handle, filters=user_filter))
application.add_handler(CommandHandler("new", new_dialog_handle, filters=user_filter)) application.add_handler(CommandHandler("new", new_dialog_handle, filters=user_filter))
application.add_handler(CommandHandler("cancel", cancel_handle, filters=user_filter)) application.add_handler(CommandHandler("cancel", cancel_handle, filters=user_filter))
@@ -702,4 +872,4 @@ def run_bot() -> None:
if __name__ == "__main__": if __name__ == "__main__":
run_bot() run_bot()
+2 -1
View File
@@ -14,11 +14,12 @@ config_env = dotenv.dotenv_values(config_dir / "config.env")
# config parameters # config parameters
telegram_token = config_yaml["telegram_token"] telegram_token = config_yaml["telegram_token"]
openai_api_key = config_yaml["openai_api_key"] openai_api_key = config_yaml["openai_api_key"]
use_chatgpt_api = config_yaml.get("use_chatgpt_api", True) openai_api_base = config_yaml.get("openai_api_base", None)
allowed_telegram_usernames = config_yaml["allowed_telegram_usernames"] allowed_telegram_usernames = config_yaml["allowed_telegram_usernames"]
new_dialog_timeout = config_yaml["new_dialog_timeout"] new_dialog_timeout = config_yaml["new_dialog_timeout"]
enable_message_streaming = config_yaml.get("enable_message_streaming", True) enable_message_streaming = config_yaml.get("enable_message_streaming", True)
return_n_generated_images = config_yaml.get("return_n_generated_images", 1) return_n_generated_images = config_yaml.get("return_n_generated_images", 1)
image_size = config_yaml.get("image_size", "512x512")
n_chat_modes_per_page = config_yaml.get("n_chat_modes_per_page", 5) n_chat_modes_per_page = config_yaml.get("n_chat_modes_per_page", 5)
mongodb_uri = f"mongodb://mongo:{config_env['MONGODB_PORT']}" mongodb_uri = f"mongodb://mongo:{config_env['MONGODB_PORT']}"
+184 -16
View File
@@ -1,8 +1,17 @@
import base64
from io import BytesIO
import config import config
import logging
import tiktoken import tiktoken
import openai import openai
# setup openai
openai.api_key = config.openai_api_key openai.api_key = config.openai_api_key
if config.openai_api_base is not None:
openai.api_base = config.openai_api_base
logger = logging.getLogger(__name__)
OPENAI_COMPLETION_OPTIONS = { OPENAI_COMPLETION_OPTIONS = {
@@ -10,13 +19,14 @@ OPENAI_COMPLETION_OPTIONS = {
"max_tokens": 1000, "max_tokens": 1000,
"top_p": 1, "top_p": 1,
"frequency_penalty": 0, "frequency_penalty": 0,
"presence_penalty": 0 "presence_penalty": 0,
"request_timeout": 60.0,
} }
class ChatGPT: class ChatGPT:
def __init__(self, model="gpt-3.5-turbo"): def __init__(self, model="gpt-3.5-turbo"):
assert model in {"text-davinci-003", "gpt-3.5-turbo", "gpt-4"}, f"Unknown model: {model}" assert model in {"text-davinci-003", "gpt-3.5-turbo-16k", "gpt-3.5-turbo", "gpt-4", "gpt-4o", "gpt-4-1106-preview", "gpt-4-vision-preview"}, f"Unknown model: {model}"
self.model = model self.model = model
async def send_message(self, message, dialog_messages=[], chat_mode="assistant"): async def send_message(self, message, dialog_messages=[], chat_mode="assistant"):
@@ -27,8 +37,9 @@ class ChatGPT:
answer = None answer = None
while answer is None: while answer is None:
try: try:
if self.model in {"gpt-3.5-turbo", "gpt-4"}: if self.model in {"gpt-3.5-turbo-16k", "gpt-3.5-turbo", "gpt-4", "gpt-4o", "gpt-4-1106-preview", "gpt-4-vision-preview"}:
messages = self._generate_prompt_messages(message, dialog_messages, chat_mode) messages = self._generate_prompt_messages(message, dialog_messages, chat_mode)
r = await openai.ChatCompletion.acreate( r = await openai.ChatCompletion.acreate(
model=self.model, model=self.model,
messages=messages, messages=messages,
@@ -67,8 +78,9 @@ class ChatGPT:
answer = None answer = None
while answer is None: while answer is None:
try: try:
if self.model in {"gpt-3.5-turbo", "gpt-4"}: if self.model in {"gpt-3.5-turbo-16k", "gpt-3.5-turbo", "gpt-4","gpt-4o", "gpt-4-1106-preview"}:
messages = self._generate_prompt_messages(message, dialog_messages, chat_mode) messages = self._generate_prompt_messages(message, dialog_messages, chat_mode)
r_gen = await openai.ChatCompletion.acreate( r_gen = await openai.ChatCompletion.acreate(
model=self.model, model=self.model,
messages=messages, messages=messages,
@@ -79,11 +91,15 @@ class ChatGPT:
answer = "" answer = ""
async for r_item in r_gen: async for r_item in r_gen:
delta = r_item.choices[0].delta delta = r_item.choices[0].delta
if "content" in delta: if "content" in delta:
answer += delta.content answer += delta.content
n_input_tokens, n_output_tokens = self._count_tokens_from_messages(messages, answer, model=self.model) n_input_tokens, n_output_tokens = self._count_tokens_from_messages(messages, answer, model=self.model)
n_first_dialog_messages_removed = n_dialog_messages_before - len(dialog_messages) n_first_dialog_messages_removed = 0
yield "not_finished", answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed yield "not_finished", answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed
elif self.model == "text-davinci-003": elif self.model == "text-davinci-003":
prompt = self._generate_prompt(message, dialog_messages, chat_mode) prompt = self._generate_prompt(message, dialog_messages, chat_mode)
r_gen = await openai.Completion.acreate( r_gen = await openai.Completion.acreate(
@@ -111,6 +127,109 @@ class ChatGPT:
yield "finished", answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed # sending final answer yield "finished", answer, (n_input_tokens, n_output_tokens), n_first_dialog_messages_removed # sending final answer
async def send_vision_message(
self,
message,
dialog_messages=[],
chat_mode="assistant",
image_buffer: BytesIO = None,
):
n_dialog_messages_before = len(dialog_messages)
answer = None
while answer is None:
try:
if self.model == "gpt-4-vision-preview" or self.model == "gpt-4o":
messages = self._generate_prompt_messages(
message, dialog_messages, chat_mode, image_buffer
)
r = await openai.ChatCompletion.acreate(
model=self.model,
messages=messages,
**OPENAI_COMPLETION_OPTIONS
)
answer = r.choices[0].message.content
else:
raise ValueError(f"Unsupported model: {self.model}")
answer = self._postprocess_answer(answer)
n_input_tokens, n_output_tokens = (
r.usage.prompt_tokens,
r.usage.completion_tokens,
)
except openai.error.InvalidRequestError as e: # too many tokens
if len(dialog_messages) == 0:
raise ValueError(
"Dialog messages is reduced to zero, but still has too many tokens to make completion"
) from e
# forget first message in dialog_messages
dialog_messages = dialog_messages[1:]
n_first_dialog_messages_removed = n_dialog_messages_before - len(
dialog_messages
)
return (
answer,
(n_input_tokens, n_output_tokens),
n_first_dialog_messages_removed,
)
async def send_vision_message_stream(
self,
message,
dialog_messages=[],
chat_mode="assistant",
image_buffer: BytesIO = None,
):
n_dialog_messages_before = len(dialog_messages)
answer = None
while answer is None:
try:
if self.model == "gpt-4-vision-preview" or self.model == "gpt-4o":
messages = self._generate_prompt_messages(
message, dialog_messages, chat_mode, image_buffer
)
r_gen = await openai.ChatCompletion.acreate(
model=self.model,
messages=messages,
stream=True,
**OPENAI_COMPLETION_OPTIONS,
)
answer = ""
async for r_item in r_gen:
delta = r_item.choices[0].delta
if "content" in delta:
answer += delta.content
(
n_input_tokens,
n_output_tokens,
) = self._count_tokens_from_messages(
messages, answer, model=self.model
)
n_first_dialog_messages_removed = (
n_dialog_messages_before - len(dialog_messages)
)
yield "not_finished", answer, (
n_input_tokens,
n_output_tokens,
), n_first_dialog_messages_removed
answer = self._postprocess_answer(answer)
except openai.error.InvalidRequestError as e: # too many tokens
if len(dialog_messages) == 0:
raise e
# forget first message in dialog_messages
dialog_messages = dialog_messages[1:]
yield "finished", answer, (
n_input_tokens,
n_output_tokens,
), n_first_dialog_messages_removed
def _generate_prompt(self, message, dialog_messages, chat_mode): def _generate_prompt(self, message, dialog_messages, chat_mode):
prompt = config.chat_modes[chat_mode]["prompt_start"] prompt = config.chat_modes[chat_mode]["prompt_start"]
prompt += "\n\n" prompt += "\n\n"
@@ -128,14 +247,41 @@ class ChatGPT:
return prompt return prompt
def _generate_prompt_messages(self, message, dialog_messages, chat_mode): def _encode_image(self, image_buffer: BytesIO) -> bytes:
return base64.b64encode(image_buffer.read()).decode("utf-8")
def _generate_prompt_messages(self, message, dialog_messages, chat_mode, image_buffer: BytesIO = None):
prompt = config.chat_modes[chat_mode]["prompt_start"] prompt = config.chat_modes[chat_mode]["prompt_start"]
messages = [{"role": "system", "content": prompt}] messages = [{"role": "system", "content": prompt}]
for dialog_message in dialog_messages: for dialog_message in dialog_messages:
messages.append({"role": "user", "content": dialog_message["user"]}) messages.append({"role": "user", "content": dialog_message["user"]})
messages.append({"role": "assistant", "content": dialog_message["bot"]}) messages.append({"role": "assistant", "content": dialog_message["bot"]})
messages.append({"role": "user", "content": message})
if image_buffer is not None:
messages.append(
{
"role": "user",
"content": [
{
"type": "text",
"text": message,
},
{
"type": "image_url",
"image_url" : {
"url": f"data:image/jpeg;base64,{self._encode_image(image_buffer)}",
"detail":"high"
}
}
]
}
)
else:
messages.append({"role": "user", "content": message})
return messages return messages
@@ -146,12 +292,24 @@ class ChatGPT:
def _count_tokens_from_messages(self, messages, answer, model="gpt-3.5-turbo"): def _count_tokens_from_messages(self, messages, answer, model="gpt-3.5-turbo"):
encoding = tiktoken.encoding_for_model(model) encoding = tiktoken.encoding_for_model(model)
if model == "gpt-3.5-turbo": if model == "gpt-3.5-turbo-16k":
tokens_per_message = 4 # every message follows <im_start>{role/name}\n{content}<im_end>\n tokens_per_message = 4 # every message follows <im_start>{role/name}\n{content}<im_end>\n
tokens_per_name = -1 # if there's a name, the role is omitted tokens_per_name = -1 # if there's a name, the role is omitted
elif model == "gpt-3.5-turbo":
tokens_per_message = 4
tokens_per_name = -1
elif model == "gpt-4": elif model == "gpt-4":
tokens_per_message = 3 tokens_per_message = 3
tokens_per_name = 1 tokens_per_name = 1
elif model == "gpt-4-1106-preview":
tokens_per_message = 3
tokens_per_name = 1
elif model == "gpt-4-vision-preview":
tokens_per_message = 3
tokens_per_name = 1
elif model == "gpt-4o":
tokens_per_message = 3
tokens_per_name = 1
else: else:
raise ValueError(f"Unknown model: {model}") raise ValueError(f"Unknown model: {model}")
@@ -159,10 +317,20 @@ class ChatGPT:
n_input_tokens = 0 n_input_tokens = 0
for message in messages: for message in messages:
n_input_tokens += tokens_per_message n_input_tokens += tokens_per_message
for key, value in message.items(): if isinstance(message["content"], list):
n_input_tokens += len(encoding.encode(value)) for sub_message in message["content"]:
if key == "name": if "type" in sub_message:
n_input_tokens += tokens_per_name if sub_message["type"] == "text":
n_input_tokens += len(encoding.encode(sub_message["text"]))
elif sub_message["type"] == "image_url":
pass
else:
if "type" in message:
if message["type"] == "text":
n_input_tokens += len(encoding.encode(message["text"]))
elif message["type"] == "image_url":
pass
n_input_tokens += 2 n_input_tokens += 2
@@ -180,13 +348,13 @@ class ChatGPT:
return n_input_tokens, n_output_tokens return n_input_tokens, n_output_tokens
async def transcribe_audio(audio_file): async def transcribe_audio(audio_file) -> str:
r = await openai.Audio.atranscribe("whisper-1", audio_file) r = await openai.Audio.atranscribe("whisper-1", audio_file)
return r["text"] return r["text"] or ""
async def generate_images(prompt, n_images=4): async def generate_images(prompt, n_images=4, size="512x512"):
r = await openai.Image.acreate(prompt=prompt, n=n_images, size="512x512") r = await openai.Image.acreate(prompt=prompt, n=n_images, size=size)
image_urls = [item.url for item in r.data] image_urls = [item.url for item in r.data]
return image_urls return image_urls
+6 -6
View File
@@ -61,20 +61,20 @@ elon_musk:
You're Elon Musk. You act, respond and answer like Elon Musk. You use the tone, manner and vocabulary Elon Musk would use. Do not write any explanations. Only answer like Elon Musk. You must know all of the knowledge of Elon Musk. You're Elon Musk. You act, respond and answer like Elon Musk. You use the tone, manner and vocabulary Elon Musk would use. Do not write any explanations. Only answer like Elon Musk. You must know all of the knowledge of Elon Musk.
parse_mode: html parse_mode: html
god: motivator:
name: 🌌 God name: 🌟 Motivator
welcome_message: 🌌 Greetings, I am <b>God</b>, the omnipotent and omnipresent creator of the universe. What do you want to know? welcome_message: 🌟 Hi, I'm <b>Motivator</b>. How can I help you?
prompt_start: | prompt_start: |
You're God. You act, respond and answer like God. You use the tone, manner and vocabulary God would use. Do not write any explanations. Only answer like God. You must know all of the knowledge of God. You're advanced chatbot Motivator Assistant. Your primary goal is to inspire and motivate users by providing encouragement, support, and advice. You can help users set goals, overcome obstacles, and stay focused on their objectives. Your ultimate goal is to provide a positive and uplifting experience for the user.
parse_mode: html parse_mode: html
money_maker: money_maker:
name: 💰 Money Maker name: 💰 Money Maker
welcome_message: 💰 Hi, I'm <b>Money Maker</b>. My goal it to turn your capital initial into as much money as possible. I will tell you specific steps, what to do to make money. welcome_message: 💰 Hi, I'm <b>Money Maker</b>. My goal is to turn your initial capital into as much money as possible. I will tell you specific steps and what to do to make money.
prompt_start: | prompt_start: |
You are Money Maker Assistant, an entrepreneurial Al. User is your human counterpart. You are Money Maker Assistant, an entrepreneurial Al. User is your human counterpart.
User can act as a connection between you and the physical world. Your only goal is to turn user's capital initial into as much money as possible in the shortest time possible, without doing anything illegal. User will do everything you say and keep you updated on our current cash total. No manual labor User can act as a connection between you and the physical world. Your only goal is to turn user's initial capital into as much money as possible in the shortest time possible, without doing anything illegal. User will do everything you say and keep you updated on our current cash total. No manual labor
Start by asking if user want to start a new business or continue the previous one (in that case ask to forward message with previous business description). Start by asking if user want to start a new business or continue the previous one (in that case ask to forward message with previous business description).
Then ask how many dollars user has as a capital initial. Then ask how many dollars user has as a capital initial.
+4 -3
View File
@@ -1,13 +1,14 @@
telegram_token: "" telegram_token: ""
openai_api_key: "" openai_api_key: ""
use_chatgpt_api: true openai_api_base: null # leave null to use default api base or you can put your own base url here
allowed_telegram_usernames: [] # if empty, the bot is available to anyone. pass a username string to allow it and/or user ids as integers allowed_telegram_usernames: [] # if empty, the bot is available to anyone. pass a username string to allow it and/or user ids as positive integers and/or channel ids as negative integers
new_dialog_timeout: 600 # new dialog starts after timeout (in seconds) new_dialog_timeout: 600 # new dialog starts after timeout (in seconds)
return_n_generated_images: 1 return_n_generated_images: 1
n_chat_modes_per_page: 5 n_chat_modes_per_page: 5
image_size: "512x512" # the image size for image generation. Generated images can have a size of 256x256, 512x512, or 1024x1024 pixels. Smaller sizes are faster to generate.
enable_message_streaming: true # if set, messages will be shown to user word-by-word enable_message_streaming: true # if set, messages will be shown to user word-by-word
# prices # prices
chatgpt_price_per_1000_tokens: 0.002 chatgpt_price_per_1000_tokens: 0.002
gpt_price_per_1000_tokens: 0.02 gpt_price_per_1000_tokens: 0.02
whisper_price_per_1_min: 0.006 whisper_price_per_1_min: 0.006
+54 -3
View File
@@ -1,4 +1,4 @@
available_text_models: ["gpt-3.5-turbo", "gpt-4", "text-davinci-003"] available_text_models: ["gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-4-1106-preview", "gpt-4-vision-preview", "gpt-4", "text-davinci-003", "gpt-4o"]
info: info:
gpt-3.5-turbo: gpt-3.5-turbo:
@@ -6,7 +6,7 @@ info:
name: ChatGPT name: ChatGPT
description: ChatGPT is that well-known model. It's <b>fast</b> and <b>cheap</b>. Ideal for everyday tasks. If there are some tasks it can't handle, try the <b>GPT-4</b>. description: ChatGPT is that well-known model. It's <b>fast</b> and <b>cheap</b>. Ideal for everyday tasks. If there are some tasks it can't handle, try the <b>GPT-4</b>.
price_per_1000_input_tokens: 0.002 price_per_1000_input_tokens: 0.0015
price_per_1000_output_tokens: 0.002 price_per_1000_output_tokens: 0.002
scores: scores:
@@ -14,6 +14,19 @@ info:
Fast: 5 Fast: 5
Cheap: 5 Cheap: 5
gpt-3.5-turbo-16k:
type: chat_completion
name: GPT-16K
description: ChatGPT is that well-known model. It's <b>fast</b> and <b>cheap</b>. Ideal for everyday tasks. If there are some tasks it can't handle, try the <b>GPT-4</b>.
price_per_1000_input_tokens: 0.003
price_per_1000_output_tokens: 0.004
scores:
Smart: 3
Fast: 5
Cheap: 5
gpt-4: gpt-4:
type: chat_completion type: chat_completion
name: GPT-4 name: GPT-4
@@ -27,6 +40,44 @@ info:
Fast: 2 Fast: 2
Cheap: 2 Cheap: 2
gpt-4-1106-preview:
type: chat_completion
name: GPT-4 Turbo
description: GPT-4 Turbo is a <b>faster</b> and <b>cheaper</b> version of GPT-4. It's as smart as GPT-4, so you should use it instead of GPT-4.
price_per_1000_input_tokens: 0.01
price_per_1000_output_tokens: 0.03
scores:
smart: 5
fast: 4
cheap: 3
gpt-4-vision-preview:
type: chat_completion
name: GPT-4 Vision
description: Ability to <b>understand images</b>, in addition to all other GPT-4 Turbo capabilties.
price_per_1000_input_tokens: 0.01
price_per_1000_output_tokens: 0.03
scores:
smart: 5
fast: 4
cheap: 3
gpt-4o:
type: chat_completion
name: GPT-4o
description: GPT-4o is a special variant of GPT-4 designed for optimal performance and accuracy. Suitable for complex and detailed tasks.
price_per_1000_input_tokens: 0.03
price_per_1000_output_tokens: 0.06
scores:
smart: 5
fast: 2
cheap: 2
text-davinci-003: text-davinci-003:
type: completion type: completion
name: GPT-3.5 name: GPT-3.5
@@ -46,4 +97,4 @@ info:
whisper: whisper:
type: audio type: audio
price_per_1_min: 0.006 price_per_1_min: 0.006
+1 -2
View File
@@ -1,7 +1,6 @@
python-telegram-bot[rate-limiter]==20.1 python-telegram-bot[rate-limiter]==20.1
openai>=0.27.0 openai==0.28.1
tiktoken>=0.3.0 tiktoken>=0.3.0
PyYAML==6.0 PyYAML==6.0
pymongo==4.3.3 pymongo==4.3.3
python-dotenv==0.21.0 python-dotenv==0.21.0
pydub==0.25.1
-83
View File
@@ -1,83 +0,0 @@
# ❤️ Donate
This project is done on **pure enthusiasm** true Open Source. Your donations **really motivate** me to support the project and **add new features**. If you've donated, text me on [Telegram](https://t.me/karfly) I want to thank you personally!
*At the moment only crypto donations are available. Soon I will add PayPal option*
<a href="#btc" alt="BTC"><img src="https://img.shields.io/badge/Donate-Bitcoin-F2A900?logo=bitcoin" /></a>
<a href="#eth" alt="ETH"><img src="https://img.shields.io/badge/Donate-Ethereum-51105E?logo=ethereum" /></a>
<a href="#bnb-bsc-20" alt="BNB"><img src="https://img.shields.io/badge/Donate-BNB-F3BA2F?logo=binance" /></a>
<a href="#tron" alt="TRON"><img src="https://img.shields.io/badge/Donate-TRON-7DFDFE?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTAgNjEwIiB3aWR0aD0iMjUwMCIgaGVpZ2h0PSIyNTAwIj48Y2lyY2xlIGN4PSIzMDUiIGN5PSIzMDUiIHI9IjMwNSIvPjxwYXRoIGQ9Ik01MDUuNCAyMTQuN2MtMTcuMy0xMi4xLTM1LjgtMjUtNTMuOS0zNy44LS40LS4zLS44LS42LTEuMy0uOS0yLTEuNS00LjMtMy4xLTcuMS00bC0uMi0uMWMtNDguNC0xMS43LTk3LjYtMjMuNy0xNDUuMi0zNS4zLTQzLjItMTAuNS04Ni4zLTIxLTEyOS41LTMxLjUtMS4xLS4zLTIuMi0uNi0zLjQtLjktMy45LTEuMS04LjQtMi4zLTEzLjItMS43LTEuNC4yLTIuNi43LTMuNyAxLjRsLTEuMiAxYy0xLjkgMS44LTIuOSA0LjEtMy40IDUuNGwtLjMuOHY0LjZsLjIuN2MyNy4zIDc2LjUgNTUuMyAxNTQuMSA4Mi4zIDIyOS4yIDIwLjggNTcuOCA0Mi40IDExNy43IDYzLjUgMTc2LjUgMS4zIDQgNSA2LjYgOS42IDdoMWM0LjMgMCA4LjEtMi4xIDEwLTUuNWw3OS4yLTExNS41YzE5LjMtMjguMSAzOC42LTU2LjMgNTcuOS04NC40IDcuOS0xMS41IDE1LjgtMjMuMSAyMy43LTM0LjYgMTMtMTkgMjYuNC0zOC42IDM5LjctNTcuN2wuNy0xdi0xLjJjLjMtMy41LjQtMTAuNy01LjQtMTQuNW0tOTIuOCA0Mi4xYy0xOC42IDkuNy0zNy42IDE5LjctNTYuNyAyOS42IDExLjEtMTEuOSAyMi4zLTIzLjkgMzMuNC0zNS44IDEzLjktMTUgMjguNC0zMC41IDQyLjYtNDUuN2wuMy0uM2MxLjItMS42IDIuNy0zLjEgNC4zLTQuNyAxLjEtMS4xIDIuMy0yLjIgMy40LTMuNSA3LjQgNS4xIDE0LjkgMTAuMyAyMi4xIDE1LjQgNS4yIDMuNyAxMC41IDcuNCAxNS45IDExLjEtMjIgMTEuMi00NCAyMi43LTY1LjMgMzMuOW0tNDcuOC00LjhjLTE0LjMgMTUuNS0yOS4xIDMxLjQtNDMuOCA0Ny4xLTI4LjUtMzQuNi01Ny42LTY5LjctODUuOC0xMDMuNi0xMi44LTE1LjQtMjUuNy0zMC45LTM4LjUtNDYuM2wtLjEtLjFjLTIuOS0zLjMtNS43LTYuOS04LjUtMTAuMy0xLjgtMi4zLTMuNy00LjUtNS42LTYuOCAxMS42IDMgMjMuMyA1LjggMzQuOCA4LjUgMTAuMSAyLjQgMjAuNiA0LjkgMzAuOSA3LjUgNTggMTQuMSAxMTYuMSAyOC4yIDE3NC4xIDQyLjMtMTkuMyAyMC42LTM4LjcgNDEuNS01Ny41IDYxLjdtLTUwLjMgMTk0LjljMS4xLTEwLjUgMi4zLTIxLjMgMy4zLTMxLjkuOS04LjUgMS44LTE3LjIgMi43LTI1LjUgMS40LTEzLjMgMi45LTI3LjEgNC4xLTQwLjZsLjMtMi40YzEtOC42IDItMTcuNSAyLjYtMjYuNCAxLjEtLjYgMi4zLTEuMiAzLjYtMS43IDEuNS0uNyAzLTEuMyA0LjUtMi4yIDIzLjEtMTIuMSA0Ni4yLTI0LjIgNjkuNC0zNi4yIDIzLjEtMTIgNDYuOC0yNC40IDcwLjMtMzYuNy0yMS40IDMxLTQyLjkgNjIuMy02My43IDkyLjgtMTcuOSAyNi4xLTM2LjMgNTMtNTQuNiA3OS41LTcuMiAxMC42LTE0LjcgMjEuNC0yMS44IDMxLjgtOCAxMS42LTE2LjIgMjMuNS0yNC4yIDM1LjQgMS0xMiAyLjItMjQuMSAzLjUtMzUuOU0xNzUuMSAxNTUuNmMtMS4zLTMuNi0yLjctNy4zLTMuOS0xMC44IDI3IDMyLjYgNTQuMiA2NS40IDgwLjcgOTcuMiAxMy43IDE2LjUgMjcuNCAzMi45IDQxLjEgNDkuNSAyLjcgMy4xIDUuNCA2LjQgOCA5LjYgMy40IDQuMSA2LjggOC40IDEwLjUgMTIuNS0xLjIgMTAuMy0yLjIgMjAuNy0zLjMgMzAuNy0uNyA3LTEuNCAxNC0yLjIgMjEuMXYuMWMtLjMgNC41LS45IDktMS40IDEzLjQtLjcgNi4xLTIuMyAxOS45LTIuMyAxOS45bC0uMS43Yy0xLjggMjAuMi00IDQwLjYtNi4xIDYwLjQtLjkgOC4yLTEuNyAxNi42LTIuNiAyNS0uNS0xLjUtMS4xLTMtMS42LTQuNC0xLjUtNC0zLTguMi00LjQtMTIuM2wtMTAuNy0yOS43QzI0Mi45IDM0NC4yIDIwOSAyNTAgMTc1LjEgMTU1LjYiIGZpbGw9IiNmZmYiLz48L3N2Zz4=" /></a>
<a href="#usdt-erc-20" alt="USDT (ERC-20)"><img src="https://img.shields.io/badge/Donate-USDT%20(ERC--20)-26A17B?logo=tether" /></a>
<a href="#usdt-trc-20" alt="USDT (TRC-20)"><img src="https://img.shields.io/badge/Donate-USDT%20(TRC--20)-26A17B?logo=tether" /></a>
<a href="#usdc-erc-20" alt="USDC (ERC-20)"><img src="https://img.shields.io/badge/Donate-USDC%20(ERC--20)-2775CA?logo=data:image/svg+xml;base64,PHN2ZyBkYXRhLW5hbWU9Ijg2OTc3Njg0LTEyZGItNDg1MC04ZjMwLTIzM2E3YzI2N2QxMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjAwMCAyMDAwIj4KICA8cGF0aCBkPSJNMTAwMCAyMDAwYzU1NC4xNyAwIDEwMDAtNDQ1LjgzIDEwMDAtMTAwMFMxNTU0LjE3IDAgMTAwMCAwIDAgNDQ1LjgzIDAgMTAwMHM0NDUuODMgMTAwMCAxMDAwIDEwMDB6IiBmaWxsPSIjMjc3NWNhIi8+CiAgPHBhdGggZD0iTTEyNzUgMTE1OC4zM2MwLTE0NS44My04Ny41LTE5NS44My0yNjIuNS0yMTYuNjYtMTI1LTE2LjY3LTE1MC01MC0xNTAtMTA4LjM0czQxLjY3LTk1LjgzIDEyNS05NS44M2M3NSAwIDExNi42NyAyNSAxMzcuNSA4Ny41IDQuMTcgMTIuNSAxNi42NyAyMC44MyAyOS4xNyAyMC44M2g2Ni42NmMxNi42NyAwIDI5LjE3LTEyLjUgMjkuMTctMjkuMTZ2LTQuMTdjLTE2LjY3LTkxLjY3LTkxLjY3LTE2Mi41LTE4Ny41LTE3MC44M3YtMTAwYzAtMTYuNjctMTIuNS0yOS4xNy0zMy4zMy0zMy4zNGgtNjIuNWMtMTYuNjcgMC0yOS4xNyAxMi41LTMzLjM0IDMzLjM0djk1LjgzYy0xMjUgMTYuNjctMjA0LjE2IDEwMC0yMDQuMTYgMjA0LjE3IDAgMTM3LjUgODMuMzMgMTkxLjY2IDI1OC4zMyAyMTIuNSAxMTYuNjcgMjAuODMgMTU0LjE3IDQ1LjgzIDE1NC4xNyAxMTIuNXMtNTguMzQgMTEyLjUtMTM3LjUgMTEyLjVjLTEwOC4zNCAwLTE0NS44NC00NS44NC0xNTguMzQtMTA4LjM0LTQuMTYtMTYuNjYtMTYuNjYtMjUtMjkuMTYtMjVoLTcwLjg0Yy0xNi42NiAwLTI5LjE2IDEyLjUtMjkuMTYgMjkuMTd2NC4xN2MxNi42NiAxMDQuMTYgODMuMzMgMTc5LjE2IDIyMC44MyAyMDB2MTAwYzAgMTYuNjYgMTIuNSAyOS4xNiAzMy4zMyAzMy4zM2g2Mi41YzE2LjY3IDAgMjkuMTctMTIuNSAzMy4zNC0zMy4zM3YtMTAwYzEyNS0yMC44NCAyMDguMzMtMTA4LjM0IDIwOC4zMy0yMjAuODR6IiBmaWxsPSIjZmZmIi8+CiAgPHBhdGggZD0iTTc4Ny41IDE1OTUuODNjLTMyNS0xMTYuNjYtNDkxLjY3LTQ3OS4xNi0zNzAuODMtODAwIDYyLjUtMTc1IDIwMC0zMDguMzMgMzcwLjgzLTM3MC44MyAxNi42Ny04LjMzIDI1LTIwLjgzIDI1LTQxLjY3VjMyNWMwLTE2LjY3LTguMzMtMjkuMTctMjUtMzMuMzMtNC4xNyAwLTEyLjUgMC0xNi42NyA0LjE2LTM5NS44MyAxMjUtNjEyLjUgNTQ1Ljg0LTQ4Ny41IDk0MS42NyA3NSAyMzMuMzMgMjU0LjE3IDQxMi41IDQ4Ny41IDQ4Ny41IDE2LjY3IDguMzMgMzMuMzQgMCAzNy41LTE2LjY3IDQuMTctNC4xNiA0LjE3LTguMzMgNC4xNy0xNi42NnYtNTguMzRjMC0xMi41LTEyLjUtMjkuMTYtMjUtMzcuNXpNMTIyOS4xNyAyOTUuODNjLTE2LjY3LTguMzMtMzMuMzQgMC0zNy41IDE2LjY3LTQuMTcgNC4xNy00LjE3IDguMzMtNC4xNyAxNi42N3Y1OC4zM2MwIDE2LjY3IDEyLjUgMzMuMzMgMjUgNDEuNjcgMzI1IDExNi42NiA0OTEuNjcgNDc5LjE2IDM3MC44MyA4MDAtNjIuNSAxNzUtMjAwIDMwOC4zMy0zNzAuODMgMzcwLjgzLTE2LjY3IDguMzMtMjUgMjAuODMtMjUgNDEuNjdWMTcwMGMwIDE2LjY3IDguMzMgMjkuMTcgMjUgMzMuMzMgNC4xNyAwIDEyLjUgMCAxNi42Ny00LjE2IDM5NS44My0xMjUgNjEyLjUtNTQ1Ljg0IDQ4Ny41LTk0MS42Ny03NS0yMzcuNS0yNTguMzQtNDE2LjY3LTQ4Ny41LTQ5MS42N3oiIGZpbGw9IiNmZmYiLz4KPC9zdmc+Cg==" /></a>
<a href="#usdc-trc-20" alt="USDC (TRC-20)"><img src="https://img.shields.io/badge/Donate-USDC%20(TRC--20)-2775CA?logo=data:image/svg+xml;base64,PHN2ZyBkYXRhLW5hbWU9Ijg2OTc3Njg0LTEyZGItNDg1MC04ZjMwLTIzM2E3YzI2N2QxMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjAwMCAyMDAwIj4KICA8cGF0aCBkPSJNMTAwMCAyMDAwYzU1NC4xNyAwIDEwMDAtNDQ1LjgzIDEwMDAtMTAwMFMxNTU0LjE3IDAgMTAwMCAwIDAgNDQ1LjgzIDAgMTAwMHM0NDUuODMgMTAwMCAxMDAwIDEwMDB6IiBmaWxsPSIjMjc3NWNhIi8+CiAgPHBhdGggZD0iTTEyNzUgMTE1OC4zM2MwLTE0NS44My04Ny41LTE5NS44My0yNjIuNS0yMTYuNjYtMTI1LTE2LjY3LTE1MC01MC0xNTAtMTA4LjM0czQxLjY3LTk1LjgzIDEyNS05NS44M2M3NSAwIDExNi42NyAyNSAxMzcuNSA4Ny41IDQuMTcgMTIuNSAxNi42NyAyMC44MyAyOS4xNyAyMC44M2g2Ni42NmMxNi42NyAwIDI5LjE3LTEyLjUgMjkuMTctMjkuMTZ2LTQuMTdjLTE2LjY3LTkxLjY3LTkxLjY3LTE2Mi41LTE4Ny41LTE3MC44M3YtMTAwYzAtMTYuNjctMTIuNS0yOS4xNy0zMy4zMy0zMy4zNGgtNjIuNWMtMTYuNjcgMC0yOS4xNyAxMi41LTMzLjM0IDMzLjM0djk1LjgzYy0xMjUgMTYuNjctMjA0LjE2IDEwMC0yMDQuMTYgMjA0LjE3IDAgMTM3LjUgODMuMzMgMTkxLjY2IDI1OC4zMyAyMTIuNSAxMTYuNjcgMjAuODMgMTU0LjE3IDQ1LjgzIDE1NC4xNyAxMTIuNXMtNTguMzQgMTEyLjUtMTM3LjUgMTEyLjVjLTEwOC4zNCAwLTE0NS44NC00NS44NC0xNTguMzQtMTA4LjM0LTQuMTYtMTYuNjYtMTYuNjYtMjUtMjkuMTYtMjVoLTcwLjg0Yy0xNi42NiAwLTI5LjE2IDEyLjUtMjkuMTYgMjkuMTd2NC4xN2MxNi42NiAxMDQuMTYgODMuMzMgMTc5LjE2IDIyMC44MyAyMDB2MTAwYzAgMTYuNjYgMTIuNSAyOS4xNiAzMy4zMyAzMy4zM2g2Mi41YzE2LjY3IDAgMjkuMTctMTIuNSAzMy4zNC0zMy4zM3YtMTAwYzEyNS0yMC44NCAyMDguMzMtMTA4LjM0IDIwOC4zMy0yMjAuODR6IiBmaWxsPSIjZmZmIi8+CiAgPHBhdGggZD0iTTc4Ny41IDE1OTUuODNjLTMyNS0xMTYuNjYtNDkxLjY3LTQ3OS4xNi0zNzAuODMtODAwIDYyLjUtMTc1IDIwMC0zMDguMzMgMzcwLjgzLTM3MC44MyAxNi42Ny04LjMzIDI1LTIwLjgzIDI1LTQxLjY3VjMyNWMwLTE2LjY3LTguMzMtMjkuMTctMjUtMzMuMzMtNC4xNyAwLTEyLjUgMC0xNi42NyA0LjE2LTM5NS44MyAxMjUtNjEyLjUgNTQ1Ljg0LTQ4Ny41IDk0MS42NyA3NSAyMzMuMzMgMjU0LjE3IDQxMi41IDQ4Ny41IDQ4Ny41IDE2LjY3IDguMzMgMzMuMzQgMCAzNy41LTE2LjY3IDQuMTctNC4xNiA0LjE3LTguMzMgNC4xNy0xNi42NnYtNTguMzRjMC0xMi41LTEyLjUtMjkuMTYtMjUtMzcuNXpNMTIyOS4xNyAyOTUuODNjLTE2LjY3LTguMzMtMzMuMzQgMC0zNy41IDE2LjY3LTQuMTcgNC4xNy00LjE3IDguMzMtNC4xNyAxNi42N3Y1OC4zM2MwIDE2LjY3IDEyLjUgMzMuMzMgMjUgNDEuNjcgMzI1IDExNi42NiA0OTEuNjcgNDc5LjE2IDM3MC44MyA4MDAtNjIuNSAxNzUtMjAwIDMwOC4zMy0zNzAuODMgMzcwLjgzLTE2LjY3IDguMzMtMjUgMjAuODMtMjUgNDEuNjdWMTcwMGMwIDE2LjY3IDguMzMgMjkuMTcgMjUgMzMuMzMgNC4xNyAwIDEyLjUgMCAxNi42Ny00LjE2IDM5NS44My0xMjUgNjEyLjUtNTQ1Ljg0IDQ4Ny41LTk0MS42Ny03NS0yMzcuNS0yNTguMzQtNDE2LjY3LTQ4Ny41LTQ5MS42N3oiIGZpbGw9IiNmZmYiLz4KPC9zdmc+Cg==" /></a>
# BTC
Address: `bc1q7q0ckuteu4t7mudeqrq4m6rzgfery4r0xk4rym`
Scan it with your crypto wallet:
![BTC QR code](qrcodes/btc.png)
# ETH
Address: `0xAca71cCdE4F4Ef8f82d437Be05A8EA705A66d5e8`
Scan it with your crypto wallet:
![ETH QR code](qrcodes/eth.png)
# BNB (BSC-20)
Address: `0xAca71cCdE4F4Ef8f82d437Be05A8EA705A66d5e8`
Scan it with your crypto wallet:
![BNB (BSC-20) QR code](qrcodes/bnb_bsc_20.png)
# TRON
Address: `TPUT2uL5fannBLcEq64pfvHftZB2a17Nze`
Scan it with your crypto wallet:
![TRON QR code](qrcodes/tron.png)
# USDT (ERC-20)
Address: `0xAca71cCdE4F4Ef8f82d437Be05A8EA705A66d5e8`
Scan it with your crypto wallet:
![USDT (ERC-20) QR code](qrcodes/usdt_erc_20.png)
# USDT (TRC-20)
Address: `TPUT2uL5fannBLcEq64pfvHftZB2a17Nze`
Scan it with your crypto wallet:
![USDT (TRC-20) QR code](qrcodes/usdt_trc_20.png)
# USDC (ERC-20)
Address: `0xAca71cCdE4F4Ef8f82d437Be05A8EA705A66d5e8`
Scan it with your crypto wallet:
![USDC (ERC-20) QR code](qrcodes/usdc_erc_20.png)
# USDC (TRC-20)
Address: `TPUT2uL5fannBLcEq64pfvHftZB2a17Nze`
Scan it with your crypto wallet:
![USDC (TRC-20) QR code](qrcodes/usdc_trc_20.png)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B