16.06, ООО «ВК»
16.06, ООО «ВК»
16.06, ООО «ВК»

Как я пытался допилить криптобота, но всё пошло не по плану

В простой криптоконвертер добавил алерты. Столкнулся с трудностями и решил их.

149 открытий5К показов
Как я пытался допилить криптобота, но всё пошло не по плану

Изначально всё выглядело просто: криптоконвертер, немного кеша в JSON и WebSocket от Binance для алертов. Но с каждой новой фичей бот начинал вести себя странно. Настройки не сохранялись, алерты спамили, структура данных ломалась, сервер выдавал ошибки, а старый скрипт — не сдавался. В этой истории — реальный опыт разработки и отладки телеграм-бота, в котором всё пошло не так, как планировалось.

Начало истории

Это был просто криптоконвертер, который работал так:

REST API CoinGecko — раз в 10 минут обновлял курс. Кэш в JSON — чтобы не дудосить API. Теперь я решил добавить алерты.

Так как мой мой бот в дальнейшем будет обрастать всё новыми и новыми функциями, я решил разделять их на отдельные файлы.

Чтобы не приходилось постоянно отправлять запросы, реализовал алерты через Websocket binance.

Сделал сохранение настроек в database.json.

Я реально думал что это будет просто, ошибся:

  • При деплое выдало Sintaxis Error , хотя на компе всё было нормально;
  • Сохранение алертов пропадало после конвертации;
  • Алерты дико спамили.

Когда я запускал бота с компа, никакой синтаксической ошибки не наблюдалось, но после того, как закинул бота на сервер, мне выдало ошибку в строке:

			f"{random.choice(ALERT_TEMPLATES).format(
		

Сначала думал, что дело в кодировке и стоит по дефолту UTF-8 BOM. Но нет. Решил: сервер не видит, что скобка открытая в этой строке закрывается ниже. Разделил код:

			alert_template = random.choice(ALERT_TEMPLATES)
formatted_alert = alert_template.format(
    crypto=symbol.upper(),
    direction=direction,
    change=abs(price_change),
    advice=random.choice(ADVICE_LIST)
)
		

Ошибка исчезла. Запустил, казалось бы, всё работает. Ставлю галочки на крипте, выхожу в меню, захожу обратно в алерты — галочки на месте. Но стоит мне перейти в конвертер и вернутся обратно, как галок и след простыл.

Как я пытался допилить криптобота, но всё пошло не по плану 1

Долго копался в коде, пока не понял, что сама структура данных была кривой:

			uuser_data = {
    "alerts": ["BTC"],  # ← Это перезаписывалось конвертером!
    "conversion": {"crypto": "ETH"}
}
		

Datetime ломал JSON — нельзя просто так сериализовать datetime.now().

Разделил струтктуру сохранения, чтобы конвертация не влияла на алерты:

			user_data = {
    "alerts": {"cryptos": ["BTC"], "threshold": 5.0},  # Теперь отдельный блок
    "conversion": {...}  # Не влияет на алерты
}
		

И добавил преобразование datetime в str

			timestamp = v["timestamp"].isoformat()  # Для сохранения
datetime.fromisoformat(v["timestamp"])  # Для загрузки
		

Закинул на сервер. При запуске бот не заработал, поэтому я включил режим отладки и мне выдало такое:

			2025-05-07 08:57:50,151 - aiogram.dispatcher - ERROR - Failed to fetch updates - TelegramConflictError: Telegram server says - Conflict: terminated by other getUpdates request; make sure that only one bot instance is running
2025-05-07 08:57:50,151 - aiogram.dispatcher - WARNING - Sleep for 1.774987 seconds and try again... (tryings = 2, bot id = 7617680908)
		

Старый скрпит не хочет уступать без боя. Это конфликт ботов.

Дело в том, что я запускаю сервисы через systemd и всегда настраиваю там автозапуск на случай падения бота. Но иногда это выходит боком. Приходится включать множество команд по убийству процесса по типу:

sudo pkill -f “python.*bot.py” && sleep 2

kill -9 <PID>

pkill -f bot.py

Но старый бот так и не умирает. А через некоторое время сам по себе перестает работать. Я пока не понял эту аномалию. Возможно, после команды sudo systemctl reload надо просто попить чаю и дать время сервису нормально перезагрузиться.

Хорошо, бота мы убили. Нового запустили. Все кнопки работают. Включили алерты на ETH и стали ждать…

И тут началось…

Как я пытался допилить криптобота, но всё пошло не по плану 2

Когда Эфириум подскачил на 7%, бот сошёл с ума, начал материться и спамить при малейшем сдвиге курса.

Было решено игнорировать мелкие колебания курса, установить минимальный порог реагирования 5% и высылать алерт не чаще чем 5 минут от предыдущего:

			def _need_alert(self, crypto: str, change: float) -> bool:
    now = time.time()
    last_alert = self.last_alerts.get(crypto, 0)
    return (
        abs(change) > self.threshold  # Порог (например, 5%)
        and (now - last_alert) > 300  # 5 минут между алертами
    )
		

В итоге мой крипобот заработал, но я уверен: при тестировании вы обнаружите еще кучу багов. Далее хочу добавить кнопки для самостоятельного выбора порогов алерта и графики курса.

Со скриптом можете ознакомится здесь.

P.S. Пишите комментарии, предлагайте свои идеи. Деконструктивная и агрессивная критика приветствуется!))

Следите за новыми постами
Следите за новыми постами по любимым темам
149 открытий5К показов