fix: использовать requests.Session для переиспользования соединений (ISSUE #8)
This commit is contained in:
parent
4425f27e1e
commit
5292f9bd0e
38
AGENTS.md
38
AGENTS.md
@ -1,18 +1,13 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Проект
|
||||
Discord-бот на Python (discord.py) для Магнитогорска.
|
||||
- Команды Discord: `!msg` (повтор текста), `!pogoda` (прогноз погоды)
|
||||
- Консольные команды: `stop` (остановка бота)
|
||||
Discord-бот на Python (discord.py)
|
||||
|
||||
## Запуск
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
python bot.py
|
||||
```
|
||||
|
||||
Введите `stop` в терминал для остановки бота.
|
||||
|
||||
## Токен
|
||||
Токен бота хранится в `.env` (переменная `DISCORD_TOKEN`). Получи токен на [Discord Developer Portal](https://discord.com/developers/applications). Шаблон в `.env.example`.
|
||||
|
||||
@ -25,27 +20,6 @@ python bot.py
|
||||
- **Все git-коммиты согласовывать с пользователем перед созданием.**
|
||||
- **Все сообщения git-коммитов писать на русском языке.**
|
||||
|
||||
## Архитектура
|
||||
```
|
||||
bot.py # Точка входа, инициализация бота, console_input()
|
||||
commands/ # Discord команды (cogs)
|
||||
__init__.py # ALL_COMMANDS — явные импорты
|
||||
pogoda.py # !pogoda — погода с retry + fallback (wttr.in → Open-Meteo)
|
||||
console_commands/ # Консольные команды
|
||||
__init__.py # ALL_CONSOLE_COMMANDS — явные импорты
|
||||
stop.py # stop — остановка бота
|
||||
```
|
||||
|
||||
### Добавление Discord команды
|
||||
1. Создать файл `commands/имя.py` с классом, наследующим `commands.Cog`
|
||||
2. Добавить импорт в `commands/__init__.py`
|
||||
3. Добавить класс в `ALL_COMMANDS`
|
||||
|
||||
### Добавление консольной команды
|
||||
1. Создать файл `console_commands/имя.py` с функцией `func(stop_event, bot)`
|
||||
2. Добавить импорт в `console_commands/__init__.py`
|
||||
3. Добавить функцию в `ALL_CONSOLE_COMMANDS`
|
||||
|
||||
## API и внешние сервисы
|
||||
|
||||
### Погода (!pogoda)
|
||||
@ -75,11 +49,11 @@ requests>=2.31.0
|
||||
## Структура данных погоды
|
||||
Команда `!pogoda` возвращает:
|
||||
```
|
||||
[TEMP] Температура: X°C (ощущается как Y°C)
|
||||
[DESC] Описание: Z
|
||||
[HUMID] Влажность: X%
|
||||
[WIND] Ветер: X м/с
|
||||
[PRESS] Давление: X мм рт. ст.
|
||||
Температура: X°C (ощущается как Y°C)
|
||||
Описание: Z
|
||||
Влажность: X%
|
||||
Ветер: X м/с
|
||||
Давление: X мм рт. ст.
|
||||
```
|
||||
|
||||
## Безопасность
|
||||
|
||||
@ -53,10 +53,11 @@
|
||||
- **Решение:** Создать `.gitignore` с правилами для Python (`__pycache__/`, `*.pyc`, `.env`, `venv/`, `*.egg-info/`), логов, временных файлов, кэша ОС и IDE.
|
||||
- **Статус:** Исправлено. `.gitignore` создан и обновлён.
|
||||
|
||||
### 8. `requests` без Session — нет переиспользования соединений
|
||||
- **Где:** все файлы (`pogoda.py`, `news.py`)
|
||||
### 8. `requests` без Session — нет переиспользования соединений ✅ РЕШЕНО
|
||||
- **Где:** `utils/pogoda.py`, `utils/news.py`, `commands/cat.py`
|
||||
- **Проблема:** Каждый `requests.get()` создаёт новое TCP-соединение. Для частых запросов это расточительно.
|
||||
- **Решение:** Создать `requests.Session()` и использовать `session.get()`.
|
||||
- **Статус:** Исправлено. Session создан в каждом модуле, все `requests.get()` заменены на `_session.get()`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -3,6 +3,8 @@ from discord.ext import commands
|
||||
import asyncio
|
||||
import requests
|
||||
|
||||
_session = requests.Session()
|
||||
|
||||
|
||||
class Cat(commands.Cog):
|
||||
"""Команда !cat — случайный котик"""
|
||||
@ -12,7 +14,7 @@ class Cat(commands.Cog):
|
||||
"""Получить случайного котика"""
|
||||
try:
|
||||
response = await asyncio.to_thread(
|
||||
requests.get,
|
||||
_session.get,
|
||||
"https://api.thecatapi.com/v1/images/search",
|
||||
timeout=10
|
||||
)
|
||||
@ -26,5 +28,5 @@ class Cat(commands.Cog):
|
||||
)
|
||||
embed.set_image(url=url)
|
||||
await ctx.send(embed=embed)
|
||||
except requests.RequestException:
|
||||
except requests.exceptions.RequestException:
|
||||
await ctx.send("Не удалось получить котика. Попробуйте позже.")
|
||||
|
||||
@ -1,17 +1,20 @@
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
|
||||
import requests
|
||||
|
||||
RSS_URL_ARTICLES = "https://habr.com/ru/rss/hubs/artificial_intelligence/articles/top/daily/?fl=ru"
|
||||
RSS_URL_POSTS = "https://habr.com/ru/rss/hubs/artificial_intelligence/news/top/daily/?fl=ru"
|
||||
|
||||
_session = requests.Session()
|
||||
|
||||
|
||||
async def fetch_rss(url):
|
||||
"""Скачать и распарсить RSS-ленту (RSS 2.0 / Atom)."""
|
||||
import asyncio
|
||||
import requests
|
||||
from xml.etree import ElementTree
|
||||
|
||||
try:
|
||||
response = await asyncio.to_thread(requests.get, url, timeout=10)
|
||||
response = await asyncio.to_thread(_session.get, url, timeout=10)
|
||||
response.raise_for_status()
|
||||
root = ElementTree.fromstring(response.content)
|
||||
|
||||
@ -60,7 +63,7 @@ async def fetch_rss(url):
|
||||
"tags": tags,
|
||||
})
|
||||
return articles[:10]
|
||||
except requests.RequestException:
|
||||
except requests.exceptions.RequestException:
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@ -2,12 +2,14 @@ import asyncio
|
||||
import requests
|
||||
from requests.exceptions import ConnectionError, Timeout, SSLError
|
||||
|
||||
_session = requests.Session()
|
||||
|
||||
|
||||
async def fetch_weather(api_url, timeout=10, max_retries=3):
|
||||
"""Получить данные о погоде с retry."""
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
response = await asyncio.to_thread(requests.get, api_url, timeout=timeout)
|
||||
response = await asyncio.to_thread(_session.get, api_url, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except (SSLError, ConnectionError, Timeout):
|
||||
@ -35,7 +37,7 @@ async def fetch_open_meteo(lat=53.4069, lon=58.9797, timeout=10, max_retries=3):
|
||||
)
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
response = await asyncio.to_thread(requests.get, url, timeout=timeout)
|
||||
response = await asyncio.to_thread(_session.get, url, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
current = data.get("current", {})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user