gengineapi 0.1.0__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {gengineapi-0.1.0 → gengineapi-0.2.1}/PKG-INFO +61 -55
  2. {gengineapi-0.1.0 → gengineapi-0.2.1}/README.md +59 -52
  3. {gengineapi-0.1.0 → gengineapi-0.2.1}/pyproject.toml +3 -10
  4. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/__init__.py +4 -0
  5. gengineapi-0.2.1/src/gengineapi/farm.py +464 -0
  6. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi.egg-info/PKG-INFO +61 -55
  7. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi.egg-info/SOURCES.txt +1 -0
  8. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi.egg-info/requires.txt +0 -1
  9. {gengineapi-0.1.0 → gengineapi-0.2.1}/setup.cfg +0 -0
  10. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/client.py +0 -0
  11. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/config.py +0 -0
  12. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/exceptions.py +0 -0
  13. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/http.py +0 -0
  14. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/modules/__init__.py +0 -0
  15. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/modules/auth.py +0 -0
  16. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/modules/base.py +0 -0
  17. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/modules/currencies.py +0 -0
  18. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/modules/finances.py +0 -0
  19. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/modules/payments.py +0 -0
  20. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/modules/transactions.py +0 -0
  21. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi/modules/users.py +0 -0
  22. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi.egg-info/dependency_links.txt +0 -0
  23. {gengineapi-0.1.0 → gengineapi-0.2.1}/src/gengineapi.egg-info/top_level.txt +0 -0
@@ -1,15 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gengineapi
3
- Version: 0.1.0
4
- Summary: Add your description here
3
+ Version: 0.2.1
4
+ Summary: G-Engine API Client
5
5
  Requires-Python: >=3.13
6
6
  Description-Content-Type: text/markdown
7
7
  Requires-Dist: aiohttp>=3.8.0
8
8
  Requires-Dist: typing-extensions>=4.0.0
9
9
  Requires-Dist: aiohttp-socks>=0.7.1
10
- Requires-Dist: uv-publish
11
10
 
12
- # G-Engine API Client
11
+ # GEngineAPI
13
12
 
14
13
  Асинхронный модульный клиент для взаимодействия с API G-Engine.
15
14
 
@@ -21,6 +20,7 @@ Requires-Dist: uv-publish
21
20
  - Автоматические повторные попытки для временных ошибок
22
21
  - Поддержка JWT-аутентификации
23
22
  - Поддержка прокси, включая SOCKS5
23
+ - Ферма клиентов с балансировкой нагрузки и стратегиями ротации
24
24
  - Подробное логирование
25
25
  - Строгая типизация с помощью аннотаций типов
26
26
  - Класс-конфигурация для централизованной настройки и повторного использования
@@ -28,33 +28,13 @@ Requires-Dist: uv-publish
28
28
  ## Установка
29
29
 
30
30
  ```bash
31
- pip install -r requirements.txt
31
+ pip install gengineapi
32
32
  ```
33
33
 
34
- Или с помощью setup.py:
34
+ Для поддержки SOCKS5 прокси убедитесь, что установлен пакет `aiohttp-socks`:
35
35
 
36
36
  ```bash
37
- pip install .
38
- ```
39
-
40
- ## Структура проекта
41
-
42
- ```
43
- gengine_client/
44
- ├── __init__.py # Экспорт основных классов
45
- ├── client.py # Основной класс клиента
46
- ├── http.py # HTTP клиент с поддержкой аутентификации
47
- ├── exceptions.py # Иерархия исключений API
48
- ├── config.py # Класс-конфигурация для настройки параметров
49
- └── modules/ # Папка с модулями API
50
- ├── __init__.py
51
- ├── base.py # Базовый класс для всех модулей
52
- ├── payments.py # Модуль для работы с платежами
53
- ├── finances.py # Модуль для работы с финансами
54
- ├── auth.py # Модуль для аутентификации
55
- ├── users.py # Модуль для работы с пользователями
56
- ├── transactions.py # Модуль для работы с транзакциями
57
- └── currencies.py # Модуль для работы с валютами
37
+ pip install aiohttp-socks
58
38
  ```
59
39
 
60
40
  ## Использование
@@ -63,7 +43,7 @@ gengine_client/
63
43
 
64
44
  ```python
65
45
  import asyncio
66
- from gengine_client import GEngineClient
46
+ from gengineapi import GEngineClient
67
47
 
68
48
  async def main():
69
49
  # Создаем клиент с существующим токеном
@@ -84,7 +64,7 @@ asyncio.run(main())
84
64
 
85
65
  ```python
86
66
  import asyncio
87
- from gengine_client import GEngineClient
67
+ from gengineapi import GEngineClient
88
68
 
89
69
  async def main():
90
70
  # Создаем клиент без токена
@@ -119,7 +99,7 @@ asyncio.run(main())
119
99
 
120
100
  ```python
121
101
  import asyncio
122
- from gengine_client import GEngineClient
102
+ from gengineapi import GEngineClient
123
103
 
124
104
  async def main():
125
105
  # Создаем клиент с использованием SOCKS5 прокси
@@ -149,7 +129,7 @@ asyncio.run(main())
149
129
 
150
130
  ```python
151
131
  import asyncio
152
- from gengine_client import GEngineConfig
132
+ from gengineapi import GEngineConfig
153
133
 
154
134
  async def main():
155
135
  # Настройка параметров клиента
@@ -188,32 +168,52 @@ async def main():
188
168
  asyncio.run(main())
189
169
  ```
190
170
 
191
- ### Загрузка конфигурации из переменных окружения
171
+ ### Использование фермы клиентов
172
+
173
+ Ферма клиентов позволяет управлять множеством клиентов с разными конфигурациями и стратегиями ротации:
192
174
 
193
175
  ```python
194
176
  import asyncio
195
- import os
196
- from gengine_client import GEngineConfig
197
-
198
- # Установка переменных окружения
199
- os.environ["GENGINE_BASE_URL"] = "https://api.example.com/api/v2"
200
- os.environ["GENGINE_TOKEN"] = "env_jwt_token"
201
- os.environ["GENGINE_TIMEOUT"] = "45"
202
- os.environ["GENGINE_MAX_RETRIES"] = "5"
203
- os.environ["GENGINE_PROXY"] = "socks5://127.0.0.1:9050" # Опционально - прокси
177
+ from gengineapi import ClientFarm
204
178
 
205
179
  async def main():
206
- # Загрузка настроек из переменных окружения
207
- GEngineConfig.load_from_env()
180
+ # Создаем ферму клиентов
181
+ farm = ClientFarm()
208
182
 
209
- # Получение клиента с настройками из переменных окружения
210
- client = await GEngineConfig.get_client()
183
+ # Добавляем несколько конфигураций с разными прокси
184
+ farm.create_config(
185
+ name="client1",
186
+ base_url="https://api.example.com/api/v2",
187
+ jwt_token="token1",
188
+ proxy="socks5://user1:pass1@host1:port1",
189
+ tags=["group1", "production"]
190
+ )
211
191
 
212
- # Использование клиента
213
- # ...
192
+ farm.create_config(
193
+ name="client2",
194
+ base_url="https://api.example.com/api/v2",
195
+ jwt_token="token2",
196
+ proxy="socks5://user2:pass2@host2:port2",
197
+ tags=["group1", "backup"]
198
+ )
214
199
 
215
- # При завершении приложения
216
- await GEngineConfig.reset()
200
+ # Устанавливаем стратегию выбора клиента
201
+ farm.set_selection_strategy("round_robin") # или "random", "least_errors", "least_recently_used"
202
+
203
+ # Получаем клиент по имени
204
+ client1 = await farm.get_client("client1")
205
+ await client1.users.get_me()
206
+
207
+ # Получаем следующий клиент согласно стратегии
208
+ client = await farm.get_next_client()
209
+ await client.currencies.get_rate(source="cb_rf", pair="USD:RUB")
210
+
211
+ # Получаем клиент по тегу
212
+ client = await farm.get_next_client(tag="production")
213
+ await client.transactions.get_transactions(limit=5)
214
+
215
+ # При завершении работы закрываем все клиенты
216
+ await farm.close_all()
217
217
 
218
218
  asyncio.run(main())
219
219
  ```
@@ -224,7 +224,7 @@ asyncio.run(main())
224
224
  import asyncio
225
225
  import uuid
226
226
  from decimal import Decimal
227
- from gengine_client import GEngineClient
227
+ from gengineapi import GEngineClient
228
228
 
229
229
  async def main():
230
230
  async with GEngineClient(
@@ -255,19 +255,25 @@ async def main():
255
255
  asyncio.run(main())
256
256
  ```
257
257
 
258
- ## Примеры
258
+ ## Доступные модули API
259
+
260
+ - `payments` - создание и выполнение платежей
261
+ - `finances` - работа с финансовыми данными
262
+ - `auth` - аутентификация и управление токенами
263
+ - `users` - работа с пользователями и их балансами
264
+ - `transactions` - работа с транзакциями
265
+ - `currencies` - работа с курсами валют
259
266
 
260
- В репозитории есть примеры использования клиента с разными сценариями:
267
+ ## Дополнительная документация
261
268
 
262
- - `examples.py` - полный набор примеров использования, включая работу с прокси
263
- - `old_examples/` - директория с устаревшими примерами
269
+ Ручки, которые дергаем - https://b2b-api.ggsel.com/api/v2/docs#
264
270
 
265
271
  ## Зависимости
266
272
 
267
273
  - Python 3.7+
268
274
  - aiohttp >= 3.8.0
269
275
  - typing-extensions >= 4.0.0
270
- - aiohttp-socks >= 0.7.1 (для поддержки SOCKS5 прокси)
276
+ - aiohttp-socks >= 0.7.1 (опционально, для поддержки SOCKS5 прокси)
271
277
 
272
278
  ## Лицензия
273
279
 
@@ -1,4 +1,4 @@
1
- # G-Engine API Client
1
+ # GEngineAPI
2
2
 
3
3
  Асинхронный модульный клиент для взаимодействия с API G-Engine.
4
4
 
@@ -10,6 +10,7 @@
10
10
  - Автоматические повторные попытки для временных ошибок
11
11
  - Поддержка JWT-аутентификации
12
12
  - Поддержка прокси, включая SOCKS5
13
+ - Ферма клиентов с балансировкой нагрузки и стратегиями ротации
13
14
  - Подробное логирование
14
15
  - Строгая типизация с помощью аннотаций типов
15
16
  - Класс-конфигурация для централизованной настройки и повторного использования
@@ -17,33 +18,13 @@
17
18
  ## Установка
18
19
 
19
20
  ```bash
20
- pip install -r requirements.txt
21
+ pip install gengineapi
21
22
  ```
22
23
 
23
- Или с помощью setup.py:
24
+ Для поддержки SOCKS5 прокси убедитесь, что установлен пакет `aiohttp-socks`:
24
25
 
25
26
  ```bash
26
- pip install .
27
- ```
28
-
29
- ## Структура проекта
30
-
31
- ```
32
- gengine_client/
33
- ├── __init__.py # Экспорт основных классов
34
- ├── client.py # Основной класс клиента
35
- ├── http.py # HTTP клиент с поддержкой аутентификации
36
- ├── exceptions.py # Иерархия исключений API
37
- ├── config.py # Класс-конфигурация для настройки параметров
38
- └── modules/ # Папка с модулями API
39
- ├── __init__.py
40
- ├── base.py # Базовый класс для всех модулей
41
- ├── payments.py # Модуль для работы с платежами
42
- ├── finances.py # Модуль для работы с финансами
43
- ├── auth.py # Модуль для аутентификации
44
- ├── users.py # Модуль для работы с пользователями
45
- ├── transactions.py # Модуль для работы с транзакциями
46
- └── currencies.py # Модуль для работы с валютами
27
+ pip install aiohttp-socks
47
28
  ```
48
29
 
49
30
  ## Использование
@@ -52,7 +33,7 @@ gengine_client/
52
33
 
53
34
  ```python
54
35
  import asyncio
55
- from gengine_client import GEngineClient
36
+ from gengineapi import GEngineClient
56
37
 
57
38
  async def main():
58
39
  # Создаем клиент с существующим токеном
@@ -73,7 +54,7 @@ asyncio.run(main())
73
54
 
74
55
  ```python
75
56
  import asyncio
76
- from gengine_client import GEngineClient
57
+ from gengineapi import GEngineClient
77
58
 
78
59
  async def main():
79
60
  # Создаем клиент без токена
@@ -108,7 +89,7 @@ asyncio.run(main())
108
89
 
109
90
  ```python
110
91
  import asyncio
111
- from gengine_client import GEngineClient
92
+ from gengineapi import GEngineClient
112
93
 
113
94
  async def main():
114
95
  # Создаем клиент с использованием SOCKS5 прокси
@@ -138,7 +119,7 @@ asyncio.run(main())
138
119
 
139
120
  ```python
140
121
  import asyncio
141
- from gengine_client import GEngineConfig
122
+ from gengineapi import GEngineConfig
142
123
 
143
124
  async def main():
144
125
  # Настройка параметров клиента
@@ -177,32 +158,52 @@ async def main():
177
158
  asyncio.run(main())
178
159
  ```
179
160
 
180
- ### Загрузка конфигурации из переменных окружения
161
+ ### Использование фермы клиентов
162
+
163
+ Ферма клиентов позволяет управлять множеством клиентов с разными конфигурациями и стратегиями ротации:
181
164
 
182
165
  ```python
183
166
  import asyncio
184
- import os
185
- from gengine_client import GEngineConfig
186
-
187
- # Установка переменных окружения
188
- os.environ["GENGINE_BASE_URL"] = "https://api.example.com/api/v2"
189
- os.environ["GENGINE_TOKEN"] = "env_jwt_token"
190
- os.environ["GENGINE_TIMEOUT"] = "45"
191
- os.environ["GENGINE_MAX_RETRIES"] = "5"
192
- os.environ["GENGINE_PROXY"] = "socks5://127.0.0.1:9050" # Опционально - прокси
167
+ from gengineapi import ClientFarm
193
168
 
194
169
  async def main():
195
- # Загрузка настроек из переменных окружения
196
- GEngineConfig.load_from_env()
170
+ # Создаем ферму клиентов
171
+ farm = ClientFarm()
197
172
 
198
- # Получение клиента с настройками из переменных окружения
199
- client = await GEngineConfig.get_client()
173
+ # Добавляем несколько конфигураций с разными прокси
174
+ farm.create_config(
175
+ name="client1",
176
+ base_url="https://api.example.com/api/v2",
177
+ jwt_token="token1",
178
+ proxy="socks5://user1:pass1@host1:port1",
179
+ tags=["group1", "production"]
180
+ )
200
181
 
201
- # Использование клиента
202
- # ...
182
+ farm.create_config(
183
+ name="client2",
184
+ base_url="https://api.example.com/api/v2",
185
+ jwt_token="token2",
186
+ proxy="socks5://user2:pass2@host2:port2",
187
+ tags=["group1", "backup"]
188
+ )
203
189
 
204
- # При завершении приложения
205
- await GEngineConfig.reset()
190
+ # Устанавливаем стратегию выбора клиента
191
+ farm.set_selection_strategy("round_robin") # или "random", "least_errors", "least_recently_used"
192
+
193
+ # Получаем клиент по имени
194
+ client1 = await farm.get_client("client1")
195
+ await client1.users.get_me()
196
+
197
+ # Получаем следующий клиент согласно стратегии
198
+ client = await farm.get_next_client()
199
+ await client.currencies.get_rate(source="cb_rf", pair="USD:RUB")
200
+
201
+ # Получаем клиент по тегу
202
+ client = await farm.get_next_client(tag="production")
203
+ await client.transactions.get_transactions(limit=5)
204
+
205
+ # При завершении работы закрываем все клиенты
206
+ await farm.close_all()
206
207
 
207
208
  asyncio.run(main())
208
209
  ```
@@ -213,7 +214,7 @@ asyncio.run(main())
213
214
  import asyncio
214
215
  import uuid
215
216
  from decimal import Decimal
216
- from gengine_client import GEngineClient
217
+ from gengineapi import GEngineClient
217
218
 
218
219
  async def main():
219
220
  async with GEngineClient(
@@ -244,19 +245,25 @@ async def main():
244
245
  asyncio.run(main())
245
246
  ```
246
247
 
247
- ## Примеры
248
+ ## Доступные модули API
249
+
250
+ - `payments` - создание и выполнение платежей
251
+ - `finances` - работа с финансовыми данными
252
+ - `auth` - аутентификация и управление токенами
253
+ - `users` - работа с пользователями и их балансами
254
+ - `transactions` - работа с транзакциями
255
+ - `currencies` - работа с курсами валют
248
256
 
249
- В репозитории есть примеры использования клиента с разными сценариями:
257
+ ## Дополнительная документация
250
258
 
251
- - `examples.py` - полный набор примеров использования, включая работу с прокси
252
- - `old_examples/` - директория с устаревшими примерами
259
+ Ручки, которые дергаем - https://b2b-api.ggsel.com/api/v2/docs#
253
260
 
254
261
  ## Зависимости
255
262
 
256
263
  - Python 3.7+
257
264
  - aiohttp >= 3.8.0
258
265
  - typing-extensions >= 4.0.0
259
- - aiohttp-socks >= 0.7.1 (для поддержки SOCKS5 прокси)
266
+ - aiohttp-socks >= 0.7.1 (опционально, для поддержки SOCKS5 прокси)
260
267
 
261
268
  ## Лицензия
262
269
 
@@ -1,18 +1,11 @@
1
1
  [project]
2
2
  name = "gengineapi"
3
- version = "0.1.0"
4
- description = "Add your description here"
3
+ version = "0.2.1"
4
+ description = "G-Engine API Client"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
7
7
  dependencies = [
8
8
  "aiohttp>=3.8.0", # Для асинхронных HTTP-запросов и WebSocket
9
9
  "typing-extensions>=4.0.0", # Для расширенных типов аннотаций
10
10
  "aiohttp-socks>=0.7.1",
11
- "uv-publish",
12
- ]
13
-
14
- [[tool.uv.index]]
15
- name = "testpypi"
16
- url = "https://test.pypi.org/simple/"
17
- publish-url = "https://test.pypi.org/legacy/"
18
- explicit = true
11
+ ]
@@ -19,6 +19,7 @@ from .exceptions import (
19
19
  ApiTimeoutError,
20
20
  ApiValidationError,
21
21
  )
22
+ from .farm import ClientFarm, ClientConfig
22
23
  from .http import AsyncHttpClient
23
24
 
24
25
  __version__ = "1.0.0"
@@ -29,6 +30,9 @@ __all__ = [
29
30
  'GEngineConfig',
30
31
  # HTTP клиент
31
32
  'AsyncHttpClient',
33
+ # Ферма клиентов
34
+ 'ClientFarm',
35
+ 'ClientConfig',
32
36
  # Исключения
33
37
  'ApiError',
34
38
  'ApiConnectionError',
@@ -0,0 +1,464 @@
1
+ """
2
+ Модуль, предоставляющий функциональность для управления фермой клиентов G-Engine API.
3
+
4
+ Позволяет создавать и управлять несколькими независимыми клиентами
5
+ с различными конфигурациями, прокси и токенами аутентификации.
6
+ """
7
+ import asyncio
8
+ import logging
9
+ import uuid
10
+ from typing import Dict, Optional, List, Any
11
+
12
+ from .client import GEngineClient
13
+
14
+
15
+ class ClientConfig:
16
+ """
17
+ Класс конфигурации для отдельного клиента.
18
+
19
+ Позволяет хранить и управлять параметрами для создания клиента.
20
+
21
+ Attributes:
22
+ name: Уникальное имя конфигурации
23
+ base_url: Базовый URL для API
24
+ jwt_token: JWT токен для аутентификации
25
+ timeout: Таймаут для запросов в секундах
26
+ max_retries: Максимальное количество повторных попыток
27
+ logger: Логгер для записи информации
28
+ proxy: Прокси для запросов
29
+ client: Экземпляр клиента, если он был создан
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ base_url: str,
35
+ jwt_token: Optional[str] = None,
36
+ timeout: int = 30,
37
+ max_retries: int = 3,
38
+ logger: Optional[logging.Logger] = None,
39
+ proxy: Optional[str] = None,
40
+ name: Optional[str] = None, # Уникальное имя конфига
41
+ tags: Optional[List[str]] = None, # Теги для группировки и фильтрации
42
+ ) -> None:
43
+ """
44
+ Инициализация конфигурации клиента.
45
+
46
+ Args:
47
+ base_url: Базовый URL для API
48
+ jwt_token: JWT токен для аутентификации (опционально)
49
+ timeout: Таймаут для запросов в секундах (по умолчанию 30)
50
+ max_retries: Максимальное количество повторных попыток (по умолчанию 3)
51
+ logger: Логгер для записи информации (опционально)
52
+ proxy: Прокси для запросов в формате 'socks5://user:pass@host:port' (опционально)
53
+ name: Уникальное имя конфигурации (опционально, генерируется автоматически)
54
+ tags: Список тегов для группировки и фильтрации (опционально)
55
+ """
56
+ self.base_url = base_url
57
+ self.jwt_token = jwt_token
58
+ self.timeout = timeout
59
+ self.max_retries = max_retries
60
+ self.logger = logger
61
+ self.proxy = proxy
62
+ self.name = name or str(uuid.uuid4())
63
+ self.tags = tags or []
64
+ self.client: Optional[GEngineClient] = None
65
+ self.last_used: float = 0 # Время последнего использования (для ротации)
66
+ self.error_count: int = 0 # Счетчик ошибок
67
+ self.success_count: int = 0 # Счетчик успешных запросов
68
+ self.is_active: bool = True # Флаг активности конфигурации
69
+
70
+ async def get_client(self) -> GEngineClient:
71
+ """
72
+ Возвращает клиент или создает новый, если он не существует.
73
+
74
+ Returns:
75
+ GEngineClient: Экземпляр клиента API
76
+ """
77
+ if self.client is None:
78
+ self.client = GEngineClient(
79
+ base_url=self.base_url,
80
+ jwt_token=self.jwt_token,
81
+ timeout=self.timeout,
82
+ max_retries=self.max_retries,
83
+ logger=self.logger,
84
+ proxy=self.proxy
85
+ )
86
+
87
+ # Обновляем время последнего использования
88
+ import time
89
+ self.last_used = time.time()
90
+
91
+ return self.client
92
+
93
+ async def close(self) -> None:
94
+ """Закрывает клиент."""
95
+ if self.client:
96
+ await self.client.close()
97
+ self.client = None
98
+
99
+ def update_token(self, jwt_token: str) -> None:
100
+ """
101
+ Обновляет JWT токен в конфигурации и в клиенте, если он существует.
102
+
103
+ Args:
104
+ jwt_token: Новый JWT токен
105
+ """
106
+ self.jwt_token = jwt_token
107
+ if self.client:
108
+ self.client.update_token(jwt_token)
109
+
110
+ def update_proxy(self, proxy: Optional[str] = None) -> None:
111
+ """
112
+ Обновляет настройки прокси в конфигурации и в клиенте, если он существует.
113
+
114
+ Args:
115
+ proxy: Новый прокси или None для отключения прокси
116
+ """
117
+ self.proxy = proxy
118
+ if self.client:
119
+ if hasattr(self.client, 'update_proxy'):
120
+ self.client.update_proxy(proxy)
121
+ else:
122
+ # Если клиент не поддерживает динамическое обновление прокси,
123
+ # закрываем его и создадим новый при следующем запросе
124
+ asyncio.create_task(self.close())
125
+
126
+ def deactivate(self) -> None:
127
+ """Деактивирует конфигурацию."""
128
+ self.is_active = False
129
+
130
+ def activate(self) -> None:
131
+ """Активирует конфигурацию."""
132
+ self.is_active = True
133
+
134
+ def to_dict(self) -> Dict[str, Any]:
135
+ """
136
+ Преобразует конфигурацию в словарь.
137
+
138
+ Returns:
139
+ Dict[str, Any]: Словарь с параметрами конфигурации
140
+ """
141
+ return {
142
+ "name": self.name,
143
+ "base_url": self.base_url,
144
+ "jwt_token": self.jwt_token,
145
+ "timeout": self.timeout,
146
+ "max_retries": self.max_retries,
147
+ "proxy": self.proxy,
148
+ "tags": self.tags,
149
+ "is_active": self.is_active,
150
+ "error_count": self.error_count,
151
+ "success_count": self.success_count,
152
+ "last_used": self.last_used,
153
+ }
154
+
155
+
156
+ class ClientFarm:
157
+ """
158
+ Управляет фермой клиентов с разными конфигурациями.
159
+
160
+ Позволяет создавать, хранить и использовать множество клиентов
161
+ с разными настройками, токенами и прокси.
162
+ """
163
+
164
+ def __init__(self, logger: Optional[logging.Logger] = None) -> None:
165
+ """
166
+ Инициализация фермы клиентов.
167
+
168
+ Args:
169
+ logger: Логгер для записи информации (опционально)
170
+ """
171
+ self.configs: Dict[str, ClientConfig] = {}
172
+ self.logger = logger or logging.getLogger(__name__)
173
+ self._selection_strategy: str = "round_robin" # Стратегия выбора клиента
174
+ self._last_used_index: int = -1 # Индекс последнего использованного клиента
175
+
176
+ def add_config(self, config: ClientConfig) -> None:
177
+ """
178
+ Добавляет конфигурацию в ферму.
179
+
180
+ Args:
181
+ config: Конфигурация клиента
182
+ """
183
+ self.configs[config.name] = config
184
+ self.logger.info(f"Добавлена конфигурация: {config.name}")
185
+
186
+ def create_config(self, **kwargs) -> ClientConfig:
187
+ """
188
+ Создает и добавляет новую конфигурацию.
189
+
190
+ Args:
191
+ **kwargs: Параметры для создания конфигурации
192
+
193
+ Returns:
194
+ ClientConfig: Созданная конфигурация
195
+ """
196
+ config = ClientConfig(**kwargs)
197
+ self.add_config(config)
198
+ return config
199
+
200
+ async def get_client(self, name: str) -> Optional[GEngineClient]:
201
+ """
202
+ Возвращает клиент по имени конфигурации.
203
+
204
+ Args:
205
+ name: Имя конфигурации
206
+
207
+ Returns:
208
+ Optional[GEngineClient]: Экземпляр клиента или None, если конфигурация не найдена
209
+ """
210
+ config = self.configs.get(name)
211
+ if config and config.is_active:
212
+ return await config.get_client()
213
+ return None
214
+
215
+ async def get_next_client(self, tag: Optional[str] = None) -> Optional[GEngineClient]:
216
+ """
217
+ Возвращает следующий клиент согласно выбранной стратегии.
218
+
219
+ Args:
220
+ tag: Тег для фильтрации клиентов (опционально)
221
+
222
+ Returns:
223
+ Optional[GEngineClient]: Экземпляр клиента или None, если нет доступных клиентов
224
+ """
225
+ active_configs = [
226
+ config for config in self.configs.values()
227
+ if config.is_active and (tag is None or tag in config.tags)
228
+ ]
229
+
230
+ if not active_configs:
231
+ self.logger.warning("Нет доступных конфигураций клиентов")
232
+ return None
233
+
234
+ if self._selection_strategy == "round_robin":
235
+ # Стратегия Round Robin
236
+ self._last_used_index = (self._last_used_index + 1) % len(active_configs)
237
+ config = active_configs[self._last_used_index]
238
+ return await config.get_client()
239
+
240
+ elif self._selection_strategy == "random":
241
+ # Случайный выбор
242
+ import random
243
+ config = random.choice(active_configs)
244
+ return await config.get_client()
245
+
246
+ elif self._selection_strategy == "least_errors":
247
+ # Выбираем клиент с наименьшим количеством ошибок
248
+ config = min(active_configs, key=lambda c: c.error_count)
249
+ return await config.get_client()
250
+
251
+ elif self._selection_strategy == "least_recently_used":
252
+ # Выбираем наименее недавно использованный клиент
253
+ config = min(active_configs, key=lambda c: c.last_used)
254
+ return await config.get_client()
255
+
256
+ else:
257
+ # По умолчанию используем Round Robin
258
+ self._last_used_index = (self._last_used_index + 1) % len(active_configs)
259
+ config = active_configs[self._last_used_index]
260
+ return await config.get_client()
261
+
262
+ def set_selection_strategy(self, strategy: str) -> None:
263
+ """
264
+ Устанавливает стратегию выбора клиента.
265
+
266
+ Args:
267
+ strategy: Стратегия выбора ('round_robin', 'random', 'least_errors', 'least_recently_used')
268
+ """
269
+ valid_strategies = ["round_robin", "random", "least_errors", "least_recently_used"]
270
+ if strategy not in valid_strategies:
271
+ raise ValueError(f"Недопустимая стратегия выбора: {strategy}. "
272
+ f"Должна быть одна из: {', '.join(valid_strategies)}")
273
+
274
+ self._selection_strategy = strategy
275
+ self.logger.info(f"Установлена стратегия выбора клиента: {strategy}")
276
+
277
+ async def close_client(self, name: str) -> None:
278
+ """
279
+ Закрывает клиент по имени конфигурации.
280
+
281
+ Args:
282
+ name: Имя конфигурации
283
+ """
284
+ config = self.configs.get(name)
285
+ if config:
286
+ await config.close()
287
+ self.logger.info(f"Клиент закрыт: {name}")
288
+
289
+ async def close_all(self) -> None:
290
+ """Закрывает все клиенты в ферме."""
291
+ for name, config in self.configs.items():
292
+ await config.close()
293
+ self.logger.info(f"Клиент закрыт: {name}")
294
+
295
+ def remove_config(self, name: str) -> None:
296
+ """
297
+ Удаляет конфигурацию из фермы.
298
+
299
+ Args:
300
+ name: Имя конфигурации
301
+ """
302
+ if name in self.configs:
303
+ config = self.configs[name]
304
+ # Запустим задачу на закрытие клиента, если он существует
305
+ if config.client:
306
+ asyncio.create_task(config.close())
307
+ del self.configs[name]
308
+ self.logger.info(f"Конфигурация удалена: {name}")
309
+
310
+ def get_configs_by_tag(self, tag: str) -> List[ClientConfig]:
311
+ """
312
+ Возвращает список конфигураций с указанным тегом.
313
+
314
+ Args:
315
+ tag: Тег для фильтрации
316
+
317
+ Returns:
318
+ List[ClientConfig]: Список конфигураций с указанным тегом
319
+ """
320
+ return [config for config in self.configs.values() if tag in config.tags]
321
+
322
+ def get_active_configs(self) -> List[ClientConfig]:
323
+ """
324
+ Возвращает список активных конфигураций.
325
+
326
+ Returns:
327
+ List[ClientConfig]: Список активных конфигураций
328
+ """
329
+ return [config for config in self.configs.values() if config.is_active]
330
+
331
+ def deactivate_config(self, name: str) -> None:
332
+ """
333
+ Деактивирует конфигурацию.
334
+
335
+ Args:
336
+ name: Имя конфигурации
337
+ """
338
+ if name in self.configs:
339
+ self.configs[name].deactivate()
340
+ self.logger.info(f"Конфигурация деактивирована: {name}")
341
+
342
+ def activate_config(self, name: str) -> None:
343
+ """
344
+ Активирует конфигурацию.
345
+
346
+ Args:
347
+ name: Имя конфигурации
348
+ """
349
+ if name in self.configs:
350
+ self.configs[name].activate()
351
+ self.logger.info(f"Конфигурация активирована: {name}")
352
+
353
+ def register_error(self, name: str) -> None:
354
+ """
355
+ Регистрирует ошибку для указанной конфигурации.
356
+
357
+ Args:
358
+ name: Имя конфигурации
359
+ """
360
+ if name in self.configs:
361
+ self.configs[name].error_count += 1
362
+
363
+ # Если превышен порог ошибок, деактивируем конфигурацию
364
+ if self.configs[name].error_count >= 10: # Пример порога
365
+ self.configs[name].deactivate()
366
+ self.logger.warning(f"Конфигурация деактивирована из-за ошибок: {name}")
367
+
368
+ def register_success(self, name: str) -> None:
369
+ """
370
+ Регистрирует успешное использование указанной конфигурации.
371
+
372
+ Args:
373
+ name: Имя конфигурации
374
+ """
375
+ if name in self.configs:
376
+ self.configs[name].success_count += 1
377
+
378
+ # Если клиент был деактивирован из-за ошибок, но успешно выполнил запрос,
379
+ # сбрасываем счетчик ошибок и активируем его снова
380
+ if not self.configs[name].is_active and self.configs[name].error_count > 0:
381
+ self.configs[name].error_count = 0
382
+ self.configs[name].activate()
383
+ self.logger.info(f"Конфигурация активирована после успешного запроса: {name}")
384
+
385
+ def get_stats(self) -> Dict[str, Dict[str, Any]]:
386
+ """
387
+ Возвращает статистику по всем конфигурациям.
388
+
389
+ Returns:
390
+ Dict[str, Dict[str, Any]]: Словарь со статистикой
391
+ """
392
+ return {name: config.to_dict() for name, config in self.configs.items()}
393
+
394
+ async def export_configs(self, file_path: str) -> None:
395
+ """
396
+ Экспортирует конфигурации в JSON-файл.
397
+
398
+ Args:
399
+ file_path: Путь к файлу для экспорта
400
+ """
401
+ import json
402
+
403
+ # Закрываем все клиенты перед экспортом
404
+ await self.close_all()
405
+
406
+ # Получаем данные конфигураций
407
+ configs_data = {name: config.to_dict() for name, config in self.configs.items()}
408
+
409
+ # Сохраняем в файл
410
+ with open(file_path, "w") as f:
411
+ json.dump(configs_data, f, indent=2)
412
+
413
+ self.logger.info(f"Конфигурации экспортированы в файл: {file_path}")
414
+
415
+ @classmethod
416
+ async def import_configs(cls, file_path: str, logger: Optional[logging.Logger] = None) -> "ClientFarm":
417
+ """
418
+ Импортирует конфигурации из JSON-файла.
419
+
420
+ Args:
421
+ file_path: Путь к файлу с конфигурациями
422
+ logger: Логгер для записи информации (опционально)
423
+
424
+ Returns:
425
+ ClientFarm: Новый экземпляр фермы клиентов с импортированными конфигурациями
426
+ """
427
+ import json
428
+
429
+ farm = cls(logger=logger)
430
+
431
+ try:
432
+ # Загружаем данные из файла
433
+ with open(file_path, "r") as f:
434
+ configs_data = json.load(f)
435
+
436
+ # Создаем конфигурации на основе данных
437
+ for name, data in configs_data.items():
438
+ # Извлекаем только параметры, необходимые для создания конфигурации
439
+ config_params = {
440
+ "name": data.get("name"),
441
+ "base_url": data.get("base_url"),
442
+ "jwt_token": data.get("jwt_token"),
443
+ "timeout": data.get("timeout"),
444
+ "max_retries": data.get("max_retries"),
445
+ "proxy": data.get("proxy"),
446
+ "tags": data.get("tags", []),
447
+ }
448
+
449
+ # Создаем конфигурацию
450
+ config = ClientConfig(**config_params)
451
+
452
+ # Устанавливаем дополнительные параметры
453
+ config.is_active = data.get("is_active", True)
454
+ config.error_count = data.get("error_count", 0)
455
+ config.success_count = data.get("success_count", 0)
456
+
457
+ # Добавляем конфигурацию в ферму
458
+ farm.add_config(config)
459
+ except Exception as e:
460
+ if logger:
461
+ logger.error(f"Ошибка при импорте конфигураций: {e}")
462
+ raise
463
+
464
+ return farm
@@ -1,15 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gengineapi
3
- Version: 0.1.0
4
- Summary: Add your description here
3
+ Version: 0.2.1
4
+ Summary: G-Engine API Client
5
5
  Requires-Python: >=3.13
6
6
  Description-Content-Type: text/markdown
7
7
  Requires-Dist: aiohttp>=3.8.0
8
8
  Requires-Dist: typing-extensions>=4.0.0
9
9
  Requires-Dist: aiohttp-socks>=0.7.1
10
- Requires-Dist: uv-publish
11
10
 
12
- # G-Engine API Client
11
+ # GEngineAPI
13
12
 
14
13
  Асинхронный модульный клиент для взаимодействия с API G-Engine.
15
14
 
@@ -21,6 +20,7 @@ Requires-Dist: uv-publish
21
20
  - Автоматические повторные попытки для временных ошибок
22
21
  - Поддержка JWT-аутентификации
23
22
  - Поддержка прокси, включая SOCKS5
23
+ - Ферма клиентов с балансировкой нагрузки и стратегиями ротации
24
24
  - Подробное логирование
25
25
  - Строгая типизация с помощью аннотаций типов
26
26
  - Класс-конфигурация для централизованной настройки и повторного использования
@@ -28,33 +28,13 @@ Requires-Dist: uv-publish
28
28
  ## Установка
29
29
 
30
30
  ```bash
31
- pip install -r requirements.txt
31
+ pip install gengineapi
32
32
  ```
33
33
 
34
- Или с помощью setup.py:
34
+ Для поддержки SOCKS5 прокси убедитесь, что установлен пакет `aiohttp-socks`:
35
35
 
36
36
  ```bash
37
- pip install .
38
- ```
39
-
40
- ## Структура проекта
41
-
42
- ```
43
- gengine_client/
44
- ├── __init__.py # Экспорт основных классов
45
- ├── client.py # Основной класс клиента
46
- ├── http.py # HTTP клиент с поддержкой аутентификации
47
- ├── exceptions.py # Иерархия исключений API
48
- ├── config.py # Класс-конфигурация для настройки параметров
49
- └── modules/ # Папка с модулями API
50
- ├── __init__.py
51
- ├── base.py # Базовый класс для всех модулей
52
- ├── payments.py # Модуль для работы с платежами
53
- ├── finances.py # Модуль для работы с финансами
54
- ├── auth.py # Модуль для аутентификации
55
- ├── users.py # Модуль для работы с пользователями
56
- ├── transactions.py # Модуль для работы с транзакциями
57
- └── currencies.py # Модуль для работы с валютами
37
+ pip install aiohttp-socks
58
38
  ```
59
39
 
60
40
  ## Использование
@@ -63,7 +43,7 @@ gengine_client/
63
43
 
64
44
  ```python
65
45
  import asyncio
66
- from gengine_client import GEngineClient
46
+ from gengineapi import GEngineClient
67
47
 
68
48
  async def main():
69
49
  # Создаем клиент с существующим токеном
@@ -84,7 +64,7 @@ asyncio.run(main())
84
64
 
85
65
  ```python
86
66
  import asyncio
87
- from gengine_client import GEngineClient
67
+ from gengineapi import GEngineClient
88
68
 
89
69
  async def main():
90
70
  # Создаем клиент без токена
@@ -119,7 +99,7 @@ asyncio.run(main())
119
99
 
120
100
  ```python
121
101
  import asyncio
122
- from gengine_client import GEngineClient
102
+ from gengineapi import GEngineClient
123
103
 
124
104
  async def main():
125
105
  # Создаем клиент с использованием SOCKS5 прокси
@@ -149,7 +129,7 @@ asyncio.run(main())
149
129
 
150
130
  ```python
151
131
  import asyncio
152
- from gengine_client import GEngineConfig
132
+ from gengineapi import GEngineConfig
153
133
 
154
134
  async def main():
155
135
  # Настройка параметров клиента
@@ -188,32 +168,52 @@ async def main():
188
168
  asyncio.run(main())
189
169
  ```
190
170
 
191
- ### Загрузка конфигурации из переменных окружения
171
+ ### Использование фермы клиентов
172
+
173
+ Ферма клиентов позволяет управлять множеством клиентов с разными конфигурациями и стратегиями ротации:
192
174
 
193
175
  ```python
194
176
  import asyncio
195
- import os
196
- from gengine_client import GEngineConfig
197
-
198
- # Установка переменных окружения
199
- os.environ["GENGINE_BASE_URL"] = "https://api.example.com/api/v2"
200
- os.environ["GENGINE_TOKEN"] = "env_jwt_token"
201
- os.environ["GENGINE_TIMEOUT"] = "45"
202
- os.environ["GENGINE_MAX_RETRIES"] = "5"
203
- os.environ["GENGINE_PROXY"] = "socks5://127.0.0.1:9050" # Опционально - прокси
177
+ from gengineapi import ClientFarm
204
178
 
205
179
  async def main():
206
- # Загрузка настроек из переменных окружения
207
- GEngineConfig.load_from_env()
180
+ # Создаем ферму клиентов
181
+ farm = ClientFarm()
208
182
 
209
- # Получение клиента с настройками из переменных окружения
210
- client = await GEngineConfig.get_client()
183
+ # Добавляем несколько конфигураций с разными прокси
184
+ farm.create_config(
185
+ name="client1",
186
+ base_url="https://api.example.com/api/v2",
187
+ jwt_token="token1",
188
+ proxy="socks5://user1:pass1@host1:port1",
189
+ tags=["group1", "production"]
190
+ )
211
191
 
212
- # Использование клиента
213
- # ...
192
+ farm.create_config(
193
+ name="client2",
194
+ base_url="https://api.example.com/api/v2",
195
+ jwt_token="token2",
196
+ proxy="socks5://user2:pass2@host2:port2",
197
+ tags=["group1", "backup"]
198
+ )
214
199
 
215
- # При завершении приложения
216
- await GEngineConfig.reset()
200
+ # Устанавливаем стратегию выбора клиента
201
+ farm.set_selection_strategy("round_robin") # или "random", "least_errors", "least_recently_used"
202
+
203
+ # Получаем клиент по имени
204
+ client1 = await farm.get_client("client1")
205
+ await client1.users.get_me()
206
+
207
+ # Получаем следующий клиент согласно стратегии
208
+ client = await farm.get_next_client()
209
+ await client.currencies.get_rate(source="cb_rf", pair="USD:RUB")
210
+
211
+ # Получаем клиент по тегу
212
+ client = await farm.get_next_client(tag="production")
213
+ await client.transactions.get_transactions(limit=5)
214
+
215
+ # При завершении работы закрываем все клиенты
216
+ await farm.close_all()
217
217
 
218
218
  asyncio.run(main())
219
219
  ```
@@ -224,7 +224,7 @@ asyncio.run(main())
224
224
  import asyncio
225
225
  import uuid
226
226
  from decimal import Decimal
227
- from gengine_client import GEngineClient
227
+ from gengineapi import GEngineClient
228
228
 
229
229
  async def main():
230
230
  async with GEngineClient(
@@ -255,19 +255,25 @@ async def main():
255
255
  asyncio.run(main())
256
256
  ```
257
257
 
258
- ## Примеры
258
+ ## Доступные модули API
259
+
260
+ - `payments` - создание и выполнение платежей
261
+ - `finances` - работа с финансовыми данными
262
+ - `auth` - аутентификация и управление токенами
263
+ - `users` - работа с пользователями и их балансами
264
+ - `transactions` - работа с транзакциями
265
+ - `currencies` - работа с курсами валют
259
266
 
260
- В репозитории есть примеры использования клиента с разными сценариями:
267
+ ## Дополнительная документация
261
268
 
262
- - `examples.py` - полный набор примеров использования, включая работу с прокси
263
- - `old_examples/` - директория с устаревшими примерами
269
+ Ручки, которые дергаем - https://b2b-api.ggsel.com/api/v2/docs#
264
270
 
265
271
  ## Зависимости
266
272
 
267
273
  - Python 3.7+
268
274
  - aiohttp >= 3.8.0
269
275
  - typing-extensions >= 4.0.0
270
- - aiohttp-socks >= 0.7.1 (для поддержки SOCKS5 прокси)
276
+ - aiohttp-socks >= 0.7.1 (опционально, для поддержки SOCKS5 прокси)
271
277
 
272
278
  ## Лицензия
273
279
 
@@ -4,6 +4,7 @@ src/gengineapi/__init__.py
4
4
  src/gengineapi/client.py
5
5
  src/gengineapi/config.py
6
6
  src/gengineapi/exceptions.py
7
+ src/gengineapi/farm.py
7
8
  src/gengineapi/http.py
8
9
  src/gengineapi.egg-info/PKG-INFO
9
10
  src/gengineapi.egg-info/SOURCES.txt
@@ -1,4 +1,3 @@
1
1
  aiohttp>=3.8.0
2
2
  typing-extensions>=4.0.0
3
3
  aiohttp-socks>=0.7.1
4
- uv-publish
File without changes