copsapi-wrapper 1.1.0__tar.gz → 1.1.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.
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/CHANGELOG.md +5 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/PKG-INFO +29 -18
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/README.md +28 -17
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/pyproject.toml +1 -1
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/_async_client.py +632 -630
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/_core.py +14 -5
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/_sync_client.py +5 -3
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/cli.py +242 -242
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/helpers.py +175 -175
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/models.py +365 -365
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/.gitignore +0 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/LICENSE +0 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/__init__.py +0 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/__main__.py +0 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/_io_async.py +0 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/_io_sync.py +0 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/cache.py +0 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/catalog.py +0 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/enums.py +0 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/errors.py +0 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/py.typed +0 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/quota.py +0 -0
- {copsapi_wrapper-1.1.0 → copsapi_wrapper-1.1.1}/src/copsapi/watcher.py +0 -0
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.1
|
|
4
|
+
|
|
5
|
+
- On Python 3.10, constants such as `GameType.CASUAL` passed to client methods are now sent to the API as their plain values (`2`), not as their names.
|
|
6
|
+
- The README and examples use the named constants throughout.
|
|
7
|
+
|
|
3
8
|
## 1.1.0
|
|
4
9
|
|
|
5
10
|
- Named constants for every code the API uses, such as `BanReason.HACKING`, `UserType.MODERATOR`, `MapId.CASTELLO`, `GameModeId.TOURNAMENT_MODE`, `LeaderboardType.KILLS` and `AutohostCommand.KICK`. Number codes are `IntEnum`s and text codes are `str` enums, so both compare equal to the plain values in the models.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: copsapi-wrapper
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Typed, quota-aware Python client for the Critical Ops Public API v2, sync and async: profiles, leaderboards, match history, weapon stats, server status and Autohost with a live room watcher.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Matchmaking-Live/copsapi-wrapper-python
|
|
6
6
|
Project-URL: Repository, https://github.com/Matchmaking-Live/copsapi-wrapper-python
|
|
@@ -95,7 +95,7 @@ What a token can do depends on its permissions:
|
|
|
95
95
|
| Endpoints | Needs |
|
|
96
96
|
| --- | --- |
|
|
97
97
|
| Profiles, servers, match history, weapon stats, leaderboards | Any valid token |
|
|
98
|
-
| Autohost (`create`, `poll`, `control`, `host`, the watcher) | A token with the `Autohost` permission |
|
|
98
|
+
| Autohost (`create`, `poll`, `control`, `host`, the watcher) | A token with the `Autohost` permission (Developer Issued) |
|
|
99
99
|
|
|
100
100
|
There are three ways to pass the token:
|
|
101
101
|
|
|
@@ -129,14 +129,14 @@ If your token lacks `Autohost`, hosting calls raise `PermissionDeniedError` with
|
|
|
129
129
|
## Sync and async
|
|
130
130
|
|
|
131
131
|
```python
|
|
132
|
-
from copsapi import CopsClient, AsyncCopsClient
|
|
132
|
+
from copsapi import CopsClient, AsyncCopsClient, LeaderboardType
|
|
133
133
|
|
|
134
134
|
with CopsClient() as cops:
|
|
135
135
|
servers = cops.servers.online()
|
|
136
136
|
|
|
137
137
|
async with AsyncCopsClient() as cops:
|
|
138
138
|
servers = await cops.servers.online()
|
|
139
|
-
async for entry in cops.leaderboards.iterate(
|
|
139
|
+
async for entry in cops.leaderboards.iterate(LeaderboardType.KILLS, max_pages=3):
|
|
140
140
|
print(entry.position, entry.basic_info.name)
|
|
141
141
|
```
|
|
142
142
|
|
|
@@ -212,9 +212,11 @@ server = cops.servers.pick(region="EU")
|
|
|
212
212
|
## Match history
|
|
213
213
|
|
|
214
214
|
```python
|
|
215
|
+
from copsapi import GameType
|
|
216
|
+
|
|
215
217
|
history = cops.matches.history(user_id)
|
|
216
218
|
match = cops.matches.one(history[0].id)
|
|
217
|
-
recent = cops.matches.recent(user_id, limit=5, game_type=
|
|
219
|
+
recent = cops.matches.recent(user_id, limit=5, game_type=GameType.RANKED)
|
|
218
220
|
```
|
|
219
221
|
|
|
220
222
|
- `history` returns the player's recent matches, newest first. The history resets with each game update.
|
|
@@ -224,26 +226,31 @@ recent = cops.matches.recent(user_id, limit=5, game_type="ranked")
|
|
|
224
226
|
## Weapon stats
|
|
225
227
|
|
|
226
228
|
```python
|
|
227
|
-
|
|
228
|
-
|
|
229
|
+
from copsapi import GameType
|
|
230
|
+
|
|
231
|
+
ranked = cops.stats.weapons(user_id, GameType.RANKED)
|
|
232
|
+
casual = cops.stats.weapons(user_id, GameType.CASUAL)
|
|
233
|
+
best = cops.stats.top_weapons(user_id, game_type=GameType.RANKED, by="k", limit=5)
|
|
229
234
|
```
|
|
230
235
|
|
|
231
|
-
- The game type is `"ranked"`
|
|
236
|
+
- The game type is `GameType.RANKED` or `GameType.CASUAL`; the strings `"ranked"` and `"casual"` work too. Custom games count as casual.
|
|
232
237
|
- `top_weapons` sorts by kills (`k`), assists (`a`) or headshots (`h`). Each `WeaponStats` also has a `.headshot_rate`.
|
|
233
238
|
|
|
234
239
|
## Leaderboards
|
|
235
240
|
|
|
236
241
|
```python
|
|
237
|
-
|
|
238
|
-
|
|
242
|
+
from copsapi import LeaderboardType
|
|
243
|
+
|
|
244
|
+
page = cops.leaderboards.page(LeaderboardType.MMR, page=1)
|
|
245
|
+
elite = cops.leaderboards.page(LeaderboardType.MMR, rank=9)
|
|
239
246
|
|
|
240
|
-
for entry in cops.leaderboards.iterate(
|
|
247
|
+
for entry in cops.leaderboards.iterate(LeaderboardType.KILLS, max_pages=10):
|
|
241
248
|
print(entry.position, entry.basic_info.name, entry.score)
|
|
242
249
|
|
|
243
|
-
top50 = cops.leaderboards.top(
|
|
250
|
+
top50 = cops.leaderboards.top(LeaderboardType.WINS, 50)
|
|
244
251
|
```
|
|
245
252
|
|
|
246
|
-
- The types are `
|
|
253
|
+
- The types are `LeaderboardType.MMR`, `KILLS`, `DEATHS`, `ASSISTS`, `MATCHES`, `WINS`, `LOSSES`, `XP` and `MVP`; the plain strings (`"mmr"`, `"kills"` and so on) work too. Pages hold 10 entries.
|
|
247
254
|
- `page` returns a `LeaderboardPage(type, page, rank, size, pages, entries)`. `rank` (0 to 9) filters to one rank.
|
|
248
255
|
- Players move as matches end, so `iterate` skips anyone already yielded on an earlier page.
|
|
249
256
|
|
|
@@ -254,25 +261,27 @@ This needs a token with the `Autohost` permission. To check before offering host
|
|
|
254
261
|
### One call: host, wait for the code, add owners
|
|
255
262
|
|
|
256
263
|
```python
|
|
264
|
+
from copsapi import GameModeId, MapId
|
|
265
|
+
|
|
257
266
|
server = cops.servers.pick(region="EU")
|
|
258
267
|
room = cops.autohost.host(
|
|
259
268
|
server_id=server.id,
|
|
260
|
-
mode=
|
|
261
|
-
map=
|
|
269
|
+
mode=GameModeId.TOURNAMENT_MODE,
|
|
270
|
+
map=MapId.CASTELLO,
|
|
262
271
|
room_name="Scrim #42",
|
|
263
272
|
owners=[captain_user_id],
|
|
264
273
|
)
|
|
265
274
|
print(f"Join with code {room.code}, password {room.password}")
|
|
266
275
|
```
|
|
267
276
|
|
|
268
|
-
- `mode` and `map`
|
|
277
|
+
- `mode` and `map` take a `GameModeId` and a `MapId`. Names (`"Tournament Mode"`, `"castello"`) and raw ids work too. Combinations the API doesn't allow raise `BadRequestError` before the request is sent; pass `validate=False` if Critical Force added a map after this release.
|
|
269
278
|
- A random 6-character password is generated unless you pass `password="..."`. Pass `password=False` for an open room.
|
|
270
279
|
- `host` raises `GameRejectedError` if the game server refuses the game (it's full or in maintenance, or the token already hosts 10 games there). It raises `GameGoneError`, or `ConnectionFailedError` with code `timeout`, if the room never appears.
|
|
271
280
|
|
|
272
281
|
### Step by step
|
|
273
282
|
|
|
274
283
|
```python
|
|
275
|
-
game = cops.autohost.create(server_id=6, mode=
|
|
284
|
+
game = cops.autohost.create(server_id=6, mode=GameModeId.DEFUSE, map=MapId.RAID, room_name="Final", password="abc")
|
|
276
285
|
ready = cops.autohost.wait_for_room(game.id)
|
|
277
286
|
games = cops.autohost.poll([game.id, other_id])
|
|
278
287
|
one = cops.autohost.get(game.id)
|
|
@@ -285,12 +294,14 @@ one = cops.autohost.get(game.id)
|
|
|
285
294
|
### Room commands
|
|
286
295
|
|
|
287
296
|
```python
|
|
297
|
+
from copsapi import AutohostCommand
|
|
298
|
+
|
|
288
299
|
cops.autohost.kick(game_id, user_id)
|
|
289
300
|
cops.autohost.add_owner(game_id, user_id)
|
|
290
301
|
cops.autohost.remove_owner(game_id, user_id)
|
|
291
302
|
cops.autohost.restart(game_id)
|
|
292
303
|
cops.autohost.close(game_id)
|
|
293
|
-
cops.autohost.control(game_id,
|
|
304
|
+
cops.autohost.control(game_id, AutohostCommand.KICK, user_id)
|
|
294
305
|
```
|
|
295
306
|
|
|
296
307
|
- `kick` removes the player and keeps them out for an hour.
|
|
@@ -60,7 +60,7 @@ What a token can do depends on its permissions:
|
|
|
60
60
|
| Endpoints | Needs |
|
|
61
61
|
| --- | --- |
|
|
62
62
|
| Profiles, servers, match history, weapon stats, leaderboards | Any valid token |
|
|
63
|
-
| Autohost (`create`, `poll`, `control`, `host`, the watcher) | A token with the `Autohost` permission |
|
|
63
|
+
| Autohost (`create`, `poll`, `control`, `host`, the watcher) | A token with the `Autohost` permission (Developer Issued) |
|
|
64
64
|
|
|
65
65
|
There are three ways to pass the token:
|
|
66
66
|
|
|
@@ -94,14 +94,14 @@ If your token lacks `Autohost`, hosting calls raise `PermissionDeniedError` with
|
|
|
94
94
|
## Sync and async
|
|
95
95
|
|
|
96
96
|
```python
|
|
97
|
-
from copsapi import CopsClient, AsyncCopsClient
|
|
97
|
+
from copsapi import CopsClient, AsyncCopsClient, LeaderboardType
|
|
98
98
|
|
|
99
99
|
with CopsClient() as cops:
|
|
100
100
|
servers = cops.servers.online()
|
|
101
101
|
|
|
102
102
|
async with AsyncCopsClient() as cops:
|
|
103
103
|
servers = await cops.servers.online()
|
|
104
|
-
async for entry in cops.leaderboards.iterate(
|
|
104
|
+
async for entry in cops.leaderboards.iterate(LeaderboardType.KILLS, max_pages=3):
|
|
105
105
|
print(entry.position, entry.basic_info.name)
|
|
106
106
|
```
|
|
107
107
|
|
|
@@ -177,9 +177,11 @@ server = cops.servers.pick(region="EU")
|
|
|
177
177
|
## Match history
|
|
178
178
|
|
|
179
179
|
```python
|
|
180
|
+
from copsapi import GameType
|
|
181
|
+
|
|
180
182
|
history = cops.matches.history(user_id)
|
|
181
183
|
match = cops.matches.one(history[0].id)
|
|
182
|
-
recent = cops.matches.recent(user_id, limit=5, game_type=
|
|
184
|
+
recent = cops.matches.recent(user_id, limit=5, game_type=GameType.RANKED)
|
|
183
185
|
```
|
|
184
186
|
|
|
185
187
|
- `history` returns the player's recent matches, newest first. The history resets with each game update.
|
|
@@ -189,26 +191,31 @@ recent = cops.matches.recent(user_id, limit=5, game_type="ranked")
|
|
|
189
191
|
## Weapon stats
|
|
190
192
|
|
|
191
193
|
```python
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
from copsapi import GameType
|
|
195
|
+
|
|
196
|
+
ranked = cops.stats.weapons(user_id, GameType.RANKED)
|
|
197
|
+
casual = cops.stats.weapons(user_id, GameType.CASUAL)
|
|
198
|
+
best = cops.stats.top_weapons(user_id, game_type=GameType.RANKED, by="k", limit=5)
|
|
194
199
|
```
|
|
195
200
|
|
|
196
|
-
- The game type is `"ranked"`
|
|
201
|
+
- The game type is `GameType.RANKED` or `GameType.CASUAL`; the strings `"ranked"` and `"casual"` work too. Custom games count as casual.
|
|
197
202
|
- `top_weapons` sorts by kills (`k`), assists (`a`) or headshots (`h`). Each `WeaponStats` also has a `.headshot_rate`.
|
|
198
203
|
|
|
199
204
|
## Leaderboards
|
|
200
205
|
|
|
201
206
|
```python
|
|
202
|
-
|
|
203
|
-
|
|
207
|
+
from copsapi import LeaderboardType
|
|
208
|
+
|
|
209
|
+
page = cops.leaderboards.page(LeaderboardType.MMR, page=1)
|
|
210
|
+
elite = cops.leaderboards.page(LeaderboardType.MMR, rank=9)
|
|
204
211
|
|
|
205
|
-
for entry in cops.leaderboards.iterate(
|
|
212
|
+
for entry in cops.leaderboards.iterate(LeaderboardType.KILLS, max_pages=10):
|
|
206
213
|
print(entry.position, entry.basic_info.name, entry.score)
|
|
207
214
|
|
|
208
|
-
top50 = cops.leaderboards.top(
|
|
215
|
+
top50 = cops.leaderboards.top(LeaderboardType.WINS, 50)
|
|
209
216
|
```
|
|
210
217
|
|
|
211
|
-
- The types are `
|
|
218
|
+
- The types are `LeaderboardType.MMR`, `KILLS`, `DEATHS`, `ASSISTS`, `MATCHES`, `WINS`, `LOSSES`, `XP` and `MVP`; the plain strings (`"mmr"`, `"kills"` and so on) work too. Pages hold 10 entries.
|
|
212
219
|
- `page` returns a `LeaderboardPage(type, page, rank, size, pages, entries)`. `rank` (0 to 9) filters to one rank.
|
|
213
220
|
- Players move as matches end, so `iterate` skips anyone already yielded on an earlier page.
|
|
214
221
|
|
|
@@ -219,25 +226,27 @@ This needs a token with the `Autohost` permission. To check before offering host
|
|
|
219
226
|
### One call: host, wait for the code, add owners
|
|
220
227
|
|
|
221
228
|
```python
|
|
229
|
+
from copsapi import GameModeId, MapId
|
|
230
|
+
|
|
222
231
|
server = cops.servers.pick(region="EU")
|
|
223
232
|
room = cops.autohost.host(
|
|
224
233
|
server_id=server.id,
|
|
225
|
-
mode=
|
|
226
|
-
map=
|
|
234
|
+
mode=GameModeId.TOURNAMENT_MODE,
|
|
235
|
+
map=MapId.CASTELLO,
|
|
227
236
|
room_name="Scrim #42",
|
|
228
237
|
owners=[captain_user_id],
|
|
229
238
|
)
|
|
230
239
|
print(f"Join with code {room.code}, password {room.password}")
|
|
231
240
|
```
|
|
232
241
|
|
|
233
|
-
- `mode` and `map`
|
|
242
|
+
- `mode` and `map` take a `GameModeId` and a `MapId`. Names (`"Tournament Mode"`, `"castello"`) and raw ids work too. Combinations the API doesn't allow raise `BadRequestError` before the request is sent; pass `validate=False` if Critical Force added a map after this release.
|
|
234
243
|
- A random 6-character password is generated unless you pass `password="..."`. Pass `password=False` for an open room.
|
|
235
244
|
- `host` raises `GameRejectedError` if the game server refuses the game (it's full or in maintenance, or the token already hosts 10 games there). It raises `GameGoneError`, or `ConnectionFailedError` with code `timeout`, if the room never appears.
|
|
236
245
|
|
|
237
246
|
### Step by step
|
|
238
247
|
|
|
239
248
|
```python
|
|
240
|
-
game = cops.autohost.create(server_id=6, mode=
|
|
249
|
+
game = cops.autohost.create(server_id=6, mode=GameModeId.DEFUSE, map=MapId.RAID, room_name="Final", password="abc")
|
|
241
250
|
ready = cops.autohost.wait_for_room(game.id)
|
|
242
251
|
games = cops.autohost.poll([game.id, other_id])
|
|
243
252
|
one = cops.autohost.get(game.id)
|
|
@@ -250,12 +259,14 @@ one = cops.autohost.get(game.id)
|
|
|
250
259
|
### Room commands
|
|
251
260
|
|
|
252
261
|
```python
|
|
262
|
+
from copsapi import AutohostCommand
|
|
263
|
+
|
|
253
264
|
cops.autohost.kick(game_id, user_id)
|
|
254
265
|
cops.autohost.add_owner(game_id, user_id)
|
|
255
266
|
cops.autohost.remove_owner(game_id, user_id)
|
|
256
267
|
cops.autohost.restart(game_id)
|
|
257
268
|
cops.autohost.close(game_id)
|
|
258
|
-
cops.autohost.control(game_id,
|
|
269
|
+
cops.autohost.control(game_id, AutohostCommand.KICK, user_id)
|
|
259
270
|
```
|
|
260
271
|
|
|
261
272
|
- `kick` removes the player and keeps them out for an hour.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "copsapi-wrapper"
|
|
7
|
-
version = "1.1.
|
|
7
|
+
version = "1.1.1"
|
|
8
8
|
description = "Typed, quota-aware Python client for the Critical Ops Public API v2, sync and async: profiles, leaderboards, match history, weapon stats, server status and Autohost with a live room watcher."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|