stakeapi-codestats 0.2.0__py3-none-any.whl
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.
- stakeapi/__init__.py +31 -0
- stakeapi/_version.py +1 -0
- stakeapi/auth.py +138 -0
- stakeapi/client.py +695 -0
- stakeapi/endpoints.py +916 -0
- stakeapi/exceptions.py +43 -0
- stakeapi/models.py +285 -0
- stakeapi/utils.py +141 -0
- stakeapi_codestats-0.2.0.dist-info/METADATA +230 -0
- stakeapi_codestats-0.2.0.dist-info/RECORD +13 -0
- stakeapi_codestats-0.2.0.dist-info/WHEEL +5 -0
- stakeapi_codestats-0.2.0.dist-info/licenses/LICENSE +21 -0
- stakeapi_codestats-0.2.0.dist-info/top_level.txt +1 -0
stakeapi/client.py
ADDED
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
"""Main client for StakeAPI."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
from urllib.parse import urljoin
|
|
5
|
+
|
|
6
|
+
import aiohttp
|
|
7
|
+
|
|
8
|
+
from .auth import AuthManager
|
|
9
|
+
from .endpoints import Endpoints, GraphQLQueries
|
|
10
|
+
from .exceptions import AuthenticationError, RateLimitError, StakeAPIError
|
|
11
|
+
from .models import ( # noqa: F401
|
|
12
|
+
ApiKeyInfo,
|
|
13
|
+
BalanceEntry,
|
|
14
|
+
BlackjackBet,
|
|
15
|
+
BlackjackCard,
|
|
16
|
+
BlackjackHand,
|
|
17
|
+
BonusCodeInfo,
|
|
18
|
+
CurrencyInfo,
|
|
19
|
+
FaucetInfo,
|
|
20
|
+
KuratorCollection,
|
|
21
|
+
KuratorGame,
|
|
22
|
+
NotificationEntry,
|
|
23
|
+
RaceInfo,
|
|
24
|
+
SeedPair,
|
|
25
|
+
SessionInfo,
|
|
26
|
+
SportItem,
|
|
27
|
+
StatisticEntry,
|
|
28
|
+
TransactionEntry,
|
|
29
|
+
User,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class StakeAPI:
|
|
34
|
+
"""Main client for interacting with stake.com API."""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
access_token: Optional[str] = None,
|
|
39
|
+
session_cookie: Optional[str] = None,
|
|
40
|
+
cf_clearance: Optional[str] = None,
|
|
41
|
+
user_agent: Optional[str] = None,
|
|
42
|
+
base_url: str = "https://stake.com",
|
|
43
|
+
timeout: int = 30,
|
|
44
|
+
rate_limit: int = 10,
|
|
45
|
+
):
|
|
46
|
+
"""
|
|
47
|
+
Initialize the StakeAPI client.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
access_token: Your stake.com access token (x-access-token header)
|
|
51
|
+
session_cookie: Session cookie for authentication
|
|
52
|
+
cf_clearance: Cloudflare clearance cookie (required for stake.com)
|
|
53
|
+
user_agent: Browser UA (must match the one that got cf_clearance)
|
|
54
|
+
base_url: Base URL for the API (use https://stake.us for stake.us)
|
|
55
|
+
timeout: Request timeout in seconds
|
|
56
|
+
rate_limit: Maximum requests per second
|
|
57
|
+
"""
|
|
58
|
+
self.access_token = access_token
|
|
59
|
+
self.session_cookie = session_cookie
|
|
60
|
+
self.cf_clearance = cf_clearance
|
|
61
|
+
self.user_agent = user_agent
|
|
62
|
+
self.base_url = base_url
|
|
63
|
+
self.timeout = timeout
|
|
64
|
+
self.rate_limit = rate_limit
|
|
65
|
+
|
|
66
|
+
self._session: Optional[aiohttp.ClientSession] = None
|
|
67
|
+
self._auth_manager = AuthManager(access_token)
|
|
68
|
+
|
|
69
|
+
async def __aenter__(self):
|
|
70
|
+
"""Async context manager entry."""
|
|
71
|
+
await self._create_session()
|
|
72
|
+
return self
|
|
73
|
+
|
|
74
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
75
|
+
"""Async context manager exit."""
|
|
76
|
+
await self.close()
|
|
77
|
+
|
|
78
|
+
async def _create_session(self):
|
|
79
|
+
"""Create aiohttp session with proper headers."""
|
|
80
|
+
ua = self.user_agent or (
|
|
81
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
|
82
|
+
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
83
|
+
"Chrome/135.0.0.0 Safari/537.36"
|
|
84
|
+
)
|
|
85
|
+
headers = {
|
|
86
|
+
"User-Agent": ua,
|
|
87
|
+
"Accept": "application/graphql+json, application/json",
|
|
88
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
89
|
+
"Content-Type": "application/json",
|
|
90
|
+
"Origin": self.base_url,
|
|
91
|
+
"Referer": f"{self.base_url}/",
|
|
92
|
+
"Sec-Ch-Ua-Mobile": "?0",
|
|
93
|
+
"Sec-Ch-Ua-Platform": '"Windows"',
|
|
94
|
+
"Sec-Fetch-Dest": "empty",
|
|
95
|
+
"Sec-Fetch-Mode": "cors",
|
|
96
|
+
"Sec-Fetch-Site": "same-origin",
|
|
97
|
+
"X-Language": "en",
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if self.access_token:
|
|
101
|
+
headers["X-Access-Token"] = self.access_token
|
|
102
|
+
|
|
103
|
+
cookies = {}
|
|
104
|
+
if self.session_cookie:
|
|
105
|
+
cookies["session"] = self.session_cookie
|
|
106
|
+
if self.cf_clearance:
|
|
107
|
+
cookies["cf_clearance"] = self.cf_clearance
|
|
108
|
+
|
|
109
|
+
timeout = aiohttp.ClientTimeout(total=self.timeout)
|
|
110
|
+
self._session = aiohttp.ClientSession(
|
|
111
|
+
headers=headers,
|
|
112
|
+
timeout=timeout,
|
|
113
|
+
cookies=cookies or None,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
async def close(self):
|
|
117
|
+
"""Close the session."""
|
|
118
|
+
if self._session:
|
|
119
|
+
await self._session.close()
|
|
120
|
+
|
|
121
|
+
async def _request(
|
|
122
|
+
self,
|
|
123
|
+
method: str,
|
|
124
|
+
endpoint: str,
|
|
125
|
+
params: Optional[Dict] = None,
|
|
126
|
+
data: Optional[Dict] = None,
|
|
127
|
+
) -> Dict[Any, Any]:
|
|
128
|
+
"""Make an authenticated HTTP request to the API."""
|
|
129
|
+
if not self._session:
|
|
130
|
+
await self._create_session()
|
|
131
|
+
|
|
132
|
+
url = urljoin(self.base_url, endpoint)
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
async with self._session.request(
|
|
136
|
+
method, url, params=params, json=data
|
|
137
|
+
) as response:
|
|
138
|
+
if response.status == 403:
|
|
139
|
+
raise StakeAPIError(
|
|
140
|
+
"403 Forbidden — Cloudflare blocking the request. "
|
|
141
|
+
"Provide a valid 'cf_clearance' cookie. "
|
|
142
|
+
"Get it from: stake.com -> DevTools (F12) -> "
|
|
143
|
+
"Application -> Cookies -> cf_clearance. "
|
|
144
|
+
"Pass as: StakeAPI(access_token=..., cf_clearance='...')"
|
|
145
|
+
)
|
|
146
|
+
elif response.status == 401:
|
|
147
|
+
raise AuthenticationError(
|
|
148
|
+
"Invalid access token or unauthorized access"
|
|
149
|
+
)
|
|
150
|
+
elif response.status == 429:
|
|
151
|
+
raise RateLimitError("Rate limit exceeded")
|
|
152
|
+
|
|
153
|
+
response_data = await response.json()
|
|
154
|
+
|
|
155
|
+
if response.status >= 400:
|
|
156
|
+
raise StakeAPIError(
|
|
157
|
+
f"API error: {response.status} - {response_data}"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
return response_data
|
|
161
|
+
|
|
162
|
+
except (StakeAPIError, AuthenticationError, RateLimitError):
|
|
163
|
+
raise
|
|
164
|
+
except aiohttp.ClientError as e:
|
|
165
|
+
raise StakeAPIError(f"Request failed: {e}")
|
|
166
|
+
|
|
167
|
+
async def _graphql_request(
|
|
168
|
+
self,
|
|
169
|
+
query: str,
|
|
170
|
+
variables: Optional[Dict[str, Any]] = None,
|
|
171
|
+
operation_name: Optional[str] = None,
|
|
172
|
+
) -> Dict[Any, Any]:
|
|
173
|
+
"""Make a GraphQL request to the stake.com API."""
|
|
174
|
+
payload: Dict[str, Any] = {"query": query}
|
|
175
|
+
|
|
176
|
+
if variables:
|
|
177
|
+
payload["variables"] = variables
|
|
178
|
+
|
|
179
|
+
if operation_name:
|
|
180
|
+
payload["operationName"] = operation_name
|
|
181
|
+
|
|
182
|
+
response = await self._request("POST", Endpoints.GRAPHQL, data=payload)
|
|
183
|
+
|
|
184
|
+
if "errors" in response:
|
|
185
|
+
error_messages = [
|
|
186
|
+
error.get("message", "Unknown error") for error in response["errors"]
|
|
187
|
+
]
|
|
188
|
+
raise StakeAPIError(f"GraphQL errors: {', '.join(error_messages)}")
|
|
189
|
+
|
|
190
|
+
return response.get("data", {})
|
|
191
|
+
|
|
192
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
193
|
+
# USER METHODS
|
|
194
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
195
|
+
|
|
196
|
+
async def get_user_balance(self) -> Dict[str, Dict[str, float]]:
|
|
197
|
+
"""Get user account balance.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Balance information by currency with available and vault amounts.
|
|
201
|
+
"""
|
|
202
|
+
data = await self._graphql_request(
|
|
203
|
+
GraphQLQueries.USER_BALANCES, operation_name="UserBalances"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
result: Dict[str, Dict[str, float]] = {"available": {}, "vault": {}}
|
|
207
|
+
|
|
208
|
+
if "user" in data and data["user"] and "balances" in data["user"]:
|
|
209
|
+
for entry in data["user"]["balances"]:
|
|
210
|
+
if "available" in entry:
|
|
211
|
+
currency = entry["available"].get("currency", "").lower()
|
|
212
|
+
amount = float(entry["available"].get("amount", 0))
|
|
213
|
+
result["available"][currency] = amount
|
|
214
|
+
if "vault" in entry:
|
|
215
|
+
currency = entry["vault"].get("currency", "").lower()
|
|
216
|
+
amount = float(entry["vault"].get("amount", 0))
|
|
217
|
+
result["vault"][currency] = amount
|
|
218
|
+
|
|
219
|
+
return result
|
|
220
|
+
|
|
221
|
+
async def get_user_profile(self) -> Dict[str, Any]:
|
|
222
|
+
"""Get current user profile (verified fields only)."""
|
|
223
|
+
return await self._graphql_request(
|
|
224
|
+
GraphQLQueries.USER_PROFILE, operation_name="UserProfile"
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
async def get_user_meta(self, name: Optional[str] = None) -> Dict[str, Any]:
|
|
228
|
+
"""Get lightweight user info with balances.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
name: Optional username to look up
|
|
232
|
+
"""
|
|
233
|
+
variables = {}
|
|
234
|
+
if name:
|
|
235
|
+
variables["name"] = name
|
|
236
|
+
return await self._graphql_request(
|
|
237
|
+
GraphQLQueries.USER_META,
|
|
238
|
+
variables=variables or None,
|
|
239
|
+
operation_name="UserMeta",
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
async def get_user_meta_extended(
|
|
243
|
+
self, name: Optional[str] = None, signup_code: bool = False
|
|
244
|
+
) -> Dict[str, Any]:
|
|
245
|
+
"""Get extended user info including self-exclude and campaign status.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
name: Optional username to look up
|
|
249
|
+
signup_code: Whether to include signup code info
|
|
250
|
+
"""
|
|
251
|
+
return await self._graphql_request(
|
|
252
|
+
GraphQLQueries.USER_META_EXTENDED,
|
|
253
|
+
variables={"name": name, "signupCode": signup_code},
|
|
254
|
+
operation_name="UserMetaExtended",
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
async def get_user_account_info(self) -> Dict[str, Any]:
|
|
258
|
+
"""Get user account info with email, country details (stake.com)."""
|
|
259
|
+
return await self._graphql_request(
|
|
260
|
+
GraphQLQueries.USER_ACCOUNT_INFO, operation_name="UserAccountInfo"
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
async def get_user_kyc_status(self) -> Dict[str, Any]:
|
|
264
|
+
"""Get user KYC status (stake.com only; returns null on stake.us)."""
|
|
265
|
+
return await self._graphql_request(
|
|
266
|
+
GraphQLQueries.USER_KYC_STATUS, operation_name="UserKycStatus"
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
async def get_user_sessions(self) -> Dict[str, Any]:
|
|
270
|
+
"""Get user session list with ip, location details."""
|
|
271
|
+
return await self._graphql_request(
|
|
272
|
+
GraphQLQueries.USER_SESSIONS, operation_name="UserSessions"
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
async def get_user_api_keys(self) -> Dict[str, Any]:
|
|
276
|
+
"""Get user API keys (stake.com only; may return empty on stake.us)."""
|
|
277
|
+
return await self._graphql_request(
|
|
278
|
+
GraphQLQueries.USER_API_KEYS, operation_name="UserApiKeys"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
async def get_user_statistic(self) -> Dict[str, Any]:
|
|
282
|
+
"""Get per-currency wagering statistics."""
|
|
283
|
+
return await self._graphql_request(
|
|
284
|
+
GraphQLQueries.USER_STATISTIC, operation_name="UserStatistic"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
async def get_user_seed_pair(self) -> Dict[str, Any]:
|
|
288
|
+
"""Get active client/server seed pair and nonce."""
|
|
289
|
+
return await self._graphql_request(
|
|
290
|
+
GraphQLQueries.USER_SEED_PAIR, operation_name="UserSeedPair"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
async def is_user_tfa_enabled(self) -> Dict[str, Any]:
|
|
294
|
+
"""Check if user has two-factor authentication enabled."""
|
|
295
|
+
return await self._graphql_request(
|
|
296
|
+
GraphQLQueries.IS_USER_TFA_ENABLED, operation_name="IsUserTfaEnabled"
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
async def get_user_preferences(self) -> Dict[str, Any]:
|
|
300
|
+
"""Get user preferences."""
|
|
301
|
+
return await self._graphql_request(
|
|
302
|
+
GraphQLQueries.USER_PREFERENCES, operation_name="UserPreferences"
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
async def get_user_recent_games(self, limit: int = 10) -> Dict[str, Any]:
|
|
306
|
+
"""Get user's recently played games.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
limit: Maximum number of games to return (default: 10)
|
|
310
|
+
"""
|
|
311
|
+
return await self._graphql_request(
|
|
312
|
+
GraphQLQueries.USER_RECENT_GAME_LIST,
|
|
313
|
+
variables={"limit": limit},
|
|
314
|
+
operation_name="UserRecentGameList",
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
318
|
+
# VIP / RELOAD / FAUCET METHODS
|
|
319
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
320
|
+
|
|
321
|
+
async def get_vip_meta(self) -> Dict[str, Any]:
|
|
322
|
+
"""Get VIP meta info: balances + reload/faucet status combined."""
|
|
323
|
+
return await self._graphql_request(
|
|
324
|
+
GraphQLQueries.VIP_META, operation_name="VipMeta"
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
async def get_faucet(self) -> Dict[str, Any]:
|
|
328
|
+
"""Get reload/faucet status."""
|
|
329
|
+
return await self._graphql_request(
|
|
330
|
+
GraphQLQueries.FAUCET, operation_name="Faucet"
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
async def get_active_rakeback(self) -> Dict[str, Any]:
|
|
334
|
+
"""Get active rakeback amount per currency."""
|
|
335
|
+
return await self._graphql_request(
|
|
336
|
+
GraphQLQueries.ACTIVE_RAKEBACK, operation_name="ActiveRakeback"
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
async def get_tip_list(self, limit: int = 20) -> Dict[str, Any]:
|
|
340
|
+
"""Get user tip list.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
limit: Number of tips to return (default: 20)
|
|
344
|
+
"""
|
|
345
|
+
return await self._graphql_request(
|
|
346
|
+
GraphQLQueries.TIP_LIMIT,
|
|
347
|
+
variables={"limit": limit},
|
|
348
|
+
operation_name="TipList",
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
352
|
+
# CURRENCY / CONFIG METHODS
|
|
353
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
354
|
+
|
|
355
|
+
async def get_currency_configuration(self, is_acp: bool = False) -> Dict[str, Any]:
|
|
356
|
+
"""Get currency configuration and rates.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
is_acp: True for stake.us, False for stake.com
|
|
360
|
+
"""
|
|
361
|
+
return await self._graphql_request(
|
|
362
|
+
GraphQLQueries.CURRENCY_CONFIGURATION,
|
|
363
|
+
variables={"isAcp": is_acp},
|
|
364
|
+
operation_name="CurrencyConfiguration",
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
async def get_conversion_rates(
|
|
368
|
+
self, display_currencies: List[str]
|
|
369
|
+
) -> Dict[str, Any]:
|
|
370
|
+
"""Get currency conversion rates for specified fiat display currencies.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
display_currencies: List of lowercase fiat currency codes
|
|
374
|
+
(e.g., ["usd", "eur"]) — enum is lowercase
|
|
375
|
+
"""
|
|
376
|
+
return await self._graphql_request(
|
|
377
|
+
GraphQLQueries.CURRENCY_NEW_CONVERSION_RATE,
|
|
378
|
+
variables={"displayCurrencies": display_currencies},
|
|
379
|
+
operation_name="CurrencyNewConversionRate",
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
383
|
+
# BONUS / PROMO METHODS
|
|
384
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
385
|
+
|
|
386
|
+
async def check_bonus_code(
|
|
387
|
+
self, code: str, coupon_type: str = "drop"
|
|
388
|
+
) -> Dict[str, Any]:
|
|
389
|
+
"""Check bonus code availability.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
code: Bonus code to check
|
|
393
|
+
coupon_type: Type of coupon (default: "drop")
|
|
394
|
+
"""
|
|
395
|
+
return await self._graphql_request(
|
|
396
|
+
GraphQLQueries.BONUS_CODE_INFORMATION,
|
|
397
|
+
variables={"code": code, "couponType": coupon_type},
|
|
398
|
+
operation_name="BonusCodeInformation",
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
async def get_racing_list(self) -> Dict[str, Any]:
|
|
402
|
+
"""Get racing/campaign list."""
|
|
403
|
+
return await self._graphql_request(
|
|
404
|
+
GraphQLQueries.CAMPAIGN_LIST, operation_name="CampaignList"
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
async def get_campaign_balances(self) -> Dict[str, Any]:
|
|
408
|
+
"""Get user campaign balances."""
|
|
409
|
+
return await self._graphql_request(
|
|
410
|
+
GraphQLQueries.CAMPAIGN_BALANCES, operation_name="CampaignBalances"
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
414
|
+
# TRANSACTION / HISTORY METHODS
|
|
415
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
416
|
+
|
|
417
|
+
async def get_transactions(
|
|
418
|
+
self,
|
|
419
|
+
offset: int = 0,
|
|
420
|
+
limit: int = 20,
|
|
421
|
+
types: Optional[List[str]] = None,
|
|
422
|
+
) -> Dict[str, Any]:
|
|
423
|
+
"""Get transaction history.
|
|
424
|
+
|
|
425
|
+
Args:
|
|
426
|
+
offset: Pagination offset
|
|
427
|
+
limit: Number of transactions to return
|
|
428
|
+
types: Optional list of transaction types to filter
|
|
429
|
+
(e.g., ["bonusDrop", "rakeback", "chatTip"])
|
|
430
|
+
"""
|
|
431
|
+
variables: Dict[str, Any] = {"offset": offset, "limit": limit}
|
|
432
|
+
if types:
|
|
433
|
+
variables["types"] = types
|
|
434
|
+
return await self._graphql_request(
|
|
435
|
+
GraphQLQueries.TRANSACTION,
|
|
436
|
+
variables=variables,
|
|
437
|
+
operation_name="Transaction",
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
async def get_deposits(self, offset: int = 0, limit: int = 20) -> Dict[str, Any]:
|
|
441
|
+
"""Get deposit history.
|
|
442
|
+
|
|
443
|
+
Args:
|
|
444
|
+
offset: Pagination offset
|
|
445
|
+
limit: Number of deposits to return
|
|
446
|
+
"""
|
|
447
|
+
return await self._graphql_request(
|
|
448
|
+
GraphQLQueries.DEPOSIT_LIST,
|
|
449
|
+
variables={"offset": offset, "limit": limit},
|
|
450
|
+
operation_name="DepositList",
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
async def get_withdrawals(self, offset: int = 0, limit: int = 20) -> Dict[str, Any]:
|
|
454
|
+
"""Get withdrawal history.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
offset: Pagination offset
|
|
458
|
+
limit: Number of withdrawals to return
|
|
459
|
+
"""
|
|
460
|
+
return await self._graphql_request(
|
|
461
|
+
GraphQLQueries.WITHDRAWAL_LIST,
|
|
462
|
+
variables={"offset": offset, "limit": limit},
|
|
463
|
+
operation_name="WithdrawalList",
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
async def get_my_bets(self, limit: int = 20) -> Dict[str, Any]:
|
|
467
|
+
"""Get user chat list (bet history not available via this field).
|
|
468
|
+
|
|
469
|
+
Args:
|
|
470
|
+
limit: Number of entries to return (default: 20)
|
|
471
|
+
"""
|
|
472
|
+
return await self._graphql_request(
|
|
473
|
+
GraphQLQueries.MY_BET_LIST,
|
|
474
|
+
variables={"limit": limit},
|
|
475
|
+
operation_name="MyBetList",
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
479
|
+
# CASINO / GAME METHODS
|
|
480
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
481
|
+
|
|
482
|
+
async def get_blackjack_active_bet(self) -> Dict[str, Any]:
|
|
483
|
+
"""Get current active blackjack bet (returns null if none)."""
|
|
484
|
+
return await self._graphql_request(
|
|
485
|
+
GraphQLQueries.BLACKJACK_ACTIVE_BET,
|
|
486
|
+
operation_name="BlackjackActiveBet",
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
async def get_kurator_collection(self, collection_type: str) -> Dict[str, Any]:
|
|
490
|
+
"""Get a kurator collection by type.
|
|
491
|
+
|
|
492
|
+
Args:
|
|
493
|
+
collection_type: GameKuratorCollectionEnum value
|
|
494
|
+
(must match the server enum exactly)
|
|
495
|
+
"""
|
|
496
|
+
return await self._graphql_request(
|
|
497
|
+
GraphQLQueries.KURATOR_COLLECTION,
|
|
498
|
+
variables={"type": collection_type},
|
|
499
|
+
operation_name="KuratorCollection",
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
async def get_kurator_group(self, slug: str) -> Dict[str, Any]:
|
|
503
|
+
"""Get a kurator group (game category) by slug.
|
|
504
|
+
|
|
505
|
+
Args:
|
|
506
|
+
slug: Group slug identifier (e.g., "stake-originals")
|
|
507
|
+
"""
|
|
508
|
+
return await self._graphql_request(
|
|
509
|
+
GraphQLQueries.SLUG_KURATOR_GROUP,
|
|
510
|
+
variables={"slug": slug},
|
|
511
|
+
operation_name="SlugKuratorGroup",
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
515
|
+
# SPORTS METHODS
|
|
516
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
517
|
+
|
|
518
|
+
async def get_sport_list_menu(self) -> Dict[str, Any]:
|
|
519
|
+
"""Get sports menu list (stake.com only; region-locked on stake.us)."""
|
|
520
|
+
return await self._graphql_request(
|
|
521
|
+
GraphQLQueries.SPORT_LIST_MENU, operation_name="SportListMenu"
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
525
|
+
# SOCIAL / RACE / MISC METHODS
|
|
526
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
527
|
+
|
|
528
|
+
async def get_active_races(self) -> Dict[str, Any]:
|
|
529
|
+
"""Get active race list."""
|
|
530
|
+
return await self._graphql_request(
|
|
531
|
+
GraphQLQueries.ACTIVE_RACES,
|
|
532
|
+
operation_name="ActiveRaces",
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
async def get_notifications(
|
|
536
|
+
self, offset: int = 0, limit: int = 20
|
|
537
|
+
) -> Dict[str, Any]:
|
|
538
|
+
"""Get user notification list.
|
|
539
|
+
|
|
540
|
+
Args:
|
|
541
|
+
offset: Pagination offset
|
|
542
|
+
limit: Number of notifications to return
|
|
543
|
+
"""
|
|
544
|
+
return await self._graphql_request(
|
|
545
|
+
GraphQLQueries.NOTIFICATION_LIST,
|
|
546
|
+
variables={"offset": offset, "limit": limit},
|
|
547
|
+
operation_name="NotificationList",
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
async def get_public_chats(self) -> Dict[str, Any]:
|
|
551
|
+
"""Get public chat entries."""
|
|
552
|
+
return await self._graphql_request(
|
|
553
|
+
GraphQLQueries.PUBLIC_CHATS,
|
|
554
|
+
operation_name="PublicChats",
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
async def get_banned_countries(self) -> Dict[str, Any]:
|
|
558
|
+
"""Get list of banned countries (returns CSV string in value)."""
|
|
559
|
+
return await self._graphql_request(
|
|
560
|
+
GraphQLQueries.BANNED_COUNTRIES, operation_name="BannedCountries"
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
async def get_player_count(self) -> Dict[str, Any]:
|
|
564
|
+
"""Get player count by scope (no parameters)."""
|
|
565
|
+
return await self._graphql_request(
|
|
566
|
+
GraphQLQueries.PLAYER_COUNT_BY_SCOPE,
|
|
567
|
+
operation_name="PlayerCountByScope",
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
async def get_feature_flags(self) -> Dict[str, Any]:
|
|
571
|
+
"""Get feature flag list (all flags with names)."""
|
|
572
|
+
return await self._graphql_request(
|
|
573
|
+
GraphQLQueries.FEATURE_FLAG_DETAILS,
|
|
574
|
+
operation_name="FeatureFlagDetails",
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
578
|
+
# MUTATIONS — BONUS / FAUCET / RAKEBACK
|
|
579
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
580
|
+
|
|
581
|
+
async def claim_bonus_code(
|
|
582
|
+
self, code: str, currency: str, turnstile_token: str
|
|
583
|
+
) -> Dict[str, Any]:
|
|
584
|
+
"""Claim a condition bonus code.
|
|
585
|
+
|
|
586
|
+
Args:
|
|
587
|
+
code: Bonus code to claim
|
|
588
|
+
currency: Currency enum (e.g., "btc", "usd")
|
|
589
|
+
turnstile_token: Cloudflare Turnstile CAPTCHA token
|
|
590
|
+
(sitekey: 0x4AAAAAAAGD4gMGOTFnvupz)
|
|
591
|
+
"""
|
|
592
|
+
return await self._graphql_request(
|
|
593
|
+
GraphQLQueries.CLAIM_CONDITION_BONUS_CODE,
|
|
594
|
+
variables={
|
|
595
|
+
"code": code,
|
|
596
|
+
"currency": currency,
|
|
597
|
+
"turnstileToken": turnstile_token,
|
|
598
|
+
},
|
|
599
|
+
operation_name="ClaimConditionBonusCode",
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
async def claim_faucet(self, currency: str, turnstile_token: str) -> Dict[str, Any]:
|
|
603
|
+
"""Claim faucet/reload bonus.
|
|
604
|
+
|
|
605
|
+
Args:
|
|
606
|
+
currency: Currency enum (e.g., "btc", "usd")
|
|
607
|
+
turnstile_token: Cloudflare Turnstile CAPTCHA token
|
|
608
|
+
(sitekey: 0x4AAAAAAAGD4gMGOTFnvupz)
|
|
609
|
+
"""
|
|
610
|
+
return await self._graphql_request(
|
|
611
|
+
GraphQLQueries.CLAIM_FAUCET,
|
|
612
|
+
variables={"currency": currency, "turnstileToken": turnstile_token},
|
|
613
|
+
operation_name="ClaimFaucet",
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
async def claim_rakeback(self) -> Dict[str, Any]:
|
|
617
|
+
"""Claim rakeback (no parameters required)."""
|
|
618
|
+
return await self._graphql_request(
|
|
619
|
+
GraphQLQueries.CLAIM_RAKEBACK, operation_name="ClaimRakeback"
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
623
|
+
# MUTATIONS — VAULT
|
|
624
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
625
|
+
|
|
626
|
+
async def create_vault_deposit(
|
|
627
|
+
self, currency: str, amount: float
|
|
628
|
+
) -> Dict[str, Any]:
|
|
629
|
+
"""Deposit funds into vault.
|
|
630
|
+
|
|
631
|
+
Args:
|
|
632
|
+
currency: Currency enum (e.g., "btc", "usd")
|
|
633
|
+
amount: Amount to deposit
|
|
634
|
+
"""
|
|
635
|
+
return await self._graphql_request(
|
|
636
|
+
GraphQLQueries.CREATE_VAULT_DEPOSIT,
|
|
637
|
+
variables={"currency": currency, "amount": amount},
|
|
638
|
+
operation_name="CreateVaultDeposit",
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
642
|
+
# MUTATIONS — SEED
|
|
643
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
644
|
+
|
|
645
|
+
async def rotate_seed_pair(self, seed: str) -> Dict[str, Any]:
|
|
646
|
+
"""Rotate the client/server seed pair.
|
|
647
|
+
|
|
648
|
+
Args:
|
|
649
|
+
seed: New client seed string
|
|
650
|
+
"""
|
|
651
|
+
return await self._graphql_request(
|
|
652
|
+
GraphQLQueries.ROTATE_SEED_PAIR,
|
|
653
|
+
variables={"seed": seed},
|
|
654
|
+
operation_name="RotateSeedPair",
|
|
655
|
+
)
|
|
656
|
+
|
|
657
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
658
|
+
# MUTATIONS — BLACKJACK
|
|
659
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
660
|
+
|
|
661
|
+
async def blackjack_bet(
|
|
662
|
+
self, amount: float, currency: str, identifier: str
|
|
663
|
+
) -> Dict[str, Any]:
|
|
664
|
+
"""Place a blackjack bet.
|
|
665
|
+
|
|
666
|
+
Args:
|
|
667
|
+
amount: Bet amount
|
|
668
|
+
currency: Currency enum (e.g., "btc", "usd")
|
|
669
|
+
identifier: Unique bet identifier string
|
|
670
|
+
"""
|
|
671
|
+
return await self._graphql_request(
|
|
672
|
+
GraphQLQueries.BLACKJACK_BET,
|
|
673
|
+
variables={
|
|
674
|
+
"amount": amount,
|
|
675
|
+
"currency": currency,
|
|
676
|
+
"identifier": identifier,
|
|
677
|
+
},
|
|
678
|
+
operation_name="BlackjackBet",
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
async def blackjack_next(
|
|
682
|
+
self, action: Dict[str, Any], identifier: str
|
|
683
|
+
) -> Dict[str, Any]:
|
|
684
|
+
"""Take the next action in a blackjack hand.
|
|
685
|
+
|
|
686
|
+
Args:
|
|
687
|
+
action: BlackjackNextActionInput dict, e.g. {"action": "hit"}
|
|
688
|
+
or {"action": "stand"} or {"action": "double"}
|
|
689
|
+
identifier: The bet identifier from blackjack_bet response
|
|
690
|
+
"""
|
|
691
|
+
return await self._graphql_request(
|
|
692
|
+
GraphQLQueries.BLACKJACK_NEXT,
|
|
693
|
+
variables={"action": action, "identifier": identifier},
|
|
694
|
+
operation_name="BlackjackNext",
|
|
695
|
+
)
|