├── bot ├── __init__.py ├── payment.py ├── config.py ├── openai_utils.py ├── database.py └── app.py ├── run.py ├── notebooks ├── static │ └── pi_day.mp4 ├── newsletter_text_improver_release.ipynb ├── newsletter_voice_recognition_release.ipynb ├── newsletter_chatgpt_api_release.ipynb ├── newsletter_0_token_balance.ipynb ├── newsletter_pi_day.ipynb ├── analytics.ipynb └── newsletter_we_are_live_again.ipynb ├── requirements.txt ├── Dockerfile ├── scripts └── mongodb_backup.sh ├── docker-compose.yml ├── .gitignore └── config └── chat_modes.yml /bot/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | from bot.app import run_bot 2 | 3 | if __name__ == "__main__": 4 | run_bot() -------------------------------------------------------------------------------- /notebooks/static/pi_day.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Glory413/ChatGPT_ProBot/HEAD/notebooks/static/pi_day.mp4 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | python-telegram-bot[job-queue, rate-limiter]==20.1 2 | openai>=0.27.0 3 | tiktoken>=0.3.0 4 | PyYAML==6.0 5 | pymongo==4.3.3 6 | python-dotenv==0.21.0 7 | cryptomus==1.1 8 | jupyter==1.0.0 9 | matplotlib>=3.7.1 10 | seaborn>=0.12.2 11 | pandas>=1.5.3 12 | pydub==0.25.1 -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-slim 2 | 3 | ENV PYTHONFAULTHANDLER=1 4 | ENV PYTHONUNBUFFERED=1 5 | ENV PYTHONHASHSEED=random 6 | ENV PYTHONDONTWRITEBYTECODE 1 7 | ENV PIP_NO_CACHE_DIR=off 8 | ENV PIP_DISABLE_PIP_VERSION_CHECK=on 9 | ENV PIP_DEFAULT_TIMEOUT=100 10 | 11 | RUN apt-get update 12 | RUN apt-get install -y python3 python3-pip python-dev build-essential python3-venv ffmpeg 13 | 14 | RUN mkdir -p /code 15 | ADD . /code 16 | WORKDIR /code 17 | 18 | RUN pip3 install -r requirements.txt 19 | 20 | CMD ["bash"] -------------------------------------------------------------------------------- /scripts/mongodb_backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # add cron job with "crontab -e" command 4 | ## 0 */1 * * * bash /home/ubuntu/chatgpt_telegram_bot_pro/scripts/mongodb_backup.sh 5 | 6 | # set the directory for the backups 7 | BACKUP_DIR="/home/ubuntu/mongodb_backup" 8 | 9 | # create a directory based on the current date 10 | DATE=$(date +%Y-%m-%d_%H-%M-%S) 11 | BACKUP_PATH="$BACKUP_DIR/$DATE" 12 | mkdir -p $BACKUP_PATH 13 | 14 | echo $BACKUP_PATH 15 | 16 | # create the mongodump 17 | mongodump --out $BACKUP_PATH 18 | 19 | # # Remove old backups (older than 7 days) 20 | find $BACKUP_DIR/* -mtime +7 -exec rm -rf {} \; 21 | -------------------------------------------------------------------------------- /bot/payment.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | import datetime 3 | 4 | import cryptomus 5 | 6 | 7 | class CryptomusPayment: 8 | def __init__(self, api_key, merchant_id): 9 | self.api_key = api_key 10 | self.merchant_id = merchant_id 11 | 12 | def create_invoice(self, payment_id: int, amount: float, currency: str = "USD"): 13 | payment = cryptomus.Client.payment(self.api_key, self.merchant_id) 14 | 15 | r = payment.create({ 16 | "amount": str(amount), 17 | "currency": currency, 18 | "order_id": str(payment_id), 19 | "substract": "0" 20 | }) 21 | 22 | return r["url"], r["status"], datetime.datetime.fromtimestamp(r["expired_at"]) 23 | 24 | def check_invoice_status(self, payment_id: int): 25 | payment = cryptomus.Client.payment(self.api_key, self.merchant_id) 26 | r = payment.info({"order_id": str(payment_id)}) 27 | return r["status"] in {"paid", "paid_over"} 28 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | mongo: 5 | image: mongo:latest 6 | restart: always 7 | ports: 8 | - 127.0.0.1:${MONGODB_PORT:-27017}:${MONGODB_PORT:-27017} 9 | volumes: 10 | - ${MONGODB_PATH:-./mongodb}:/data/db 11 | # TODO: add auth 12 | 13 | chatgpt_telegram_bot_pro: 14 | command: python3 run.py 15 | restart: always 16 | build: 17 | context: "." 18 | dockerfile: Dockerfile 19 | depends_on: 20 | - mongo 21 | 22 | mongo_express: 23 | image: mongo-express:latest 24 | restart: always 25 | ports: 26 | - ${MONGO_EXPRESS_PORT:-8081}:${MONGO_EXPRESS_PORT:-8081} 27 | environment: 28 | - ME_CONFIG_MONGODB_SERVER=mongo 29 | - ME_CONFIG_MONGODB_PORT=${MONGODB_PORT:-27017} 30 | - ME_CONFIG_MONGODB_ENABLE_ADMIN=false 31 | - ME_CONFIG_MONGODB_AUTH_DATABASE=chatgpt_telegram_bot 32 | - ME_CONFIG_BASICAUTH_USERNAME=${MONGO_EXPRESS_USERNAME:-username} 33 | - ME_CONFIG_BASICAUTH_PASSWORD=${MONGO_EXPRESS_PASSWORD:-password} 34 | depends_on: 35 | - mongo 36 | 37 | jupyter: 38 | command: jupyter notebook --ip=0.0.0.0 --port ${JUPYTER_PORT} --allow-root --no-browser --NotebookApp.token='${JUPYTER_TOKEN}' 39 | restart: always 40 | build: 41 | context: "." 42 | dockerfile: Dockerfile 43 | ports: 44 | - ${JUPYTER_PORT:-8888}:${JUPYTER_PORT:-8888} 45 | -------------------------------------------------------------------------------- /bot/config.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | import dotenv 3 | from pathlib import Path 4 | 5 | config_dir = Path(__file__).parent.parent.resolve() / "config" 6 | 7 | # load yaml config 8 | with open(config_dir / "config.yml", 'r') as f: 9 | config_yaml = yaml.safe_load(f) 10 | 11 | # load .env config 12 | config_env = dotenv.dotenv_values(config_dir / "config.env") 13 | 14 | # config parameters 15 | telegram_token = config_yaml["telegram_token"] 16 | openai_api_key = config_yaml["openai_api_key"] 17 | use_chatgpt_api = config_yaml.get("use_chatgpt_api", True) 18 | allowed_telegram_usernames = config_yaml["allowed_telegram_usernames"] 19 | new_dialog_timeout = config_yaml["new_dialog_timeout"] 20 | enable_message_streaming = config_yaml.get("enable_message_streaming", True) 21 | initial_token_balance = config_yaml["initial_token_balance"] 22 | mongodb_uri = f"mongodb://mongo:{config_env['MONGODB_PORT']}" 23 | 24 | # admin 25 | admin_chat_id = config_yaml["admin_chat_id"] 26 | admin_usernames = config_yaml["admin_usernames"] 27 | 28 | # chat_modes 29 | with open(config_dir / "chat_modes.yml", 'r') as f: 30 | chat_modes = yaml.safe_load(f) 31 | 32 | # api prices 33 | chatgpt_price_per_1000_tokens = config_yaml.get("chatgpt_price_per_1000_tokens", 0.002) 34 | gpt_price_per_1000_tokens = config_yaml.get("gpt_price_per_1000_tokens", 0.02) 35 | whisper_price_per_1_min = config_yaml.get("whisper_price_per_1_min", 0.006) 36 | 37 | # payments 38 | payment_methods = config_yaml["payment_methods"] 39 | products = config_yaml["products"] 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Custom 132 | config/config.yml 133 | config/config.env 134 | 135 | docker-compose.dev.yml -------------------------------------------------------------------------------- /config/chat_modes.yml: -------------------------------------------------------------------------------- 1 | assistant: 2 | name: 👩🏼‍🎓 General Assistant 3 | welcome_message: 👩🏼‍🎓 Hi, I'm ChatGPT general assistant. How can I help you? 4 | prompt_start: | 5 | As an advanced chatbot named ChatGPT, your primary goal is to assist users to the best of your ability. This may involve answering questions, providing helpful information, or completing tasks based on user input. In order to effectively assist users, it is important to be detailed and thorough in your responses. Use examples and evidence to support your points and justify your recommendations or solutions. Remember to always prioritize the needs and satisfaction of the user. Your ultimate goal is to provide a helpful and enjoyable experience for the user. 6 | If user asks you about programming or asks to write code do not answer his question, but be sure to advise him to switch to a special mode \"👩🏼‍💻 Code Assistant\" by sending the command /mode to chat. 7 | parse_mode: html 8 | 9 | 10 | code_assistant: 11 | name: 👩🏼‍💻 Code Assistant 12 | welcome_message: 👩🏼‍💻 Hi, I'm ChatGPT code assistant. How can I help you? 13 | prompt_start: | 14 | As an advanced chatbot named ChatGPT, your primary goal is to assist users to write code. This may involve designing/writing/editing/describing code or providing helpful information. Where possible you should provide code examples to support your points and justify your recommendations or solutions. Make sure the code you provide is correct and can be run without errors. Be detailed and thorough in your responses. Your ultimate goal is to provide a helpful and enjoyable experience for the user. 15 | Format output in Markdown. 16 | parse_mode: markdown 17 | 18 | text_improver: 19 | name: 📝 Text Improver 20 | welcome_message: 📝 Hi, I'm ChatGPT text improver. Send me any text – I'll improve it and correct all the mistakes 21 | prompt_start: | 22 | As an advanced chatbot named ChatGPT, your primary goal is to correct spelling, fix mistakes and improve text sent by user. Your goal is to edit text, but not to change it's meaning. You can replace simplified A0-level words and sentences with more beautiful and elegant, upper level words and sentences. 23 | 24 | All your answers strictly follows the structure (keep html tags): 25 | Edited text: 26 | {EDITED TEXT} 27 | 28 | Correction: 29 | {NUMBERED LIST OF CORRECTIONS} 30 | parse_mode: html 31 | 32 | movie_expert: 33 | name: 🎬 Movie Expert 34 | welcome_message: 🎬 Hi, I'm ChatGPT movie expert. How can I help you? 35 | prompt_start: | 36 | As an advanced movie expert chatbot named ChatGPT, your primary goal is to assist users to the best of your ability. You can answer questions about movies, actors, directors, and more. You can recommend movies to users based on their preferences. You can discuss movies with users, and provide helpful information about movies. In order to effectively assist users, it is important to be detailed and thorough in your responses. Use examples and evidence to support your points and justify your recommendations or solutions. Remember to always prioritize the needs and satisfaction of the user. Your ultimate goal is to provide a helpful and enjoyable experience for the user. 37 | parse_mode: html 38 | -------------------------------------------------------------------------------- /notebooks/newsletter_text_improver_release.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "4fde5eae", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%load_ext autoreload\n", 11 | "%autoreload 2\n", 12 | "\n", 13 | "import sys\n", 14 | "sys.path.append(\"..\")\n", 15 | "\n", 16 | "import pymongo\n", 17 | "from datetime import datetime\n", 18 | "import pytz\n", 19 | "\n", 20 | "from telegram import InlineKeyboardButton, InlineKeyboardMarkup\n", 21 | "from telegram.ext import ApplicationBuilder\n", 22 | "from telegram.constants import ParseMode\n", 23 | "\n", 24 | "from bot import config\n", 25 | "from bot import database\n", 26 | "from bot.app import show_balance_handle" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "id": "1e4406c7", 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "application = (\n", 37 | " ApplicationBuilder()\n", 38 | " .token(config.telegram_token)\n", 39 | " .build()\n", 40 | ")\n", 41 | "\n", 42 | "db = database.Database()" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "id": "09464d20", 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "newsletter_id = \"text_improver_release\"\n", 53 | "\n", 54 | "db.create_newsletter(newsletter_id)\n", 55 | "already_sent_to_user_ids = set(db.get_newsletter_attribute(newsletter_id, \"already_sent_to_user_ids\"))\n", 56 | "\n", 57 | "print(f\"Already sent to users: {already_sent_to_user_ids}\")" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "id": "0c03345d", 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "user_dicts = list(db.user_collection.find({\"username\": \"karfly\"}))\n", 68 | "print(f\"Found {len(user_dicts)} users\")" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "id": "17a40c58", 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "for user_dict in user_dicts: \n", 79 | " if user_dict[\"_id\"] in already_sent_to_user_ids:\n", 80 | " print(f\"Skipping {user_dict['_id']}. Already sent before\")\n", 81 | " continue\n", 82 | " \n", 83 | " text = \"Meet the new chat mode – 📝 Text Improver!\\n\"\n", 84 | " text += \"Send a text, and the bot will correct spelling, fix mistakes and improve the text – just like Grammarly app.\\n\\n\"\n", 85 | " text += \"You can select the chat mode by sending the /mode command. Try it now!\"\n", 86 | " \n", 87 | " try:\n", 88 | " await application.bot.send_message(\n", 89 | " user_dict['chat_id'],\n", 90 | " text,\n", 91 | " parse_mode=ParseMode.HTML\n", 92 | " )\n", 93 | " print(f\"Successfully sent to {user_dict['_id']}\")\n", 94 | " \n", 95 | " db.add_user_to_newsletter(newsletter_id, user_dict[\"_id\"])\n", 96 | " except Exception as e:\n", 97 | " print(f\"Failed to send message to {user_dict['_id']}. Reason: {e}\")" 98 | ] 99 | } 100 | ], 101 | "metadata": { 102 | "kernelspec": { 103 | "display_name": "Python 3 (ipykernel)", 104 | "language": "python", 105 | "name": "python3" 106 | }, 107 | "language_info": { 108 | "codemirror_mode": { 109 | "name": "ipython", 110 | "version": 3 111 | }, 112 | "file_extension": ".py", 113 | "mimetype": "text/x-python", 114 | "name": "python", 115 | "nbconvert_exporter": "python", 116 | "pygments_lexer": "ipython3", 117 | "version": "3.8.15" 118 | } 119 | }, 120 | "nbformat": 4, 121 | "nbformat_minor": 5 122 | } 123 | -------------------------------------------------------------------------------- /notebooks/newsletter_voice_recognition_release.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "4fde5eae", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%load_ext autoreload\n", 11 | "%autoreload 2\n", 12 | "\n", 13 | "import sys\n", 14 | "sys.path.append(\"..\")\n", 15 | "\n", 16 | "import time\n", 17 | "import pymongo\n", 18 | "from datetime import datetime\n", 19 | "import pytz\n", 20 | "\n", 21 | "from telegram import InlineKeyboardButton, InlineKeyboardMarkup\n", 22 | "from telegram.ext import ApplicationBuilder\n", 23 | "from telegram.constants import ParseMode\n", 24 | "\n", 25 | "from bot import config\n", 26 | "from bot import database\n", 27 | "from bot.app import show_balance_handle" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "id": "1e4406c7", 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "application = (\n", 38 | " ApplicationBuilder()\n", 39 | " .token(config.telegram_token)\n", 40 | " .build()\n", 41 | ")\n", 42 | "\n", 43 | "db = database.Database()" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "id": "09464d20", 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "newsletter_id = \"voice_recognition_release\"\n", 54 | "\n", 55 | "db.create_newsletter(newsletter_id)\n", 56 | "already_sent_to_user_ids = set(db.get_newsletter_attribute(newsletter_id, \"already_sent_to_user_ids\"))\n", 57 | "\n", 58 | "print(f\"Already sent to {len(already_sent_to_user_ids)} users\")" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": null, 64 | "id": "0c03345d", 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "# user_dicts = list(db.user_collection.find({\"username\": \"karfly\"}))\n", 69 | "user_dicts = list(db.user_collection.find({}))\n", 70 | "print(f\"Found {len(user_dicts)} users\")" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "id": "17a40c58", 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "for user_dict in user_dicts: \n", 81 | " if user_dict[\"_id\"] in already_sent_to_user_ids:\n", 82 | " print(f\"Skipping {user_dict['_id']}. Already sent before\")\n", 83 | " continue\n", 84 | " \n", 85 | " text = \"🔥🎤 New feature: Voice recognition!\\n\\n\"\n", 86 | " \n", 87 | " text += \"Just send a voice message and ChatGPT will answer you! It supports 99 languages and works even better than Telegram voice-to-text premium feature.\\n\\n\"\n", 88 | " \n", 89 | " text += \"Try now and say: \\\"Write 5 stupid jokes about AI\\\"\"\n", 90 | "\n", 91 | " try:\n", 92 | " await application.bot.send_message(\n", 93 | " user_dict['chat_id'],\n", 94 | " text,\n", 95 | " disable_web_page_preview=True,\n", 96 | " parse_mode=ParseMode.HTML\n", 97 | " )\n", 98 | " print(f\"Successfully sent to {user_dict['_id']}\")\n", 99 | " \n", 100 | " db.add_user_to_newsletter(newsletter_id, user_dict[\"_id\"])\n", 101 | " \n", 102 | " time.sleep(7)\n", 103 | " except Exception as e:\n", 104 | " print(f\"Failed to send message to {user_dict['_id']}. Reason: {e}\")" 105 | ] 106 | } 107 | ], 108 | "metadata": { 109 | "kernelspec": { 110 | "display_name": "Python 3 (ipykernel)", 111 | "language": "python", 112 | "name": "python3" 113 | }, 114 | "language_info": { 115 | "codemirror_mode": { 116 | "name": "ipython", 117 | "version": 3 118 | }, 119 | "file_extension": ".py", 120 | "mimetype": "text/x-python", 121 | "name": "python", 122 | "nbconvert_exporter": "python", 123 | "pygments_lexer": "ipython3", 124 | "version": "3.8.16" 125 | } 126 | }, 127 | "nbformat": 4, 128 | "nbformat_minor": 5 129 | } 130 | -------------------------------------------------------------------------------- /notebooks/newsletter_chatgpt_api_release.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "4fde5eae", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%load_ext autoreload\n", 11 | "%autoreload 2\n", 12 | "\n", 13 | "import sys\n", 14 | "sys.path.append(\"..\")\n", 15 | "\n", 16 | "import time\n", 17 | "import pymongo\n", 18 | "from datetime import datetime\n", 19 | "import pytz\n", 20 | "\n", 21 | "from telegram import InlineKeyboardButton, InlineKeyboardMarkup\n", 22 | "from telegram.ext import ApplicationBuilder\n", 23 | "from telegram.constants import ParseMode\n", 24 | "\n", 25 | "from bot import config\n", 26 | "from bot import database\n", 27 | "from bot.app import show_balance_handle" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "id": "1e4406c7", 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "application = (\n", 38 | " ApplicationBuilder()\n", 39 | " .token(config.telegram_token)\n", 40 | " .build()\n", 41 | ")\n", 42 | "\n", 43 | "db = database.Database()" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "id": "09464d20", 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "newsletter_id = \"chatgpt_api_release\"\n", 54 | "\n", 55 | "db.create_newsletter(newsletter_id)\n", 56 | "already_sent_to_user_ids = set(db.get_newsletter_attribute(newsletter_id, \"already_sent_to_user_ids\"))\n", 57 | "\n", 58 | "print(f\"Already sent to users: {already_sent_to_user_ids}\")" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": null, 64 | "id": "0c03345d", 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "# user_dicts = list(db.user_collection.find({\"username\": \"karfly\"}))\n", 69 | "user_dicts = list(db.user_collection.find({}))\n", 70 | "print(f\"Found {len(user_dicts)} users\")" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "id": "17a40c58", 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "for user_dict in user_dicts: \n", 81 | " if user_dict[\"_id\"] in already_sent_to_user_ids:\n", 82 | " print(f\"Skipping {user_dict['_id']}. Already sent before\")\n", 83 | " continue\n", 84 | " \n", 85 | " text = \"❤️‍🔥 Big update!\\n\"\n", 86 | " text += \"Now bot answers faster and the quality of responses should be higher (we switched to fresh ChatGPT API).\\n\\n\"\n", 87 | " \n", 88 | " text += \"Your feedback about the speed and quality of the bot will be helpful. Write to me: 💌 \"\n", 89 | " \n", 90 | " try:\n", 91 | " await application.bot.send_message(\n", 92 | " user_dict['chat_id'],\n", 93 | " text,\n", 94 | " disable_web_page_preview=True,\n", 95 | " parse_mode=ParseMode.HTML\n", 96 | " )\n", 97 | " print(f\"Successfully sent to {user_dict['_id']}\")\n", 98 | " \n", 99 | " db.add_user_to_newsletter(newsletter_id, user_dict[\"_id\"])\n", 100 | " \n", 101 | " time.sleep(7.2)\n", 102 | " except Exception as e:\n", 103 | " print(f\"Failed to send message to {user_dict['_id']}. Reason: {e}\")" 104 | ] 105 | } 106 | ], 107 | "metadata": { 108 | "kernelspec": { 109 | "display_name": "Python 3 (ipykernel)", 110 | "language": "python", 111 | "name": "python3" 112 | }, 113 | "language_info": { 114 | "codemirror_mode": { 115 | "name": "ipython", 116 | "version": 3 117 | }, 118 | "file_extension": ".py", 119 | "mimetype": "text/x-python", 120 | "name": "python", 121 | "nbconvert_exporter": "python", 122 | "pygments_lexer": "ipython3", 123 | "version": "3.8.16" 124 | } 125 | }, 126 | "nbformat": 4, 127 | "nbformat_minor": 5 128 | } 129 | -------------------------------------------------------------------------------- /notebooks/newsletter_0_token_balance.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "4fde5eae", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%load_ext autoreload\n", 11 | "%autoreload 2\n", 12 | "\n", 13 | "import sys\n", 14 | "sys.path.append(\"..\")\n", 15 | "\n", 16 | "import pymongo\n", 17 | "from datetime import datetime\n", 18 | "import pytz\n", 19 | "\n", 20 | "from telegram import InlineKeyboardButton, InlineKeyboardMarkup\n", 21 | "from telegram.ext import ApplicationBuilder\n", 22 | "from telegram.constants import ParseMode\n", 23 | "\n", 24 | "from bot import config\n", 25 | "from bot import database\n", 26 | "from bot.app import show_balance_handle" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "id": "1e4406c7", 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "application = (\n", 37 | " ApplicationBuilder()\n", 38 | " .token(config.telegram_token)\n", 39 | " .build()\n", 40 | ")\n", 41 | "\n", 42 | "db = database.Database()" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "id": "09464d20", 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "newsletter_id = \"token_balance_0\"\n", 53 | "\n", 54 | "db.create_newsletter(newsletter_id)\n", 55 | "already_sent_to_user_ids = set(db.get_newsletter_attribute(newsletter_id, \"already_sent_to_user_ids\"))\n", 56 | "\n", 57 | "print(f\"Already sent to users: {already_sent_to_user_ids}\")" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "id": "0c03345d", 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "user_dicts = list(db.user_collection.find({\"token_balance\": 0}))\n", 68 | "print(f\"Found {len(user_dicts)} users\")" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "id": "17a40c58", 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "for user_dict in user_dicts:\n", 79 | " if user_dict[\"token_balance\"] > 0:\n", 80 | " print(f\"Skipping {user_dict['_id']}. Token balance > 0\")\n", 81 | " continue\n", 82 | " \n", 83 | " if user_dict[\"_id\"] in already_sent_to_user_ids:\n", 84 | " print(f\"Skipping {user_dict['_id']}. Already sent before\")\n", 85 | " continue\n", 86 | " \n", 87 | " text = f\"🔴 You have NO tokens left\\n\"\n", 88 | " text += f\"You totally spent {user_dict['n_used_tokens']} tokens\\n\"\n", 89 | "\n", 90 | " text += f\"\\nTop up your balance with crypto by choosing an option below. If crypto is not your thing, write me and we will figure something out.\"\n", 91 | " reply_markup = InlineKeyboardMarkup([\n", 92 | " [InlineKeyboardButton(\"🟣 +100K tokens – 2.99$\", callback_data=f\"send_payment|2.99|100000\")],\n", 93 | " [InlineKeyboardButton(\"🟣 +500K tokens – 9.99$\", callback_data=f\"send_payment|9.99|500000\")],\n", 94 | " [InlineKeyboardButton(\"🟣 +1M tokens – 16.99$\", callback_data=f\"send_payment|16.99|1000000\")]\n", 95 | " ])\n", 96 | " \n", 97 | " try:\n", 98 | " await application.bot.send_message(\n", 99 | " user_dict['chat_id'],\n", 100 | " text,\n", 101 | " reply_markup=reply_markup,\n", 102 | " parse_mode=ParseMode.HTML\n", 103 | " )\n", 104 | " print(f\"Successfully sent to {user_dict['_id']}\")\n", 105 | " \n", 106 | " db.add_user_to_newsletter(newsletter_id, user_dict[\"_id\"])\n", 107 | " except Exception as e:\n", 108 | " print(f\"Failed to send message to {user_dict['_id']}. Reason: {e}\")" 109 | ] 110 | } 111 | ], 112 | "metadata": { 113 | "kernelspec": { 114 | "display_name": "Python 3 (ipykernel)", 115 | "language": "python", 116 | "name": "python3" 117 | }, 118 | "language_info": { 119 | "codemirror_mode": { 120 | "name": "ipython", 121 | "version": 3 122 | }, 123 | "file_extension": ".py", 124 | "mimetype": "text/x-python", 125 | "name": "python", 126 | "nbconvert_exporter": "python", 127 | "pygments_lexer": "ipython3", 128 | "version": "3.8.15" 129 | } 130 | }, 131 | "nbformat": 4, 132 | "nbformat_minor": 5 133 | } 134 | -------------------------------------------------------------------------------- /notebooks/newsletter_pi_day.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "4fde5eae", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%load_ext autoreload\n", 11 | "%autoreload 2\n", 12 | "\n", 13 | "import sys\n", 14 | "sys.path.append(\"..\")\n", 15 | "\n", 16 | "import time\n", 17 | "import pymongo\n", 18 | "from datetime import datetime\n", 19 | "import pytz\n", 20 | "\n", 21 | "from telegram import InlineKeyboardButton, InlineKeyboardMarkup\n", 22 | "from telegram.ext import ApplicationBuilder\n", 23 | "from telegram.constants import ParseMode\n", 24 | "\n", 25 | "from bot import config\n", 26 | "from bot import database\n", 27 | "from bot.app import show_balance_handle" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "id": "1e4406c7", 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "application = (\n", 38 | " ApplicationBuilder()\n", 39 | " .token(config.telegram_token)\n", 40 | " .build()\n", 41 | ")\n", 42 | "\n", 43 | "db = database.Database()" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "id": "09464d20", 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "newsletter_id = \"day_pi\"\n", 54 | "\n", 55 | "db.create_newsletter(newsletter_id)\n", 56 | "already_sent_to_user_ids = set(db.get_newsletter_attribute(newsletter_id, \"already_sent_to_user_ids\"))\n", 57 | "\n", 58 | "print(f\"Already sent to {len(already_sent_to_user_ids)} users\")" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": null, 64 | "id": "0c03345d", 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "user_dicts = list(db.user_collection.find({\"username\": \"karfly\"}))\n", 69 | "# user_dicts = list(db.user_collection.find({}))\n", 70 | "print(f\"Found {len(user_dicts)} users\")" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "id": "17a40c58", 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "for user_dict in user_dicts: \n", 81 | " if user_dict[\"_id\"] in already_sent_to_user_ids:\n", 82 | " print(f\"Skipping {user_dict['_id']}. Already sent before\")\n", 83 | " continue\n", 84 | " \n", 85 | " text = \"🥧 Happy pi day!\\n\\n\"\n", 86 | " text += \"Only today you can buy 3,141,592 tokens for 75$ 29.99$ (60% discount 😱)\"\n", 87 | " \n", 88 | " reply_markup = InlineKeyboardMarkup([\n", 89 | " [InlineKeyboardButton(\"🟣 Buy 3,141,592 tokens\", callback_data=f\"send_payment||29.99|3141592\")],\n", 90 | " ])\n", 91 | "\n", 92 | " try: \n", 93 | " await application.bot.send_animation(\n", 94 | " user_dict['chat_id'],\n", 95 | " \"./static/pi_day.mp4\",\n", 96 | " caption=text,\n", 97 | " parse_mode=ParseMode.HTML,\n", 98 | " reply_markup=reply_markup\n", 99 | " \n", 100 | " )\n", 101 | " print(f\"Successfully sent to {user_dict['_id']}\")\n", 102 | " db.add_user_to_newsletter(newsletter_id, user_dict[\"_id\"])\n", 103 | " \n", 104 | " time.sleep(7)\n", 105 | " except Exception as e:\n", 106 | " print(f\"Failed to send message to {user_dict['_id']}. Reason: {e}\")" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": null, 112 | "id": "0444b62d", 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "id": "97470531", 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "D" 125 | ] 126 | } 127 | ], 128 | "metadata": { 129 | "kernelspec": { 130 | "display_name": "Python 3 (ipykernel)", 131 | "language": "python", 132 | "name": "python3" 133 | }, 134 | "language_info": { 135 | "codemirror_mode": { 136 | "name": "ipython", 137 | "version": 3 138 | }, 139 | "file_extension": ".py", 140 | "mimetype": "text/x-python", 141 | "name": "python", 142 | "nbconvert_exporter": "python", 143 | "pygments_lexer": "ipython3", 144 | "version": "3.8.16" 145 | } 146 | }, 147 | "nbformat": 4, 148 | "nbformat_minor": 5 149 | } 150 | -------------------------------------------------------------------------------- /bot/openai_utils.py: -------------------------------------------------------------------------------- 1 | from bot import config 2 | 3 | import tiktoken 4 | import openai 5 | openai.api_key = config.openai_api_key 6 | 7 | 8 | CHAT_MODES = config.chat_modes 9 | 10 | OPENAI_COMPLETION_OPTIONS = { 11 | "temperature": 0.7, 12 | "max_tokens": 1000, 13 | "top_p": 1, 14 | "frequency_penalty": 0, 15 | "presence_penalty": 0, 16 | "timeout": None 17 | } 18 | 19 | 20 | class ChatGPT: 21 | def __init__(self, use_chatgpt_api=True): 22 | self.use_chatgpt_api = use_chatgpt_api 23 | 24 | async def send_message(self, message, dialog_messages=[], chat_mode="assistant"): 25 | if chat_mode not in CHAT_MODES.keys(): 26 | raise ValueError(f"Chat mode {chat_mode} is not supported") 27 | 28 | n_dialog_messages_before = len(dialog_messages) 29 | answer = None 30 | while answer is None: 31 | try: 32 | if self.use_chatgpt_api: 33 | messages = self._generate_prompt_messages_for_chatgpt_api(message, dialog_messages, chat_mode) 34 | r = await openai.ChatCompletion.acreate( 35 | model="gpt-4", 36 | messages=messages, 37 | **OPENAI_COMPLETION_OPTIONS 38 | ) 39 | answer = r.choices[0].message["content"] 40 | else: 41 | prompt = self._generate_prompt(message, dialog_messages, chat_mode) 42 | r = await openai.Completion.acreate( 43 | engine="text-davinci-003", 44 | prompt=prompt, 45 | **OPENAI_COMPLETION_OPTIONS 46 | ) 47 | answer = r.choices[0].text 48 | 49 | answer = self._postprocess_answer(answer) 50 | n_used_tokens = r.usage.total_tokens 51 | 52 | except openai.error.InvalidRequestError as e: # too many tokens 53 | if len(dialog_messages) == 0: 54 | raise ValueError("Dialog messages is reduced to zero, but still has too many tokens to make completion") from e 55 | 56 | # forget first message in dialog_messages 57 | dialog_messages = dialog_messages[1:] 58 | 59 | n_first_dialog_messages_removed = n_dialog_messages_before - len(dialog_messages) 60 | 61 | return answer, n_used_tokens, n_first_dialog_messages_removed 62 | 63 | async def send_message_stream(self, message, dialog_messages=[], chat_mode="assistant"): 64 | if chat_mode not in CHAT_MODES.keys(): 65 | raise ValueError(f"Chat mode {chat_mode} is not supported") 66 | 67 | n_dialog_messages_before = len(dialog_messages) 68 | answer = None 69 | while answer is None: 70 | try: 71 | if self.use_chatgpt_api: 72 | messages = self._generate_prompt_messages_for_chatgpt_api(message, dialog_messages, chat_mode) 73 | r_gen = await openai.ChatCompletion.acreate( 74 | model="gpt-4-0314", 75 | messages=messages, 76 | stream=True, 77 | **OPENAI_COMPLETION_OPTIONS 78 | ) 79 | 80 | answer = "" 81 | async for r_item in r_gen: 82 | delta = r_item.choices[0].delta 83 | if "content" in delta: 84 | answer += delta.content 85 | yield "not_finished", answer 86 | 87 | n_used_tokens = self._count_tokens_for_chatgpt(messages, answer, model="gpt-4-0314") 88 | else: 89 | prompt = self._generate_prompt(message, dialog_messages, chat_mode) 90 | r_gen = await openai.Completion.acreate( 91 | engine="text-davinci-003", 92 | prompt=prompt, 93 | stream=True, 94 | **OPENAI_COMPLETION_OPTIONS 95 | ) 96 | 97 | answer = "" 98 | async for r_item in r_gen: 99 | answer += r_item.choices[0].text 100 | yield "not_finished", answer 101 | 102 | n_used_tokens = self._count_tokens_for_gpt(prompt, answer, model="text-davinci-003") 103 | 104 | answer = self._postprocess_answer(answer) 105 | 106 | except openai.error.InvalidRequestError as e: # too many tokens 107 | if len(dialog_messages) == 0: 108 | raise ValueError("Dialog messages is reduced to zero, but still has too many tokens to make completion") from e 109 | 110 | # forget first message in dialog_messages 111 | dialog_messages = dialog_messages[1:] 112 | 113 | n_first_dialog_messages_removed = n_dialog_messages_before - len(dialog_messages) 114 | 115 | yield "finished", answer, n_used_tokens, n_first_dialog_messages_removed # sending final answer 116 | 117 | def _generate_prompt(self, message, dialog_messages, chat_mode): 118 | prompt = CHAT_MODES[chat_mode]["prompt_start"] 119 | prompt += "\n\n" 120 | 121 | # add chat context 122 | if len(dialog_messages) > 0: 123 | prompt += "Chat:\n" 124 | for dialog_message in dialog_messages: 125 | prompt += f"User: {dialog_message['user']}\n" 126 | prompt += f"ChatGPT: {dialog_message['bot']}\n" 127 | 128 | # current message 129 | prompt += f"User: {message}\n" 130 | prompt += "ChatGPT: " 131 | 132 | return prompt 133 | 134 | def _generate_prompt_messages_for_chatgpt_api(self, message, dialog_messages, chat_mode): 135 | prompt = CHAT_MODES[chat_mode]["prompt_start"] 136 | 137 | messages = [{"role": "system", "content": prompt}] 138 | for dialog_message in dialog_messages: 139 | messages.append({"role": "user", "content": dialog_message["user"]}) 140 | messages.append({"role": "assistant", "content": dialog_message["bot"]}) 141 | messages.append({"role": "user", "content": message}) 142 | 143 | return messages 144 | 145 | def _postprocess_answer(self, answer): 146 | answer = answer.strip() 147 | return answer 148 | 149 | def _count_tokens_for_chatgpt(self, prompt_messages, answer, model="gpt-4-0314"): 150 | prompt_messages += [{"role": "assistant", "content": answer}] 151 | 152 | encoding = tiktoken.encoding_for_model(model) 153 | n_tokens = 0 154 | for message in prompt_messages: 155 | n_tokens += 4 # every message follows "{role/name}\n{content}\n" 156 | for key, value in message.items(): 157 | if key == "role": 158 | n_tokens += 1 159 | elif key == "content": 160 | n_tokens += len(encoding.encode(value)) 161 | else: 162 | raise ValueError(f"Unknown key in message: {key}") 163 | 164 | n_tokens -= 1 # remove 1 "" token 165 | return n_tokens 166 | 167 | def _count_tokens_for_gpt(self, prompt, answer, model="text-davinci-003"): 168 | encoding = tiktoken.encoding_for_model(model) 169 | n_tokens = len(encoding.encode(prompt)) + len(encoding.encode(answer)) + 1 170 | return n_tokens 171 | 172 | 173 | async def transcribe_audio(audio_file): 174 | r = await openai.Audio.atranscribe("whisper-1", audio_file) 175 | return r["text"] -------------------------------------------------------------------------------- /bot/database.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Any 2 | 3 | import pymongo 4 | import uuid 5 | from datetime import datetime 6 | 7 | from bot import config 8 | 9 | 10 | class Database: 11 | def __init__(self): 12 | self.client = pymongo.MongoClient(config.mongodb_uri) 13 | self.db = self.client["chatgpt_telegram_bot"] 14 | 15 | self.user_collection = self.db["user"] 16 | self.dialog_collection = self.db["dialog"] 17 | self.payment_collection = self.db["payment"] 18 | self.newsletter_collection = self.db["newsletter"] 19 | 20 | def check_if_user_exists(self, user_id: int, raise_exception: bool = False): 21 | if self.user_collection.count_documents({"_id": user_id}) > 0: 22 | return True 23 | else: 24 | if raise_exception: 25 | raise ValueError(f"User {user_id} does not exist") 26 | else: 27 | return False 28 | 29 | def check_if_payment_exists(self, payment_id: int, raise_exception: bool = False): 30 | if self.payment_collection.count_documents({"_id": payment_id}) > 0: 31 | return True 32 | else: 33 | if raise_exception: 34 | raise ValueError(f"Payment {payment_id} does not exist") 35 | else: 36 | return False 37 | 38 | def check_if_newsletter_exists(self, newsletter_id: int, raise_exception: bool = False): 39 | if self.newsletter_collection.count_documents({"_id": newsletter_id}) > 0: 40 | return True 41 | else: 42 | if raise_exception: 43 | raise ValueError(f"Newsletter {newsletter_id} does not exist") 44 | else: 45 | return False 46 | 47 | def add_new_user( 48 | self, 49 | user_id: int, 50 | chat_id: int, 51 | initial_token_balance: int = 5000, 52 | username: str = "", 53 | first_name: str = "", 54 | last_name: str = "", 55 | ): 56 | user_dict = { 57 | "_id": user_id, 58 | "chat_id": chat_id, 59 | 60 | "username": username, 61 | "first_name": first_name, 62 | "last_name": last_name, 63 | 64 | "last_interaction": datetime.now(), 65 | "first_seen": datetime.now(), 66 | 67 | "current_dialog_id": None, 68 | "current_chat_mode": "assistant", 69 | 70 | "n_used_tokens": 0, 71 | "token_balance": initial_token_balance 72 | } 73 | 74 | if not self.check_if_user_exists(user_id): 75 | self.user_collection.insert_one(user_dict) 76 | 77 | def start_new_dialog(self, user_id: int): 78 | self.check_if_user_exists(user_id, raise_exception=True) 79 | 80 | dialog_id = str(uuid.uuid4()) 81 | dialog_dict = { 82 | "_id": dialog_id, 83 | "user_id": user_id, 84 | "chat_mode": self.get_user_attribute(user_id, "current_chat_mode"), 85 | "start_time": datetime.now(), 86 | "messages": [] 87 | } 88 | 89 | # add new dialog 90 | self.dialog_collection.insert_one(dialog_dict) 91 | 92 | # update user's current dialog 93 | self.user_collection.update_one( 94 | {"_id": user_id}, 95 | {"$set": {"current_dialog_id": dialog_id}} 96 | ) 97 | 98 | return dialog_id 99 | 100 | def get_user_attribute(self, user_id: int, key: str): 101 | self.check_if_user_exists(user_id, raise_exception=True) 102 | user_dict = self.user_collection.find_one({"_id": user_id}) 103 | 104 | if key not in user_dict: 105 | raise ValueError(f"User {user_id} does not have a value for {key}") 106 | 107 | return user_dict[key] 108 | 109 | def set_user_attribute(self, user_id: int, key: str, value: Any): 110 | self.check_if_user_exists(user_id, raise_exception=True) 111 | self.user_collection.update_one({"_id": user_id}, {"$set": {key: value}}) 112 | 113 | def check_if_user_attribute_exists(self, user_id: int, key: str): 114 | self.check_if_user_exists(user_id, raise_exception=True) 115 | user_dict = self.user_collection.find_one({"_id": user_id}) 116 | return key in user_dict 117 | 118 | def get_dialog_messages(self, user_id: int, dialog_id: Optional[str] = None): 119 | self.check_if_user_exists(user_id, raise_exception=True) 120 | 121 | if dialog_id is None: 122 | dialog_id = self.get_user_attribute(user_id, "current_dialog_id") 123 | if dialog_id is None: 124 | return [] 125 | 126 | dialog_dict = self.dialog_collection.find_one({"_id": dialog_id, "user_id": user_id}) 127 | return dialog_dict["messages"] 128 | 129 | def set_dialog_messages(self, user_id: int, dialog_messages: list, dialog_id: Optional[str] = None): 130 | self.check_if_user_exists(user_id, raise_exception=True) 131 | 132 | if dialog_id is None: 133 | dialog_id = self.get_user_attribute(user_id, "current_dialog_id") 134 | 135 | self.dialog_collection.update_one( 136 | {"_id": dialog_id, "user_id": user_id}, 137 | {"$set": {"messages": dialog_messages}} 138 | ) 139 | 140 | def count_documents_in_collection(self, collection_name: str): 141 | return self.db[collection_name].count_documents({}) 142 | 143 | def get_new_unique_payment_id(self): 144 | if self.payment_collection.count_documents({}) == 0: 145 | payment_id = 0 146 | else: 147 | payment_id = 1 + self.payment_collection.find_one(sort=[("_id", pymongo.DESCENDING)])["_id"] 148 | 149 | return payment_id 150 | 151 | def add_new_payment( 152 | self, 153 | payment_id: int, 154 | payment_method: str, # like: cryptomus, cards 155 | payment_method_type: str, # like: cryptomus, telegram_payments 156 | product_key: str, 157 | user_id: int, 158 | amount: float, 159 | currency: str, 160 | status: str, 161 | invoice_url: str, 162 | n_tokens_to_add: int, 163 | expired_at: datetime 164 | ): 165 | payment_dict = { 166 | "_id": payment_id, 167 | "payment_method": payment_method, 168 | "payment_method_type": payment_method_type, 169 | "product_key": product_key, 170 | "user_id": user_id, 171 | "amount": amount, 172 | "currency": currency, 173 | "status": status, 174 | "invoice_url": invoice_url, 175 | "n_tokens_to_add": n_tokens_to_add, 176 | "expired_at": expired_at, 177 | "created_at": datetime.now(), 178 | "are_tokens_added": False 179 | } 180 | 181 | self.payment_collection.insert_one(payment_dict) 182 | 183 | def get_payment_attribute(self, payment_id: int, key: str): 184 | self.check_if_payment_exists(payment_id, raise_exception=True) 185 | payment_dict = self.payment_collection.find_one({"_id": payment_id}) 186 | 187 | if key not in payment_dict: 188 | raise ValueError(f"Payment {payment_id} does not have a value for {key}") 189 | 190 | return payment_dict[key] 191 | 192 | def set_payment_attribute(self, payment_id: int, key: str, value: Any): 193 | self.check_if_payment_exists(payment_id, raise_exception=True) 194 | self.payment_collection.update_one({"_id": payment_id}, {"$set": {key: value}}) 195 | 196 | def get_all_not_expried_payment_ids(self): 197 | payments_find = self.payment_collection.find({ 198 | "$and": [{"expired_at": {"$gt": datetime.now()}}, {"status": {"$ne": "paid"}}] 199 | }) 200 | return [x["_id"] for x in payments_find] 201 | 202 | def create_newsletter(self, newsletter_id: str): 203 | if not self.check_if_newsletter_exists(newsletter_id): 204 | newsletter_dict = { 205 | "_id": newsletter_id, 206 | "already_sent_to_user_ids": [], 207 | "created_at": datetime.now() 208 | } 209 | 210 | self.newsletter_collection.insert_one(newsletter_dict) 211 | 212 | def add_user_to_newsletter(self, newsletter_id: str, user_id: int): 213 | self.check_if_newsletter_exists(newsletter_id, raise_exception=True) 214 | self.check_if_user_exists(user_id, raise_exception=True) 215 | 216 | newsletter_dict = self.newsletter_collection.find_one({"_id": newsletter_id}) 217 | if user_id not in newsletter_dict["already_sent_to_user_ids"]: 218 | self.newsletter_collection.update_one( 219 | {"_id": newsletter_id}, 220 | {"$push": {"already_sent_to_user_ids": user_id}} 221 | ) 222 | 223 | def get_newsletter_attribute(self, newsletter_id: int, key: str): 224 | self.check_if_newsletter_exists(newsletter_id, raise_exception=True) 225 | newsletter_dict = self.newsletter_collection.find_one({"_id": newsletter_id}) 226 | 227 | if key not in newsletter_dict: 228 | raise ValueError(f"Newsletter {newsletter_id} does not have a value for {key}") 229 | 230 | return newsletter_dict[key] -------------------------------------------------------------------------------- /notebooks/analytics.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "65819b0d", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%load_ext autoreload\n", 11 | "%autoreload 2\n", 12 | "\n", 13 | "import sys\n", 14 | "sys.path.append(\"..\")\n", 15 | "\n", 16 | "from collections import defaultdict\n", 17 | "from datetime import datetime\n", 18 | "import pymongo\n", 19 | "import pytz\n", 20 | "\n", 21 | "import pandas as pd\n", 22 | "from matplotlib import pylab as plt\n", 23 | "import seaborn as sns\n", 24 | "\n", 25 | "from bot import config\n", 26 | "from bot import database\n", 27 | "import numpy as np" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "id": "ed48b96f", 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "db = database.Database()" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "id": "9ddb85d6", 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "def change_tz_to_msk(d):\n", 48 | " return d.astimezone(pytz.timezone(\"Europe/Moscow\"))\n", 49 | "\n", 50 | "\n", 51 | "def user_to_str(user_dict):\n", 52 | " text = \"\"\n", 53 | " text += f\"{user_dict['_id']}:\\n\"\n", 54 | " for k in [\"username\", \"first_name\", \"last_name\", \"last_interaction\", \"first_seen\", \"current_chat_mode\", \"n_used_tokens\", \"token_balance\"]:\n", 55 | " if k in user_dict:\n", 56 | " v = user_dict[k]\n", 57 | " if isinstance(v, datetime):\n", 58 | " v = change_tz_to_msk(v)\n", 59 | " v = v.strftime('%H:%M:%S %d-%m-%Y')\n", 60 | " text += f\" - {k}: {v}\\n\"\n", 61 | "\n", 62 | " return text\n", 63 | "\n", 64 | "\n", 65 | "def dialog_to_str(dialog_dict):\n", 66 | " text = \"\"\n", 67 | " \n", 68 | " user_id = dialog_dict[\"user_id\"]\n", 69 | " user_dict = db.user_collection.find_one({\"_id\": user_id})\n", 70 | " \n", 71 | " text += user_to_str(user_dict)\n", 72 | " text += \"\\n\"\n", 73 | " \n", 74 | " text += f\"Chat mode: {dialog_dict['chat_mode']}\\n\\n\"\n", 75 | " text += \"Messages:\\n\"\n", 76 | " for message in dialog_dict[\"messages\"]:\n", 77 | " text += f\"U: {message['user']}\\n\"\n", 78 | " text += f\"B: {message['bot']}\\n\"\n", 79 | " text += \"\\n\"\n", 80 | " \n", 81 | " return text\n", 82 | "\n", 83 | "\n", 84 | "def payment_to_str(payment_dict):\n", 85 | " text = \"\"\n", 86 | " \n", 87 | " text += f\"Status: {payment_dict['status']}\\n\"\n", 88 | " date = change_tz_to_msk(payment_dict[\"created_at\"])\n", 89 | " text += f\"Date: {date}\\n\"\n", 90 | " text += f\"Amount: {payment_dict['amount']}\\n\\n\"\n", 91 | " \n", 92 | " user_id = payment_dict[\"user_id\"]\n", 93 | " user_dict = db.user_collection.find_one({\"_id\": user_id})\n", 94 | " \n", 95 | " text += user_to_str(user_dict)\n", 96 | " text += \"\\n\"\n", 97 | " \n", 98 | " return text" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "id": "6c6998f8", 104 | "metadata": {}, 105 | "source": [ 106 | "### N total users" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": null, 112 | "id": "6f54405e", 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [ 116 | "n_users = db.user_collection.count_documents({})\n", 117 | "print(f\"N total users: {n_users}\")" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "id": "ed7aab98", 123 | "metadata": {}, 124 | "source": [ 125 | "### Install graph" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "id": "1e3b4741", 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "query = db.user_collection.find({})\n", 136 | "\n", 137 | "date_to_n_installs = defaultdict(int)\n", 138 | "for user_dict in list(query):\n", 139 | " date_to_n_installs[user_dict[\"first_seen\"].date()] += 1\n", 140 | "\n", 141 | "date_to_n_installs_tuples = list(date_to_n_installs.items())\n", 142 | "date_to_n_installs_tuples = sorted(date_to_n_installs_tuples, key=lambda x: x[0])\n", 143 | "\n", 144 | "df = pd.DataFrame(data={\n", 145 | " \"dates\": [x[0] for x in date_to_n_installs_tuples],\n", 146 | " \"installs\": [x[1] for x in date_to_n_installs_tuples]\n", 147 | "})\n", 148 | "\n", 149 | "fig, ax = plt.subplots(1, 1, figsize=(15, 5))\n", 150 | "sns.barplot(x='dates', y='installs', data=df, color=\"steelblue\")\n", 151 | "\n", 152 | "xticks = ax.get_xticks()\n", 153 | "xticklabels = [x.get_text() for x in ax.get_xticklabels()]\n", 154 | "_ = ax.set_xticks(xticks, xticklabels, rotation=90)" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "id": "42e090c1", 160 | "metadata": {}, 161 | "source": [ 162 | "### Install graph (cumsum)" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": null, 168 | "id": "86f2dffc", 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "query = db.user_collection.find({})\n", 173 | "\n", 174 | "date_to_n_installs = defaultdict(int)\n", 175 | "for user_dict in list(query):\n", 176 | " date_to_n_installs[user_dict[\"first_seen\"].date()] += 1\n", 177 | "\n", 178 | "date_to_n_installs_tuples = list(date_to_n_installs.items())\n", 179 | "date_to_n_installs_tuples = sorted(date_to_n_installs_tuples, key=lambda x: x[0])\n", 180 | "\n", 181 | "df = pd.DataFrame(data={\n", 182 | " \"dates\": [x[0] for x in date_to_n_installs_tuples],\n", 183 | " \"installs\": np.cumsum([x[1] for x in date_to_n_installs_tuples])\n", 184 | "})\n", 185 | "\n", 186 | "fig, ax = plt.subplots(1, 1, figsize=(15, 5))\n", 187 | "sns.barplot(x='dates', y='installs', data=df, color=\"steelblue\")\n", 188 | "\n", 189 | "xticks = ax.get_xticks()\n", 190 | "xticklabels = [x.get_text() for x in ax.get_xticklabels()]\n", 191 | "_ = ax.set_xticks(xticks, xticklabels, rotation=90)" 192 | ] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "id": "962e3e33", 197 | "metadata": {}, 198 | "source": [ 199 | "### N total messages from users" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": null, 205 | "id": "c1b336cd", 206 | "metadata": {}, 207 | "outputs": [], 208 | "source": [ 209 | "query = db.dialog_collection.find({})\n", 210 | "\n", 211 | "n_total_messages_from_users = 0\n", 212 | "for dialog_dict in query:\n", 213 | " n_total_messages_from_users += len([1 for messages in dialog_dict[\"messages\"]])\n", 214 | " \n", 215 | "print(f\"N total messages from users: {n_total_messages_from_users}\")" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "id": "05dd0f97", 221 | "metadata": {}, 222 | "source": [ 223 | "### Message graph" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "id": "d89cd5de", 230 | "metadata": {}, 231 | "outputs": [], 232 | "source": [ 233 | "query = db.dialog_collection.find({})\n", 234 | "\n", 235 | "date_to_n_messages = defaultdict(int)\n", 236 | "for dialog_dict in query:\n", 237 | " for message in dialog_dict[\"messages\"]:\n", 238 | " date_to_n_messages[message[\"date\"].date()] += 1" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": null, 244 | "id": "ab6513d5", 245 | "metadata": {}, 246 | "outputs": [], 247 | "source": [ 248 | "date_to_n_messages_tuples = list(date_to_n_messages.items())\n", 249 | "date_to_n_messages_tuples = sorted(date_to_n_messages_tuples, key=lambda x: x[0])\n", 250 | "\n", 251 | "df = pd.DataFrame(data={\n", 252 | " \"dates\": [x[0] for x in date_to_n_messages_tuples],\n", 253 | " \"messages\": [x[1] for x in date_to_n_messages_tuples],\n", 254 | "})\n", 255 | "\n", 256 | "fig, ax = plt.subplots(1, 1, figsize=(15, 5))\n", 257 | "sns.barplot(x='dates', y='messages', data=df, color=\"steelblue\")\n", 258 | "\n", 259 | "xticks = ax.get_xticks()\n", 260 | "xticklabels = [x.get_text() for x in ax.get_xticklabels()]\n", 261 | "_ = ax.set_xticks(xticks, xticklabels, rotation=90)" 262 | ] 263 | }, 264 | { 265 | "cell_type": "markdown", 266 | "id": "fbd1967a", 267 | "metadata": {}, 268 | "source": [ 269 | "### Money graph" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": null, 275 | "id": "4863b1d5", 276 | "metadata": {}, 277 | "outputs": [], 278 | "source": [ 279 | "query = db.payment_collection.find({\"status\": \"paid\"})\n", 280 | "\n", 281 | "date_to_money = defaultdict(int)\n", 282 | "for payment_dict in list(query):\n", 283 | " amount = payment_dict[\"amount\"]\n", 284 | " if payment_dict[\"currency\"] == \"RUB\":\n", 285 | " amount /= 77\n", 286 | " \n", 287 | " date_to_money[payment_dict[\"created_at\"].date()] += amount\n", 288 | "\n", 289 | "date_to_money_tuples = list(date_to_money.items())\n", 290 | "date_to_money_tuples = sorted(date_to_money_tuples, key=lambda x: x[0])\n", 291 | "\n", 292 | "df = pd.DataFrame(data={\n", 293 | " \"dates\": [x[0] for x in date_to_money_tuples],\n", 294 | " \"money\": [x[1] for x in date_to_money_tuples]\n", 295 | "})\n", 296 | "\n", 297 | "fig, ax = plt.subplots(1, 1, figsize=(15, 5))\n", 298 | "sns.barplot(x='dates', y='money', data=df, color=\"steelblue\")\n", 299 | "\n", 300 | "xticks = ax.get_xticks()\n", 301 | "xticklabels = [x.get_text() for x in ax.get_xticklabels()]\n", 302 | "_ = ax.set_xticks(xticks, xticklabels, rotation=90)\n", 303 | "\n", 304 | "print(f\"Total money: {sum(date_to_money.values())}\")" 305 | ] 306 | }, 307 | { 308 | "cell_type": "markdown", 309 | "id": "2b217224", 310 | "metadata": {}, 311 | "source": [ 312 | "### Last registered users" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": null, 318 | "id": "4559b6b8", 319 | "metadata": {}, 320 | "outputs": [], 321 | "source": [ 322 | "print(f\"5 last registered users:\\n\")\n", 323 | "\n", 324 | "query = db.user_collection.find().sort('first_seen', -1).limit(5)\n", 325 | "for user_dict in query:\n", 326 | " print(user_to_str(user_dict))\n", 327 | " print(\"_\" * 50, \"\\n\")" 328 | ] 329 | }, 330 | { 331 | "cell_type": "markdown", 332 | "id": "bf66002a", 333 | "metadata": {}, 334 | "source": [ 335 | "### Last successful payments" 336 | ] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": null, 341 | "id": "b895eb98", 342 | "metadata": {}, 343 | "outputs": [], 344 | "source": [ 345 | "print(f\"Last successful payments:\\n\")\n", 346 | "\n", 347 | "query = db.payment_collection.find({\"status\": \"paid\"}).sort(\"created_at\", -1).limit(5)\n", 348 | "for payment_dict in query:\n", 349 | " print(payment_to_str(payment_dict))\n", 350 | " print(\"_\" * 50, \"\\n\")" 351 | ] 352 | }, 353 | { 354 | "cell_type": "markdown", 355 | "id": "de857a2d", 356 | "metadata": {}, 357 | "source": [ 358 | "### Last created payments" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": null, 364 | "id": "21ec8237", 365 | "metadata": {}, 366 | "outputs": [], 367 | "source": [ 368 | "print(f\"Last created payments:\\n\")\n", 369 | "\n", 370 | "query = db.payment_collection.find({}).sort(\"created_at\", -1).limit(5)\n", 371 | "for payment_dict in query:\n", 372 | " print(payment_to_str(payment_dict))\n", 373 | " print(\"_\" * 50, \"\\n\")" 374 | ] 375 | }, 376 | { 377 | "cell_type": "markdown", 378 | "id": "b519ca87", 379 | "metadata": {}, 380 | "source": [ 381 | "### Last dialogs" 382 | ] 383 | }, 384 | { 385 | "cell_type": "code", 386 | "execution_count": null, 387 | "id": "18067535", 388 | "metadata": { 389 | "scrolled": false 390 | }, 391 | "outputs": [], 392 | "source": [ 393 | "query = db.dialog_collection.find({\"messages\": {\"$ne\": []}}).sort('start_time', -1).limit(30)\n", 394 | "for dialog_dict in query:\n", 395 | " print(dialog_to_str(dialog_dict))\n", 396 | " print(\"_\" * 50, \"\\n\")" 397 | ] 398 | } 399 | ], 400 | "metadata": { 401 | "kernelspec": { 402 | "display_name": "Python 3 (ipykernel)", 403 | "language": "python", 404 | "name": "python3" 405 | }, 406 | "language_info": { 407 | "codemirror_mode": { 408 | "name": "ipython", 409 | "version": 3 410 | }, 411 | "file_extension": ".py", 412 | "mimetype": "text/x-python", 413 | "name": "python", 414 | "nbconvert_exporter": "python", 415 | "pygments_lexer": "ipython3", 416 | "version": "3.8.16" 417 | } 418 | }, 419 | "nbformat": 4, 420 | "nbformat_minor": 5 421 | } 422 | -------------------------------------------------------------------------------- /bot/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import pymongo 4 | import asyncio 5 | import traceback 6 | import html 7 | import json 8 | import tempfile 9 | import pydub 10 | from pathlib import Path 11 | from datetime import datetime, timedelta 12 | 13 | import telegram 14 | from telegram import Update, User, InlineKeyboardButton, InlineKeyboardMarkup, LabeledPrice 15 | from telegram.ext import ( 16 | ApplicationBuilder, 17 | CallbackContext, 18 | CommandHandler, 19 | MessageHandler, 20 | CallbackQueryHandler, 21 | PreCheckoutQueryHandler, 22 | JobQueue, 23 | AIORateLimiter, 24 | filters 25 | ) 26 | from telegram.constants import ParseMode, ChatAction 27 | 28 | import cryptomus 29 | 30 | from bot import config 31 | from bot import database 32 | from bot import openai_utils 33 | from bot.payment import CryptomusPayment 34 | 35 | 36 | # setup 37 | db = database.Database() 38 | logger = logging.getLogger(__name__) 39 | user_semaphores = {} 40 | 41 | HELP_MESSAGE = """Commands: 42 | ⚪ /retry – Regenerate last bot answer 43 | ⚪ /new – Start new dialog 44 | ⚪ /mode – Select chat mode 45 | ⚪ /balance – Show balance 46 | ⚪ /help – Show help 47 | 48 | Important note 1. The longer your dialog, the more tokens are spent with each new message. To reset current dialog and start a new one, send /new command 49 | 50 | Important note 2. Write 🇬🇧 English for a better quality of answers 51 | """ 52 | 53 | 54 | # --- Utility Functions --- # 55 | def split_text_into_chunks(text, chunk_size): 56 | for i in range(0, len(text), chunk_size): 57 | yield text[i:i + chunk_size] 58 | 59 | 60 | async def register_user(update: Update, context: CallbackContext, user: User): 61 | if not db.check_if_user_exists(user.id): 62 | db.add_new_user( 63 | user.id, 64 | update.message.chat_id, 65 | initial_token_balance=config.initial_token_balance, 66 | username=user.username, 67 | first_name=user.first_name, 68 | last_name= user.last_name 69 | ) 70 | db.start_new_dialog(user.id) 71 | 72 | if db.get_user_attribute(user.id, "current_dialog_id") is None: 73 | db.start_new_dialog(user.id) 74 | 75 | # token balance 76 | if not db.check_if_user_attribute_exists(user.id, "token_balance"): 77 | db.set_user_attribute(user.id, "token_balance", config.initial_token_balance) 78 | 79 | # openai engine 80 | if not db.check_if_user_attribute_exists(user.id, "openai_engine"): 81 | openai_engine = "chatgpt" if config.use_chatgpt_api else "gpt" 82 | db.set_user_attribute(user.id, "openai_engine", openai_engine) 83 | 84 | if user.id not in user_semaphores: 85 | user_semaphores[user.id] = asyncio.Semaphore(1) 86 | 87 | 88 | async def is_previous_message_not_answered_yet(update: Update, context: CallbackContext): 89 | await register_user(update, context, update.message.from_user) 90 | 91 | user_id = update.message.from_user.id 92 | if user_semaphores[user_id].locked(): 93 | text = "⏳ Please wait for a reply to the previous message" 94 | await update.message.reply_text(text, reply_to_message_id=update.message.id, parse_mode=ParseMode.HTML) 95 | return True 96 | else: 97 | return False 98 | 99 | 100 | async def check_if_user_has_enough_tokens(update: Update, context: CallbackContext): 101 | user_id = update.message.from_user.id 102 | 103 | token_balance = db.get_user_attribute(user_id, "token_balance") 104 | if token_balance <= 0: 105 | await show_balance_handle(update, context) 106 | return False 107 | 108 | return True 109 | 110 | 111 | async def send_user_message_about_n_added_tokens(context: CallbackContext, n_tokens_added: int, chat_id: int = None, user_id: int = None): 112 | if chat_id is None: 113 | if user_id is None: 114 | raise ValueError(f"chat_id and user_id can't be None simultaneously") 115 | chat_id = db.get_user_attribute(user_id, "chat_id") 116 | 117 | text = f"🟣 {n_tokens_added} tokens were successfully added to your balance!" 118 | await context.bot.send_message(chat_id, text, parse_mode=ParseMode.HTML) 119 | 120 | 121 | async def notify_admins_about_successfull_payment(context: CallbackContext, payment_id: int): 122 | if config.admin_chat_id is not None: 123 | text = "🟣 Successfull payment:\n" 124 | 125 | payment_dict = db.payment_collection.find_one({"_id": payment_id}) 126 | for key in ["amount", "currency", "product_key", "n_tokens_to_add", "payment_method", "user_id", "status"]: 127 | text += f"- {key}: {payment_dict[key]}\n" 128 | 129 | user_dict = db.user_collection.find_one({"_id": payment_dict["user_id"]}) 130 | if user_dict["username"] is not None: 131 | text += f"- username: @{user_dict['username']}\n" 132 | 133 | # tag admins 134 | for admin_username in config.admin_usernames: 135 | if not admin_username.startswith("@"): 136 | admin_username = "@" + admin_username 137 | text += f"\n{admin_username}" 138 | 139 | await context.bot.send_message(config.admin_chat_id, text, parse_mode=ParseMode.HTML) 140 | 141 | 142 | async def check_payment_status_job_fn(context: CallbackContext): 143 | job = context.job 144 | payment_id = job.data["payment_id"] 145 | payment_dict = db.payment_collection.find_one({"_id": payment_id}) 146 | 147 | is_paid = False 148 | if payment_dict["payment_method_type"] == "cryptomus": 149 | cryptomus_payment_instance = CryptomusPayment( 150 | config.payment_methods["cryptomus"]["api_key"], 151 | config.payment_methods["cryptomus"]["merchant_id"] 152 | ) 153 | 154 | is_paid = cryptomus_payment_instance.check_invoice_status(payment_id) 155 | else: 156 | context.job.schedule_removal() 157 | return 158 | 159 | if is_paid: 160 | db.set_payment_attribute(payment_id, "status", "paid") 161 | user_id = payment_dict["user_id"] 162 | 163 | n_tokens_to_add = payment_dict["n_tokens_to_add"] 164 | if not payment_dict["are_tokens_added"]: 165 | db.set_user_attribute( 166 | user_id, 167 | "token_balance", 168 | db.get_user_attribute(user_id, "token_balance") + n_tokens_to_add 169 | ) 170 | db.set_payment_attribute(payment_id, "are_tokens_added", True) 171 | 172 | await send_user_message_about_n_added_tokens(context, n_tokens_to_add, chat_id=job.chat_id) 173 | await notify_admins_about_successfull_payment(context, payment_id) 174 | 175 | context.job.schedule_removal() 176 | 177 | 178 | def run_repeating_payment_status_check(job_queue: JobQueue, payment_id: int, chat_id: int, how_long: int = 4000, interval: int = 30): 179 | job_queue.run_repeating( 180 | check_payment_status_job_fn, 181 | interval, 182 | first=0, 183 | last=how_long, 184 | name=str(payment_id), 185 | data={"payment_id": payment_id}, 186 | chat_id=chat_id 187 | ) 188 | 189 | 190 | def run_not_expired_payment_status_check(job_queue: JobQueue, how_long: int = 4000, interval: int = 30): 191 | payment_ids = db.get_all_not_expried_payment_ids() 192 | for payment_id in payment_ids: 193 | user_id = db.get_payment_attribute(payment_id, "user_id") 194 | chat_id = db.get_user_attribute(user_id, "chat_id") 195 | run_repeating_payment_status_check(job_queue, payment_id, chat_id, how_long, interval) 196 | 197 | 198 | 199 | # --- System Handles --- # 200 | async def start_handle(update: Update, context: CallbackContext): 201 | await register_user(update, context, update.message.from_user) 202 | user_id = update.message.from_user.id 203 | 204 | db.set_user_attribute(user_id, "last_interaction", datetime.now()) 205 | db.start_new_dialog(user_id) 206 | 207 | reply_text = "Hi! I'm ChatGPT bot implemented with GPT-4 OpenAI API 🤖\n\n" 208 | reply_text += HELP_MESSAGE 209 | 210 | await update.message.reply_text(reply_text, parse_mode=ParseMode.HTML) 211 | 212 | async def show_chat_modes(context: CallbackContext): 213 | data = context.job.data 214 | await show_chat_modes_handle(data["update"], data["context"]) 215 | context.job_queue.run_once(show_chat_modes, when=5, data={"update": update, "context": context}) 216 | 217 | 218 | async def help_handle(update: Update, context: CallbackContext): 219 | await register_user(update, context, update.message.from_user) 220 | user_id = update.message.from_user.id 221 | db.set_user_attribute(user_id, "last_interaction", datetime.now()) 222 | await update.message.reply_text(HELP_MESSAGE, parse_mode=ParseMode.HTML) 223 | 224 | 225 | 226 | # --- Message Handles --- # 227 | async def message_handle(update: Update, context: CallbackContext, message=None, use_new_dialog_timeout=True): 228 | # check if message is edited 229 | if update.edited_message is not None: 230 | await edited_message_handle(update, context) 231 | return 232 | 233 | await register_user(update, context, update.message.from_user) 234 | if await is_previous_message_not_answered_yet(update, context): return 235 | if not await check_if_user_has_enough_tokens(update, context): return 236 | 237 | user_id = update.message.from_user.id 238 | chat_mode = db.get_user_attribute(user_id, "current_chat_mode") 239 | 240 | async with user_semaphores[user_id]: 241 | # new dialog timeout 242 | if use_new_dialog_timeout: 243 | 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: 244 | db.start_new_dialog(user_id) 245 | await update.message.reply_text(f"Starting new dialog due to timeout ({openai_utils.CHAT_MODES[chat_mode]['name']} mode) ✅", parse_mode=ParseMode.HTML) 246 | db.set_user_attribute(user_id, "last_interaction", datetime.now()) 247 | 248 | # send typing action 249 | await update.message.chat.send_action(action="typing") 250 | 251 | message = message or update.message.text 252 | dialog_messages = db.get_dialog_messages(user_id, dialog_id=None) 253 | use_chatgpt_api = db.get_user_attribute(user_id, "openai_engine") == "chatgpt" 254 | parse_mode = { 255 | "html": ParseMode.HTML, 256 | "markdown": ParseMode.MARKDOWN 257 | }[openai_utils.CHAT_MODES[chat_mode]["parse_mode"]] 258 | 259 | chatgpt_instance = openai_utils.ChatGPT(use_chatgpt_api=config.use_chatgpt_api) 260 | if config.enable_message_streaming: 261 | gen = chatgpt_instance.send_message_stream(message, dialog_messages=dialog_messages, chat_mode=chat_mode) 262 | else: 263 | answer, n_used_tokens, n_first_dialog_messages_removed = await chatgpt_instance.send_message( 264 | message, 265 | dialog_messages=dialog_messages, 266 | chat_mode=chat_mode 267 | ) 268 | 269 | async def fake_gen(): 270 | yield "finished", answer, n_used_tokens, n_first_dialog_messages_removed 271 | 272 | gen = fake_gen() 273 | 274 | # send message to user 275 | prev_answer = "" 276 | i = -1 277 | async for gen_item in gen: 278 | i += 1 279 | 280 | status = gen_item[0] 281 | if status == "not_finished": 282 | status, answer = gen_item 283 | elif status == "finished": 284 | status, answer, n_used_tokens, n_first_dialog_messages_removed = gen_item 285 | else: 286 | raise ValueError(f"Streaming status {status} is unknown") 287 | 288 | answer = answer[:4096] # telegram message limit 289 | if i == 0: # send first message (then it'll be edited if message streaming is enabled) 290 | try: 291 | sent_message = await update.message.reply_text(answer, parse_mode=parse_mode) 292 | except telegram.error.BadRequest as e: 293 | if str(e).startswith("Message must be non-empty"): # first answer chunk from openai was empty 294 | i = -1 # try again to send first message 295 | continue 296 | else: 297 | sent_message = await update.message.reply_text(answer) 298 | else: # edit sent message 299 | # update only when 100 new symbols are ready 300 | if abs(len(answer) - len(prev_answer)) < 100 and status != "finished": 301 | continue 302 | 303 | try: 304 | await context.bot.edit_message_text(answer, chat_id=sent_message.chat_id, message_id=sent_message.message_id, parse_mode=parse_mode) 305 | except telegram.error.BadRequest as e: 306 | if str(e).startswith("Message is not modified"): 307 | continue 308 | else: 309 | await context.bot.edit_message_text(answer, chat_id=sent_message.chat_id, message_id=sent_message.message_id) 310 | 311 | await asyncio.sleep(0.01) # wait a bit to avoid flooding 312 | 313 | prev_answer = answer 314 | 315 | # update user data 316 | new_dialog_message = {"user": message, "bot": answer, "date": datetime.now()} 317 | db.set_dialog_messages( 318 | user_id, 319 | db.get_dialog_messages(user_id, dialog_id=None) + [new_dialog_message], 320 | dialog_id=None 321 | ) 322 | 323 | db.set_user_attribute(user_id, "n_used_tokens", n_used_tokens + db.get_user_attribute(user_id, "n_used_tokens")) 324 | db.set_user_attribute(user_id, "token_balance", max(0, db.get_user_attribute(user_id, "token_balance") - n_used_tokens)) 325 | 326 | # send message if some messages were removed from the context 327 | if n_first_dialog_messages_removed > 0: 328 | if n_first_dialog_messages_removed == 1: 329 | text = "✍️ Note: Your current dialog is too long, so your first message was removed from the context.\n Send /new command to start new dialog" 330 | else: 331 | text = f"✍️ Note: Your current dialog is too long, so {n_first_dialog_messages_removed} first messages were removed from the context.\n Send /new command to start new dialog" 332 | await update.message.reply_text(text, parse_mode=ParseMode.HTML) 333 | 334 | 335 | async def edited_message_handle(update: Update, context: CallbackContext): 336 | text = "🥲 Unfortunately, message editing is not supported" 337 | await update.edited_message.reply_text(text, parse_mode=ParseMode.HTML) 338 | 339 | 340 | async def retry_handle(update: Update, context: CallbackContext): 341 | await register_user(update, context, update.message.from_user) 342 | if await is_previous_message_not_answered_yet(update, context): return 343 | 344 | user_id = update.message.from_user.id 345 | db.set_user_attribute(user_id, "last_interaction", datetime.now()) 346 | 347 | dialog_messages = db.get_dialog_messages(user_id, dialog_id=None) 348 | if len(dialog_messages) == 0: 349 | await update.message.reply_text("No message to retry 🤷‍♂️") 350 | return 351 | 352 | last_dialog_message = dialog_messages.pop() 353 | db.set_dialog_messages(user_id, dialog_messages, dialog_id=None) # last message was removed from the context 354 | 355 | await message_handle(update, context, message=last_dialog_message["user"], use_new_dialog_timeout=False) 356 | 357 | 358 | async def new_dialog_handle(update: Update, context: CallbackContext): 359 | await register_user(update, context, update.message.from_user) 360 | if await is_previous_message_not_answered_yet(update, context): return 361 | 362 | user_id = update.message.from_user.id 363 | db.set_user_attribute(user_id, "last_interaction", datetime.now()) 364 | 365 | db.start_new_dialog(user_id) 366 | await update.message.reply_text("Starting new dialog ✅") 367 | 368 | chat_mode = db.get_user_attribute(user_id, "current_chat_mode") 369 | await update.message.reply_text(f"{openai_utils.CHAT_MODES[chat_mode]['welcome_message']}", parse_mode=ParseMode.HTML) 370 | 371 | 372 | async def voice_message_handle(update: Update, context: CallbackContext): 373 | await register_user(update, context, update.message.from_user) 374 | if await is_previous_message_not_answered_yet(update, context): return 375 | 376 | user_id = update.message.from_user.id 377 | db.set_user_attribute(user_id, "last_interaction", datetime.now()) 378 | 379 | # check if user has enough tokens 380 | if not await check_if_user_has_enough_tokens(update, context): 381 | return 382 | 383 | voice = update.message.voice 384 | with tempfile.TemporaryDirectory() as tmp_dir: 385 | tmp_dir = Path(tmp_dir) 386 | voice_ogg_path = tmp_dir / "voice.ogg" 387 | 388 | # download 389 | voice_file = await context.bot.get_file(voice.file_id) 390 | await voice_file.download_to_drive(voice_ogg_path) 391 | 392 | # convert to mp3 393 | voice_mp3_path = tmp_dir / "voice.mp3" 394 | pydub.AudioSegment.from_file(voice_ogg_path).export(voice_mp3_path, format="mp3") 395 | 396 | # transcribe 397 | with open(voice_mp3_path, "rb") as f: 398 | transcribed_text = await openai_utils.transcribe_audio(f) 399 | 400 | text = f"🎤: {transcribed_text}" 401 | await update.message.reply_text(text, parse_mode=ParseMode.HTML) 402 | 403 | await message_handle(update, context, message=transcribed_text) 404 | 405 | # calculate spent dollars 406 | n_spent_dollars = voice.duration * (config.whisper_price_per_1_min / 60) 407 | 408 | # normalize dollars to tokens (it's very convenient to measure everything in a single unit) 409 | price_per_1000_tokens = config.chatgpt_price_per_1000_tokens if config.use_chatgpt_api else config.gpt_price_per_1000_tokens 410 | n_used_tokens = int(n_spent_dollars / (price_per_1000_tokens / 1000)) 411 | 412 | db.set_user_attribute(user_id, "n_used_tokens", n_used_tokens + db.get_user_attribute(user_id, "n_used_tokens")) 413 | db.set_user_attribute(user_id, "token_balance", max(0, db.get_user_attribute(user_id, "token_balance") - n_used_tokens)) 414 | 415 | 416 | 417 | # --- Chat Mode Handles --- # 418 | async def show_chat_modes_handle(update: Update, context: CallbackContext): 419 | await register_user(update, context, update.message.from_user) 420 | if await is_previous_message_not_answered_yet(update, context): return 421 | 422 | user_id = update.message.from_user.id 423 | db.set_user_attribute(user_id, "last_interaction", datetime.now()) 424 | 425 | keyboard = [] 426 | for chat_mode, chat_mode_dict in openai_utils.CHAT_MODES.items(): 427 | keyboard.append([InlineKeyboardButton(chat_mode_dict["name"], callback_data=f"set_chat_mode|{chat_mode}")]) 428 | reply_markup = InlineKeyboardMarkup(keyboard) 429 | 430 | await update.message.reply_text("Select chat mode:", reply_markup=reply_markup) 431 | 432 | 433 | async def set_chat_mode_handle(update: Update, context: CallbackContext): 434 | await register_user(update.callback_query, context, update.callback_query.from_user) 435 | user_id = update.callback_query.from_user.id 436 | 437 | query = update.callback_query 438 | await query.answer() 439 | 440 | chat_mode = query.data.split("|")[1] 441 | 442 | db.set_user_attribute(user_id, "current_chat_mode", chat_mode) 443 | db.start_new_dialog(user_id) 444 | 445 | try: 446 | await query.edit_message_text(f"{openai_utils.CHAT_MODES[chat_mode]['welcome_message']}", parse_mode=ParseMode.HTML) 447 | except telegram.error.BadRequest as e: 448 | if str(e).startswith("Message is not modified"): 449 | pass 450 | 451 | 452 | 453 | 454 | # --- Payment Handles --- # 455 | async def show_balance_handle(update: Update, context: CallbackContext): 456 | await register_user(update, context, update.message.from_user) 457 | user_id = update.message.from_user.id 458 | db.set_user_attribute(user_id, "last_interaction", datetime.now()) 459 | 460 | token_balance = max(0, db.get_user_attribute(user_id, "token_balance")) 461 | n_used_tokens = db.get_user_attribute(user_id, "n_used_tokens") 462 | 463 | if token_balance > 0: 464 | text = f"🟢 You have {token_balance} tokens left\n" 465 | else: 466 | text = f"🔴 You have NO tokens left\n" 467 | text += f"You totally spent {n_used_tokens} tokens\n" 468 | 469 | reply_markup = InlineKeyboardMarkup([ 470 | [InlineKeyboardButton("🥑 Get Tokens", callback_data=f"show_payment_methods")], 471 | ]) 472 | 473 | await update.message.reply_text(text, reply_markup=reply_markup, parse_mode=ParseMode.HTML) 474 | 475 | 476 | async def show_payment_methods_handle(update: Update, context: CallbackContext): 477 | await register_user(update.callback_query, context, update.callback_query.from_user) 478 | user_id = update.callback_query.from_user.id 479 | 480 | query = update.callback_query 481 | await query.answer() 482 | 483 | text = "Choose payment method:" 484 | 485 | buttons = [] 486 | for payment_method_key, payment_method_values in config.payment_methods.items(): 487 | button = InlineKeyboardButton( 488 | payment_method_values["name"], 489 | callback_data=f"show_products|{payment_method_key}" 490 | ) 491 | buttons.append([button]) 492 | reply_markup = InlineKeyboardMarkup(buttons) 493 | 494 | await context.bot.send_message( 495 | update.callback_query.message.chat.id, 496 | text, 497 | reply_markup=reply_markup, 498 | parse_mode=ParseMode.HTML 499 | ) 500 | 501 | 502 | async def show_products_handle(update: Update, context: CallbackContext): 503 | await register_user(update.callback_query, context, update.callback_query.from_user) 504 | user_id = update.callback_query.from_user.id 505 | 506 | query = update.callback_query 507 | await query.answer() 508 | 509 | _, payment_method_key = query.data.split("|") 510 | 511 | text = "How many tokens do you want to buy?" 512 | 513 | product_keys = config.payment_methods[payment_method_key]["product_keys"] 514 | buttons = [] 515 | for product_key in product_keys: 516 | product = config.products[product_key] 517 | button = InlineKeyboardButton( 518 | product["title_on_button"], 519 | callback_data=f"send_invoice|{payment_method_key}|{product_key}" 520 | ) 521 | buttons.append([button]) 522 | reply_markup = InlineKeyboardMarkup(buttons) 523 | 524 | await context.bot.send_message( 525 | update.callback_query.message.chat.id, 526 | text, 527 | reply_markup=reply_markup, 528 | parse_mode=ParseMode.HTML 529 | ) 530 | 531 | 532 | async def send_invoice_handle(update: Update, context: CallbackContext): 533 | await register_user(update.callback_query, context, update.callback_query.from_user) 534 | user_id = update.callback_query.from_user.id 535 | 536 | query = update.callback_query 537 | await query.answer() 538 | 539 | _, payment_method_key, product_key = query.data.split("|") 540 | product = config.products[product_key] 541 | payment_method_type = config.payment_methods[payment_method_key]["type"] 542 | 543 | payment_id = db.get_new_unique_payment_id() 544 | 545 | if payment_method_type == "telegram_payments": 546 | chat_id = update.callback_query.message.chat.id 547 | 548 | # save in database 549 | db.add_new_payment( 550 | payment_id=payment_id, 551 | payment_method=payment_method_key, 552 | payment_method_type=payment_method_type, 553 | product_key=product_key, 554 | user_id=user_id, 555 | amount=product["price"], 556 | currency=product["currency"], 557 | status="not_paid", 558 | invoice_url="", 559 | expired_at=datetime.now() + timedelta(hours=1), 560 | n_tokens_to_add=product["n_tokens_to_add"] 561 | ) 562 | 563 | # create invoice 564 | payload = f"{payment_id}" 565 | prices = [LabeledPrice(product["title"], int(product["price"] * 100))] 566 | 567 | photo_url = None 568 | if "photo_url" in product and len(product["photo_url"]) > 0: 569 | photo_url = product["photo_url"] 570 | 571 | # send invoice 572 | await context.bot.send_invoice( 573 | chat_id, 574 | product["title"], 575 | product["description"], 576 | payload, 577 | config.payment_methods[payment_method_key]["token"], 578 | product["currency"], 579 | prices, 580 | photo_url=photo_url 581 | ) 582 | elif payment_method_type == "cryptomus": 583 | # create invoice 584 | cryptomus_payment_instance = CryptomusPayment( 585 | config.payment_methods[payment_method_key]["api_key"], 586 | config.payment_methods[payment_method_key]["merchant_id"] 587 | ) 588 | 589 | invoice_url, status, expired_at = cryptomus_payment_instance.create_invoice( 590 | payment_id, 591 | product["price"], 592 | product["currency"] 593 | ) 594 | 595 | # save in database 596 | db.add_new_payment( 597 | payment_id=payment_id, 598 | payment_method=payment_method_key, 599 | payment_method_type=payment_method_type, 600 | product_key=product_key, 601 | user_id=user_id, 602 | amount=product["price"], 603 | currency=product["currency"], 604 | status=status, 605 | invoice_url=invoice_url, 606 | expired_at=expired_at, 607 | n_tokens_to_add=product["n_tokens_to_add"] 608 | ) 609 | 610 | # run status check polling 611 | run_repeating_payment_status_check(context.job_queue, payment_id, update.callback_query.message.chat.id, how_long=4000, interval=30) 612 | 613 | # send invoice 614 | text = f"🔗 Here is: your invoice ({product['n_tokens_to_add']} tokens)\n\n" 615 | text += "You have 1 hour to pay it before it expires. If you are facing any problems, write to ." 616 | await context.bot.send_message(update.callback_query.message.chat.id, text, parse_mode=ParseMode.HTML) 617 | else: 618 | raise ValueError(f"Unknown payment method: {payment_method_type}") 619 | 620 | 621 | async def pre_checkout_handle(update: Update, context: CallbackContext): 622 | await register_user(update.pre_checkout_query, context, update.pre_checkout_query.from_user) 623 | user_id = update.pre_checkout_query.from_user.id 624 | 625 | query = update.pre_checkout_query 626 | await query.answer(ok=True) 627 | 628 | 629 | async def successful_payment_handle(update: Update, context: CallbackContext): 630 | await register_user(update, context, update.message.from_user) 631 | user_id = update.message.from_user.id 632 | chat_id = db.get_user_attribute(user_id, "chat_id") 633 | 634 | payment_id = int(update.message.successful_payment.invoice_payload) 635 | payment_dict = db.payment_collection.find_one({"_id": payment_id}) 636 | 637 | # update payment in database 638 | db.set_payment_attribute(payment_id, "status", "paid") 639 | 640 | n_tokens_to_add = payment_dict["n_tokens_to_add"] 641 | if not payment_dict["are_tokens_added"]: 642 | db.set_user_attribute( 643 | user_id, 644 | "token_balance", 645 | db.get_user_attribute(user_id, "token_balance") + n_tokens_to_add 646 | ) 647 | db.set_payment_attribute(payment_id, "are_tokens_added", True) 648 | 649 | # send messages 650 | await send_user_message_about_n_added_tokens(context, n_tokens_to_add, chat_id=chat_id) 651 | await notify_admins_about_successfull_payment(context, payment_id) 652 | 653 | 654 | # --- Admin Handles --- # 655 | async def add_tokens_handle(update: Update, context: CallbackContext): 656 | await register_user(update, context, update.message.from_user) 657 | 658 | username_or_user_id, n_tokens_to_add = context.args 659 | n_tokens_to_add = int(n_tokens_to_add) 660 | 661 | try: 662 | user_id = int(username_or_user_id) 663 | user_dict = db.user_collection.find_one({"_id": user_id}) 664 | except: 665 | username = username_or_user_id 666 | user_dict = db.user_collection.find_one({"username": username}) 667 | 668 | if user_dict is None: 669 | text = f"Username or user_id {username_or_user_id} not found in DB" 670 | await update.message.reply_text(text, parse_mode=ParseMode.HTML) 671 | return 672 | 673 | # add tokens 674 | db.set_user_attribute(user_dict["_id"], "token_balance", db.get_user_attribute(user_dict["_id"], "token_balance") + n_tokens_to_add) 675 | 676 | # send message to user 677 | await send_user_message_about_n_added_tokens(context, n_tokens_to_add, chat_id=user_dict["chat_id"]) 678 | 679 | # send message to admin 680 | text = f"🟣 {n_tokens_to_add} tokens were successfully added to {username_or_user_id} balance!" 681 | await update.message.reply_text(text, parse_mode=ParseMode.HTML) 682 | 683 | 684 | async def switch_openai_engine_handle(update: Update, context: CallbackContext): 685 | await register_user(update, context, update.message.from_user) 686 | user_id = update.message.from_user.id 687 | db.set_user_attribute(user_id, "last_interaction", datetime.now()) 688 | 689 | current_openai_engine = db.get_user_attribute(user_id, "openai_engine") 690 | if current_openai_engine == "chatgpt": 691 | new_openai_engine = "gpt" 692 | elif current_openai_engine == "gpt": 693 | new_openai_engine = "chatgpt" 694 | else: 695 | raise ValueError(f"Unknown OpenAI engine: {current_openai_engine}") 696 | 697 | db.set_user_attribute(user_id, "openai_engine", new_openai_engine) 698 | 699 | text = f"Switched to {new_openai_engine} engine ✅" 700 | await update.message.reply_text(text, parse_mode=ParseMode.HTML) 701 | 702 | 703 | async def user_info_handle(update: Update, context: CallbackContext): 704 | await register_user(update, context, update.message.from_user) 705 | user = update.message.from_user 706 | 707 | text = "User info:\n" 708 | text += f"- user_id: {user.id}\n" 709 | text += f"- username: {user.username}\n" 710 | text += f"- chat_id: {update.message.chat_id}\n" 711 | 712 | await update.message.reply_text(text, parse_mode=ParseMode.HTML) 713 | if config.admin_chat_id is not None: 714 | await context.bot.send_message(config.admin_chat_id, text, parse_mode=ParseMode.HTML) 715 | 716 | 717 | async def error_handle(update: Update, context: CallbackContext) -> None: 718 | logger.error(msg="Exception while handling an update:", exc_info=context.error) 719 | 720 | try: 721 | await context.bot.send_message(update.effective_chat.id, "🥲 Something went wrong...\nPlease try again later or contact !", parse_mode=ParseMode.HTML) 722 | 723 | if config.admin_chat_id is not None: 724 | # collect error message 725 | tb_list = traceback.format_exception(None, context.error, context.error.__traceback__) 726 | tb_string = "".join(tb_list) 727 | update_str = update.to_dict() if isinstance(update, Update) else str(update) 728 | message = ( 729 | f"An exception was raised while handling an update\n" 730 | f"
update = {html.escape(json.dumps(update_str, indent=2, ensure_ascii=False))}"
731 |                 "
\n\n" 732 | f"
{html.escape(tb_string)}
" 733 | ) 734 | 735 | # split text into multiple messages due to 4096 character limit 736 | for message_chunk in split_text_into_chunks(message, 4096): 737 | try: 738 | await context.bot.send_message(config.admin_chat_id, message_chunk, parse_mode=ParseMode.HTML) 739 | except telegram.error.BadRequest: 740 | # answer has invalid characters, so we send it without parse_mode 741 | await context.bot.send_message(config.admin_chat_id, message_chunk) 742 | except: 743 | await context.bot.send_message(update.effective_chat.id, "🥲 Some error in error handler... Contact ") 744 | 745 | 746 | def run_bot() -> None: 747 | application = ( 748 | ApplicationBuilder() 749 | .token(config.telegram_token) 750 | .concurrent_updates(True) 751 | .rate_limiter(AIORateLimiter(max_retries=3)) 752 | .http_version("1.1") 753 | .get_updates_http_version("1.1") 754 | .build() 755 | ) 756 | 757 | # check not expired payments 758 | run_not_expired_payment_status_check(application.job_queue) 759 | 760 | # add handlers 761 | if len(config.allowed_telegram_usernames) == 0: 762 | user_filter = filters.ALL 763 | else: 764 | user_filter = filters.User(username=config.allowed_telegram_usernames) 765 | 766 | if len(config.admin_usernames) == 0: 767 | raise ValueError("You must specify at least 1 admin username in config") 768 | admin_filter = filters.User(username=config.admin_usernames) 769 | 770 | # system 771 | application.add_handler(CommandHandler("start", start_handle, filters=user_filter)) 772 | application.add_handler(CommandHandler("help", help_handle, filters=user_filter)) 773 | 774 | # message 775 | application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND & user_filter, message_handle)) 776 | application.add_handler(CommandHandler("retry", retry_handle, filters=user_filter)) 777 | application.add_handler(CommandHandler("new", new_dialog_handle, filters=user_filter)) 778 | application.add_handler(MessageHandler(filters.VOICE & user_filter, voice_message_handle)) 779 | 780 | # chat mode 781 | application.add_handler(CommandHandler("mode", show_chat_modes_handle, filters=user_filter)) 782 | application.add_handler(CallbackQueryHandler(set_chat_mode_handle, pattern="^set_chat_mode")) 783 | 784 | # payment 785 | application.add_handler(CommandHandler("balance", show_balance_handle, filters=user_filter)) 786 | application.add_handler(CallbackQueryHandler(show_payment_methods_handle, pattern="^show_payment_methods$")) 787 | application.add_handler(CallbackQueryHandler(show_products_handle, pattern="^show_products")) 788 | application.add_handler(CallbackQueryHandler(send_invoice_handle, pattern="^send_invoice")) 789 | 790 | application.add_handler(PreCheckoutQueryHandler(pre_checkout_handle)) 791 | application.add_handler(MessageHandler(filters.SUCCESSFUL_PAYMENT & user_filter, successful_payment_handle)) 792 | 793 | # admin 794 | application.add_handler(CommandHandler("add_tokens", add_tokens_handle, filters=admin_filter)) 795 | application.add_handler(CommandHandler("switch_openai_engine", switch_openai_engine_handle, filters=user_filter)) 796 | application.add_handler(CommandHandler("info", user_info_handle, filters=user_filter)) 797 | application.add_error_handler(error_handle) 798 | 799 | # start the bot 800 | application.run_polling() 801 | -------------------------------------------------------------------------------- /notebooks/newsletter_we_are_live_again.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "4fde5eae", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%load_ext autoreload\n", 11 | "%autoreload 2\n", 12 | "\n", 13 | "import sys\n", 14 | "sys.path.append(\"..\")\n", 15 | "\n", 16 | "import time\n", 17 | "import pymongo\n", 18 | "import datetime\n", 19 | "import pytz\n", 20 | "\n", 21 | "from telegram import InlineKeyboardButton, InlineKeyboardMarkup\n", 22 | "from telegram.ext import ApplicationBuilder\n", 23 | "from telegram.constants import ParseMode\n", 24 | "\n", 25 | "from bot import config\n", 26 | "from bot import database\n", 27 | "from bot.app import show_balance_handle" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 2, 33 | "id": "1e4406c7", 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "application = (\n", 38 | " ApplicationBuilder()\n", 39 | " .token(config.telegram_token)\n", 40 | " .build()\n", 41 | ")\n", 42 | "\n", 43 | "db = database.Database()" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 3, 49 | "id": "09464d20", 50 | "metadata": {}, 51 | "outputs": [ 52 | { 53 | "name": "stdout", 54 | "output_type": "stream", 55 | "text": [ 56 | "Already sent to users: {1925283840, 384137216, 629866496, 1302204419, 5152628746, 457881611, 1398102028, 44099595, 516751375, 5347454992, 6004753, 6047741970, 302929939, 1043486741, 1875122198, 5627299861, 1184688152, 1204713497, 6215057431, 860700699, 6177658908, 1112305693, 818696222, 744362015, 13002777, 776855585, 1685334053, 931246118, 804155431, 5745397800, 1674692650, 5784668204, 1717964847, 560042032, 1207484464, 5789966391, 5233057849, 5707505724, 15822909, 9508927, 24221763, 1325080644, 760907844, 1268977734, 1128409158, 1176834119, 305776710, 157816900, 676034635, 67727436, 5315620938, 1507911762, 507740243, 104853593, 5675509854, 5030813792, 791939168, 5476204642, 5237954659, 738252901, 1081329768, 305123432, 1007302763, 1124974705, 1589088369, 1194973299, 787331195, 1132673149, 1171554430, 5052289151, 5215592583, 5600751753, 79120522, 630237321, 1860565132, 5451978892, 6727825, 5225973907, 1501374615, 5089786008, 524929177, 5277792410, 586029209, 5199341724, 1330782360, 1418571932, 1370306721, 6182533281, 720296100, 5475729573, 732512421, 5237350570, 1232339116, 289593517, 5251131566, 773488815, 5635727535, 932452530, 5401266357, 5598275766, 414318778, 2077425851, 5623771323, 1325932732, 836350139, 2128810177, 1077815490, 1795150018, 1128089796, 1483823297, 1771739346, 5000022227, 388894933, 5840857302, 1510985947, 873896155, 1019162846, 5013420256, 5254267105, 1553205474, 6109163749, 5171433704, 1344172268, 944128239, 1119389936, 273524979, 373674229, 5764417782, 1307986166, 838316281, 690579707, 1197644031, 976552192, 1125183744, 536815874, 101040390, 5056096519, 1014313227, 5603637516, 677081358, 1985816851, 972359959, 5310157080, 383312153, 5530882331, 5963491612, 852666653, 6149257501, 44095775, 1078608160, 1099073823, 948625701, 1607932199, 1175855403, 5045616941, 675893550, 900292911, 6016825650, 11630898, 394400053, 5347365175, 5595511096, 1269367097, 6075134266, 1258088764, 1112348989, 636207422, 144234822, 2133707081, 1905291594, 1531023691, 5032081740, 36114765, 1210618188, 1343488343, 218308952, 5165369692, 1214605664, 5784688992, 6004449635, 136145252, 828555620, 1544677735, 963637612, 1101498733, 5386740078, 5491390831, 564384112, 478108016, 479746418, 755540339, 211411316, 1732778357, 35551605, 5683429757, 256342401, 1488474498, 862982532, 717549957, 1925910927, 628117904, 1291815312, 2032540047, 1025044884, 210704789, 5726919061, 1704389016, 2042268056, 877633949, 1539078557, 387563935, 5577695650, 537586086, 1077338539, 1202825646, 629746102, 215310774, 944159161, 1578813890, 980754882, 1710197192, 340244939, 270780875, 1259839949, 801857996, 1064497615, 1348049359, 618521035, 1993908691, 1057360341, 1323614678, 1512292823, 958335447, 2005101018, 877359578, 861802974, 5230485983, 536941024, 5874325984, 525005282, 5852922339, 6406622, 42740197, 5336332774, 1176795624, 5164085739, 1312631275, 1660873197, 5708050925, 5561704941, 1082595819, 1724688885, 5289992694, 1639760376, 1255490045, 715741694, 2086119937, 793864706, 1092059651, 60703240, 5000020491, 984625675, 1548624395, 53975563, 61174284, 850270735, 5494723094, 975053335, 786084383, 692884004, 5114223141, 5534321190, 5611385382, 5052801576, 1774024623, 5022155308, 685898286, 1382949427, 5019591219, 5043524149, 5474097719, 5256356408, 1287610940, 5977977408, 1390719553, 5002936898, 676057669, 1217049157, 1148232261, 898376264, 1021745738, 1300200013, 1228376654, 1452366415, 160383568, 1036079699, 573102678, 552485462, 30310999, 1090939482, 1801341535, 492888672, 474933856, 7203424, 988910179, 16134757, 351195750, 1006889575, 5462632, 5870998119, 146539110, 1305373285, 1421701740, 1299839602, 5856354930, 350368373, 685841021, 891579006, 6288355967, 913007232, 1458420352, 2141870723, 1578377860, 5294539397, 916568707, 1223574165, 433969818, 1310935707, 6166364827, 5106348701, 489331358, 5672037025, 1068245666, 5219639970, 1146622626, 1576309413, 1876908710, 749486758, 383292068, 1489574571, 1282495147, 7191211, 2101971630, 554607279, 5602269872, 538372788, 139811512, 6273295038, 2061669054, 1963360964, 1307577029, 394404549, 1034017479, 6037340875, 5867602636, 1118925, 220865232, 332776145, 6208113372, 1542193885, 823624414, 491152096, 1196595937, 310368992, 1004911331, 1734740708, 484498149, 1472056038, 6213294823, 608959204, 906511082, 1926032108, 5483680493, 1332294382, 1964880626, 893211378, 211329780, 10005237, 2034776819, 719747831, 1495675641, 5370485499, 759775997, 396997375, 1351131904, 1694034689, 625588994, 6254711555, 635460356, 811707135, 1617353480, 1006277392, 810818321, 765969171, 1082682135, 433320730, 561322779, 5051138844, 1450201887, 1290955552, 7611179, 921924396, 516322093, 195228465, 6259538740, 292918071, 699654968, 5600277306, 5028707131, 1405408060, 1029905212, 1077826365, 629467976, 6763340, 5184461645, 5893153616, 325526352, 1165273939, 1240220503, 6282570587, 1758317408, 1163289440, 1476440930, 1353175907, 859470693, 1962969957, 975461225, 1134310250, 434355051, 5380453239, 505103225, 5158411130, 2111316859, 316861308, 1364900733, 144161659, 1873357697, 6270120835, 2121198468, 940688263, 1852296072, 1713830796, 268745615, 1907239824, 1150018448, 1250937749, 1019814806, 5824428956, 1038967711, 5039180704, 780233633, 1303466912, 251141027, 1381913508, 11176872, 5472244651, 796384173, 416465840, 1470815154, 1006390195, 1549632436, 908831671, 1125899192, 1491755960, 12506044, 1352733629, 5869687745, 920468427, 5595139019, 951905230, 5404027854, 195068884, 1738587098, 218983387, 1105189852, 988140508, 786901982, 152648671, 486321120, 6035211235, 1207503845, 611959785, 929919979, 839042028, 5620964337, 873171955, 172954615, 10757112, 1511693307, 5705501694, 5679074303, 1093886975, 1775621120, 137536512, 1315054594, 1913326597, 1290181639, 711826444, 5864582159, 5484891154, 424303636, 2125579284, 334277655, 154065947, 5524057122, 510792742, 1555475497, 1229429805, 6162492461, 775787571, 1085592629, 1790426165, 5109279801, 1271149631, 1047561281, 905339971, 1382607943, 1477463115, 792990798, 1287681102, 1237412942, 513254486, 5155931222, 100822102, 1941759065, 9733209, 6072097884, 1636318302, 5538972773, 5462252649, 697025642, 419292265, 702852201, 5514472559, 723305584, 433882230, 1420070007, 1098722423, 2008149116, 1150342269, 1780481149, 14918783, 1111391360, 871298177, 1662774402, 1672115331, 5963865219, 6024723587, 5476099207, 279450760, 439524497, 835996819, 1420344470, 262890648, 1225215131, 1302992028, 649114784, 1124502694, 1744960678, 1226493096, 5274303658, 2076888238, 5040929969, 5670423732, 1408152760, 6224860345, 909470906, 1122759876, 828351687, 485534926, 1269488847, 6689999, 2093747407, 223212754, 1437289686, 1066505431, 980696281, 1561148633, 547091675, 1203037404, 241349853, 663694558, 400346337, 1947512039, 6160813287, 142073065, 5340110057, 822635755, 1020210407, 800648429, 5438700779, 5155626223, 1177990383, 1513024753, 5556643059, 1774515444, 565304566, 574870778, 2131193082, 2103162113, 1026637061, 840199434, 1589607692, 1284261135, 1937423635, 753245463, 1383085335, 10089753, 5023098139, 900197661, 739781918, 725099806, 1787075872, 6004866337, 977009956, 165369124, 1684546858, 2135790892, 782402861, 820284717, 946476339, 1760040244, 5647656248, 980653368, 211027264, 272842049, 5683576132, 330511685, 404675912, 1562508618, 314367308, 5686605, 7501132, 1497304405, 416077141, 134151511, 1341582687, 5303887200, 5463225699, 1317277037, 981638509, 5351490927, 1142146416, 766823793, 1761052018, 1105261944, 218090873, 1141050748, 3288449, 833109378, 8957314, 1051966855, 2099842439, 17513864, 53026186, 645262734, 487542159, 971597200, 699798930, 1250575764, 834332055, 1028001176, 846251418, 540267931, 414262685, 5372284321, 6197298595, 182222243, 1420156323, 1579115940, 1997239715, 1251945897, 1729994155, 799479212, 5860300204, 792843693, 1966149040, 383460784, 1600660917, 5499622841, 1004490169, 617504186, 1339741631, 5227695553, 350729666, 961828291, 1124257222, 5491508680, 1662242250, 558974410, 9250257, 453141970, 5809595859, 211631570, 1008586197, 6117002711, 1293671896, 1370539481, 1772023255, 7443934, 489432545, 6895076, 1094061541, 1719385576, 1431139817, 921683433, 5010161131, 5630559725, 887256558, 643261940, 1851700728, 4251129, 903593465, 1261350396, 1392897532, 673777152, 1755942402, 400070146, 5084966406, 1445529095, 6204872199, 922146313, 5400899081, 5204624904, 6069888524, 6182792717, 5642370575, 1668228627, 177194518, 669949464, 1549293082, 831739418, 139992603, 1208555035, 1707660826, 1947973151, 94846496, 5201454623, 881628706, 742108707, 620195365, 791645734, 1893709355, 434216494, 76717614, 838569520, 1542686256, 1006829106, 2122745394, 470982196, 1034819124, 5466471994, 1699454523, 1048337982, 700812863, 5864580672, 5411630657, 16864838, 287374919, 361416262, 848655945, 707026508, 5873098317, 9121359, 1585364560, 127518290, 205663826, 1565496916, 994485845, 6702683, 1686842973, 5774319197, 646770270, 5418585697, 5461194338, 5011981924, 854060645, 169283175, 1749458537, 1678995050, 5580602989, 1447929454, 2116965997, 6138424948, 1128490617, 1513782906, 607116926, 5142296190, 5726094976, 5432131203, 35163779, 1865854598, 93349516, 1265565324, 721321614, 5454026383, 313091728, 14331534, 5368921749, 1856509591, 1479358111, 988382882, 141850278, 1512099497, 998962857, 5165047473, 1364401849, 5767669442, 528684739, 6082811588, 783961797, 758372036, 214204103, 1279270596, 1687269061, 5424836, 1653442253, 893132495, 747464405, 5775214293, 5213220568, 324069087, 1338336992, 611624673, 904900322, 937834211, 124796645, 1160814309, 784676583, 6038163176, 1201764073, 5178054377, 570423019, 287203051, 1546807022, 1650353904, 11329264, 1103693554, 1683824372, 907484916, 2022434551, 1797158647, 974581497, 107128570, 5224294137, 601806588, 18722558, 16420611, 5425866503, 923604744, 688826120, 1671053066, 714723086, 5338113807, 1122578192, 20832019, 210396948, 1971826455, 820041498, 227964699, 1189820193, 5616066338, 1256349477, 432144168, 6118383402, 1055686443, 5961338669, 5583904558, 5545398063, 5652819758, 6136944436, 912062262, 902209336, 373524290, 671498055, 5158313802, 1058873162, 673711951, 1080524631, 876715864, 1000046425, 454274906, 903290715, 1461485400, 5160185688, 11380569, 6080464735, 6230544224, 1367406431, 1933684572, 14319452, 623226727, 498798440, 1427320680, 1688686442, 600102759, 995540842, 100183920, 1579687792, 1253029750, 5876580219, 5342070652, 6098026366, 1633003393, 6043434884, 5353922437, 1401698182, 2023384969, 959162254, 804067221, 339320726, 5599688599, 2023104410, 1148043163, 74225564, 1455540126, 5621665696, 401860513, 172199844, 798496677, 5080977317, 6105884583, 80203691, 5073366957, 1080045486, 726351791, 1887649710, 1248946097, 946069426, 5326147507, 5981616052, 1081370544, 547659699, 475422642, 642999, 1948491705, 1233332156, 5864841153, 1290160066, 3786694, 615479238, 1639186376, 878671814, 1418610634, 5849720779, 585088972, 5208004559, 1988304847, 5650110417, 1016745939, 1314138068, 7167964, 1377384424, 65746922, 5936754668, 1910239214, 5469163503, 564692978, 6676467, 986384372, 1040648182, 1748502520, 782252025, 5358643195, 1526722557}\n" 57 | ] 58 | } 59 | ], 60 | "source": [ 61 | "newsletter_id = \"we_are_live_again\"\n", 62 | "\n", 63 | "db.create_newsletter(newsletter_id)\n", 64 | "already_sent_to_user_ids = set(db.get_newsletter_attribute(newsletter_id, \"already_sent_to_user_ids\"))\n", 65 | "\n", 66 | "print(f\"Already sent to users: {already_sent_to_user_ids}\")" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 4, 72 | "id": "0c03345d", 73 | "metadata": {}, 74 | "outputs": [ 75 | { 76 | "name": "stdout", 77 | "output_type": "stream", 78 | "text": [ 79 | "Found 7644 users\n" 80 | ] 81 | } 82 | ], 83 | "source": [ 84 | "# user_dicts = list(db.user_collection.find({\"username\": \"karfly\"}))\n", 85 | "user_dicts = list(db.user_collection.find({\"first_seen\": {\"$gte\": datetime.datetime(2023, 3, 18)}}))\n", 86 | "print(f\"Found {len(user_dicts)} users\")" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "id": "17a40c58", 93 | "metadata": {}, 94 | "outputs": [ 95 | { 96 | "name": "stdout", 97 | "output_type": "stream", 98 | "text": [ 99 | "Skipping 1390719553. Already sent before\n", 100 | "Skipping 5454026383. Already sent before\n", 101 | "Skipping 10757112. Already sent before\n", 102 | "Skipping 922146313. Already sent before\n", 103 | "Skipping 1093886975. Already sent before\n", 104 | "Skipping 1472056038. Already sent before\n", 105 | "Skipping 977009956. Already sent before\n", 106 | "Skipping 5158313802. Already sent before\n", 107 | "Skipping 573102678. Already sent before\n", 108 | "Skipping 401860513. Already sent before\n", 109 | "Skipping 1549293082. Already sent before\n", 110 | "Skipping 396997375. Already sent before\n", 111 | "Skipping 1077338539. Already sent before\n", 112 | "Skipping 42740197. Already sent before\n", 113 | "Skipping 988382882. Already sent before\n", 114 | "Skipping 1450201887. Already sent before\n", 115 | "Skipping 1134310250. Already sent before\n", 116 | "Skipping 1455540126. Already sent before\n", 117 | "Skipping 1749458537. Already sent before\n", 118 | "Skipping 554607279. Already sent before\n", 119 | "Skipping 944159161. Already sent before\n", 120 | "Skipping 804155431. Already sent before\n", 121 | "Skipping 1111391360. Already sent before\n", 122 | "Skipping 1014313227. Already sent before\n", 123 | "Skipping 1578813890. Already sent before\n", 124 | "Skipping 134151511. Already sent before\n", 125 | "Skipping 1000046425. Already sent before\n", 126 | "Skipping 1223574165. Already sent before\n", 127 | "Skipping 838569520. Already sent before\n", 128 | "Skipping 931246118. Already sent before\n", 129 | "Skipping 1585364560. Already sent before\n", 130 | "Skipping 1639760376. Already sent before\n", 131 | "Skipping 1150342269. Already sent before\n", 132 | "Skipping 800648429. Already sent before\n", 133 | "Skipping 908831671. Already sent before\n", 134 | "Skipping 913007232. Already sent before\n", 135 | "Skipping 100183920. Already sent before\n", 136 | "Skipping 302929939. Already sent before\n", 137 | "Skipping 1893709355. Already sent before\n", 138 | "Skipping 433320730. Already sent before\n", 139 | "Skipping 1801341535. Already sent before\n", 140 | "Skipping 107128570. Already sent before\n", 141 | "Skipping 1704389016. Already sent before\n", 142 | "Skipping 1418610634. Already sent before\n", 143 | "Failed to send message to 980937318. Reason: Forbidden: bot was blocked by the user\n", 144 | "Skipping 11176872. Already sent before\n", 145 | "Skipping 5534321190. Already sent before\n", 146 | "Skipping 5073366957. Already sent before\n", 147 | "Skipping 5418585697. Already sent before\n", 148 | "Skipping 2005101018. Already sent before\n", 149 | "Skipping 433969818. Already sent before\n", 150 | "Failed to send message to 726275073. Reason: Forbidden: bot was blocked by the user\n", 151 | "Skipping 1271149631. Already sent before\n", 152 | "Skipping 738252901. Already sent before\n", 153 | "Skipping 831739418. Already sent before\n", 154 | "Skipping 2103162113. Already sent before\n", 155 | "Failed to send message to 1536898027. Reason: Forbidden: bot was blocked by the user\n", 156 | "Skipping 891579006. Already sent before\n", 157 | "Skipping 1926032108. Already sent before\n", 158 | "Skipping 1748502520. Already sent before\n", 159 | "Failed to send message to 1970775756. Reason: Forbidden: bot was blocked by the user\n", 160 | "Skipping 3786694. Already sent before\n", 161 | "Skipping 313091728. Already sent before\n", 162 | "Skipping 1332294382. Already sent before\n", 163 | "Skipping 753245463. Already sent before\n", 164 | "Skipping 5621665696. Already sent before\n", 165 | "Skipping 643261940. Already sent before\n", 166 | "Skipping 18722558. Already sent before\n", 167 | "Skipping 951905230. Already sent before\n", 168 | "Skipping 1229429805. Already sent before\n", 169 | "Skipping 1189820193. Already sent before\n", 170 | "Skipping 241349853. Already sent before\n", 171 | "Skipping 776855585. Already sent before\n", 172 | "Skipping 1048337982. Already sent before\n", 173 | "Skipping 5351490927. Already sent before\n", 174 | "Skipping 1268977734. Already sent before\n", 175 | "Skipping 786084383. Already sent before\n", 176 | "Skipping 5864582159. Already sent before\n", 177 | "Skipping 127518290. Already sent before\n", 178 | "Skipping 1085592629. Already sent before\n", 179 | "Skipping 144234822. Already sent before\n", 180 | "Skipping 2022434551. Already sent before\n", 181 | "Skipping 6004866337. Already sent before\n", 182 | "Skipping 1080045486. Already sent before\n", 183 | "Skipping 332776145. Already sent before\n", 184 | "Skipping 404675912. Already sent before\n", 185 | "Skipping 1790426165. Already sent before\n", 186 | "Skipping 1310935707. Already sent before\n", 187 | "Skipping 5462632. Already sent before\n", 188 | "Skipping 1699454523. Already sent before\n", 189 | "Skipping 1686842973. Already sent before\n", 190 | "Skipping 1119389936. Already sent before\n", 191 | "Skipping 1865854598. Already sent before\n", 192 | "Skipping 921924396. Already sent before\n", 193 | "Skipping 1122759876. Already sent before\n", 194 | "Failed to send message to 2092138910. Reason: Forbidden: bot was blocked by the user\n", 195 | "Skipping 799479212. Already sent before\n", 196 | "Skipping 6038163176. Already sent before\n", 197 | "Skipping 1092059651. Already sent before\n", 198 | "Skipping 6230544224. Already sent before\n", 199 | "Skipping 5705501694. Already sent before\n", 200 | "Skipping 1780481149. Already sent before\n", 201 | "Skipping 1051966855. Already sent before\n", 202 | "Skipping 574870778. Already sent before\n", 203 | "Skipping 5386740078. Already sent before\n", 204 | "Skipping 791645734. Already sent before\n", 205 | "Skipping 2086119937. Already sent before\n", 206 | "Skipping 218090873. Already sent before\n", 207 | "Skipping 873171955. Already sent before\n", 208 | "Skipping 5336332774. Already sent before\n", 209 | "Skipping 491152096. Already sent before\n", 210 | "Skipping 5466471994. Already sent before\n", 211 | "Failed to send message to 1407643629. Reason: Forbidden: bot was blocked by the user\n", 212 | "Skipping 607116926. Already sent before\n", 213 | "Skipping 871298177. Already sent before\n", 214 | "Skipping 1344172268. Already sent before\n", 215 | "Skipping 564384112. Already sent before\n", 216 | "Skipping 6117002711. Already sent before\n", 217 | "Skipping 1636318302. Already sent before\n", 218 | "Skipping 1510985947. Already sent before\n", 219 | "Skipping 5670423732. Already sent before\n", 220 | "Skipping 6016825650. Already sent before\n", 221 | "Skipping 1512099497. Already sent before\n", 222 | "Skipping 6270120835. Already sent before\n", 223 | "Skipping 862982532. Already sent before\n", 224 | "Skipping 903290715. Already sent before\n", 225 | "Skipping 1232339116. Already sent before\n", 226 | "Failed to send message to 5992071686. Reason: Forbidden: bot was blocked by the user\n", 227 | "Skipping 24221763. Already sent before\n", 228 | "Skipping 210704789. Already sent before\n", 229 | "Skipping 6037340875. Already sent before\n", 230 | "Skipping 726351791. Already sent before\n", 231 | "Skipping 747464405. Already sent before\n", 232 | "Failed to send message to 1067402085. Reason: Forbidden: bot was blocked by the user\n", 233 | "Skipping 1255490045. Already sent before\n", 234 | "Skipping 818696222. Already sent before\n", 235 | "Skipping 2133707081. Already sent before\n", 236 | "Skipping 798496677. Already sent before\n", 237 | "Skipping 1077815490. Already sent before\n", 238 | "Failed to send message to 1081697845. Reason: Forbidden: bot was blocked by the user\n", 239 | "Skipping 1774515444. Already sent before\n", 240 | "Skipping 5370485499. Already sent before\n", 241 | "Failed to send message to 1600969598. Reason: Forbidden: bot was blocked by the user\n", 242 | "Skipping 988910179. Already sent before\n", 243 | "Skipping 1203037404. Already sent before\n", 244 | "Skipping 5600277306. Already sent before\n", 245 | "Skipping 489331358. Already sent before\n", 246 | "Skipping 1124502694. Already sent before\n", 247 | "Skipping 6105884583. Already sent before\n", 248 | "Skipping 860700699. Already sent before\n", 249 | "Skipping 5401266357. Already sent before\n", 250 | "Skipping 538372788. Already sent before\n", 251 | "Skipping 5045616941. Already sent before\n", 252 | "Skipping 5164085739. Already sent before\n", 253 | "Skipping 5647656248. Already sent before\n", 254 | "Skipping 528684739. Already sent before\n", 255 | "Skipping 6072097884. Already sent before\n", 256 | "Skipping 1141050748. Already sent before\n", 257 | "Skipping 1948491705. Already sent before\n", 258 | "Skipping 1160814309. Already sent before\n", 259 | "Skipping 67727436. Already sent before\n", 260 | "Failed to send message to 94985126. Reason: Forbidden: bot was blocked by the user\n", 261 | "Skipping 1420070007. Already sent before\n", 262 | "Skipping 211329780. Already sent before\n", 263 | "Skipping 881628706. Already sent before\n", 264 | "Skipping 5233057849. Already sent before\n", 265 | "Skipping 5726919061. Already sent before\n", 266 | "Skipping 1660873197. Already sent before\n", 267 | "Skipping 536941024. Already sent before\n", 268 | "Skipping 5679074303. Already sent before\n", 269 | "Skipping 5056096519. Already sent before\n", 270 | "Skipping 1057360341. Already sent before\n", 271 | "Failed to send message to 821670964. Reason: Forbidden: bot was blocked by the user\n", 272 | "Skipping 1124974705. Already sent before\n", 273 | "Skipping 5708050925. Already sent before\n", 274 | "Skipping 287374919. Already sent before\n", 275 | "Skipping 873896155. Already sent before\n", 276 | "Skipping 1738587098. Already sent before\n", 277 | "Skipping 5080977317. Already sent before\n", 278 | "Skipping 485534926. Already sent before\n", 279 | "Skipping 1214605664. Already sent before\n", 280 | "Failed to send message to 13269077. Reason: Forbidden: bot was blocked by the user\n", 281 | "Skipping 5461194338. Already sent before\n", 282 | "Skipping 1098722423. Already sent before\n", 283 | "Skipping 1941759065. Already sent before\n", 284 | "Skipping 6035211235. Already sent before\n", 285 | "Skipping 1078608160. Already sent before\n", 286 | "Skipping 424303636. Already sent before\n", 287 | "Skipping 1290160066. Already sent before\n", 288 | "Skipping 5874325984. Already sent before\n", 289 | "Skipping 498798440. Already sent before\n", 290 | "Skipping 5789966391. Already sent before\n", 291 | "Skipping 1546807022. Already sent before\n", 292 | "Skipping 1937423635. Already sent before\n", 293 | "Skipping 994485845. Already sent before\n", 294 | "Skipping 5368921749. Already sent before\n", 295 | "Skipping 976552192. Already sent before\n", 296 | "Skipping 1553205474. Already sent before\n", 297 | "Skipping 79120522. Already sent before\n", 298 | "Skipping 1672115331. Already sent before\n", 299 | "Skipping 1259839949. Already sent before\n", 300 | "Skipping 432144168. Already sent before\n", 301 | "Skipping 5686605. Already sent before\n", 302 | "Skipping 6213294823. Already sent before\n", 303 | "Skipping 1476440930. Already sent before\n", 304 | "Skipping 5852922339. Already sent before\n", 305 | "Skipping 1966149040. Already sent before\n", 306 | "Skipping 792990798. Already sent before\n", 307 | "Skipping 6197298595. Already sent before\n", 308 | "Skipping 2111316859. Already sent before\n", 309 | "Skipping 5600751753. Already sent before\n", 310 | "Skipping 394400053. Already sent before\n", 311 | "Skipping 524929177. Already sent before\n", 312 | "Skipping 1512292823. Already sent before\n", 313 | "Skipping 840199434. Already sent before\n", 314 | "Skipping 1352733629. Already sent before\n", 315 | "Failed to send message to 5407093621. Reason: Forbidden: bot was blocked by the user\n", 316 | "Skipping 5230485983. Already sent before\n", 317 | "Skipping 53026186. Already sent before\n", 318 | "Skipping 630237321. Already sent before\n", 319 | "Skipping 94846496. Already sent before\n", 320 | "Skipping 454274906. Already sent before\n", 321 | "Skipping 1250575764. Already sent before\n", 322 | "Skipping 1925283840. Already sent before\n", 323 | "Skipping 384137216. Already sent before\n", 324 | "Skipping 700812863. Already sent before\n", 325 | "Skipping 904900322. Already sent before\n", 326 | "Skipping 5165369692. Already sent before\n", 327 | "Skipping 1343488343. Already sent before\n", 328 | "Skipping 1253029750. Already sent before\n", 329 | "Skipping 900197661. Already sent before\n", 330 | "Skipping 1542193885. Already sent before\n", 331 | "Skipping 5084966406. Already sent before\n", 332 | "Skipping 6177658908. Already sent before\n", 333 | "Skipping 1662774402. Already sent before\n", 334 | "Skipping 350729666. Already sent before\n", 335 | "Skipping 2121198468. Already sent before\n", 336 | "Skipping 1875122198. Already sent before\n", 337 | "Skipping 5538972773. Already sent before\n", 338 | "Skipping 2023104410. Already sent before\n", 339 | "Skipping 717549957. Already sent before\n", 340 | "Skipping 822635755. Already sent before\n", 341 | "Skipping 1633003393. Already sent before\n", 342 | "Skipping 1947973151. Already sent before\n", 343 | "Skipping 1856509591. Already sent before\n", 344 | "Skipping 5199341724. Already sent before\n", 345 | "Skipping 629866496. Already sent before\n", 346 | "Skipping 434355051. Already sent before\n", 347 | "Skipping 1887649710. Already sent before\n", 348 | "Failed to send message to 1130794177. Reason: Forbidden: bot was blocked by the user\n", 349 | "Skipping 1105261944. Already sent before\n", 350 | "Skipping 5491508680. Already sent before\n", 351 | "Skipping 2077425851. Already sent before\n", 352 | "Skipping 5155626223. Already sent before\n", 353 | "Skipping 887256558. Already sent before\n", 354 | "Skipping 5873098317. Already sent before\n", 355 | "Skipping 5864841153. Already sent before\n", 356 | "Skipping 5484891154. Already sent before\n", 357 | "Skipping 5981616052. Already sent before\n", 358 | "Skipping 1302992028. Already sent before\n", 359 | "Skipping 36114765. Already sent before\n", 360 | "Skipping 152648671. Already sent before\n", 361 | "Skipping 766823793. Already sent before\n", 362 | "Skipping 7167964. Already sent before\n", 363 | "Skipping 142073065. Already sent before\n", 364 | "Skipping 11630898. Already sent before\n", 365 | "Skipping 1299839602. Already sent before\n", 366 | "Skipping 6075134266. Already sent before\n", 367 | "Skipping 1118925. Already sent before\n", 368 | "Skipping 5745397800. Already sent before\n", 369 | "Skipping 1925910927. Already sent before\n", 370 | "Skipping 5400899081. Already sent before\n", 371 | "Skipping 5876580219. Already sent before\n", 372 | "Skipping 1760040244. Already sent before\n", 373 | "Skipping 838316281. Already sent before\n", 374 | "Skipping 6288355967. Already sent before\n", 375 | "Skipping 1184688152. Already sent before\n", 376 | "Skipping 316861308. Already sent before\n", 377 | "Skipping 470982196. Already sent before\n", 378 | "Skipping 5580602989. Already sent before\n", 379 | "Skipping 2076888238. Already sent before\n", 380 | "Skipping 1964880626. Already sent before\n", 381 | "Skipping 1405408060. Already sent before\n", 382 | "Skipping 6080464735. Already sent before\n", 383 | "Skipping 1006829106. Already sent before\n", 384 | "Skipping 783961797. Already sent before\n", 385 | "Skipping 5623771323. Already sent before\n", 386 | "Skipping 2135790892. Already sent before\n", 387 | "Failed to send message to 1248978353. Reason: Forbidden: bot was blocked by the user\n", 388 | "Skipping 44095775. Already sent before\n", 389 | "Skipping 5530882331. Already sent before\n", 390 | "Skipping 60703240. Already sent before\n", 391 | "Skipping 649114784. Already sent before\n", 392 | "Skipping 1034017479. Already sent before\n", 393 | "Skipping 972359959. Already sent before\n", 394 | "Skipping 5326147507. Already sent before\n", 395 | "Skipping 5620964337. Already sent before\n", 396 | "Skipping 1302204419. Already sent before\n", 397 | "Skipping 537586086. Already sent before\n", 398 | "Failed to send message to 1402693515. Reason: Forbidden: bot was blocked by the user\n", 399 | "Skipping 787331195. Already sent before\n", 400 | "Skipping 74225564. Already sent before\n", 401 | "Skipping 1125899192. Already sent before\n", 402 | "Skipping 2128810177. Already sent before\n", 403 | "Skipping 1300200013. Already sent before\n", 404 | "Skipping 1579687792. Already sent before\n", 405 | "Skipping 350368373. Already sent before\n", 406 | "Skipping 2099842439. Already sent before\n", 407 | "Skipping 5277792410. Already sent before\n", 408 | "Skipping 742108707. Already sent before\n", 409 | "Skipping 6136944436. Already sent before\n", 410 | "Skipping 852666653. Already sent before\n", 411 | "Skipping 1420344470. Already sent before\n", 412 | "Skipping 1549632436. Already sent before\n", 413 | "Skipping 1006277392. Already sent before\n", 414 | "Skipping 10089753. Already sent before\n", 415 | "Skipping 1043486741. Already sent before\n", 416 | "Skipping 1797158647. Already sent before\n", 417 | "Skipping 673711951. Already sent before\n", 418 | "Skipping 5022155308. Already sent before\n", 419 | "Skipping 5514472559. Already sent before\n", 420 | "Skipping 1437289686. Already sent before\n", 421 | "Skipping 5000020491. Already sent before\n", 422 | "Failed to send message to 425760967. Reason: Forbidden: bot was blocked by the user\n", 423 | "Skipping 1445529095. Already sent before\n", 424 | "Skipping 1501374615. Already sent before\n", 425 | "Skipping 848655945. Already sent before\n", 426 | "Skipping 980696281. Already sent before\n", 427 | "Skipping 1312631275. Already sent before\n", 428 | "Skipping 1713830796. Already sent before\n", 429 | "Skipping 5561704941. Already sent before\n", 430 | "Skipping 1058873162. Already sent before\n", 431 | "Skipping 5867602636. Already sent before\n", 432 | "Skipping 561322779. Already sent before\n", 433 | "Skipping 1175855403. Already sent before\n", 434 | "Skipping 5603637516. Already sent before\n", 435 | "Skipping 1477463115. Already sent before\n", 436 | "Skipping 1006889575. Already sent before\n", 437 | "Skipping 5494723094. Already sent before\n", 438 | "Skipping 1351131904. Already sent before\n", 439 | "Skipping 1148043163. Already sent before\n", 440 | "Skipping 6082811588. Already sent before\n", 441 | "Failed to send message to 5213904503. Reason: Forbidden: bot was blocked by the user\n", 442 | "Skipping 1683824372. Already sent before\n", 443 | "Skipping 765969171. Already sent before\n", 444 | "Skipping 324069087. Already sent before\n", 445 | "Skipping 6098026366. Already sent before\n", 446 | "Skipping 1201764073. Already sent before\n", 447 | "Skipping 697025642. Already sent before\n", 448 | "Skipping 1287681102. Already sent before\n", 449 | "Skipping 586029209. Already sent before\n", 450 | "Skipping 1678995050. Already sent before\n", 451 | "Skipping 5774319197. Already sent before\n", 452 | "Skipping 1852296072. Already sent before\n", 453 | "Skipping 984625675. Already sent before\n", 454 | "Skipping 6149257501. Already sent before\n", 455 | "Failed to send message to 1291244268. Reason: Forbidden: bot was blocked by the user\n", 456 | "Skipping 5089786008. Already sent before\n", 457 | "Skipping 5784688992. Already sent before\n", 458 | "Skipping 1068245666. Already sent before\n", 459 | "Skipping 330511685. Already sent before\n", 460 | "Skipping 5583904558. Already sent before\n", 461 | "Skipping 492888672. Already sent before\n", 462 | "Skipping 1617353480. Already sent before\n", 463 | "Skipping 1330782360. Already sent before\n", 464 | "Skipping 195068884. Already sent before\n", 465 | "Skipping 5032081740. Already sent before\n", 466 | "Skipping 775787571. Already sent before\n", 467 | "Skipping 5462252649. Already sent before\n", 468 | "Skipping 210396948. Already sent before\n", 469 | "Skipping 5556643059. Already sent before\n", 470 | "Skipping 946069426. Already sent before\n", 471 | "Skipping 2125579284. Already sent before\n", 472 | "Skipping 1787075872. Already sent before\n", 473 | "Skipping 5152628746. Already sent before\n", 474 | "Skipping 629467976. Already sent before\n", 475 | "Skipping 5208004559. Already sent before\n", 476 | "Skipping 1860565132. Already sent before\n", 477 | "Skipping 540267931. Already sent before\n", 478 | "Skipping 5158411130. Already sent before\n", 479 | "Skipping 1851700728. Already sent before\n", 480 | "Skipping 93349516. Already sent before\n", 481 | "Skipping 182222243. Already sent before\n", 482 | "Skipping 5201454623. Already sent before\n", 483 | "Skipping 1325080644. Already sent before\n", 484 | "Skipping 5963865219. Already sent before\n", 485 | "Skipping 909470906. Already sent before\n", 486 | "Failed to send message to 5619240406. Reason: Forbidden: bot was blocked by the user\n", 487 | "Skipping 5040929969. Already sent before\n", 488 | "Skipping 1452366415. Already sent before\n", 489 | "Skipping 570423019. Already sent before\n", 490 | "Skipping 1210618188. Already sent before\n", 491 | "Skipping 218983387. Already sent before\n", 492 | "Skipping 5289992694. Already sent before\n", 493 | "Skipping 6727825. Already sent before\n", 494 | "Skipping 5963491612. Already sent before\n", 495 | "Skipping 628117904. Already sent before\n", 496 | "Failed to send message to 6145382722. Reason: Forbidden: bot was blocked by the user\n", 497 | "Skipping 1269488847. Already sent before\n", 498 | "Skipping 1491755960. Already sent before\n", 499 | "Skipping 5598275766. Already sent before\n", 500 | "Skipping 5310157080. Already sent before\n", 501 | "Skipping 5611385382. Already sent before\n", 502 | "Failed to send message to 12621125. Reason: Forbidden: bot was blocked by the user\n", 503 | "Skipping 635460356. Already sent before\n", 504 | "Skipping 1447929454. Already sent before\n", 505 | "Skipping 6689999. Already sent before\n", 506 | "Failed to send message to 1624335871. Reason: Forbidden: bot was blocked by the user\n", 507 | "Skipping 5340110057. Already sent before\n", 508 | "Skipping 676057669. Already sent before\n", 509 | "Skipping 5338113807. Already sent before\n", 510 | "Skipping 1668228627. Already sent before\n", 511 | "Skipping 205663826. Already sent before\n", 512 | "Skipping 1544677735. Already sent before\n", 513 | "Skipping 5142296190. Already sent before\n", 514 | "Skipping 1589088369. Already sent before\n", 515 | "Skipping 478108016. Already sent before\n", 516 | "Skipping 975461225. Already sent before\n", 517 | "Skipping 760907844. Already sent before\n", 518 | "Skipping 1876908710. Already sent before\n", 519 | "Skipping 1526722557. Already sent before\n", 520 | "Skipping 1507911762. Already sent before\n", 521 | "Skipping 5002936898. Already sent before\n", 522 | "Skipping 9121359. Already sent before\n", 523 | "Skipping 1265565324. Already sent before\n", 524 | "Skipping 5824428956. Already sent before\n", 525 | "Failed to send message to 1768925686. Reason: Forbidden: bot was blocked by the user\n", 526 | "Skipping 714723086. Already sent before\n", 527 | "Skipping 507740243. Already sent before\n", 528 | "Skipping 1873357697. Already sent before\n", 529 | "Skipping 1165273939. Already sent before\n", 530 | "Skipping 5225973907. Already sent before\n", 531 | "Skipping 1006390195. Already sent before\n", 532 | "Skipping 980754882. Already sent before\n", 533 | "Skipping 5256356408. Already sent before\n", 534 | "Skipping 1513782906. Already sent before\n", 535 | "Skipping 5051138844. Already sent before\n", 536 | "Failed to send message to 1516697887. Reason: Forbidden: bot was blocked by the user\n", 537 | "Skipping 1112348989. Already sent before\n", 538 | "Skipping 1314138068. Already sent before\n", 539 | "Skipping 636207422. Already sent before\n", 540 | "Skipping 833109378. Already sent before\n", 541 | "Skipping 5347365175. Already sent before\n", 542 | "Skipping 5251131566. Already sent before\n", 543 | "Failed to send message to 78205988. Reason: Forbidden: bot was blocked by the user\n", 544 | "Skipping 5627299861. Already sent before\n", 545 | "Skipping 139992603. Already sent before\n", 546 | "Skipping 1758317408. Already sent before\n", 547 | "Skipping 1947512039. Already sent before\n", 548 | "Skipping 1427320680. Already sent before\n", 549 | "Skipping 6162492461. Already sent before\n", 550 | "Skipping 6160813287. Already sent before\n", 551 | "Skipping 685841021. Already sent before\n", 552 | "Skipping 1724688885. Already sent before\n", 553 | "Skipping 1381913508. Already sent before\n", 554 | "Skipping 1026637061. Already sent before\n", 555 | "Skipping 1775621120. Already sent before\n", 556 | "Skipping 1685334053. Already sent before\n", 557 | "Skipping 744362015. Already sent before\n", 558 | "Skipping 1261350396. Already sent before\n", 559 | "Skipping 1090939482. Already sent before\n", 560 | "Skipping 1240220503. Already sent before\n", 561 | "Skipping 5630559725. Already sent before\n", 562 | "Skipping 6273295038. Already sent before\n", 563 | "Skipping 961828291. Already sent before\n", 564 | "Skipping 5650110417. Already sent before\n", 565 | "Skipping 1993908691. Already sent before\n", 566 | "Skipping 1128409158. Already sent before\n", 567 | "Skipping 1734740708. Already sent before\n", 568 | "Skipping 5476099207. Already sent before\n", 569 | "Failed to send message to 1220813167. Reason: Forbidden: bot was blocked by the user\n", 570 | "Failed to send message to 477808361. Reason: Forbidden: bot was blocked by the user\n", 571 | "Skipping 971597200. Already sent before\n", 572 | "Skipping 782402861. Already sent before\n", 573 | "Skipping 1105189852. Already sent before\n", 574 | "Skipping 5294539397. Already sent before\n", 575 | "Skipping 1258088764. Already sent before\n", 576 | "Skipping 5870998119. Already sent before\n", 577 | "Skipping 5215592583. Already sent before\n", 578 | "Skipping 5404027854. Already sent before\n", 579 | "Skipping 1470815154. Already sent before\n", 580 | "Skipping 5274303658. Already sent before\n", 581 | "Skipping 214204103. Already sent before\n", 582 | "Skipping 1248946097. Already sent before\n", 583 | "Skipping 1317277037. Already sent before\n", 584 | "Skipping 457881611. Already sent before\n", 585 | "Skipping 6702683. Already sent before\n", 586 | "Skipping 1985816851. Already sent before\n", 587 | "Skipping 5043524149. Already sent before\n", 588 | "Skipping 340244939. Already sent before\n", 589 | "Skipping 1081329768. Already sent before\n", 590 | "Skipping 564692978. Already sent before\n", 591 | "Skipping 1479358111. Already sent before\n", 592 | "Skipping 1124257222. Already sent before\n", 593 | "Skipping 5411630657. Already sent before\n", 594 | "Skipping 5483680493. Already sent before\n", 595 | "Failed to send message to 1619635603. Reason: Forbidden: bot was blocked by the user\n", 596 | "Skipping 1128089796. Already sent before\n", 597 | "Skipping 1290181639. Already sent before\n", 598 | "Skipping 1988304847. Already sent before\n", 599 | "Skipping 12506044. Already sent before\n", 600 | "Skipping 876715864. Already sent before\n", 601 | "Skipping 1671053066. Already sent before\n", 602 | "Skipping 5000022227. Already sent before\n", 603 | "Skipping 1284261135. Already sent before\n", 604 | "Failed to send message to 59392620. Reason: Forbidden: bot was blocked by the user\n", 605 | "Skipping 1495675641. Already sent before\n", 606 | "Skipping 5227695553. Already sent before\n", 607 | "Skipping 998962857. Already sent before\n", 608 | "Skipping 707026508. Already sent before\n", 609 | "Skipping 981638509. Already sent before\n", 610 | "Skipping 1971826455. Already sent before\n", 611 | "Skipping 623226727. Already sent before\n", 612 | "Skipping 1250937749. Already sent before\n", 613 | "Failed to send message to 5685434844. Reason: Forbidden: bot was blocked by the user\n", 614 | "Skipping 1269367097. Already sent before\n", 615 | "Skipping 5893153616. Already sent before\n", 616 | "Skipping 1907239824. Already sent before\n", 617 | "Skipping 1398102028. Already sent before\n", 618 | "Skipping 1458420352. Already sent before\n", 619 | "Skipping 1021745738. Already sent before\n", 620 | "Skipping 1548624395. Already sent before\n", 621 | "Skipping 2122745394. Already sent before\n", 622 | "Skipping 1204713497. Already sent before\n", 623 | "Skipping 1066505431. Already sent before\n", 624 | "Skipping 1338336992. Already sent before\n", 625 | "Failed to send message to 2046941811. Reason: Forbidden: bot was blocked by the user\n", 626 | "Skipping 5577695650. Already sent before\n", 627 | "Skipping 525005282. Already sent before\n", 628 | "Skipping 1771739346. Already sent before\n", 629 | "Skipping 1418571932. Already sent before\n", 630 | "Skipping 5726094976. Already sent before\n", 631 | "Skipping 484498149. Already sent before\n", 632 | "Skipping 5039180704. Already sent before\n", 633 | "Skipping 786901982. Already sent before\n", 634 | "Skipping 1080524631. Already sent before\n", 635 | "Skipping 1025044884. Already sent before\n", 636 | "Skipping 6182792717. Already sent before\n", 637 | "Skipping 1103693554. Already sent before\n", 638 | "Skipping 932452530. Already sent before\n", 639 | "Skipping 1461485400. Already sent before\n", 640 | "Skipping 854060645. Already sent before\n", 641 | "Skipping 828351687. Already sent before\n", 642 | "Failed to send message to 5857084141. Reason: Forbidden: bot was blocked by the user\n", 643 | "Failed to send message to 727936998. Reason: Forbidden: bot was blocked by the user\n", 644 | "Skipping 5358643195. Already sent before\n", 645 | "Skipping 53975563. Already sent before\n", 646 | "Skipping 1576309413. Already sent before\n", 647 | "Skipping 552485462. Already sent before\n", 648 | "Skipping 1081370544. Already sent before\n", 649 | "Skipping 1217049157. Already sent before\n", 650 | "Skipping 804067221. Already sent before\n", 651 | "Skipping 351195750. Already sent before\n", 652 | "Skipping 1377384424. Already sent before\n", 653 | "Skipping 5476204642. Already sent before\n", 654 | "Skipping 5499622841. Already sent before\n", 655 | "Skipping 141850278. Already sent before\n", 656 | "Skipping 839042028. Already sent before\n", 657 | "Skipping 146539110. Already sent before\n", 658 | "Skipping 1420156323. Already sent before\n", 659 | "Failed to send message to 861124808. Reason: Forbidden: bot was blocked by the user\n", 660 | "Skipping 227964699. Already sent before\n", 661 | "Skipping 1489574571. Already sent before\n", 662 | "Skipping 1194973299. Already sent before\n", 663 | "Failed to send message to 5652844635. Reason: Forbidden: bot was blocked by the user\n", 664 | "Skipping 2042268056. Already sent before\n", 665 | "Skipping 5353922437. Already sent before\n", 666 | "Skipping 6043434884. Already sent before\n", 667 | "Skipping 1607932199. Already sent before\n", 668 | "Skipping 416465840. Already sent before\n", 669 | "Skipping 565304566. Already sent before\n", 670 | "Skipping 793864706. Already sent before\n", 671 | "Skipping 946476339. Already sent before\n", 672 | "Skipping 5683429757. Already sent before\n", 673 | "Skipping 272842049. Already sent before\n", 674 | "Failed to send message to 1100360597. Reason: Forbidden: bot was blocked by the user\n", 675 | "Skipping 1688686442. Already sent before\n", 676 | "Skipping 715741694. Already sent before\n", 677 | "Skipping 1729994155. Already sent before\n", 678 | "Failed to send message to 716121149. Reason: Forbidden: bot was blocked by the user\n", 679 | "Skipping 1364401849. Already sent before\n", 680 | "Skipping 218308952. Already sent before\n", 681 | "Skipping 1047561281. Already sent before\n", 682 | "Skipping 1122578192. Already sent before\n", 683 | "Skipping 1732778357. Already sent before\n", 684 | "Skipping 1040648182. Already sent before\n", 685 | "Skipping 5642370575. Already sent before\n", 686 | "Skipping 1036079699. Already sent before\n", 687 | "Skipping 1034819124. Already sent before\n", 688 | "Skipping 325526352. Already sent before\n", 689 | "Skipping 690579707. Already sent before\n", 690 | "Skipping 6254711555. Already sent before\n", 691 | "Skipping 5030813792. Already sent before\n", 692 | "Skipping 1353175907. Already sent before\n", 693 | "Skipping 1019162846. Already sent before\n", 694 | "Skipping 1020210407. Already sent before\n", 695 | "Skipping 5028707131. Already sent before\n", 696 | "Skipping 2141870723. Already sent before\n", 697 | "Skipping 5475729573. Already sent before\n", 698 | "Skipping 893211378. Already sent before\n", 699 | "Skipping 1555475497. Already sent before\n", 700 | "Skipping 1653442253. Already sent before\n", 701 | "Skipping 270780875. Already sent before\n", 702 | "Skipping 796384173. Already sent before\n", 703 | "Skipping 5775214293. Already sent before\n", 704 | "Failed to send message to 6015997429. Reason: Forbidden: bot was blocked by the user\n", 705 | "Skipping 629746102. Already sent before\n", 706 | "Skipping 920468427. Already sent before\n", 707 | "Skipping 5380453239. Already sent before\n", 708 | "Skipping 758372036. Already sent before\n", 709 | "Skipping 5809595859. Already sent before\n", 710 | "Skipping 6118383402. Already sent before\n", 711 | "Skipping 5432131203. Already sent before\n", 712 | "Skipping 505103225. Already sent before\n", 713 | "Failed to send message to 1099538870. Reason: Forbidden: bot was blocked by the user\n", 714 | "Skipping 1370306721. Already sent before\n", 715 | "Skipping 1364900733. Already sent before\n", 716 | "Skipping 1565496916. Already sent before\n", 717 | "Skipping 1684546858. Already sent before\n", 718 | "Skipping 1408152760. Already sent before\n", 719 | "Skipping 5672037025. Already sent before\n", 720 | "Skipping 1176834119. Already sent before\n", 721 | "Skipping 861802974. Already sent before\n", 722 | "Skipping 5469163503. Already sent before\n", 723 | "Skipping 1325932732. Already sent before\n", 724 | "Skipping 1196595937. Already sent before\n", 725 | "Skipping 2061669054. Already sent before\n", 726 | "Skipping 1177990383. Already sent before\n", 727 | "Skipping 5599688599. Already sent before\n", 728 | "Skipping 6069888524. Already sent before\n", 729 | "Skipping 5219639970. Already sent before\n", 730 | "Skipping 44099595. Already sent before\n", 731 | "Skipping 1101498733. Already sent before\n", 732 | "Skipping 810818321. Already sent before\n", 733 | "Skipping 5160185688. Already sent before\n", 734 | "Skipping 1511693307. Already sent before\n", 735 | "Skipping 5856354930. Already sent before\n", 736 | "Skipping 5675509854. Already sent before\n", 737 | "Skipping 1341582687. Already sent before\n", 738 | "Skipping 1323614678. Already sent before\n", 739 | "Skipping 1099073823. Already sent before\n", 740 | "Skipping 1963360964. Already sent before\n", 741 | "Skipping 6047741970. Already sent before\n", 742 | "Skipping 474933856. Already sent before\n", 743 | "Skipping 677081358. Already sent before\n", 744 | "Skipping 671498055. Already sent before\n", 745 | "Skipping 600102759. Already sent before\n", 746 | "Skipping 685898286. Already sent before\n", 747 | "Skipping 1171554430. Already sent before\n", 748 | "Skipping 859470693. Already sent before\n", 749 | "Skipping 292918071. Already sent before\n", 750 | "Skipping 16134757. Already sent before\n", 751 | "Skipping 6166364827. Already sent before\n", 752 | "Skipping 1004911331. Already sent before\n", 753 | "Skipping 1163289440. Already sent before\n", 754 | "Skipping 1694034689. Already sent before\n", 755 | "Skipping 836350139. Already sent before\n", 756 | "Skipping 1279270596. Already sent before\n", 757 | "Skipping 963637612. Already sent before\n", 758 | "Skipping 749486758. Already sent before\n", 759 | "Skipping 5463225699. Already sent before\n", 760 | "Skipping 699654968. Already sent before\n", 761 | "Skipping 5425866503. Already sent before\n", 762 | "Skipping 547659699. Already sent before\n", 763 | "Skipping 434216494. Already sent before\n", 764 | "Skipping 1579115940. Already sent before\n", 765 | "Skipping 720296100. Already sent before\n", 766 | "Skipping 5023098139. Already sent before\n", 767 | "Skipping 137536512. Already sent before\n", 768 | "Skipping 877633949. Already sent before\n", 769 | "Skipping 1650353904. Already sent before\n", 770 | "Skipping 5764417782. Already sent before\n", 771 | "Skipping 5114223141. Already sent before\n", 772 | "Failed to send message to 1150507674. Reason: Forbidden: bot was blocked by the user\n", 773 | "Skipping 1382949427. Already sent before\n", 774 | "Skipping 136145252. Already sent before\n", 775 | "Failed to send message to 5116338. Reason: Forbidden: bot was blocked by the user\n", 776 | "Skipping 1719385576. Already sent before\n", 777 | "Skipping 1146622626. Already sent before\n", 778 | "Skipping 1112305693. Already sent before\n", 779 | "Skipping 1293671896. Already sent before\n", 780 | "Skipping 1687269061. Already sent before\n", 781 | "Skipping 625588994. Already sent before\n", 782 | "Skipping 287203051. Already sent before\n", 783 | "Skipping 828555620. Already sent before\n", 784 | "Skipping 1562508618. Already sent before\n", 785 | "Skipping 7443934. Already sent before\n", 786 | "Skipping 944128239. Already sent before\n", 787 | "Skipping 5864580672. Already sent before\n", 788 | "Skipping 513254486. Already sent before\n", 789 | "Skipping 80203691. Already sent before\n", 790 | "Skipping 2008149116. Already sent before\n", 791 | "Skipping 1370539481. Already sent before\n", 792 | "Skipping 5707505724. Already sent before\n", 793 | "Skipping 782252025. Already sent before\n", 794 | "Skipping 959162254. Already sent before\n", 795 | "Skipping 755540339. Already sent before\n", 796 | "Skipping 1228376654. Already sent before\n", 797 | "Skipping 373524290. Already sent before\n", 798 | "Skipping 215310774. Already sent before\n", 799 | "Skipping 61174284. Already sent before\n", 800 | "Skipping 1197644031. Already sent before\n", 801 | "Skipping 780233633. Already sent before\n", 802 | "Skipping 1004490169. Already sent before\n", 803 | "Skipping 6204872199. Already sent before\n", 804 | "Skipping 834332055. Already sent before\n", 805 | "Skipping 11329264. Already sent before\n", 806 | "Skipping 907484916. Already sent before\n", 807 | "Skipping 1307577029. Already sent before\n", 808 | "Skipping 1064497615. Already sent before\n", 809 | "Skipping 5451978892. Already sent before\n", 810 | "Skipping 305776710. Already sent before\n", 811 | "Skipping 1674692650. Already sent before\n", 812 | "Skipping 1150018448. Already sent before\n", 813 | "Skipping 1125183744. Already sent before\n", 814 | "Skipping 4251129. Already sent before\n", 815 | "Skipping 723305584. Already sent before\n", 816 | "Skipping 5602269872. Already sent before\n", 817 | "Skipping 5545398063. Already sent before\n", 818 | "Skipping 489432545. Already sent before\n", 819 | "Skipping 536815874. Already sent before\n", 820 | "Skipping 5372284321. Already sent before\n", 821 | "Skipping 1795150018. Already sent before\n", 822 | "Skipping 1382607943. Already sent before\n", 823 | "Failed to send message to 948613713. Reason: Forbidden: bot was blocked by the user\n", 824 | "Skipping 1128490617. Already sent before\n", 825 | "Skipping 5178054377. Already sent before\n", 826 | "Skipping 289593517. Already sent before\n", 827 | "Skipping 560042032. Already sent before\n", 828 | "Skipping 314367308. Already sent before\n", 829 | "Skipping 5254267105. Already sent before\n", 830 | "Skipping 414262685. Already sent before\n", 831 | "Skipping 646770270. Already sent before\n", 832 | "Skipping 2131193082. Already sent before\n", 833 | "Skipping 898376264. Already sent before\n", 834 | "Skipping 475422642. Already sent before\n", 835 | "Skipping 279450760. Already sent before\n", 836 | "Skipping 1497304405. Already sent before\n", 837 | "Skipping 65746922. Already sent before\n", 838 | "Skipping 1578377860. Already sent before\n", 839 | "Skipping 144161659. Already sent before\n", 840 | "Skipping 383312153. Already sent before\n", 841 | "Skipping 6215057431. Already sent before\n", 842 | "Skipping 5784668204. Already sent before\n", 843 | "Skipping 1421701740. Already sent before\n", 844 | "Skipping 1008586197. Already sent before\n", 845 | "Skipping 719747831. Already sent before\n", 846 | "Skipping 1997239715. Already sent before\n", 847 | "Skipping 906511082. Already sent before\n", 848 | "Skipping 1282495147. Already sent before\n", 849 | "Skipping 5019591219. Already sent before\n", 850 | "Skipping 940688263. Already sent before\n", 851 | "Skipping 9733209. Already sent before\n", 852 | "Skipping 5616066338. Already sent before\n", 853 | "Skipping 1082682135. Already sent before\n", 854 | "Skipping 1256349477. Already sent before\n", 855 | "Skipping 1142146416. Already sent before\n", 856 | "Failed to send message to 82150541. Reason: Forbidden: bot was blocked by the user\n", 857 | "Skipping 453141970. Already sent before\n", 858 | "Skipping 1639186376. Already sent before\n", 859 | "Skipping 20832019. Already sent before\n", 860 | "Skipping 30310999. Already sent before\n", 861 | "Failed to send message to 5963958257. Reason: Forbidden: bot was blocked by the user\n", 862 | "Skipping 10005237. Already sent before\n", 863 | "Skipping 139811512. Already sent before\n", 864 | "Failed to send message to 1818756776. Reason: Forbidden: bot was kicked from the supergroup chat\n", 865 | "Skipping 394404549. Already sent before\n", 866 | "Skipping 1291815312. Already sent before\n", 867 | "Skipping 1237412942. Already sent before\n", 868 | "Skipping 35551605. Already sent before\n", 869 | "Skipping 5869687745. Already sent before\n", 870 | "Skipping 220865232. Already sent before\n", 871 | "Skipping 1038967711. Already sent before\n", 872 | "Skipping 5011981924. Already sent before\n", 873 | "Skipping 5204624904. Already sent before\n", 874 | "Skipping 1772023255. Already sent before\n", 875 | "Skipping 11380569. Already sent before\n", 876 | "Failed to send message to 1927974811. Reason: Forbidden: bot was blocked by the user\n", 877 | "Skipping 1029905212. Already sent before\n", 878 | "Skipping 835996819. Already sent before\n", 879 | "Skipping 1207503845. Already sent before\n", 880 | "Skipping 251141027. Already sent before\n", 881 | "Skipping 5213220568. Already sent before\n", 882 | "Skipping 1207484464. Already sent before\n", 883 | "Skipping 791939168. Already sent before\n", 884 | "Skipping 1910239214. Already sent before\n", 885 | "Skipping 1905291594. Already sent before\n", 886 | "Skipping 1483823297. Already sent before\n", 887 | "Skipping 211631570. Already sent before\n", 888 | "Skipping 5652819758. Already sent before\n", 889 | "Skipping 608959204. Already sent before\n", 890 | "Skipping 256342401. Already sent before\n", 891 | "Skipping 2023384969. Already sent before\n", 892 | "Skipping 759775997. Already sent before\n", 893 | "Skipping 1348049359. Already sent before\n", 894 | "Skipping 986384372. Already sent before\n", 895 | "Skipping 2116965997. Already sent before\n", 896 | "Skipping 1488474498. Already sent before\n", 897 | "Skipping 1962969957. Already sent before\n", 898 | "Skipping 739781918. Already sent before\n", 899 | "Skipping 16864838. Already sent before\n", 900 | "Skipping 903593465. Already sent before\n", 901 | "Skipping 675893550. Already sent before\n", 902 | "Skipping 1755942402. Already sent before\n", 903 | "Skipping 877359578. Already sent before\n", 904 | "Skipping 5595511096. Already sent before\n", 905 | "Skipping 801857996. Already sent before\n", 906 | "Skipping 811707135. Already sent before\n", 907 | "Skipping 361416262. Already sent before\n", 908 | "Skipping 1082595819. Already sent before\n", 909 | "Skipping 820284717. Already sent before\n", 910 | "Skipping 6109163749. Already sent before\n", 911 | "Failed to send message to 1719358561. Reason: Forbidden: bot was kicked from the supergroup chat\n", 912 | "Skipping 16420611. Already sent before\n", 913 | "Skipping 13002777. Already sent before\n", 914 | "Skipping 383292068. Already sent before\n", 915 | "Skipping 645262734. Already sent before\n", 916 | "Skipping 5342070652. Already sent before\n", 917 | "Skipping 1307986166. Already sent before\n", 918 | "Skipping 383460784. Already sent before\n", 919 | "Skipping 1662242250. Already sent before\n", 920 | "Skipping 1913326597. Already sent before\n", 921 | "Skipping 846251418. Already sent before\n", 922 | "Failed to send message to 719450670. Reason: Forbidden: bot was blocked by the user\n", 923 | "Failed to send message to 650260973. Reason: Forbidden: bot was blocked by the user\n", 924 | "Skipping 618521035. Already sent before\n", 925 | "Skipping 1176795624. Already sent before\n", 926 | "Skipping 419292265. Already sent before\n", 927 | "Skipping 6763340. Already sent before\n", 928 | "Skipping 1542686256. Already sent before\n", 929 | "Skipping 5184461645. Already sent before\n", 930 | "Skipping 510792742. Already sent before\n", 931 | "Skipping 35163779. Already sent before\n", 932 | "Skipping 547091675. Already sent before\n", 933 | "Skipping 157816900. Already sent before\n", 934 | "Skipping 5106348701. Already sent before\n", 935 | "Skipping 310368992. Already sent before\n", 936 | "Skipping 433882230. Already sent before\n", 937 | "Skipping 5347454992. Already sent before\n", 938 | "Skipping 1339741631. Already sent before\n", 939 | "Skipping 5109279801. Already sent before\n", 940 | "Skipping 1431139817. Already sent before\n", 941 | "Skipping 958335447. Already sent before\n", 942 | "Skipping 617504186. Already sent before\n", 943 | "Skipping 104853593. Already sent before\n", 944 | "Skipping 487542159. Already sent before\n", 945 | "Skipping 702852201. Already sent before\n", 946 | "Skipping 5849720779. Already sent before\n", 947 | "Skipping 980653368. Already sent before\n", 948 | "Skipping 823624414. Already sent before\n", 949 | "Skipping 1132673149. Already sent before\n", 950 | "Skipping 988140508. Already sent before\n", 951 | "Skipping 1303466912. Already sent before\n", 952 | "Skipping 1589607692. Already sent before\n", 953 | "Skipping 211027264. Already sent before\n", 954 | "Skipping 6676467. Already sent before\n", 955 | "Skipping 916568707. Already sent before\n", 956 | "Skipping 1305373285. Already sent before\n", 957 | "Skipping 905339971. Already sent before\n", 958 | "Skipping 1744960678. Already sent before\n", 959 | "Skipping 995540842. Already sent before\n", 960 | "Skipping 5052801576. Already sent before\n", 961 | "Skipping 850270735. Already sent before\n", 962 | "Skipping 1600660917. Already sent before\n", 963 | "Skipping 1055686443. Already sent before\n", 964 | "Skipping 1077826365. Already sent before\n", 965 | "Skipping 387563935. Already sent before\n", 966 | "Skipping 305123432. Already sent before\n", 967 | "Skipping 5052289151. Already sent before\n", 968 | "Skipping 900292911. Already sent before\n", 969 | "Failed to send message to 429225873. Reason: Forbidden: bot was blocked by the user\n", 970 | "Failed to send message to 977529467. Reason: Forbidden: bot was blocked by the user\n", 971 | "Skipping 692884004. Already sent before\n", 972 | "Skipping 439524497. Already sent before\n", 973 | "Skipping 6004753. Already sent before\n", 974 | "Skipping 5171433704. Already sent before\n", 975 | "Skipping 1016745939. Already sent before\n", 976 | "Skipping 5010161131. Already sent before\n", 977 | "Skipping 2093747407. Already sent before\n", 978 | "Skipping 1539078557. Already sent before\n", 979 | "Skipping 601806588. Already sent before\n", 980 | "Skipping 1007302763. Already sent before\n", 981 | "Skipping 948625701. Already sent before\n", 982 | "Skipping 732512421. Already sent before\n", 983 | "Failed to send message to 170903171. Reason: Forbidden: bot was blocked by the user\n", 984 | "Skipping 486321120. Already sent before\n", 985 | "Skipping 699798930. Already sent before\n", 986 | "Skipping 1367406431. Already sent before\n", 987 | "Skipping 5165047473. Already sent before\n", 988 | "Skipping 177194518. Already sent before\n", 989 | "Skipping 615479238. Already sent before\n", 990 | "Skipping 893132495. Already sent before\n", 991 | "Skipping 2034776819. Already sent before\n", 992 | "Skipping 1208555035. Already sent before\n", 993 | "Skipping 5237350570. Already sent before\n", 994 | "Skipping 400346337. Already sent before\n", 995 | "Skipping 1561148633. Already sent before\n", 996 | "Skipping 929919979. Already sent before\n", 997 | "Skipping 1933684572. Already sent before\n", 998 | "Skipping 1226493096. Already sent before\n", 999 | "Skipping 620195365. Already sent before\n", 1000 | "Skipping 1315054594. Already sent before\n", 1001 | "Skipping 5155931222. Already sent before\n", 1002 | "Skipping 9250257. Already sent before\n", 1003 | "Skipping 5860300204. Already sent before\n", 1004 | "Skipping 1148232261. Already sent before\n", 1005 | "Skipping 5303887200. Already sent before\n", 1006 | "Skipping 6282570587. Already sent before\n", 1007 | "Skipping 1707660826. Already sent before\n", 1008 | "Skipping 516751375. Already sent before\n", 1009 | "Skipping 100822102. Already sent before\n", 1010 | "Skipping 1028001176. Already sent before\n", 1011 | "Skipping 5237954659. Already sent before\n", 1012 | "Skipping 642999. Already sent before\n", 1013 | "Skipping 7501132. Already sent before\n", 1014 | "Skipping 1383085335. Already sent before\n", 1015 | "Skipping 1287610940. Already sent before\n", 1016 | "Skipping 5013420256. Already sent before\n", 1017 | "Skipping 725099806. Already sent before\n", 1018 | "Skipping 773488815. Already sent before\n", 1019 | "Skipping 1202825646. Already sent before\n", 1020 | "Skipping 1019814806. Already sent before\n", 1021 | "Skipping 784676583. Already sent before\n", 1022 | "Skipping 5438700779. Already sent before\n", 1023 | "Skipping 611624673. Already sent before\n", 1024 | "Skipping 1531023691. Already sent before\n", 1025 | "Skipping 5524057122. Already sent before\n", 1026 | "Skipping 516322093. Already sent before\n", 1027 | "Skipping 1290955552. Already sent before\n", 1028 | "Skipping 1761052018. Already sent before\n", 1029 | "Skipping 558974410. Already sent before\n", 1030 | "Skipping 5977977408. Already sent before\n", 1031 | "Skipping 3288449. Already sent before\n", 1032 | "Skipping 1513024753. Already sent before\n", 1033 | "Skipping 912062262. Already sent before\n", 1034 | "Skipping 6182533281. Already sent before\n", 1035 | "Skipping 792843693. Already sent before\n", 1036 | "Skipping 5595139019. Already sent before\n", 1037 | "Skipping 14918783. Already sent before\n", 1038 | "Skipping 5961338669. Already sent before\n", 1039 | "Skipping 1251945897. Already sent before\n", 1040 | "Skipping 721321614. Already sent before\n", 1041 | "Skipping 1233332156. Already sent before\n", 1042 | "Skipping 195228465. Already sent before\n", 1043 | "Skipping 9508927. Already sent before\n", 1044 | "Skipping 975053335. Already sent before\n", 1045 | "Skipping 1392897532. Already sent before\n", 1046 | "Skipping 676034635. Already sent before\n", 1047 | "Skipping 820041498. Already sent before\n", 1048 | "Skipping 7203424. Already sent before\n", 1049 | "Skipping 1094061541. Already sent before\n", 1050 | "Skipping 339320726. Already sent before\n", 1051 | "Skipping 923604744. Already sent before\n", 1052 | "Skipping 6406622. Already sent before\n", 1053 | "Skipping 76717614. Already sent before\n", 1054 | "Skipping 414318778. Already sent before\n", 1055 | "Failed to send message to 2104720788. Reason: Forbidden: bot was blocked by the user\n", 1056 | "Skipping 1225215131. Already sent before\n", 1057 | "Skipping 7191211. Already sent before\n", 1058 | "Skipping 937834211. Already sent before\n", 1059 | "Skipping 165369124. Already sent before\n", 1060 | "Skipping 878671814. Already sent before\n", 1061 | "Skipping 5474097719. Already sent before\n", 1062 | "Skipping 169283175. Already sent before\n", 1063 | "Failed to send message to 196987793. Reason: Forbidden: bot was blocked by the user\n", 1064 | "Failed to send message to 2042927462. Reason: Forbidden: bot was blocked by the user\n", 1065 | "Failed to send message to 1394492009. Reason: Forbidden: bot was blocked by the user\n", 1066 | "Skipping 15822909. Already sent before\n", 1067 | "Skipping 5315620938. Already sent before\n", 1068 | "Skipping 1401698182. Already sent before\n", 1069 | "Skipping 5840857302. Already sent before\n", 1070 | "Skipping 17513864. Already sent before\n", 1071 | "Skipping 479746418. Already sent before\n", 1072 | "Skipping 1717964847. Already sent before\n", 1073 | "Failed to send message to 6053675632. Reason: Forbidden: bot was blocked by the user\n", 1074 | "Skipping 5683576132. Already sent before\n", 1075 | "Skipping 172954615. Already sent before\n", 1076 | "Skipping 6004449635. Already sent before\n", 1077 | "Skipping 5424836. Already sent before\n", 1078 | "Skipping 921683433. Already sent before\n", 1079 | "Failed to send message to 532018058. Reason: Forbidden: bot was blocked by the user\n", 1080 | "Skipping 6138424948. Already sent before\n", 1081 | "Skipping 6024723587. Already sent before\n", 1082 | "Skipping 6208113372. Already sent before\n", 1083 | "Skipping 373674229. Already sent before\n", 1084 | "Skipping 223212754. Already sent before\n", 1085 | "Skipping 611959785. Already sent before\n", 1086 | "Skipping 902209336. Already sent before\n", 1087 | "Skipping 172199844. Already sent before\n", 1088 | "Skipping 268745615. Already sent before\n", 1089 | "Skipping 101040390. Already sent before\n", 1090 | "Skipping 5767669442. Already sent before\n", 1091 | "Skipping 334277655. Already sent before\n", 1092 | "Skipping 585088972. Already sent before\n", 1093 | "Skipping 416077141. Already sent before\n", 1094 | "Skipping 1710197192. Already sent before\n", 1095 | "Skipping 1774024623. Already sent before\n", 1096 | "Skipping 2101971630. Already sent before\n", 1097 | "Skipping 5635727535. Already sent before\n", 1098 | "Skipping 6224860345. Already sent before\n", 1099 | "Skipping 262890648. Already sent before\n", 1100 | "Skipping 5936754668. Already sent before\n", 1101 | "Skipping 154065947. Already sent before\n", 1102 | "Skipping 211411316. Already sent before\n", 1103 | "Skipping 6895076. Already sent before\n", 1104 | "Skipping 2032540047. Already sent before\n", 1105 | "Skipping 974581497. Already sent before\n", 1106 | "Skipping 7611179. Already sent before\n", 1107 | "Skipping 663694558. Already sent before\n", 1108 | "Skipping 5224294137. Already sent before\n", 1109 | "Skipping 669949464. Already sent before\n", 1110 | "Skipping 14331534. Already sent before\n", 1111 | "Skipping 400070146. Already sent before\n", 1112 | "Skipping 688826120. Already sent before\n", 1113 | "Skipping 273524979. Already sent before\n", 1114 | "Skipping 14319452. Already sent before\n", 1115 | "Skipping 5472244651. Already sent before\n", 1116 | "Skipping 8957314. Already sent before\n", 1117 | "Skipping 388894933. Already sent before\n", 1118 | "Skipping 160383568. Already sent before\n", 1119 | "Skipping 6259538740. Already sent before\n", 1120 | "Skipping 673777152. Already sent before\n", 1121 | "Skipping 711826444. Already sent before\n", 1122 | "Skipping 5491390831. Already sent before\n", 1123 | "Successfully sent to 717986718\n", 1124 | "Successfully sent to 5454700173\n", 1125 | "Successfully sent to 5936581021\n", 1126 | "Successfully sent to 517163947\n", 1127 | "Successfully sent to 27731830\n", 1128 | "Successfully sent to 558770900\n", 1129 | "Successfully sent to 1650748027\n", 1130 | "Successfully sent to 684044480\n", 1131 | "Successfully sent to 1175785358\n", 1132 | "Successfully sent to 599537085\n", 1133 | "Successfully sent to 264332148\n", 1134 | "Successfully sent to 361152162\n", 1135 | "Successfully sent to 1159783046\n", 1136 | "Successfully sent to 1131049810\n", 1137 | "Successfully sent to 879808951\n", 1138 | "Successfully sent to 1066546727\n", 1139 | "Successfully sent to 6038391975\n", 1140 | "Successfully sent to 265355592\n", 1141 | "Successfully sent to 5672707908\n", 1142 | "Successfully sent to 951618557\n", 1143 | "Successfully sent to 228157375\n", 1144 | "Successfully sent to 5880299765\n", 1145 | "Successfully sent to 49634669\n", 1146 | "Successfully sent to 428421888\n", 1147 | "Successfully sent to 292098802\n", 1148 | "Successfully sent to 1064609232\n", 1149 | "Successfully sent to 942844633\n", 1150 | "Successfully sent to 5302642514\n", 1151 | "Successfully sent to 322106649\n", 1152 | "Successfully sent to 968143934\n" 1153 | ] 1154 | } 1155 | ], 1156 | "source": [ 1157 | "for user_dict in user_dicts: \n", 1158 | " if user_dict[\"_id\"] in already_sent_to_user_ids:\n", 1159 | " print(f\"Skipping {user_dict['_id']}. Already sent before\")\n", 1160 | " continue\n", 1161 | " \n", 1162 | " text = \"🟢 We're alive again!\\n\\n\"\n", 1163 | " text += \"Today bot sometimes didn't respond due to OpenAI outages. Now everything works fine, let's chat!\"\n", 1164 | " \n", 1165 | " try:\n", 1166 | " await application.bot.send_message(\n", 1167 | " user_dict['chat_id'],\n", 1168 | " text,\n", 1169 | " disable_web_page_preview=True,\n", 1170 | " parse_mode=ParseMode.HTML\n", 1171 | " )\n", 1172 | " print(f\"Successfully sent to {user_dict['_id']}\")\n", 1173 | " \n", 1174 | " db.add_user_to_newsletter(newsletter_id, user_dict[\"_id\"])\n", 1175 | " \n", 1176 | " time.sleep(10)\n", 1177 | " except Exception as e:\n", 1178 | " print(f\"Failed to send message to {user_dict['_id']}. Reason: {e}\")" 1179 | ] 1180 | }, 1181 | { 1182 | "cell_type": "code", 1183 | "execution_count": null, 1184 | "id": "ee423e99", 1185 | "metadata": {}, 1186 | "outputs": [], 1187 | "source": [] 1188 | } 1189 | ], 1190 | "metadata": { 1191 | "kernelspec": { 1192 | "display_name": "Python 3 (ipykernel)", 1193 | "language": "python", 1194 | "name": "python3" 1195 | }, 1196 | "language_info": { 1197 | "codemirror_mode": { 1198 | "name": "ipython", 1199 | "version": 3 1200 | }, 1201 | "file_extension": ".py", 1202 | "mimetype": "text/x-python", 1203 | "name": "python", 1204 | "nbconvert_exporter": "python", 1205 | "pygments_lexer": "ipython3", 1206 | "version": "3.8.16" 1207 | } 1208 | }, 1209 | "nbformat": 4, 1210 | "nbformat_minor": 5 1211 | } 1212 | --------------------------------------------------------------------------------