From 538213eae9a7ea2aedae4aced23236b53f786495 Mon Sep 17 00:00:00 2001 From: gitadmin Date: Sat, 10 Jan 2026 18:13:25 +0300 Subject: [PATCH] =?UTF-8?q?[=D0=92=D0=9E=D0=9B=D0=9A]=20=D0=BE=D1=87=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=BD=D0=B0=D1=8F=20=D0=BE=D1=82=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B0=20=D1=82=D0=B0=D0=B9=D0=BC=D0=B0=D1=83=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 4 +-- tg_publish.py | 72 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/.env b/.env index a66ef6b..193d324 100644 --- a/.env +++ b/.env @@ -61,11 +61,11 @@ MDB_PW = 'T38tU83r69' # Debug bot @posto_1638_bot # BOT_TOKEN = "8301828103:AAHDuBkw7EUkmMvv5TPpVJaLeeBLu-EB21U" # @info_1638_bot -# POSTER_BOT_TOKEN = "8215624618:AAF6VtqRtFr4twFRTiTjedSDE6txkbIy6rc" +POSTER_BOT_TOKEN = "8215624618:AAF6VtqRtFr4twFRTiTjedSDE6txkbIy6rc" # VOLK production bot @VOLK_informer_bot # POSTER_BOT_TOKEN = "8279661544:AAEsJee6icCUn_AbWxhDpk2CLxQhzaartuI" # VOLK backup production bot @VOLK_informer2_bot -POSTER_BOT_TOKEN = "8001674071:AAFyGW4xibAFnuApEaSJvshqEyYHmD7RRts" +# POSTER_BOT_TOKEN = "8001674071:AAFyGW4xibAFnuApEaSJvshqEyYHmD7RRts" # VOLK responder bot # RESPONDER_BOT_TOKEN = "8072579275:AAFiyKN1EPPnP5ZVZNTDGEriYJf1xADbfsY" # RESPONDER_BOT_NAME = "VOLK_responder_bot" diff --git a/tg_publish.py b/tg_publish.py index 05a88ef..aff2ac5 100644 --- a/tg_publish.py +++ b/tg_publish.py @@ -8,6 +8,9 @@ import json import re import httpx import requests +from requests.adapters import HTTPAdapter +from urllib3.util.retry import Retry +from urllib3 import Timeout as Urllib3Timeout import traceback import io import sys @@ -153,11 +156,14 @@ def _send_photo_sync(send_photo_url, image_data, caption, chat_id, disable_notif caption: Подпись к фото chat_id: ID чата/канала disable_notification: Отключить уведомления - timeout: Кортеж (connect_timeout, read_timeout) или число для общего таймаута + timeout: Кортеж (connect_timeout, read_timeout, write_timeout) или число для общего таймаута Returns: requests.Response объект """ + import socket + from urllib3.connection import HTTPSConnection, HTTPConnection + try: # Используем requests для стабильной работы в gunicorn # requests более надежен для синхронных операций в контексте WSGI @@ -172,17 +178,59 @@ def _send_photo_sync(send_photo_url, image_data, caption, chat_id, disable_notif 'disable_notification': str(disable_notification).lower() } + # Извлекаем таймауты из кортежа + if isinstance(timeout, tuple): + if len(timeout) >= 3: + connect_timeout, read_timeout, write_timeout = timeout[0], timeout[1], timeout[2] + elif len(timeout) == 2: + connect_timeout, read_timeout = timeout[0], timeout[1] + write_timeout = read_timeout # Используем read timeout для write + else: + connect_timeout = read_timeout = write_timeout = timeout[0] + else: + connect_timeout = read_timeout = write_timeout = timeout + + # Создаем сессию с кастомным адаптером + session = requests.Session() + + # Создаем кастомный HTTPAdapter с установкой socket timeout + class CustomHTTPAdapter(HTTPAdapter): + def init_poolmanager(self, *args, **kwargs): + # Устанавливаем socket timeout через socket_options + # Это применяется ко всем операциям с сокетом (read и write) + socket_options = kwargs.get('socket_options', []) + socket_options.append((socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)) + kwargs['socket_options'] = socket_options + pool = super().init_poolmanager(*args, **kwargs) + # Устанавливаем socket timeout для всех соединений в пуле + # Это применяется ко всем операциям с сокетом (read и write) + original_connect = pool.ConnectionCls.connect + def connect_with_timeout(self): + result = original_connect(self) + if hasattr(self, 'sock') and self.sock: + self.sock.settimeout(write_timeout) + return result + pool.ConnectionCls.connect = connect_with_timeout + return pool + + # Монтируем кастомный адаптер + adapter = CustomHTTPAdapter(max_retries=Retry(total=0)) + session.mount('http://', adapter) + session.mount('https://', adapter) + # Отправляем запрос с увеличенным таймаутом - # requests.post принимает timeout как кортеж (connect, read) или число - response = requests.post( + # Используем максимальный таймаут для всех операций + max_timeout = max(connect_timeout, read_timeout, write_timeout) + response = session.post( send_photo_url, files=files, data=data, - timeout=timeout, + timeout=(connect_timeout, max_timeout), # (connect, read) stream=False # Отключаем потоковую передачу для стабильности ) return response + except requests.exceptions.Timeout as e: # Пробрасываем таймаут как есть, чтобы его можно было обработать выше raise @@ -192,6 +240,10 @@ def _send_photo_sync(send_photo_url, image_data, caption, chat_id, disable_notif except Exception as e: # Пробрасываем остальные исключения как есть raise + finally: + # Закрываем сессию + if 'session' in locals(): + session.close() def _edit_caption_sync(edit_caption_url, chat_id, message_id, caption, timeout): """ @@ -404,9 +456,9 @@ async def publish_to_tg(vk_post_id): # Вызываем синхронную функцию через asyncio.to_thread() # Это позволяет избежать проблем с event loop в gunicorn try: - # Используем кортеж для таймаута: (connect, read) - # requests использует один таймаут для всех операций, поэтому используем максимальный - timeout_tuple = (10.0, write_timeout) # (connect, read/write) + # Используем кортеж для таймаута: (connect, read, write) + # write_timeout применяется к операциям записи в сокет + timeout_tuple = (10.0, write_timeout, write_timeout) # (connect, read, write) photo_response = await asyncio.to_thread( _send_photo_sync, @@ -631,7 +683,8 @@ async def publish_to_tg(vk_post_id): edit_caption_success = False try: # Используем requests через asyncio.to_thread() для стабильной работы в gunicorn - edit_timeout = (10.0, 30.0) # (connect, read) + # Увеличиваем таймаут для редактирования caption + edit_timeout = (10.0, 60.0, 60.0) # (connect, read, write) edit_caption_response = await asyncio.to_thread( _edit_caption_sync, @@ -697,7 +750,8 @@ async def publish_to_tg(vk_post_id): edit_text_success = False try: # Используем requests через asyncio.to_thread() для стабильной работы в gunicorn - edit_timeout = (10.0, 30.0) # (connect, read) + # Увеличиваем таймаут для редактирования текста + edit_timeout = (10.0, 60.0, 60.0) # (connect, read, write) edit_text_response = await asyncio.to_thread( _edit_text_sync,