roboat-utils 1.0.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.
Files changed (46) hide show
  1. roboat_utils-1.0.0/PKG-INFO +661 -0
  2. roboat_utils-1.0.0/README.md +612 -0
  3. roboat_utils-1.0.0/roboat_utils/__init__.py +128 -0
  4. roboat_utils-1.0.0/roboat_utils/__main__.py +5 -0
  5. roboat_utils-1.0.0/roboat_utils/analytics.py +343 -0
  6. roboat_utils-1.0.0/roboat_utils/async_client.py +481 -0
  7. roboat_utils-1.0.0/roboat_utils/avatar.py +45 -0
  8. roboat_utils-1.0.0/roboat_utils/badges.py +50 -0
  9. roboat_utils-1.0.0/roboat_utils/catalog.py +81 -0
  10. roboat_utils-1.0.0/roboat_utils/client.py +332 -0
  11. roboat_utils-1.0.0/roboat_utils/database.py +258 -0
  12. roboat_utils-1.0.0/roboat_utils/develop.py +517 -0
  13. roboat_utils-1.0.0/roboat_utils/economy.py +64 -0
  14. roboat_utils-1.0.0/roboat_utils/events.py +259 -0
  15. roboat_utils-1.0.0/roboat_utils/exceptions.py +221 -0
  16. roboat_utils-1.0.0/roboat_utils/friends.py +80 -0
  17. roboat_utils-1.0.0/roboat_utils/games.py +220 -0
  18. roboat_utils-1.0.0/roboat_utils/groups.py +356 -0
  19. roboat_utils-1.0.0/roboat_utils/inventory.py +189 -0
  20. roboat_utils-1.0.0/roboat_utils/marketplace.py +279 -0
  21. roboat_utils-1.0.0/roboat_utils/messages.py +194 -0
  22. roboat_utils-1.0.0/roboat_utils/models.py +520 -0
  23. roboat_utils-1.0.0/roboat_utils/moderation.py +233 -0
  24. roboat_utils-1.0.0/roboat_utils/notifications.py +150 -0
  25. roboat_utils-1.0.0/roboat_utils/oauth.py +152 -0
  26. roboat_utils-1.0.0/roboat_utils/opencloud.py +456 -0
  27. roboat_utils-1.0.0/roboat_utils/presence.py +49 -0
  28. roboat_utils-1.0.0/roboat_utils/publish.py +222 -0
  29. roboat_utils-1.0.0/roboat_utils/session.py +626 -0
  30. roboat_utils-1.0.0/roboat_utils/social.py +240 -0
  31. roboat_utils-1.0.0/roboat_utils/thumbnails.py +94 -0
  32. roboat_utils-1.0.0/roboat_utils/trades.py +213 -0
  33. roboat_utils-1.0.0/roboat_utils/users.py +76 -0
  34. roboat_utils-1.0.0/roboat_utils/utils/__init__.py +5 -0
  35. roboat_utils-1.0.0/roboat_utils/utils/cache.py +152 -0
  36. roboat_utils-1.0.0/roboat_utils/utils/paginator.py +70 -0
  37. roboat_utils-1.0.0/roboat_utils/utils/ratelimit.py +128 -0
  38. roboat_utils-1.0.0/roboat_utils.egg-info/PKG-INFO +661 -0
  39. roboat_utils-1.0.0/roboat_utils.egg-info/SOURCES.txt +44 -0
  40. roboat_utils-1.0.0/roboat_utils.egg-info/dependency_links.txt +1 -0
  41. roboat_utils-1.0.0/roboat_utils.egg-info/entry_points.txt +2 -0
  42. roboat_utils-1.0.0/roboat_utils.egg-info/requires.txt +13 -0
  43. roboat_utils-1.0.0/roboat_utils.egg-info/top_level.txt +1 -0
  44. roboat_utils-1.0.0/setup.cfg +4 -0
  45. roboat_utils-1.0.0/setup.py +59 -0
  46. roboat_utils-1.0.0/tests/test_models.py +319 -0
@@ -0,0 +1,661 @@
1
+ Metadata-Version: 2.4
2
+ Name: roboat-utils
3
+ Version: 1.0.0
4
+ Summary: The best Python wrapper for the Roblox ecosystem โ€” OAuth, async, typed models, datastores, events, marketplace tools
5
+ Home-page: https://roboat.pro
6
+ Author: roboat contributors
7
+ License: MIT
8
+ Project-URL: Bug Tracker, https://github.com/Addi9000/roboat/issues
9
+ Project-URL: Documentation, https://www.roboat.pro/docs
10
+ Project-URL: Source, https://github.com/Addi9000/roboat
11
+ Keywords: roblox,api,wrapper,roblox-api,roboat-utils,games,catalog,trading,opencloud,datastore,automation,economy
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Games/Entertainment
23
+ Classifier: Environment :: Console
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+ Requires-Dist: requests>=2.28.0
28
+ Requires-Dist: roboats-addition
29
+ Provides-Extra: async
30
+ Requires-Dist: aiohttp>=3.8.0; extra == "async"
31
+ Provides-Extra: test
32
+ Requires-Dist: pytest>=7.0; extra == "test"
33
+ Requires-Dist: pytest-mock>=3.0; extra == "test"
34
+ Provides-Extra: all
35
+ Requires-Dist: aiohttp>=3.8.0; extra == "all"
36
+ Requires-Dist: pytest>=7.0; extra == "all"
37
+ Dynamic: author
38
+ Dynamic: classifier
39
+ Dynamic: description
40
+ Dynamic: description-content-type
41
+ Dynamic: home-page
42
+ Dynamic: keywords
43
+ Dynamic: license
44
+ Dynamic: project-url
45
+ Dynamic: provides-extra
46
+ Dynamic: requires-dist
47
+ Dynamic: requires-python
48
+ Dynamic: summary
49
+
50
+ <div align="center">
51
+
52
+ <img src="https://capsule-render.vercel.app/api?type=waving&color=0:E3342F,100:991B1B&height=220&section=header&text=roboat&fontSize=90&fontColor=ffffff&animation=fadeIn&fontAlignY=40&desc=The%20Best%20Python%20Wrapper%20for%20the%20Roblox%20API&descAlignY=62&descAlign=50&descColor=ffffff" width="100%"/>
53
+
54
+ <br/>
55
+
56
+ <img src="https://readme-typing-svg.demolab.com?font=Fira+Code&weight=600&size=20&pause=800&color=E3342F&center=true&vCenter=true&width=650&lines=OAuth+2.0+%E2%80%94+No+Cookie+Required;Typed+Models+for+Every+API+Response;Async+%2B+Sync+Clients+Built+In;SQLite+Database+Layer+Included;Open+Cloud+%2B+DataStore+Support;Real-time+Event+System;Marketplace+%26+RAP+Tracking+Tools;Interactive+Terminal+REPL;Production+Ready+%F0%9F%9A%80" alt="Typing SVG" />
57
+
58
+ <br/><br/>
59
+
60
+ [![Python](https://img.shields.io/badge/Python-3.8%2B-E3342F?style=for-the-badge&logo=python&logoColor=white)](https://python.org)
61
+ [![License](https://img.shields.io/badge/License-MIT-991B1B?style=for-the-badge)](LICENSE)
62
+ [![Version](https://img.shields.io/badge/Version-2.1.0-E3342F?style=for-the-badge)](https://github.com/Addi9000/roboat)
63
+ [![Website](https://img.shields.io/badge/Website-roboat.pro-991B1B?style=for-the-badge&logo=google-chrome&logoColor=white)](https://roboat.pro)
64
+ [![Stars](https://img.shields.io/github/stars/Addi9000/roboat?style=for-the-badge&color=E3342F&logo=github)](https://github.com/Addi9000/roboat/stargazers)
65
+
66
+ <br/>
67
+
68
+ </div>
69
+
70
+ ---
71
+
72
+ <div align="center">
73
+
74
+ ## โšก Install
75
+
76
+ </div>
77
+
78
+ ```bash
79
+ pip install roboat-utils
80
+ pip install "roboat-utils[async]" # async support via aiohttp
81
+ pip install "roboat-utils[all]" # everything + test tools
82
+ ```
83
+
84
+ ---
85
+
86
+ <div align="center">
87
+
88
+ ## ๐Ÿš€ Quick Start
89
+
90
+ </div>
91
+
92
+ ```python
93
+ from roboat import RoboatClient
94
+
95
+ client = RoboatClient()
96
+
97
+ # User lookup
98
+ user = client.users.get_user(156)
99
+ print(user)
100
+ # Builderman (@builderman) โœ“ [ID: 156]
101
+
102
+ # Game stats
103
+ game = client.games.get_game(2753915549)
104
+ print(f"{game.name} โ€” {game.visits:,} visits | {game.playing:,} playing")
105
+
106
+ # Catalog search
107
+ items = client.catalog.search(keyword="fedora", category="Accessories", sort_type="Sales")
108
+ for item in items:
109
+ print(f"{item.name} โ€” {item.price}R$")
110
+ ```
111
+
112
+ ---
113
+
114
+ <div align="center">
115
+
116
+ ## ๐Ÿ–ฅ๏ธ Interactive Terminal
117
+
118
+ </div>
119
+
120
+ ```bash
121
+ roboat
122
+ # or: python -m roboat
123
+ ```
124
+
125
+ ```
126
+ ____ _ _ _ ____ ___
127
+ | _ \ ___ | |__ | | _____ __/ \ | _ \_ _|
128
+ | |_) / _ \| '_ \| |/ _ \ \/ / _ \ | |_) | |
129
+ | _ < (_) | |_) | | (_) > < ___ \| __/| |
130
+ |_| \_\___/|_.__/|_|\___/_/\_/_/ \_|_| |___|
131
+
132
+ roboat v2.1.0 โ€” roboat.pro โ€” type 'help' to begin
133
+
134
+ ยป start 156
135
+ ยป auth
136
+ ยป game 2753915549
137
+ ยป inventory 156
138
+ ยป rap 156
139
+ ยป likes 2753915549
140
+ ยป friends 156
141
+ ```
142
+
143
+ ---
144
+
145
+ <div align="center">
146
+
147
+ ## ๐Ÿ” OAuth Authentication
148
+
149
+ </div>
150
+
151
+ roboat uses **Roblox OAuth 2.0** โ€” no cookie extraction, no browser DevTools.
152
+
153
+ ```python
154
+ from roboat import OAuthManager, RoboatClient
155
+
156
+ manager = OAuthManager(
157
+ on_success=lambda token: print("โœ… Authenticated!"),
158
+ on_failure=lambda err: print(f"โŒ Failed: {err}"),
159
+ timeout=120,
160
+ )
161
+
162
+ token = manager.authenticate() # opens browser, 120s countdown
163
+
164
+ if token:
165
+ client = RoboatClient(oauth_token=token)
166
+ print(f"Logged in as {client.username()}")
167
+ ```
168
+
169
+ In the terminal, type `auth`. A browser window opens, you log in, and you're done. A **live 120-second countdown** is shown while waiting.
170
+
171
+ ---
172
+
173
+ <div align="center">
174
+
175
+ ## ๐Ÿ“ฆ Repository Structure
176
+
177
+ </div>
178
+
179
+ ```
180
+ roboat/
181
+ โ”œโ”€โ”€ roboat/ Source package (35 modules)
182
+ โ”‚ โ”œโ”€โ”€ utils/ Cache, rate limiter, paginator
183
+ โ”‚ โ””โ”€โ”€ *.py All API modules
184
+ โ”œโ”€โ”€ examples/ 8 ready-to-run example scripts
185
+ โ”œโ”€โ”€ tests/ Unit tests โ€” no network required
186
+ โ”œโ”€โ”€ benchmarks/ Performance benchmarks
187
+ โ”œโ”€โ”€ docs/ Architecture, endpoints, models, FAQ
188
+ โ”œโ”€โ”€ tools/ CLI utilities (bulk lookup, RAP snapshot, game monitor)
189
+ โ”œโ”€โ”€ integrations/ Discord bot, Flask REST API
190
+ โ”œโ”€โ”€ typestubs/ .pyi type stubs for IDE autocomplete
191
+ โ”œโ”€โ”€ scripts/ Dev scripts (env check, stub generator)
192
+ โ””โ”€โ”€ .github/ CI/CD workflows, issue templates
193
+ ```
194
+
195
+ ---
196
+
197
+ <div align="center">
198
+
199
+ ## ๐Ÿง  Clients
200
+
201
+ </div>
202
+
203
+ ### Sync โ€” `RoboatClient`
204
+
205
+ ```python
206
+ from roboat import RoboatClient, ClientBuilder
207
+
208
+ # Simple
209
+ client = RoboatClient()
210
+
211
+ # Builder โ€” full control
212
+ client = (
213
+ ClientBuilder()
214
+ .set_oauth_token("TOKEN")
215
+ .set_timeout(15)
216
+ .set_cache_ttl(60) # cache responses for 60 seconds
217
+ .set_rate_limit(10) # max 10 requests/second
218
+ .set_proxy("http://proxy:8080")
219
+ .build()
220
+ )
221
+ ```
222
+
223
+ ### Async โ€” `AsyncRoboatClient`
224
+
225
+ ```python
226
+ import asyncio
227
+ from roboat import AsyncRoboatClient
228
+
229
+ async def main():
230
+ async with AsyncRoboatClient() as client:
231
+
232
+ # Parallel fetch โ€” all at once
233
+ game, votes, icons = await asyncio.gather(
234
+ client.games.get_game(2753915549),
235
+ client.games.get_votes([2753915549]),
236
+ client.thumbnails.get_game_icons([2753915549]),
237
+ )
238
+
239
+ # Bulk fetch 500 users โ€” auto-chunked into batches of 100
240
+ users = await client.users.get_users_by_ids(list(range(1, 501)))
241
+ print(f"Fetched {len(users)} users")
242
+
243
+ asyncio.run(main())
244
+ ```
245
+
246
+ ### Open Cloud โ€” `RoboatCloudClient`
247
+
248
+ ```python
249
+ from roboat import RoboatCloudClient
250
+
251
+ cloud = RoboatCloudClient(api_key="roblox-KEY-xxxxx")
252
+ cloud.datastores.set(universe_id, "PlayerData", "player_1", {"coins": 500})
253
+ ```
254
+
255
+ ---
256
+
257
+ <div align="center">
258
+
259
+ ## ๐Ÿ“ก API Coverage
260
+
261
+ </div>
262
+
263
+ <div align="center">
264
+
265
+ | API | Endpoint | Methods |
266
+ |:---|:---|:---:|
267
+ | Users | `users.roblox.com` | 7 |
268
+ | Games | `games.roblox.com` | 15 |
269
+ | Catalog | `catalog.roblox.com` | 8 |
270
+ | Groups | `groups.roblox.com` | 22 |
271
+ | Friends | `friends.roblox.com` | 11 |
272
+ | Thumbnails | `thumbnails.roblox.com` | 7 |
273
+ | Badges | `badges.roblox.com` | 4 |
274
+ | Economy | `economy.roblox.com` | 5 |
275
+ | Presence | `presence.roblox.com` | 3 |
276
+ | Avatar | `avatar.roblox.com` | 5 |
277
+ | Trades | `trades.roblox.com` | 7 |
278
+ | Messages | `privatemessages.roblox.com` | 7 |
279
+ | Inventory | `inventory.roblox.com` | 8 |
280
+ | Develop | `develop.roblox.com` | 14 |
281
+ | DataStores | `apis.roblox.com/datastores` | 7 |
282
+ | Ordered DS | `apis.roblox.com/ordered-data-stores` | 5 |
283
+ | Messaging | `apis.roblox.com/messaging-service` | 3 |
284
+ | Bans | `apis.roblox.com/cloud/v2` | 4 |
285
+ | Notifications | `apis.roblox.com/cloud/v2` | 3 |
286
+ | Asset Upload | `apis.roblox.com/assets` | 5 |
287
+
288
+ </div>
289
+
290
+ ---
291
+
292
+ <div align="center">
293
+
294
+ ## ๐Ÿ‘ค Users
295
+
296
+ </div>
297
+
298
+ ```python
299
+ user = client.users.get_user(156)
300
+ users = client.users.get_users_by_ids([1, 156, 261]) # up to 100 at once
301
+ users = client.users.get_users_by_usernames(["Roblox", "builderman"])
302
+ page = client.users.search_users("builderman", limit=10)
303
+ page = client.users.get_username_history(156)
304
+ ok = client.users.validate_username("mycoolname")
305
+ ```
306
+
307
+ ---
308
+
309
+ <div align="center">
310
+
311
+ ## ๐ŸŽฎ Games
312
+
313
+ </div>
314
+
315
+ ```python
316
+ game = client.games.get_game(2753915549)
317
+ game = client.games.get_game_from_place(6872265039)
318
+ visits = client.games.get_visits([2753915549, 286090429]) # {id: count}
319
+ votes = client.games.get_votes([2753915549])
320
+ servers = client.games.get_servers(6872265039, limit=10)
321
+ page = client.games.search_games("obby", limit=20)
322
+ page = client.games.get_user_games(156)
323
+ page = client.games.get_group_games(2868472)
324
+ count = client.games.get_favorite_count(2753915549)
325
+ ```
326
+
327
+ ---
328
+
329
+ <div align="center">
330
+
331
+ ## ๐Ÿ‘ฅ Groups
332
+
333
+ </div>
334
+
335
+ ```python
336
+ group = client.groups.get_group(7)
337
+ roles = client.groups.get_roles(7)
338
+ role = client.groups.get_role_by_name(7, "Member")
339
+ members = client.groups.get_members(7, limit=100)
340
+ is_in = client.groups.is_member(7, user_id=156)
341
+
342
+ # Management (auth required)
343
+ client.groups.set_member_role(7, user_id=1234, role_id=role.id)
344
+ client.groups.kick_member(7, user_id=1234)
345
+ client.groups.post_shout(7, "Welcome everyone!")
346
+ client.groups.accept_all_join_requests(7) # accepts ALL pending
347
+ client.groups.pay_out(7, user_id=1234, amount=500)
348
+ ```
349
+
350
+ ---
351
+
352
+ <div align="center">
353
+
354
+ ## ๐Ÿ’ฐ Marketplace & Economy
355
+
356
+ </div>
357
+
358
+ ```python
359
+ from roboat.marketplace import MarketplaceAPI
360
+
361
+ market = MarketplaceAPI(client)
362
+
363
+ # Full limited data โ€” RAP, trend, remaining supply
364
+ data = market.get_limited_data(1365767)
365
+ print(f"{data.name} RAP: {data.recent_average_price:,}R$ Trend: {data.price_trend}")
366
+
367
+ # Profit estimator โ€” includes Roblox 30% fee
368
+ profit = market.estimate_resale_profit(1365767, purchase_price=12000)
369
+ print(f"Net profit: {profit.estimated_profit:,}R$ ROI: {profit.roi_percent}%")
370
+
371
+ # Find underpriced limiteds (below 85% of RAP)
372
+ deals = market.find_underpriced_limiteds([1365767, 1028606, 19027209])
373
+
374
+ # RAP tracker โ€” snapshot and diff over time
375
+ tracker = market.create_rap_tracker([1365767, 1028606])
376
+ tracker.snapshot()
377
+ # ... wait some time ...
378
+ tracker.snapshot()
379
+ print(tracker.summary())
380
+ ```
381
+
382
+ ---
383
+
384
+ <div align="center">
385
+
386
+ ## ๐Ÿค Social Graph
387
+
388
+ </div>
389
+
390
+ ```python
391
+ from roboat.social import SocialGraph
392
+
393
+ sg = SocialGraph(client)
394
+
395
+ mutuals = sg.mutual_friends(156, 261)
396
+ is_following = sg.does_follow(follower_id=156, target_id=261)
397
+ snap = sg.presence_snapshot([156, 261, 1234])
398
+ online_ids = sg.who_is_online([156, 261, 1234])
399
+ nodes = sg.most_followed_in_group([156, 261, 1234]) # parallel fetch
400
+ suggestions = sg.follow_suggestions(156, limit=10)
401
+ ```
402
+
403
+ ---
404
+
405
+ <div align="center">
406
+
407
+ ## ๐Ÿ”ง Open Cloud โ€” Developer Tools
408
+
409
+ </div>
410
+
411
+ ```python
412
+ API_KEY = "roblox-KEY-xxxxx"
413
+ UNIVERSE = 123456789
414
+
415
+ # DataStores
416
+ client.develop.set_datastore_entry(UNIVERSE, "PlayerData", "player_1", {"coins": 500}, API_KEY)
417
+ client.develop.get_datastore_entry(UNIVERSE, "PlayerData", "player_1", API_KEY)
418
+ client.develop.increment_datastore_entry(UNIVERSE, "Stats", "deaths", 1, API_KEY)
419
+ client.develop.list_datastore_keys(UNIVERSE, "PlayerData", API_KEY)
420
+
421
+ # Ordered DataStores (leaderboards)
422
+ client.develop.list_ordered_datastore(UNIVERSE, "Leaderboard", API_KEY, max_page_size=10)
423
+ client.develop.set_ordered_datastore_entry(UNIVERSE, "Leaderboard", "player_1", 9500, API_KEY)
424
+
425
+ # MessagingService โ€” reaches all live servers instantly
426
+ client.develop.announce(UNIVERSE, API_KEY, "Double XP starts now!")
427
+ client.develop.broadcast_shutdown(UNIVERSE, API_KEY)
428
+
429
+ # Bans
430
+ client.develop.ban_user(UNIVERSE, 1234, API_KEY, duration_seconds=86400,
431
+ display_reason="Temporarily banned.")
432
+ client.develop.unban_user(UNIVERSE, 1234, API_KEY)
433
+ ```
434
+
435
+ ---
436
+
437
+ <div align="center">
438
+
439
+ ## ๐Ÿ—„๏ธ Database Layer
440
+
441
+ </div>
442
+
443
+ ```python
444
+ from roboat import SessionDatabase
445
+
446
+ db = SessionDatabase.load_or_create("myproject")
447
+
448
+ db.save_user(client.users.get_user(156))
449
+ db.save_game(client.games.get_game(2753915549))
450
+
451
+ db.set("tracked_ids", [156, 261, 1234])
452
+ val = db.get("tracked_ids")
453
+
454
+ print(db.stats())
455
+ # {'users': 10, 'games': 5, 'session_keys': 3, 'log_entries': 42}
456
+ db.close()
457
+ ```
458
+
459
+ ---
460
+
461
+ <div align="center">
462
+
463
+ ## โšก Events
464
+
465
+ </div>
466
+
467
+ ```python
468
+ from roboat import EventPoller
469
+
470
+ poller = EventPoller(client, interval=30)
471
+
472
+ @poller.on_friend_online
473
+ def on_online(user):
474
+ print(f"๐ŸŸข {user.display_name} came online!")
475
+
476
+ @poller.on_new_friend
477
+ def on_friend(user):
478
+ print(f"๐Ÿค New friend: {user.display_name}")
479
+
480
+ poller.track_game(2753915549, milestone_step=1_000_000)
481
+
482
+ @poller.on("visit_milestone")
483
+ def on_milestone(game, count):
484
+ print(f"๐ŸŽ‰ {game.name} hit {count:,} visits!")
485
+
486
+ poller.start(interval=30) # background thread
487
+ ```
488
+
489
+ ---
490
+
491
+ <div align="center">
492
+
493
+ ## ๐Ÿ› ๏ธ CLI Tools
494
+
495
+ </div>
496
+
497
+ ```bash
498
+ # Bulk user lookup โ†’ CSV or JSON
499
+ python tools/bulk_lookup.py --ids 1 156 261 --format csv
500
+ python tools/bulk_lookup.py --usernames Roblox builderman --format json
501
+
502
+ # RAP snapshot and diff
503
+ python tools/rap_snapshot.py --user 156
504
+ python tools/rap_snapshot.py --user 156 --diff # compare to last run
505
+
506
+ # Live game monitor with milestone alerts
507
+ python tools/game_monitor.py --universe 2753915549 --interval 60
508
+
509
+ # Environment health check
510
+ python scripts/check_env.py
511
+
512
+ # Generate .pyi type stubs
513
+ python scripts/generate_stub.py
514
+ ```
515
+
516
+ ---
517
+
518
+ <div align="center">
519
+
520
+ ## ๐Ÿ”— Integrations
521
+
522
+ </div>
523
+
524
+ | Integration | File | Description |
525
+ |:---|:---|:---|
526
+ | Discord Bot | `integrations/discord_bot.py` | Slash commands: `/user`, `/game`, `/status` |
527
+ | Flask REST API | `integrations/flask_api.py` | REST endpoints for all major resources |
528
+
529
+ ---
530
+
531
+ <div align="center">
532
+
533
+ ## ๐Ÿšจ Error Handling
534
+
535
+ </div>
536
+
537
+ ```python
538
+ from roboat import (
539
+ UserNotFoundError, GameNotFoundError,
540
+ RateLimitedError, NotAuthenticatedError, RoboatAPIError,
541
+ )
542
+ from roboat.utils import retry
543
+
544
+ @retry(max_attempts=3, backoff=2.0)
545
+ def safe_get(user_id):
546
+ return client.users.get_user(user_id)
547
+
548
+ try:
549
+ user = safe_get(99999999999)
550
+ except UserNotFoundError:
551
+ print("User not found")
552
+ except RateLimitedError:
553
+ print("Rate limited")
554
+ except NotAuthenticatedError:
555
+ print("Need OAuth token")
556
+ except RoboatAPIError as e:
557
+ print(f"API error: {e}")
558
+ ```
559
+
560
+ ---
561
+
562
+ <div align="center">
563
+
564
+ ## ๐Ÿ“Š Pagination
565
+
566
+ </div>
567
+
568
+ ```python
569
+ from roboat.utils import Paginator
570
+
571
+ # Lazily iterate ALL followers โ€” auto-fetches every page
572
+ for follower in Paginator(
573
+ lambda cursor: client.friends.get_followers(156, limit=100, cursor=cursor)
574
+ ):
575
+ print(follower)
576
+
577
+ # Collect first 500
578
+ top_500 = Paginator(
579
+ lambda c: client.friends.get_followers(156, limit=100, cursor=c),
580
+ max_items=500,
581
+ ).collect()
582
+ ```
583
+
584
+ ---
585
+
586
+ <div align="center">
587
+
588
+ ## ๐Ÿ“‹ Terminal Commands
589
+
590
+ </div>
591
+
592
+ <div align="center">
593
+
594
+ | Command | Description |
595
+ |:---|:---|
596
+ | `start <userid>` | Begin session (required first) |
597
+ | `auth` | OAuth login โ€” browser opens, 120s |
598
+ | `whoami` | Current session info |
599
+ | `newdb / loaddb / listdb` | Database management |
600
+ | `user <id>` | User profile |
601
+ | `game <id>` | Game stats + votes |
602
+ | `friends / followers <id>` | Social counts |
603
+ | `likes <id>` | Vote breakdown |
604
+ | `search user/game <kw>` | Search |
605
+ | `presence / avatar <id>` | Status + avatar |
606
+ | `servers <placeid>` | Active servers |
607
+ | `badges <id>` | Game badges |
608
+ | `catalog <keyword>` | Shop search |
609
+ | `trades` | Trade list |
610
+ | `inventory / rap <id>` | Limiteds + RAP |
611
+ | `messages` | Private messages |
612
+ | `owns <uid> <assetid>` | Ownership check |
613
+ | `universe <id>` | Developer universe info |
614
+ | `save user/game <id>` | Save to DB |
615
+ | `cache [clear]` | Cache stats |
616
+ | `watch <id>` | Visit milestone alerts |
617
+ | `history / clear / exit` | Utility |
618
+
619
+ </div>
620
+
621
+ ---
622
+
623
+ <div align="center">
624
+
625
+ ## โš–๏ธ License
626
+
627
+ </div>
628
+
629
+ ```
630
+ MIT License
631
+
632
+ Copyright (c) 2024 roboat contributors
633
+
634
+ Permission is hereby granted, free of charge, to any person obtaining a copy
635
+ of this software and associated documentation files (the "Software"), to deal
636
+ in the Software without restriction, including without limitation the rights
637
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
638
+ copies of the Software, and to permit persons to whom the Software is
639
+ furnished to do so, subject to the following conditions:
640
+
641
+ The above copyright notice and this permission notice shall be included in
642
+ all copies or substantial portions of the Software.
643
+
644
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
645
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
646
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
647
+ ```
648
+
649
+ Full license: [LICENSE](LICENSE)
650
+
651
+ ---
652
+
653
+ <div align="center">
654
+
655
+ <img src="https://capsule-render.vercel.app/api?type=waving&color=0:991B1B,100:E3342F&height=140&section=footer" width="100%"/>
656
+
657
+ **[roboat.pro](https://roboat.pro)** &nbsp;ยท&nbsp; **[GitHub](https://github.com/Addi9000/roboat)** &nbsp;ยท&nbsp; **[Issues](https://github.com/Addi9000/roboat/issues)**
658
+
659
+ *Built with โค๏ธ for the Roblox developer community*
660
+
661
+ </div>