artifacts-mmo 0.1.0__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.
- artifacts_mmo-0.1.0/.gitattributes +2 -0
- artifacts_mmo-0.1.0/PKG-INFO +12 -0
- artifacts_mmo-0.1.0/README.md +622 -0
- artifacts_mmo-0.1.0/examples/basic_usage.py +83 -0
- artifacts_mmo-0.1.0/examples/combat_loop_5chars.py +155 -0
- artifacts_mmo-0.1.0/pyproject.toml +24 -0
- artifacts_mmo-0.1.0/src/artifacts/__init__.py +76 -0
- artifacts_mmo-0.1.0/src/artifacts/api/__init__.py +1 -0
- artifacts_mmo-0.1.0/src/artifacts/api/accounts.py +79 -0
- artifacts_mmo-0.1.0/src/artifacts/api/achievements.py +36 -0
- artifacts_mmo-0.1.0/src/artifacts/api/badges.py +27 -0
- artifacts_mmo-0.1.0/src/artifacts/api/characters.py +46 -0
- artifacts_mmo-0.1.0/src/artifacts/api/effects.py +27 -0
- artifacts_mmo-0.1.0/src/artifacts/api/events.py +38 -0
- artifacts_mmo-0.1.0/src/artifacts/api/grand_exchange.py +60 -0
- artifacts_mmo-0.1.0/src/artifacts/api/items.py +46 -0
- artifacts_mmo-0.1.0/src/artifacts/api/leaderboard.py +49 -0
- artifacts_mmo-0.1.0/src/artifacts/api/maps.py +72 -0
- artifacts_mmo-0.1.0/src/artifacts/api/monsters.py +44 -0
- artifacts_mmo-0.1.0/src/artifacts/api/my_account.py +111 -0
- artifacts_mmo-0.1.0/src/artifacts/api/npcs.py +67 -0
- artifacts_mmo-0.1.0/src/artifacts/api/resources.py +46 -0
- artifacts_mmo-0.1.0/src/artifacts/api/sandbox.py +64 -0
- artifacts_mmo-0.1.0/src/artifacts/api/server.py +19 -0
- artifacts_mmo-0.1.0/src/artifacts/api/simulation.py +33 -0
- artifacts_mmo-0.1.0/src/artifacts/api/tasks.py +62 -0
- artifacts_mmo-0.1.0/src/artifacts/api/token.py +24 -0
- artifacts_mmo-0.1.0/src/artifacts/character.py +428 -0
- artifacts_mmo-0.1.0/src/artifacts/client.py +99 -0
- artifacts_mmo-0.1.0/src/artifacts/cooldown.py +26 -0
- artifacts_mmo-0.1.0/src/artifacts/errors.py +133 -0
- artifacts_mmo-0.1.0/src/artifacts/http.py +198 -0
- artifacts_mmo-0.1.0/src/artifacts/models/__init__.py +263 -0
- artifacts_mmo-0.1.0/src/artifacts/models/account.py +33 -0
- artifacts_mmo-0.1.0/src/artifacts/models/achievements.py +45 -0
- artifacts_mmo-0.1.0/src/artifacts/models/badges.py +35 -0
- artifacts_mmo-0.1.0/src/artifacts/models/bank.py +14 -0
- artifacts_mmo-0.1.0/src/artifacts/models/character.py +129 -0
- artifacts_mmo-0.1.0/src/artifacts/models/combat.py +59 -0
- artifacts_mmo-0.1.0/src/artifacts/models/common.py +68 -0
- artifacts_mmo-0.1.0/src/artifacts/models/effects.py +13 -0
- artifacts_mmo-0.1.0/src/artifacts/models/enums.py +298 -0
- artifacts_mmo-0.1.0/src/artifacts/models/errors.py +11 -0
- artifacts_mmo-0.1.0/src/artifacts/models/events.py +39 -0
- artifacts_mmo-0.1.0/src/artifacts/models/grand_exchange.py +44 -0
- artifacts_mmo-0.1.0/src/artifacts/models/items.py +28 -0
- artifacts_mmo-0.1.0/src/artifacts/models/leaderboard.py +42 -0
- artifacts_mmo-0.1.0/src/artifacts/models/logs.py +31 -0
- artifacts_mmo-0.1.0/src/artifacts/models/maps.py +42 -0
- artifacts_mmo-0.1.0/src/artifacts/models/monsters.py +37 -0
- artifacts_mmo-0.1.0/src/artifacts/models/npcs.py +30 -0
- artifacts_mmo-0.1.0/src/artifacts/models/pagination.py +17 -0
- artifacts_mmo-0.1.0/src/artifacts/models/resources.py +14 -0
- artifacts_mmo-0.1.0/src/artifacts/models/responses.py +194 -0
- artifacts_mmo-0.1.0/src/artifacts/models/sandbox.py +26 -0
- artifacts_mmo-0.1.0/src/artifacts/models/server.py +20 -0
- artifacts_mmo-0.1.0/src/artifacts/models/simulation.py +11 -0
- artifacts_mmo-0.1.0/src/artifacts/models/tasks.py +30 -0
- artifacts_mmo-0.1.0/tmpclaude-7cd1-cwd +1 -0
- artifacts_mmo-0.1.0/tmpclaude-81d2-cwd +1 -0
- artifacts_mmo-0.1.0/tmpclaude-a324-cwd +1 -0
- artifacts_mmo-0.1.0/tmpclaude-eb80-cwd +1 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: artifacts-mmo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Async Python wrapper for the Artifacts MMO API
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: aiohttp<4,>=3.9
|
|
8
|
+
Requires-Dist: pydantic<3,>=2.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: aioresponses; extra == 'dev'
|
|
11
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
12
|
+
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
# Artifacts MMO - Python Wrapper
|
|
2
|
+
|
|
3
|
+
Async Python wrapper for the [Artifacts MMO](https://artifactsmmo.com/) API. Control up to 5 characters simultaneously with full type safety and IDE autocompletion.
|
|
4
|
+
|
|
5
|
+
- **65 endpoints** covered (every single one)
|
|
6
|
+
- **Async** built on `aiohttp` -- run multiple characters in parallel
|
|
7
|
+
- **Typed** with Pydantic v2 models -- full IDE autocompletion
|
|
8
|
+
- **Simple** -- character-centric design, beginner friendly
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install -e .
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Requirements:** Python 3.10+
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import asyncio
|
|
22
|
+
from artifacts import ArtifactsClient, wait_for_cooldown
|
|
23
|
+
|
|
24
|
+
async def main():
|
|
25
|
+
async with ArtifactsClient(token="your_token_here") as client:
|
|
26
|
+
# Get a character controller
|
|
27
|
+
char = client.character("MyCharacter")
|
|
28
|
+
|
|
29
|
+
# Fetch character info
|
|
30
|
+
info = await char.get()
|
|
31
|
+
print(f"{info.name} lv{info.level} HP={info.hp}/{info.max_hp}")
|
|
32
|
+
|
|
33
|
+
# Fight a monster
|
|
34
|
+
result = await char.fight()
|
|
35
|
+
print(f"Result: {result.fight.result.value}")
|
|
36
|
+
|
|
37
|
+
# Wait for cooldown before next action
|
|
38
|
+
await wait_for_cooldown(result.cooldown)
|
|
39
|
+
|
|
40
|
+
asyncio.run(main())
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Getting a Token
|
|
44
|
+
|
|
45
|
+
You need a JWT token to authenticate. Generate one from your account credentials:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
async with ArtifactsClient() as client:
|
|
49
|
+
token = await client.token.generate("your_username", "your_password")
|
|
50
|
+
print(token) # Use this token from now on
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or use a token you already have from the Artifacts website.
|
|
54
|
+
|
|
55
|
+
## Custom API URL
|
|
56
|
+
|
|
57
|
+
By default the wrapper connects to `https://api.artifactsmmo.com/`. To use a different server (e.g. sandbox):
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
client = ArtifactsClient(
|
|
61
|
+
token="your_token",
|
|
62
|
+
base_url="https://api.sandbox.artifactsmmo.com",
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Architecture Overview
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
ArtifactsClient
|
|
70
|
+
├── .character("name") -> Character (34 action methods)
|
|
71
|
+
├── .server -> ServerAPI
|
|
72
|
+
├── .token -> TokenAPI
|
|
73
|
+
├── .accounts -> AccountsAPI
|
|
74
|
+
├── .my_account -> MyAccountAPI
|
|
75
|
+
├── .characters -> CharactersAPI
|
|
76
|
+
├── .items -> ItemsAPI
|
|
77
|
+
├── .monsters -> MonstersAPI
|
|
78
|
+
├── .maps -> MapsAPI
|
|
79
|
+
├── .resources -> ResourcesAPI
|
|
80
|
+
├── .npcs -> NPCsAPI
|
|
81
|
+
├── .events -> EventsAPI
|
|
82
|
+
├── .achievements -> AchievementsAPI
|
|
83
|
+
├── .badges -> BadgesAPI
|
|
84
|
+
├── .effects -> EffectsAPI
|
|
85
|
+
├── .grand_exchange -> GrandExchangeAPI
|
|
86
|
+
├── .leaderboard -> LeaderboardAPI
|
|
87
|
+
├── .tasks -> TasksAPI
|
|
88
|
+
├── .simulation -> SimulationAPI
|
|
89
|
+
└── .sandbox -> SandboxAPI
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Fetching Game Data
|
|
95
|
+
|
|
96
|
+
All game data endpoints are read-only and return typed Pydantic models. Paginated results come wrapped in `DataPage[T]`.
|
|
97
|
+
|
|
98
|
+
### Items
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
# Get a single item by code
|
|
102
|
+
item = await client.items.get("copper_ore")
|
|
103
|
+
print(f"{item.name} (lv{item.level}, type={item.type.value})")
|
|
104
|
+
|
|
105
|
+
# List items with filters
|
|
106
|
+
page = await client.items.get_all(min_level=1, max_level=10, type="resource", size=20)
|
|
107
|
+
for item in page.data:
|
|
108
|
+
print(f" {item.code}: {item.name}")
|
|
109
|
+
print(f"Page {page.page}/{page.pages} (total: {page.total})")
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Monsters
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
monster = await client.monsters.get("chicken")
|
|
116
|
+
print(f"{monster.name} lv{monster.level} HP={monster.hp}")
|
|
117
|
+
print(f"Drops: {[(d.code, d.rate) for d in monster.drops]}")
|
|
118
|
+
|
|
119
|
+
# List all monsters in a level range
|
|
120
|
+
page = await client.monsters.get_all(min_level=1, max_level=5)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Maps
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
# Find maps containing a specific monster
|
|
127
|
+
maps = await client.maps.get_all(content_type="monster", content_code="chicken")
|
|
128
|
+
for m in maps.data:
|
|
129
|
+
print(f" ({m.x},{m.y}) layer={m.layer.value}")
|
|
130
|
+
|
|
131
|
+
# Get a specific map tile
|
|
132
|
+
tile = await client.maps.get_by_position("overworld", 0, 1)
|
|
133
|
+
|
|
134
|
+
# Get a map by its ID
|
|
135
|
+
tile = await client.maps.get_by_id(42)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Resources
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
# All mining resources
|
|
142
|
+
page = await client.resources.get_all(skill="mining", min_level=1)
|
|
143
|
+
for r in page.data:
|
|
144
|
+
print(f" {r.code} (lv{r.level}) drops: {[d.code for d in r.drops]}")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### NPCs
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
# List all NPCs
|
|
151
|
+
npcs = await client.npcs.get_all()
|
|
152
|
+
|
|
153
|
+
# Get items sold by an NPC
|
|
154
|
+
items = await client.npcs.get_items("merchant_1")
|
|
155
|
+
for i in items.data:
|
|
156
|
+
print(f" {i.code} buy={i.buy_price} sell={i.sell_price}")
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Other game data
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
# Achievements
|
|
163
|
+
all_achievements = await client.achievements.get_all()
|
|
164
|
+
single = await client.achievements.get("first_kill")
|
|
165
|
+
|
|
166
|
+
# Badges
|
|
167
|
+
badges = await client.badges.get_all()
|
|
168
|
+
|
|
169
|
+
# Effects
|
|
170
|
+
effects = await client.effects.get_all()
|
|
171
|
+
|
|
172
|
+
# Events
|
|
173
|
+
active = await client.events.get_all_active()
|
|
174
|
+
all_events = await client.events.get_all()
|
|
175
|
+
|
|
176
|
+
# Tasks
|
|
177
|
+
tasks = await client.tasks.get_all(type="monsters", min_level=1)
|
|
178
|
+
rewards = await client.tasks.get_all_rewards()
|
|
179
|
+
|
|
180
|
+
# Leaderboard
|
|
181
|
+
top_chars = await client.leaderboard.get_characters(sort="combat")
|
|
182
|
+
top_accounts = await client.leaderboard.get_accounts(sort="gold")
|
|
183
|
+
|
|
184
|
+
# Grand Exchange
|
|
185
|
+
orders = await client.grand_exchange.get_orders(code="copper_ore")
|
|
186
|
+
history = await client.grand_exchange.get_history("copper_ore")
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Pagination
|
|
190
|
+
|
|
191
|
+
All list endpoints return a `DataPage[T]`:
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
page = await client.items.get_all(page=1, size=50)
|
|
195
|
+
page.data # list[ItemSchema] -- the items on this page
|
|
196
|
+
page.total # int -- total number of items across all pages
|
|
197
|
+
page.page # int -- current page number
|
|
198
|
+
page.pages # int -- total number of pages
|
|
199
|
+
page.size # int -- page size
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
To fetch every item across all pages at once, use the HTTP client helper:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
all_items = await client._http.get_all_pages("/items", ItemSchema, page_size=100)
|
|
206
|
+
# Returns a flat list[ItemSchema] with every item
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Account & Bank
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
# Account details
|
|
215
|
+
account = await client.my_account.get_details()
|
|
216
|
+
print(f"{account.username} -- gems={account.gems}")
|
|
217
|
+
|
|
218
|
+
# Bank info
|
|
219
|
+
bank = await client.my_account.get_bank()
|
|
220
|
+
print(f"Gold: {bank.gold}, Slots: {bank.slots}/{bank.slots}")
|
|
221
|
+
|
|
222
|
+
# Bank items
|
|
223
|
+
bank_items = await client.my_account.get_bank_items()
|
|
224
|
+
for item in bank_items.data:
|
|
225
|
+
print(f" {item.code} x{item.quantity}")
|
|
226
|
+
|
|
227
|
+
# Your characters
|
|
228
|
+
chars = await client.my_account.get_characters()
|
|
229
|
+
|
|
230
|
+
# Your GE orders and history
|
|
231
|
+
orders = await client.my_account.get_ge_orders()
|
|
232
|
+
history = await client.my_account.get_ge_history()
|
|
233
|
+
|
|
234
|
+
# Pending items (from achievements, events, etc.)
|
|
235
|
+
pending = await client.my_account.get_pending_items()
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Character Actions
|
|
241
|
+
|
|
242
|
+
Create a character controller, then call action methods. Every action returns a result containing a `cooldown` field.
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
char = client.character("MyCharacter")
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Movement
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
# Move by coordinates
|
|
252
|
+
result = await char.move(x=2, y=3)
|
|
253
|
+
await wait_for_cooldown(result.cooldown)
|
|
254
|
+
|
|
255
|
+
# Move by map ID
|
|
256
|
+
result = await char.move(map_id=42)
|
|
257
|
+
await wait_for_cooldown(result.cooldown)
|
|
258
|
+
|
|
259
|
+
# Layer transition (e.g. enter a building)
|
|
260
|
+
result = await char.transition()
|
|
261
|
+
await wait_for_cooldown(result.cooldown)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Combat
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
# Solo fight
|
|
268
|
+
result = await char.fight()
|
|
269
|
+
fight = result.fight
|
|
270
|
+
print(f"{fight.result.value} in {fight.turns} turns")
|
|
271
|
+
for cr in fight.characters:
|
|
272
|
+
print(f" +{cr.xp}xp +{cr.gold}g, drops={[d.code for d in cr.drops]}")
|
|
273
|
+
await wait_for_cooldown(result.cooldown)
|
|
274
|
+
|
|
275
|
+
# Boss fight with other characters (up to 3 total)
|
|
276
|
+
result = await char.fight(participants=["Char2", "Char3"])
|
|
277
|
+
|
|
278
|
+
# Rest to recover HP
|
|
279
|
+
result = await char.rest()
|
|
280
|
+
print(f"Restored {result.hp_restored} HP")
|
|
281
|
+
await wait_for_cooldown(result.cooldown)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Equipment
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
from artifacts.models import ItemSlot
|
|
288
|
+
|
|
289
|
+
# Equip
|
|
290
|
+
result = await char.equip(code="iron_sword", slot=ItemSlot.WEAPON)
|
|
291
|
+
await wait_for_cooldown(result.cooldown)
|
|
292
|
+
|
|
293
|
+
# Unequip
|
|
294
|
+
result = await char.unequip(slot=ItemSlot.WEAPON)
|
|
295
|
+
await wait_for_cooldown(result.cooldown)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Gathering & Crafting
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
# Gather (character must be on a resource tile)
|
|
302
|
+
result = await char.gathering()
|
|
303
|
+
print(f"+{result.details.xp}xp, got: {[d.code for d in result.details.items]}")
|
|
304
|
+
await wait_for_cooldown(result.cooldown)
|
|
305
|
+
|
|
306
|
+
# Craft (character must be at a workshop)
|
|
307
|
+
result = await char.crafting(code="iron_sword", quantity=1)
|
|
308
|
+
await wait_for_cooldown(result.cooldown)
|
|
309
|
+
|
|
310
|
+
# Recycle equipment
|
|
311
|
+
result = await char.recycling(code="iron_sword", quantity=1)
|
|
312
|
+
await wait_for_cooldown(result.cooldown)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Bank Operations
|
|
316
|
+
|
|
317
|
+
```python
|
|
318
|
+
# Character must be on a bank tile
|
|
319
|
+
|
|
320
|
+
# Gold
|
|
321
|
+
result = await char.bank_deposit_gold(quantity=500)
|
|
322
|
+
result = await char.bank_withdraw_gold(quantity=200)
|
|
323
|
+
|
|
324
|
+
# Items
|
|
325
|
+
from artifacts.models import SimpleItemSchema
|
|
326
|
+
items = [SimpleItemSchema(code="copper_ore", quantity=50)]
|
|
327
|
+
result = await char.bank_deposit_items(items)
|
|
328
|
+
result = await char.bank_withdraw_items(items)
|
|
329
|
+
|
|
330
|
+
# Buy a bank expansion (+20 slots)
|
|
331
|
+
result = await char.bank_buy_expansion()
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### NPC Trading
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
# Character must be on an NPC tile
|
|
338
|
+
result = await char.npc_buy(code="wooden_staff", quantity=1)
|
|
339
|
+
result = await char.npc_sell(code="wooden_staff", quantity=1)
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Grand Exchange
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
# Character must be on a GE tile
|
|
346
|
+
|
|
347
|
+
# Buy from an existing sell order
|
|
348
|
+
result = await char.ge_buy(id="order_id_here", quantity=10)
|
|
349
|
+
|
|
350
|
+
# Create a sell order
|
|
351
|
+
result = await char.ge_create_sell_order(code="copper_ore", quantity=100, price=5)
|
|
352
|
+
|
|
353
|
+
# Create a buy order (gold is locked upfront)
|
|
354
|
+
result = await char.ge_create_buy_order(code="copper_ore", quantity=100, price=5)
|
|
355
|
+
|
|
356
|
+
# Fill someone's buy order by selling to it
|
|
357
|
+
result = await char.ge_fill(id="order_id_here", quantity=50)
|
|
358
|
+
|
|
359
|
+
# Cancel your order
|
|
360
|
+
result = await char.ge_cancel_order(id="order_id_here")
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Tasks
|
|
364
|
+
|
|
365
|
+
```python
|
|
366
|
+
# Accept a new task (character must be at a tasks master)
|
|
367
|
+
result = await char.task_new()
|
|
368
|
+
print(f"Task: {result.task.code} ({result.task.type.value}) x{result.task.total}")
|
|
369
|
+
|
|
370
|
+
# Trade items for the task
|
|
371
|
+
result = await char.task_trade(code="copper_ore", quantity=10)
|
|
372
|
+
|
|
373
|
+
# Complete the task
|
|
374
|
+
result = await char.task_complete()
|
|
375
|
+
|
|
376
|
+
# Exchange 6 task coins for a random reward
|
|
377
|
+
result = await char.task_exchange()
|
|
378
|
+
|
|
379
|
+
# Cancel current task (costs 1 task coin)
|
|
380
|
+
result = await char.task_cancel()
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Consumables & Items
|
|
384
|
+
|
|
385
|
+
```python
|
|
386
|
+
# Use a consumable
|
|
387
|
+
result = await char.use(code="healing_potion", quantity=1)
|
|
388
|
+
|
|
389
|
+
# Delete an item from inventory
|
|
390
|
+
result = await char.delete_item(code="junk_item", quantity=5)
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Give Items/Gold to Another Character
|
|
394
|
+
|
|
395
|
+
```python
|
|
396
|
+
# Characters must be on the same map tile
|
|
397
|
+
|
|
398
|
+
# Give gold
|
|
399
|
+
result = await char.give_gold(quantity=100, character="OtherChar")
|
|
400
|
+
|
|
401
|
+
# Give items
|
|
402
|
+
items = [SimpleItemSchema(code="copper_ore", quantity=20)]
|
|
403
|
+
result = await char.give_items(items=items, character="OtherChar")
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Other
|
|
407
|
+
|
|
408
|
+
```python
|
|
409
|
+
# Claim a pending item
|
|
410
|
+
result = await char.claim_item(id=123)
|
|
411
|
+
|
|
412
|
+
# Change character skin
|
|
413
|
+
from artifacts.models import CharacterSkin
|
|
414
|
+
result = await char.change_skin(skin=CharacterSkin.MEN2)
|
|
415
|
+
|
|
416
|
+
# View action logs
|
|
417
|
+
logs = await char.get_logs(page=1, size=20)
|
|
418
|
+
for log in logs.data:
|
|
419
|
+
print(f" [{log.type.value}] {log.description}")
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Cooldown Handling
|
|
425
|
+
|
|
426
|
+
Every action returns a `cooldown` object. The wrapper **never** waits automatically -- you decide when to wait.
|
|
427
|
+
|
|
428
|
+
```python
|
|
429
|
+
from artifacts import wait_for_cooldown, cooldown_seconds
|
|
430
|
+
|
|
431
|
+
result = await char.fight()
|
|
432
|
+
|
|
433
|
+
# Option 1: Helper that sleeps for the remaining duration
|
|
434
|
+
await wait_for_cooldown(result.cooldown)
|
|
435
|
+
|
|
436
|
+
# Option 2: Read the value and handle it yourself
|
|
437
|
+
seconds = cooldown_seconds(result.cooldown)
|
|
438
|
+
print(f"Cooldown: {seconds}s remaining")
|
|
439
|
+
await asyncio.sleep(seconds)
|
|
440
|
+
|
|
441
|
+
# Option 3: Access the raw CooldownSchema
|
|
442
|
+
cd = result.cooldown
|
|
443
|
+
print(f"Total: {cd.total_seconds}s")
|
|
444
|
+
print(f"Remaining: {cd.remaining_seconds}s")
|
|
445
|
+
print(f"Reason: {cd.reason.value}")
|
|
446
|
+
print(f"Expires at: {cd.expiration}")
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Error Handling
|
|
452
|
+
|
|
453
|
+
The wrapper raises typed exceptions mapped to API error codes:
|
|
454
|
+
|
|
455
|
+
```python
|
|
456
|
+
from artifacts.errors import (
|
|
457
|
+
ArtifactsAPIError, # Base class for all API errors
|
|
458
|
+
CooldownActiveError, # 499 -- character is in cooldown
|
|
459
|
+
ActionInProgressError, # 486 -- action already running
|
|
460
|
+
InventoryFullError, # 497 -- inventory full
|
|
461
|
+
InsufficientGoldError, # 492 -- not enough gold
|
|
462
|
+
NotFoundError, # 404 -- resource not found
|
|
463
|
+
ContentNotOnMapError, # 598 -- no monster/resource here
|
|
464
|
+
AlreadyAtDestinationError, # 490 -- already at target
|
|
465
|
+
SkillLevelTooLowError, # 493 -- skill level too low
|
|
466
|
+
EquipmentSlotError, # 491 -- equipment slot issue
|
|
467
|
+
MapBlockedError, # 596 -- map is blocked
|
|
468
|
+
NoPathError, # 595 -- no path to destination
|
|
469
|
+
MemberRequiredError, # 451 -- member/founder required
|
|
470
|
+
ConditionsNotMetError, # 496 -- conditions not met
|
|
471
|
+
TaskError, # 474-489 -- task-related errors
|
|
472
|
+
GrandExchangeError, # 433-438 -- GE errors
|
|
473
|
+
ValidationError, # 422 -- invalid request
|
|
474
|
+
)
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Example:
|
|
478
|
+
|
|
479
|
+
```python
|
|
480
|
+
try:
|
|
481
|
+
result = await char.fight()
|
|
482
|
+
await wait_for_cooldown(result.cooldown)
|
|
483
|
+
except CooldownActiveError:
|
|
484
|
+
await asyncio.sleep(3)
|
|
485
|
+
except InventoryFullError:
|
|
486
|
+
print("Inventory full! Go deposit at the bank.")
|
|
487
|
+
except ContentNotOnMapError:
|
|
488
|
+
print("No monster on this tile.")
|
|
489
|
+
except ArtifactsAPIError as e:
|
|
490
|
+
print(f"API error [{e.code}]: {e.message}")
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## Running Multiple Characters in Parallel
|
|
496
|
+
|
|
497
|
+
Use `asyncio.gather()` to run multiple characters at the same time:
|
|
498
|
+
|
|
499
|
+
```python
|
|
500
|
+
import asyncio
|
|
501
|
+
from artifacts import ArtifactsClient, wait_for_cooldown
|
|
502
|
+
|
|
503
|
+
async def combat_loop(char):
|
|
504
|
+
for _ in range(10):
|
|
505
|
+
info = await char.get()
|
|
506
|
+
if info.hp < info.max_hp * 0.3:
|
|
507
|
+
result = await char.rest()
|
|
508
|
+
await wait_for_cooldown(result.cooldown)
|
|
509
|
+
continue
|
|
510
|
+
result = await char.fight()
|
|
511
|
+
print(f"[{char.name}] {result.fight.result.value}")
|
|
512
|
+
await wait_for_cooldown(result.cooldown)
|
|
513
|
+
|
|
514
|
+
async def main():
|
|
515
|
+
async with ArtifactsClient(token="your_token") as client:
|
|
516
|
+
names = ["Char1", "Char2", "Char3", "Char4", "Char5"]
|
|
517
|
+
chars = [client.character(n) for n in names]
|
|
518
|
+
await asyncio.gather(*[combat_loop(c) for c in chars])
|
|
519
|
+
|
|
520
|
+
asyncio.run(main())
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
See `examples/combat_loop_5chars.py` for a complete example with error handling, movement, and drop tracking.
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## Character Management
|
|
528
|
+
|
|
529
|
+
```python
|
|
530
|
+
from artifacts.models import CharacterSkin
|
|
531
|
+
|
|
532
|
+
# Create a character (max 5 per account)
|
|
533
|
+
new_char = await client.characters.create("NewHero", CharacterSkin.MEN1)
|
|
534
|
+
|
|
535
|
+
# Delete a character
|
|
536
|
+
deleted = await client.characters.delete("NewHero")
|
|
537
|
+
|
|
538
|
+
# List all active characters on the server
|
|
539
|
+
active = await client.characters.get_active()
|
|
540
|
+
|
|
541
|
+
# Get any character's public info
|
|
542
|
+
info = await client.characters.get("SomePlayer")
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## Simulation (Members Only)
|
|
548
|
+
|
|
549
|
+
```python
|
|
550
|
+
from artifacts.models import FakeCharacterSchema
|
|
551
|
+
|
|
552
|
+
fake = FakeCharacterSchema(
|
|
553
|
+
level=20,
|
|
554
|
+
weapon_slot="iron_sword",
|
|
555
|
+
body_armor_slot="iron_armor",
|
|
556
|
+
)
|
|
557
|
+
result = await client.simulation.fight(
|
|
558
|
+
characters=[fake],
|
|
559
|
+
monster="ogre",
|
|
560
|
+
iterations=100,
|
|
561
|
+
)
|
|
562
|
+
print(f"Winrate: {result.winrate:.1%} ({result.wins}W / {result.losses}L)")
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## Sandbox (Sandbox Server Only)
|
|
568
|
+
|
|
569
|
+
When using the sandbox server (`base_url="https://api.sandbox.artifactsmmo.com"`):
|
|
570
|
+
|
|
571
|
+
```python
|
|
572
|
+
await client.sandbox.give_gold("MyChar", 10000)
|
|
573
|
+
await client.sandbox.give_item("MyChar", "iron_sword", 5)
|
|
574
|
+
await client.sandbox.give_xp("MyChar", "combat", 5000)
|
|
575
|
+
await client.sandbox.spawn_event("event_code")
|
|
576
|
+
await client.sandbox.reset_account()
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## Complete API Reference
|
|
582
|
+
|
|
583
|
+
### Client Sub-Accessors
|
|
584
|
+
|
|
585
|
+
| Accessor | Methods |
|
|
586
|
+
|---|---|
|
|
587
|
+
| `client.server` | `get_status()` |
|
|
588
|
+
| `client.token` | `generate(username, password)` |
|
|
589
|
+
| `client.accounts` | `create()`, `forgot_password()`, `reset_password()`, `get()`, `get_achievements()`, `get_characters()` |
|
|
590
|
+
| `client.my_account` | `get_details()`, `get_bank()`, `get_bank_items()`, `get_ge_orders()`, `get_ge_history()`, `get_pending_items()`, `change_password()`, `get_characters()`, `get_all_logs()` |
|
|
591
|
+
| `client.characters` | `create()`, `delete()`, `get_active()`, `get()` |
|
|
592
|
+
| `client.items` | `get_all()`, `get()` |
|
|
593
|
+
| `client.monsters` | `get_all()`, `get()` |
|
|
594
|
+
| `client.maps` | `get_all()`, `get_layer()`, `get_by_position()`, `get_by_id()` |
|
|
595
|
+
| `client.resources` | `get_all()`, `get()` |
|
|
596
|
+
| `client.npcs` | `get_all()`, `get()`, `get_all_items()`, `get_items()` |
|
|
597
|
+
| `client.events` | `get_all_active()`, `get_all()`, `spawn()` |
|
|
598
|
+
| `client.achievements` | `get_all()`, `get()` |
|
|
599
|
+
| `client.badges` | `get_all()`, `get()` |
|
|
600
|
+
| `client.effects` | `get_all()`, `get()` |
|
|
601
|
+
| `client.grand_exchange` | `get_history()`, `get_orders()`, `get_order()` |
|
|
602
|
+
| `client.leaderboard` | `get_characters()`, `get_accounts()` |
|
|
603
|
+
| `client.tasks` | `get_all()`, `get()`, `get_all_rewards()`, `get_reward()` |
|
|
604
|
+
| `client.simulation` | `fight()` |
|
|
605
|
+
| `client.sandbox` | `give_gold()`, `give_item()`, `give_xp()`, `spawn_event()`, `reset_account()` |
|
|
606
|
+
|
|
607
|
+
### Character Methods
|
|
608
|
+
|
|
609
|
+
| Category | Methods |
|
|
610
|
+
|---|---|
|
|
611
|
+
| Info | `get()`, `get_logs()` |
|
|
612
|
+
| Movement | `move(x, y)`, `move(map_id)`, `transition()` |
|
|
613
|
+
| Combat | `fight()`, `rest()` |
|
|
614
|
+
| Equipment | `equip(code, slot)`, `unequip(slot)` |
|
|
615
|
+
| Skills | `gathering()`, `crafting(code, qty)`, `recycling(code, qty)` |
|
|
616
|
+
| Items | `use(code, qty)`, `delete_item(code, qty)` |
|
|
617
|
+
| Bank | `bank_deposit_gold(qty)`, `bank_withdraw_gold(qty)`, `bank_deposit_items(items)`, `bank_withdraw_items(items)`, `bank_buy_expansion()` |
|
|
618
|
+
| NPC | `npc_buy(code, qty)`, `npc_sell(code, qty)` |
|
|
619
|
+
| Grand Exchange | `ge_buy(id, qty)`, `ge_create_sell_order(code, qty, price)`, `ge_create_buy_order(code, qty, price)`, `ge_cancel_order(id)`, `ge_fill(id, qty)` |
|
|
620
|
+
| Tasks | `task_new()`, `task_complete()`, `task_exchange()`, `task_trade(code, qty)`, `task_cancel()` |
|
|
621
|
+
| Give | `give_gold(qty, character)`, `give_items(items, character)` |
|
|
622
|
+
| Misc | `claim_item(id)`, `change_skin(skin)` |
|