roboat
Advanced tools
+328
-215
| Metadata-Version: 2.4 | ||
| Name: roboat | ||
| Version: 1.1.0 | ||
| Version: 2.1.0 | ||
| Summary: The best Python wrapper for the Roblox API โ OAuth, async, typed models, datastores, events, marketplace tools | ||
| Home-page: https://github.com/Addi9000/roboat | ||
| Home-page: https://roboat.pro | ||
| Author: roboat contributors | ||
@@ -54,16 +54,17 @@ Author-email: bwesttwink@gmail.com | ||
| <img src="https://capsule-render.vercel.app/api?type=waving&color=0:6EE7F7,100:A78BFA&height=200§ion=header&text=roboat&fontSize=80&fontColor=ffffff&animation=fadeIn&fontAlignY=38&desc=The%20best%20Python%20wrapper%20for%20the%20Roblox%20API&descAlignY=60&descAlign=50" width="100%"/> | ||
| <img src="https://capsule-render.vercel.app/api?type=waving&color=0:E3342F,100:991B1B&height=220§ion=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%"/> | ||
| <br/> | ||
| [](https://python.org) | ||
| [](LICENSE) | ||
| [](https://github.com/valeoncehadadream/roboat) | ||
| [](https://github.com/valeoncehadadream/roboat) | ||
| <img src="https://readme-typing-svg.demolab.com?font=Fira+Code&weight=600&size=20&pause=800&color=E3342F¢er=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" /> | ||
| <br/> | ||
| <br/><br/> | ||
| <img src="https://readme-typing-svg.demolab.com?font=Fira+Code&size=22&pause=1000&color=6EE7F7¢er=true&vCenter=true&width=600&lines=OAuth+2.0+Authentication;Typed+Models+for+Every+Response;Async+%2B+Sync+Clients;Built-in+SQLite+Database;Open+Cloud+%2B+DataStores;Real-time+Event+System;Marketplace+%26+Economy+Tools;Interactive+Terminal+REPL" alt="Typing SVG" /> | ||
| [](https://python.org) | ||
| [](LICENSE) | ||
| [](https://github.com/Addi9000/roboat) | ||
| [](https://roboat.pro) | ||
| [](https://github.com/Addi9000/roboat/stargazers) | ||
| <br/><br/> | ||
| <br/> | ||
@@ -74,8 +75,12 @@ </div> | ||
| ## Install | ||
| <div align="center"> | ||
| ## โก Install | ||
| </div> | ||
| ```bash | ||
| pip install . | ||
| pip install ".[async]" # with aiohttp for async support | ||
| pip install ".[all]" # everything including tests | ||
| pip install roboat | ||
| pip install "roboat[async]" # async support via aiohttp | ||
| pip install "roboat[all]" # everything + test tools | ||
| ``` | ||
@@ -85,4 +90,8 @@ | ||
| ## Quick Start | ||
| <div align="center"> | ||
| ## ๐ Quick Start | ||
| </div> | ||
| ```python | ||
@@ -93,7 +102,15 @@ from roboat import RoboatClient | ||
| # User lookup | ||
| user = client.users.get_user(156) | ||
| print(user) # Builderman (@builderman) [ID: 156] | ||
| print(user) | ||
| # Builderman (@builderman) โ [ID: 156] | ||
| # Game stats | ||
| game = client.games.get_game(2753915549) | ||
| print(f"{game.name} โ {game.visits:,} visits") | ||
| print(f"{game.name} โ {game.visits:,} visits | {game.playing:,} playing") | ||
| # Catalog search | ||
| items = client.catalog.search(keyword="fedora", category="Accessories", sort_type="Sales") | ||
| for item in items: | ||
| print(f"{item.name} โ {item.price}R$") | ||
| ``` | ||
@@ -103,8 +120,11 @@ | ||
| ## Interactive Terminal | ||
| <div align="center"> | ||
| ## ๐ฅ๏ธ Interactive Terminal | ||
| </div> | ||
| ```bash | ||
| roboat | ||
| # or | ||
| python -m roboat | ||
| # or: python -m roboat | ||
| ``` | ||
@@ -119,3 +139,3 @@ | ||
| roboat v2.1.0 โ type 'help' to begin | ||
| roboat v2.1.0 โ roboat.pro โ type 'help' to begin | ||
@@ -126,10 +146,17 @@ ยป start 156 | ||
| ยป inventory 156 | ||
| ยป rap 156 | ||
| ยป likes 2753915549 | ||
| ยป friends 156 | ||
| ``` | ||
| Every command requires `start <userid>` first. The terminal will remind you if you forget. | ||
| --- | ||
| ## OAuth Authentication | ||
| <div align="center"> | ||
| ## ๐ OAuth Authentication | ||
| </div> | ||
| roboat uses **Roblox OAuth 2.0** โ no cookie extraction, no browser DevTools. | ||
| ```python | ||
@@ -139,58 +166,50 @@ from roboat import OAuthManager, RoboatClient | ||
| manager = OAuthManager( | ||
| on_success=lambda token: print("Authenticated!"), | ||
| on_failure=lambda err: print(f"Failed: {err}"), | ||
| on_success=lambda token: print("โ Authenticated!"), | ||
| on_failure=lambda err: print(f"โ Failed: {err}"), | ||
| timeout=120, | ||
| ) | ||
| token = manager.authenticate() # opens browser, waits 120s | ||
| token = manager.authenticate() # opens browser, 120s countdown | ||
| if token: | ||
| client = RoboatClient(oauth_token=token) | ||
| print(f"Logged in as {client.username()}") | ||
| ``` | ||
| In the terminal, type `auth`. Your browser opens automatically and you have **120 seconds** to complete login with a live countdown shown. | ||
| 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. | ||
| --- | ||
| ## All Modules | ||
| <div align="center"> | ||
| | Module | What It Does | | ||
| |:---|:---| | ||
| | `client.py` | Main sync client with caching and rate limiting | | ||
| | `async_client.py` | Full async mirror using aiohttp | | ||
| | `oauth.py` | OAuth 2.0 with 120s timeout | | ||
| | `session.py` | Interactive terminal REPL | | ||
| | `database.py` | Local SQLite persistence | | ||
| | `models.py` | 20+ typed dataclass response models | | ||
| | `exceptions.py` | Typed exceptions per HTTP status | | ||
| | `events.py` | Background polling event system | | ||
| | `analytics.py` | Parallel report aggregation | | ||
| | `marketplace.py` | Limited items, RAP tracking, profit estimator | | ||
| | `social.py` | Friend graph, mutual analysis, presence tools | | ||
| | `notifications.py` | Experience push notifications | | ||
| | `publish.py` | Asset upload (images, audio, models) | | ||
| | `moderation.py` | Reports, blocks, chat filter | | ||
| | `opencloud.py` | Open Cloud API key client | | ||
| | `develop.py` | Universe, DataStores, bans, Team Create | | ||
| | `users.py` | User lookup, search, bulk fetch | | ||
| | `games.py` | Games, visits, servers, votes | | ||
| | `catalog.py` | Avatar shop search and item details | | ||
| | `groups.py` | Full group management and payouts | | ||
| | `friends.py` | Friends, followers, followings | | ||
| | `thumbnails.py` | All thumbnail types as {id: url} | | ||
| | `badges.py` | Badges and award dates | | ||
| | `economy.py` | Transactions and resale data | | ||
| | `presence.py` | Online and in-game status | | ||
| | `avatar.py` | Avatar assets and outfits | | ||
| | `trades.py` | Trade list, send, accept, decline | | ||
| | `messages.py` | Private messages and chat | | ||
| | `inventory.py` | Ownership, RAP, collectibles | | ||
| | `utils/cache.py` | Thread-safe TTL LRU cache | | ||
| | `utils/ratelimit.py` | Token bucket and @retry decorator | | ||
| | `utils/paginator.py` | Lazy auto-pagination iterator | | ||
| ## ๐ฆ Repository Structure | ||
| </div> | ||
| ``` | ||
| roboat/ | ||
| โโโ roboat/ Source package (35 modules) | ||
| โ โโโ utils/ Cache, rate limiter, paginator | ||
| โ โโโ *.py All API modules | ||
| โโโ examples/ 8 ready-to-run example scripts | ||
| โโโ tests/ Unit tests โ no network required | ||
| โโโ benchmarks/ Performance benchmarks | ||
| โโโ docs/ Architecture, endpoints, models, FAQ | ||
| โโโ tools/ CLI utilities (bulk lookup, RAP snapshot, game monitor) | ||
| โโโ integrations/ Discord bot, Flask REST API | ||
| โโโ typestubs/ .pyi type stubs for IDE autocomplete | ||
| โโโ scripts/ Dev scripts (env check, stub generator) | ||
| โโโ .github/ CI/CD workflows, issue templates | ||
| ``` | ||
| --- | ||
| ## Client Setup | ||
| <div align="center"> | ||
| ## ๐ง Clients | ||
| </div> | ||
| ### Sync โ `RoboatClient` | ||
| ```python | ||
@@ -205,7 +224,6 @@ from roboat import RoboatClient, ClientBuilder | ||
| ClientBuilder() | ||
| .set_oauth_token("YOUR_TOKEN") | ||
| .set_oauth_token("TOKEN") | ||
| .set_timeout(15) | ||
| .set_cache_ttl(60) | ||
| .set_cache_size(512) | ||
| .set_rate_limit(10) | ||
| .set_cache_ttl(60) # cache responses for 60 seconds | ||
| .set_rate_limit(10) # max 10 requests/second | ||
| .set_proxy("http://proxy:8080") | ||
@@ -216,3 +234,3 @@ .build() | ||
| ## Async Client | ||
| ### Async โ `AsyncRoboatClient` | ||
@@ -225,3 +243,5 @@ ```python | ||
| async with AsyncRoboatClient() as client: | ||
| game, votes, icon = await asyncio.gather( | ||
| # Parallel fetch โ all at once | ||
| game, votes, icons = await asyncio.gather( | ||
| client.games.get_game(2753915549), | ||
@@ -231,3 +251,6 @@ client.games.get_votes([2753915549]), | ||
| ) | ||
| # Bulk fetch 500 users โ auto-chunked into batches of 100 | ||
| users = await client.users.get_users_by_ids(list(range(1, 501))) | ||
| print(f"Fetched {len(users)} users") | ||
@@ -237,33 +260,97 @@ asyncio.run(main()) | ||
| ### Open Cloud โ `RoboatCloudClient` | ||
| ```python | ||
| from roboat import RoboatCloudClient | ||
| cloud = RoboatCloudClient(api_key="roblox-KEY-xxxxx") | ||
| cloud.datastores.set(universe_id, "PlayerData", "player_1", {"coins": 500}) | ||
| ``` | ||
| --- | ||
| ## Users | ||
| <div align="center"> | ||
| ## ๐ก API Coverage | ||
| </div> | ||
| <div align="center"> | ||
| | API | Endpoint | Methods | | ||
| |:---|:---|:---:| | ||
| | Users | `users.roblox.com` | 7 | | ||
| | Games | `games.roblox.com` | 15 | | ||
| | Catalog | `catalog.roblox.com` | 8 | | ||
| | Groups | `groups.roblox.com` | 22 | | ||
| | Friends | `friends.roblox.com` | 11 | | ||
| | Thumbnails | `thumbnails.roblox.com` | 7 | | ||
| | Badges | `badges.roblox.com` | 4 | | ||
| | Economy | `economy.roblox.com` | 5 | | ||
| | Presence | `presence.roblox.com` | 3 | | ||
| | Avatar | `avatar.roblox.com` | 5 | | ||
| | Trades | `trades.roblox.com` | 7 | | ||
| | Messages | `privatemessages.roblox.com` | 7 | | ||
| | Inventory | `inventory.roblox.com` | 8 | | ||
| | Develop | `develop.roblox.com` | 14 | | ||
| | DataStores | `apis.roblox.com/datastores` | 7 | | ||
| | Ordered DS | `apis.roblox.com/ordered-data-stores` | 5 | | ||
| | Messaging | `apis.roblox.com/messaging-service` | 3 | | ||
| | Bans | `apis.roblox.com/cloud/v2` | 4 | | ||
| | Notifications | `apis.roblox.com/cloud/v2` | 3 | | ||
| | Asset Upload | `apis.roblox.com/assets` | 5 | | ||
| </div> | ||
| --- | ||
| <div align="center"> | ||
| ## ๐ค Users | ||
| </div> | ||
| ```python | ||
| user = client.users.get_user(156) | ||
| users = client.users.get_users_by_ids([1, 156, 261]) | ||
| users = client.users.get_users_by_ids([1, 156, 261]) # up to 100 at once | ||
| users = client.users.get_users_by_usernames(["Roblox", "builderman"]) | ||
| page = client.users.search_users("builderman", limit=10) | ||
| page = client.users.get_username_history(156) | ||
| ok = client.users.validate_username("mycoolname") | ||
| ``` | ||
| ## Games | ||
| --- | ||
| <div align="center"> | ||
| ## ๐ฎ Games | ||
| </div> | ||
| ```python | ||
| game = client.games.get_game(2753915549) | ||
| game = client.games.get_game_from_place(6872265039) | ||
| visits = client.games.get_visits([2753915549, 286090429]) | ||
| votes = client.games.get_votes([2753915549]) | ||
| page = client.games.get_servers(6872265039, limit=10) | ||
| page = client.games.get_user_games(156) | ||
| page = client.games.get_group_games(2868472) | ||
| page = client.games.search_games("obby", limit=20) | ||
| game = client.games.get_game(2753915549) | ||
| game = client.games.get_game_from_place(6872265039) | ||
| visits = client.games.get_visits([2753915549, 286090429]) # {id: count} | ||
| votes = client.games.get_votes([2753915549]) | ||
| servers = client.games.get_servers(6872265039, limit=10) | ||
| page = client.games.search_games("obby", limit=20) | ||
| page = client.games.get_user_games(156) | ||
| page = client.games.get_group_games(2868472) | ||
| count = client.games.get_favorite_count(2753915549) | ||
| ``` | ||
| ## Groups | ||
| --- | ||
| <div align="center"> | ||
| ## ๐ฅ Groups | ||
| </div> | ||
| ```python | ||
| group = client.groups.get_group(7) | ||
| roles = client.groups.get_roles(7) | ||
| role = client.groups.get_role_by_name(7, "Member") | ||
| members = client.groups.get_members(7, limit=100) | ||
| is_in = client.groups.is_member(7, user_id=156) | ||
@@ -273,20 +360,14 @@ # Management (auth required) | ||
| client.groups.kick_member(7, user_id=1234) | ||
| client.groups.post_shout(7, "Message here") | ||
| client.groups.post_to_wall(7, "Wall post") | ||
| client.groups.accept_all_join_requests(7) | ||
| client.groups.post_shout(7, "Welcome everyone!") | ||
| client.groups.accept_all_join_requests(7) # accepts ALL pending | ||
| client.groups.pay_out(7, user_id=1234, amount=500) | ||
| ``` | ||
| ## Catalog | ||
| --- | ||
| ```python | ||
| page = client.catalog.search(keyword="fedora", category="Accessories", sort_type="Sales") | ||
| item = client.catalog.get_asset(1028606) | ||
| item = client.catalog.get_bundle(192) | ||
| resale = client.catalog.get_resale_data(1028606) | ||
| ``` | ||
| <div align="center"> | ||
| --- | ||
| ## ๐ฐ Marketplace & Economy | ||
| ## Marketplace Tools | ||
| </div> | ||
@@ -298,10 +379,17 @@ ```python | ||
| # Full limited data โ RAP, trend, remaining supply | ||
| data = market.get_limited_data(1365767) | ||
| print(data.recent_average_price, data.price_trend) | ||
| print(f"{data.name} RAP: {data.recent_average_price:,}R$ Trend: {data.price_trend}") | ||
| # Profit estimator โ includes Roblox 30% fee | ||
| profit = market.estimate_resale_profit(1365767, purchase_price=12000) | ||
| deals = market.find_underpriced_limiteds([1365767, 1028606]) | ||
| print(f"Net profit: {profit.estimated_profit:,}R$ ROI: {profit.roi_percent}%") | ||
| # Find underpriced limiteds (below 85% of RAP) | ||
| deals = market.find_underpriced_limiteds([1365767, 1028606, 19027209]) | ||
| # RAP tracker โ snapshot and diff over time | ||
| tracker = market.create_rap_tracker([1365767, 1028606]) | ||
| tracker.snapshot() | ||
| # ... wait some time ... | ||
| tracker.snapshot() | ||
@@ -311,70 +399,32 @@ print(tracker.summary()) | ||
| ## Social Graph | ||
| --- | ||
| ```python | ||
| from roboat.social import SocialGraph | ||
| <div align="center"> | ||
| sg = SocialGraph(client) | ||
| ## ๐ค Social Graph | ||
| mutuals = sg.mutual_friends(156, 261) | ||
| is_following = sg.does_follow(156, 261) | ||
| snap = sg.presence_snapshot([156, 261, 1234]) | ||
| online_ids = sg.who_is_online([156, 261, 1234]) | ||
| nodes = sg.most_followed_in_group([156, 261, 1234]) | ||
| suggestions = sg.follow_suggestions(156, limit=10) | ||
| ``` | ||
| </div> | ||
| ## Notifications | ||
| ```python | ||
| from roboat.notifications import NotificationsAPI | ||
| from roboat.social import SocialGraph | ||
| notif = NotificationsAPI(api_key="roblox-KEY-xxxx") | ||
| sg = SocialGraph(client) | ||
| result = notif.send( | ||
| universe_id=123456789, | ||
| user_id=1234, | ||
| message_id="daily-reward", | ||
| attributes={"coins": "500"}, | ||
| join_experience={"launchData": "reward"}, | ||
| ) | ||
| results = notif.send_bulk(123456789, [1234, 5678], "daily-reward") | ||
| quota = notif.get_quota(123456789) | ||
| mutuals = sg.mutual_friends(156, 261) | ||
| is_following = sg.does_follow(follower_id=156, target_id=261) | ||
| snap = sg.presence_snapshot([156, 261, 1234]) | ||
| online_ids = sg.who_is_online([156, 261, 1234]) | ||
| nodes = sg.most_followed_in_group([156, 261, 1234]) # parallel fetch | ||
| suggestions = sg.follow_suggestions(156, limit=10) | ||
| ``` | ||
| ## Asset Publishing | ||
| --- | ||
| ```python | ||
| from roboat.publish import PublishAPI | ||
| <div align="center"> | ||
| pub = PublishAPI(api_key="roblox-KEY-xxxx", creator_id=156, creator_type="User") | ||
| ## ๐ง Open Cloud โ Developer Tools | ||
| asset = pub.upload_image("thumbnail.png", name="Game Icon") | ||
| asset = pub.upload_audio("bgm.ogg", name="Background Music") | ||
| asset = pub.upload_model("char.fbx", name="Character Model") | ||
| asset = pub.upload_auto("file.png", name="Auto Detected") | ||
| </div> | ||
| final = pub.wait_for_asset(asset.operation_id, max_wait=30) | ||
| ``` | ||
| ## Moderation | ||
| ```python | ||
| from roboat.moderation import ModerationAPI | ||
| mod = ModerationAPI(client) | ||
| standing = mod.get_account_standing(user_id=1234) | ||
| mod.report_user(1234, reason="Spam") | ||
| mod.report_asset(11111, reason="Inappropriate") | ||
| mod.block_user(1234) | ||
| result = mod.filter_text("Hello!", user_id=1234) | ||
| ``` | ||
| --- | ||
| ## Open Cloud โ Developer Tools | ||
| ```python | ||
| API_KEY = "roblox-KEY-xxxxx" | ||
@@ -384,6 +434,5 @@ UNIVERSE = 123456789 | ||
| # DataStores | ||
| client.develop.set_datastore_entry(UNIVERSE, "PlayerData", "player_1234", {"coins": 500}, API_KEY) | ||
| client.develop.get_datastore_entry(UNIVERSE, "PlayerData", "player_1234", API_KEY) | ||
| client.develop.set_datastore_entry(UNIVERSE, "PlayerData", "player_1", {"coins": 500}, API_KEY) | ||
| client.develop.get_datastore_entry(UNIVERSE, "PlayerData", "player_1", API_KEY) | ||
| client.develop.increment_datastore_entry(UNIVERSE, "Stats", "deaths", 1, API_KEY) | ||
| client.develop.delete_datastore_entry(UNIVERSE, "Sessions", "key", API_KEY) | ||
| client.develop.list_datastore_keys(UNIVERSE, "PlayerData", API_KEY) | ||
@@ -393,23 +442,12 @@ | ||
| client.develop.list_ordered_datastore(UNIVERSE, "Leaderboard", API_KEY, max_page_size=10) | ||
| client.develop.set_ordered_datastore_entry(UNIVERSE, "Leaderboard", "player_1234", 9500, API_KEY) | ||
| client.develop.increment_ordered_datastore(UNIVERSE, "Leaderboard", "player_1234", 100, API_KEY) | ||
| client.develop.set_ordered_datastore_entry(UNIVERSE, "Leaderboard", "player_1", 9500, API_KEY) | ||
| # MessagingService โ broadcast to all live servers | ||
| client.develop.publish_message(UNIVERSE, "Announcements", "Event in 5 minutes!", API_KEY) | ||
| client.develop.announce(UNIVERSE, API_KEY, "Server restart soon!") | ||
| # MessagingService โ reaches all live servers instantly | ||
| client.develop.announce(UNIVERSE, API_KEY, "Double XP starts now!") | ||
| client.develop.broadcast_shutdown(UNIVERSE, API_KEY) | ||
| # Bans | ||
| client.develop.ban_user(UNIVERSE, 1234, API_KEY, duration_seconds=86400, display_reason="Banned.") | ||
| client.develop.ban_user(UNIVERSE, 5678, API_KEY, duration_seconds=None) # permanent | ||
| client.develop.ban_user(UNIVERSE, 1234, API_KEY, duration_seconds=86400, | ||
| display_reason="Temporarily banned.") | ||
| client.develop.unban_user(UNIVERSE, 1234, API_KEY) | ||
| client.develop.list_bans(UNIVERSE, API_KEY) | ||
| # Team Create | ||
| client.develop.update_team_create(UNIVERSE, is_enabled=True) | ||
| client.develop.add_team_create_member(UNIVERSE, user_id=1234) | ||
| client.develop.get_team_create_members(UNIVERSE) | ||
| # Game Stats | ||
| stats = client.develop.get_game_stats(UNIVERSE, stat_type="Visits", granularity="Daily") | ||
| ``` | ||
@@ -419,4 +457,8 @@ | ||
| ## Database Layer | ||
| <div align="center"> | ||
| ## ๐๏ธ Database Layer | ||
| </div> | ||
| ```python | ||
@@ -430,6 +472,2 @@ from roboat import SessionDatabase | ||
| user_data = db.get_user(156) | ||
| all_users = db.get_all_users() | ||
| all_games = db.get_all_games() | ||
| db.set("tracked_ids", [156, 261, 1234]) | ||
@@ -440,3 +478,2 @@ val = db.get("tracked_ids") | ||
| # {'users': 10, 'games': 5, 'session_keys': 3, 'log_entries': 42} | ||
| db.close() | ||
@@ -447,4 +484,8 @@ ``` | ||
| ## Events | ||
| <div align="center"> | ||
| ## โก Events | ||
| </div> | ||
| ```python | ||
@@ -457,7 +498,7 @@ from roboat import EventPoller | ||
| def on_online(user): | ||
| print(f"{user.display_name} came online!") | ||
| print(f"๐ข {user.display_name} came online!") | ||
| @poller.on_new_friend | ||
| def on_friend(user): | ||
| print(f"New friend: {user.display_name}") | ||
| print(f"๐ค New friend: {user.display_name}") | ||
@@ -468,5 +509,5 @@ poller.track_game(2753915549, milestone_step=1_000_000) | ||
| def on_milestone(game, count): | ||
| print(f"{game.name} hit {count:,} visits!") | ||
| print(f"๐ {game.name} hit {count:,} visits!") | ||
| poller.start(interval=30) | ||
| poller.start(interval=30) # background thread | ||
| ``` | ||
@@ -476,16 +517,25 @@ | ||
| ## Pagination | ||
| <div align="center"> | ||
| ```python | ||
| from roboat.utils import Paginator | ||
| ## ๐ ๏ธ CLI Tools | ||
| for follower in Paginator( | ||
| lambda cursor: client.friends.get_followers(156, limit=100, cursor=cursor) | ||
| ): | ||
| print(follower) | ||
| </div> | ||
| top_500 = Paginator( | ||
| lambda c: client.friends.get_followers(156, limit=100, cursor=c), | ||
| max_items=500, | ||
| ).collect() | ||
| ```bash | ||
| # Bulk user lookup โ CSV or JSON | ||
| python tools/bulk_lookup.py --ids 1 156 261 --format csv | ||
| python tools/bulk_lookup.py --usernames Roblox builderman --format json | ||
| # RAP snapshot and diff | ||
| python tools/rap_snapshot.py --user 156 | ||
| python tools/rap_snapshot.py --user 156 --diff # compare to last run | ||
| # Live game monitor with milestone alerts | ||
| python tools/game_monitor.py --universe 2753915549 --interval 60 | ||
| # Environment health check | ||
| python scripts/check_env.py | ||
| # Generate .pyi type stubs | ||
| python scripts/generate_stub.py | ||
| ``` | ||
@@ -495,6 +545,26 @@ | ||
| ## Error Handling | ||
| <div align="center"> | ||
| ## ๐ Integrations | ||
| </div> | ||
| | Integration | File | Description | | ||
| |:---|:---|:---| | ||
| | Discord Bot | `integrations/discord_bot.py` | Slash commands: `/user`, `/game`, `/status` | | ||
| | Flask REST API | `integrations/flask_api.py` | REST endpoints for all major resources | | ||
| --- | ||
| <div align="center"> | ||
| ## ๐จ Error Handling | ||
| </div> | ||
| ```python | ||
| from roboat import UserNotFoundError, RateLimitedError, RoboatAPIError | ||
| from roboat import ( | ||
| UserNotFoundError, GameNotFoundError, | ||
| RateLimitedError, NotAuthenticatedError, RoboatAPIError, | ||
| ) | ||
| from roboat.utils import retry | ||
@@ -509,7 +579,9 @@ | ||
| except UserNotFoundError: | ||
| print("Not found") | ||
| print("User not found") | ||
| except RateLimitedError: | ||
| print("Rate limited") | ||
| except NotAuthenticatedError: | ||
| print("Need OAuth token") | ||
| except RoboatAPIError as e: | ||
| print(f"Error: {e}") | ||
| print(f"API error: {e}") | ||
| ``` | ||
@@ -519,13 +591,22 @@ | ||
| ## Analytics | ||
| <div align="center"> | ||
| ## ๐ Pagination | ||
| </div> | ||
| ```python | ||
| from roboat.analytics import Analytics | ||
| from roboat.utils import Paginator | ||
| an = Analytics(client) | ||
| # Lazily iterate ALL followers โ auto-fetches every page | ||
| for follower in Paginator( | ||
| lambda cursor: client.friends.get_followers(156, limit=100, cursor=cursor) | ||
| ): | ||
| print(follower) | ||
| print(an.user_report(156)) | ||
| print(an.compare_games([2753915549, 286090429])) | ||
| print(an.group_report(7)) | ||
| print(an.rich_leaderboard_str([2753915549, 286090429], by="visits")) | ||
| # Collect first 500 | ||
| top_500 = Paginator( | ||
| lambda c: client.friends.get_followers(156, limit=100, cursor=c), | ||
| max_items=500, | ||
| ).collect() | ||
| ``` | ||
@@ -535,16 +616,22 @@ | ||
| ## Terminal Commands | ||
| <div align="center"> | ||
| ## ๐ Terminal Commands | ||
| </div> | ||
| <div align="center"> | ||
| | Command | Description | | ||
| |:---|:---| | ||
| | `start <userid>` | Begin session (required first) | | ||
| | `auth` | OAuth login via browser | | ||
| | `auth` | OAuth login โ browser opens, 120s | | ||
| | `whoami` | Current session info | | ||
| | `newdb / loaddb / listdb` | Database management | | ||
| | `user <id>` | User profile | | ||
| | `game <id>` | Game stats | | ||
| | `friends / followers <id>` | Social info | | ||
| | `likes <id>` | Vote stats | | ||
| | `game <id>` | Game stats + votes | | ||
| | `friends / followers <id>` | Social counts | | ||
| | `likes <id>` | Vote breakdown | | ||
| | `search user/game <kw>` | Search | | ||
| | `presence / avatar <id>` | Status and avatar | | ||
| | `presence / avatar <id>` | Status + avatar | | ||
| | `servers <placeid>` | Active servers | | ||
@@ -554,6 +641,6 @@ | `badges <id>` | Game badges | | ||
| | `trades` | Trade list | | ||
| | `inventory / rap <id>` | Limiteds and RAP | | ||
| | `inventory / rap <id>` | Limiteds + RAP | | ||
| | `messages` | Private messages | | ||
| | `owns <uid> <assetid>` | Ownership check | | ||
| | `universe <id>` | Dev universe info | | ||
| | `universe <id>` | Developer universe info | | ||
| | `save user/game <id>` | Save to DB | | ||
@@ -564,8 +651,34 @@ | `cache [clear]` | Cache stats | | ||
| </div> | ||
| --- | ||
| ## License | ||
| <div align="center"> | ||
| MIT | ||
| ## โ๏ธ License | ||
| </div> | ||
| ``` | ||
| MIT License | ||
| Copyright (c) 2024 roboat contributors | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in | ||
| all copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| ``` | ||
| Full license: [LICENSE](LICENSE) | ||
| --- | ||
@@ -575,8 +688,8 @@ | ||
| <img src="https://capsule-render.vercel.app/api?type=waving&color=0:A78BFA,100:6EE7F7&height=120§ion=footer" width="100%"/> | ||
| <img src="https://capsule-render.vercel.app/api?type=waving&color=0:991B1B,100:E3342F&height=140§ion=footer" width="100%"/> | ||
| **Built for the Roblox developer community** | ||
| **[roboat.pro](https://roboat.pro)** ยท **[GitHub](https://github.com/Addi9000/roboat)** ยท **[Issues](https://github.com/Addi9000/roboat/issues)** | ||
| [](https://github.com/valeoncehadadream/roboat) | ||
| *Built with โค๏ธ for the Roblox developer community* | ||
| </div> |
+326
-213
| <div align="center"> | ||
| <img src="https://capsule-render.vercel.app/api?type=waving&color=0:6EE7F7,100:A78BFA&height=200§ion=header&text=roboat&fontSize=80&fontColor=ffffff&animation=fadeIn&fontAlignY=38&desc=The%20best%20Python%20wrapper%20for%20the%20Roblox%20API&descAlignY=60&descAlign=50" width="100%"/> | ||
| <img src="https://capsule-render.vercel.app/api?type=waving&color=0:E3342F,100:991B1B&height=220§ion=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%"/> | ||
| <br/> | ||
| [](https://python.org) | ||
| [](LICENSE) | ||
| [](https://github.com/valeoncehadadream/roboat) | ||
| [](https://github.com/valeoncehadadream/roboat) | ||
| <img src="https://readme-typing-svg.demolab.com?font=Fira+Code&weight=600&size=20&pause=800&color=E3342F¢er=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" /> | ||
| <br/> | ||
| <br/><br/> | ||
| <img src="https://readme-typing-svg.demolab.com?font=Fira+Code&size=22&pause=1000&color=6EE7F7¢er=true&vCenter=true&width=600&lines=OAuth+2.0+Authentication;Typed+Models+for+Every+Response;Async+%2B+Sync+Clients;Built-in+SQLite+Database;Open+Cloud+%2B+DataStores;Real-time+Event+System;Marketplace+%26+Economy+Tools;Interactive+Terminal+REPL" alt="Typing SVG" /> | ||
| [](https://python.org) | ||
| [](LICENSE) | ||
| [](https://github.com/Addi9000/roboat) | ||
| [](https://roboat.pro) | ||
| [](https://github.com/Addi9000/roboat/stargazers) | ||
| <br/><br/> | ||
| <br/> | ||
@@ -22,8 +23,12 @@ </div> | ||
| ## Install | ||
| <div align="center"> | ||
| ## โก Install | ||
| </div> | ||
| ```bash | ||
| pip install . | ||
| pip install ".[async]" # with aiohttp for async support | ||
| pip install ".[all]" # everything including tests | ||
| pip install roboat | ||
| pip install "roboat[async]" # async support via aiohttp | ||
| pip install "roboat[all]" # everything + test tools | ||
| ``` | ||
@@ -33,4 +38,8 @@ | ||
| ## Quick Start | ||
| <div align="center"> | ||
| ## ๐ Quick Start | ||
| </div> | ||
| ```python | ||
@@ -41,7 +50,15 @@ from roboat import RoboatClient | ||
| # User lookup | ||
| user = client.users.get_user(156) | ||
| print(user) # Builderman (@builderman) [ID: 156] | ||
| print(user) | ||
| # Builderman (@builderman) โ [ID: 156] | ||
| # Game stats | ||
| game = client.games.get_game(2753915549) | ||
| print(f"{game.name} โ {game.visits:,} visits") | ||
| print(f"{game.name} โ {game.visits:,} visits | {game.playing:,} playing") | ||
| # Catalog search | ||
| items = client.catalog.search(keyword="fedora", category="Accessories", sort_type="Sales") | ||
| for item in items: | ||
| print(f"{item.name} โ {item.price}R$") | ||
| ``` | ||
@@ -51,8 +68,11 @@ | ||
| ## Interactive Terminal | ||
| <div align="center"> | ||
| ## ๐ฅ๏ธ Interactive Terminal | ||
| </div> | ||
| ```bash | ||
| roboat | ||
| # or | ||
| python -m roboat | ||
| # or: python -m roboat | ||
| ``` | ||
@@ -67,3 +87,3 @@ | ||
| roboat v2.1.0 โ type 'help' to begin | ||
| roboat v2.1.0 โ roboat.pro โ type 'help' to begin | ||
@@ -74,10 +94,17 @@ ยป start 156 | ||
| ยป inventory 156 | ||
| ยป rap 156 | ||
| ยป likes 2753915549 | ||
| ยป friends 156 | ||
| ``` | ||
| Every command requires `start <userid>` first. The terminal will remind you if you forget. | ||
| --- | ||
| ## OAuth Authentication | ||
| <div align="center"> | ||
| ## ๐ OAuth Authentication | ||
| </div> | ||
| roboat uses **Roblox OAuth 2.0** โ no cookie extraction, no browser DevTools. | ||
| ```python | ||
@@ -87,58 +114,50 @@ from roboat import OAuthManager, RoboatClient | ||
| manager = OAuthManager( | ||
| on_success=lambda token: print("Authenticated!"), | ||
| on_failure=lambda err: print(f"Failed: {err}"), | ||
| on_success=lambda token: print("โ Authenticated!"), | ||
| on_failure=lambda err: print(f"โ Failed: {err}"), | ||
| timeout=120, | ||
| ) | ||
| token = manager.authenticate() # opens browser, waits 120s | ||
| token = manager.authenticate() # opens browser, 120s countdown | ||
| if token: | ||
| client = RoboatClient(oauth_token=token) | ||
| print(f"Logged in as {client.username()}") | ||
| ``` | ||
| In the terminal, type `auth`. Your browser opens automatically and you have **120 seconds** to complete login with a live countdown shown. | ||
| 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. | ||
| --- | ||
| ## All Modules | ||
| <div align="center"> | ||
| | Module | What It Does | | ||
| |:---|:---| | ||
| | `client.py` | Main sync client with caching and rate limiting | | ||
| | `async_client.py` | Full async mirror using aiohttp | | ||
| | `oauth.py` | OAuth 2.0 with 120s timeout | | ||
| | `session.py` | Interactive terminal REPL | | ||
| | `database.py` | Local SQLite persistence | | ||
| | `models.py` | 20+ typed dataclass response models | | ||
| | `exceptions.py` | Typed exceptions per HTTP status | | ||
| | `events.py` | Background polling event system | | ||
| | `analytics.py` | Parallel report aggregation | | ||
| | `marketplace.py` | Limited items, RAP tracking, profit estimator | | ||
| | `social.py` | Friend graph, mutual analysis, presence tools | | ||
| | `notifications.py` | Experience push notifications | | ||
| | `publish.py` | Asset upload (images, audio, models) | | ||
| | `moderation.py` | Reports, blocks, chat filter | | ||
| | `opencloud.py` | Open Cloud API key client | | ||
| | `develop.py` | Universe, DataStores, bans, Team Create | | ||
| | `users.py` | User lookup, search, bulk fetch | | ||
| | `games.py` | Games, visits, servers, votes | | ||
| | `catalog.py` | Avatar shop search and item details | | ||
| | `groups.py` | Full group management and payouts | | ||
| | `friends.py` | Friends, followers, followings | | ||
| | `thumbnails.py` | All thumbnail types as {id: url} | | ||
| | `badges.py` | Badges and award dates | | ||
| | `economy.py` | Transactions and resale data | | ||
| | `presence.py` | Online and in-game status | | ||
| | `avatar.py` | Avatar assets and outfits | | ||
| | `trades.py` | Trade list, send, accept, decline | | ||
| | `messages.py` | Private messages and chat | | ||
| | `inventory.py` | Ownership, RAP, collectibles | | ||
| | `utils/cache.py` | Thread-safe TTL LRU cache | | ||
| | `utils/ratelimit.py` | Token bucket and @retry decorator | | ||
| | `utils/paginator.py` | Lazy auto-pagination iterator | | ||
| ## ๐ฆ Repository Structure | ||
| </div> | ||
| ``` | ||
| roboat/ | ||
| โโโ roboat/ Source package (35 modules) | ||
| โ โโโ utils/ Cache, rate limiter, paginator | ||
| โ โโโ *.py All API modules | ||
| โโโ examples/ 8 ready-to-run example scripts | ||
| โโโ tests/ Unit tests โ no network required | ||
| โโโ benchmarks/ Performance benchmarks | ||
| โโโ docs/ Architecture, endpoints, models, FAQ | ||
| โโโ tools/ CLI utilities (bulk lookup, RAP snapshot, game monitor) | ||
| โโโ integrations/ Discord bot, Flask REST API | ||
| โโโ typestubs/ .pyi type stubs for IDE autocomplete | ||
| โโโ scripts/ Dev scripts (env check, stub generator) | ||
| โโโ .github/ CI/CD workflows, issue templates | ||
| ``` | ||
| --- | ||
| ## Client Setup | ||
| <div align="center"> | ||
| ## ๐ง Clients | ||
| </div> | ||
| ### Sync โ `RoboatClient` | ||
| ```python | ||
@@ -153,7 +172,6 @@ from roboat import RoboatClient, ClientBuilder | ||
| ClientBuilder() | ||
| .set_oauth_token("YOUR_TOKEN") | ||
| .set_oauth_token("TOKEN") | ||
| .set_timeout(15) | ||
| .set_cache_ttl(60) | ||
| .set_cache_size(512) | ||
| .set_rate_limit(10) | ||
| .set_cache_ttl(60) # cache responses for 60 seconds | ||
| .set_rate_limit(10) # max 10 requests/second | ||
| .set_proxy("http://proxy:8080") | ||
@@ -164,3 +182,3 @@ .build() | ||
| ## Async Client | ||
| ### Async โ `AsyncRoboatClient` | ||
@@ -173,3 +191,5 @@ ```python | ||
| async with AsyncRoboatClient() as client: | ||
| game, votes, icon = await asyncio.gather( | ||
| # Parallel fetch โ all at once | ||
| game, votes, icons = await asyncio.gather( | ||
| client.games.get_game(2753915549), | ||
@@ -179,3 +199,6 @@ client.games.get_votes([2753915549]), | ||
| ) | ||
| # Bulk fetch 500 users โ auto-chunked into batches of 100 | ||
| users = await client.users.get_users_by_ids(list(range(1, 501))) | ||
| print(f"Fetched {len(users)} users") | ||
@@ -185,33 +208,97 @@ asyncio.run(main()) | ||
| ### Open Cloud โ `RoboatCloudClient` | ||
| ```python | ||
| from roboat import RoboatCloudClient | ||
| cloud = RoboatCloudClient(api_key="roblox-KEY-xxxxx") | ||
| cloud.datastores.set(universe_id, "PlayerData", "player_1", {"coins": 500}) | ||
| ``` | ||
| --- | ||
| ## Users | ||
| <div align="center"> | ||
| ## ๐ก API Coverage | ||
| </div> | ||
| <div align="center"> | ||
| | API | Endpoint | Methods | | ||
| |:---|:---|:---:| | ||
| | Users | `users.roblox.com` | 7 | | ||
| | Games | `games.roblox.com` | 15 | | ||
| | Catalog | `catalog.roblox.com` | 8 | | ||
| | Groups | `groups.roblox.com` | 22 | | ||
| | Friends | `friends.roblox.com` | 11 | | ||
| | Thumbnails | `thumbnails.roblox.com` | 7 | | ||
| | Badges | `badges.roblox.com` | 4 | | ||
| | Economy | `economy.roblox.com` | 5 | | ||
| | Presence | `presence.roblox.com` | 3 | | ||
| | Avatar | `avatar.roblox.com` | 5 | | ||
| | Trades | `trades.roblox.com` | 7 | | ||
| | Messages | `privatemessages.roblox.com` | 7 | | ||
| | Inventory | `inventory.roblox.com` | 8 | | ||
| | Develop | `develop.roblox.com` | 14 | | ||
| | DataStores | `apis.roblox.com/datastores` | 7 | | ||
| | Ordered DS | `apis.roblox.com/ordered-data-stores` | 5 | | ||
| | Messaging | `apis.roblox.com/messaging-service` | 3 | | ||
| | Bans | `apis.roblox.com/cloud/v2` | 4 | | ||
| | Notifications | `apis.roblox.com/cloud/v2` | 3 | | ||
| | Asset Upload | `apis.roblox.com/assets` | 5 | | ||
| </div> | ||
| --- | ||
| <div align="center"> | ||
| ## ๐ค Users | ||
| </div> | ||
| ```python | ||
| user = client.users.get_user(156) | ||
| users = client.users.get_users_by_ids([1, 156, 261]) | ||
| users = client.users.get_users_by_ids([1, 156, 261]) # up to 100 at once | ||
| users = client.users.get_users_by_usernames(["Roblox", "builderman"]) | ||
| page = client.users.search_users("builderman", limit=10) | ||
| page = client.users.get_username_history(156) | ||
| ok = client.users.validate_username("mycoolname") | ||
| ``` | ||
| ## Games | ||
| --- | ||
| <div align="center"> | ||
| ## ๐ฎ Games | ||
| </div> | ||
| ```python | ||
| game = client.games.get_game(2753915549) | ||
| game = client.games.get_game_from_place(6872265039) | ||
| visits = client.games.get_visits([2753915549, 286090429]) | ||
| votes = client.games.get_votes([2753915549]) | ||
| page = client.games.get_servers(6872265039, limit=10) | ||
| page = client.games.get_user_games(156) | ||
| page = client.games.get_group_games(2868472) | ||
| page = client.games.search_games("obby", limit=20) | ||
| game = client.games.get_game(2753915549) | ||
| game = client.games.get_game_from_place(6872265039) | ||
| visits = client.games.get_visits([2753915549, 286090429]) # {id: count} | ||
| votes = client.games.get_votes([2753915549]) | ||
| servers = client.games.get_servers(6872265039, limit=10) | ||
| page = client.games.search_games("obby", limit=20) | ||
| page = client.games.get_user_games(156) | ||
| page = client.games.get_group_games(2868472) | ||
| count = client.games.get_favorite_count(2753915549) | ||
| ``` | ||
| ## Groups | ||
| --- | ||
| <div align="center"> | ||
| ## ๐ฅ Groups | ||
| </div> | ||
| ```python | ||
| group = client.groups.get_group(7) | ||
| roles = client.groups.get_roles(7) | ||
| role = client.groups.get_role_by_name(7, "Member") | ||
| members = client.groups.get_members(7, limit=100) | ||
| is_in = client.groups.is_member(7, user_id=156) | ||
@@ -221,20 +308,14 @@ # Management (auth required) | ||
| client.groups.kick_member(7, user_id=1234) | ||
| client.groups.post_shout(7, "Message here") | ||
| client.groups.post_to_wall(7, "Wall post") | ||
| client.groups.accept_all_join_requests(7) | ||
| client.groups.post_shout(7, "Welcome everyone!") | ||
| client.groups.accept_all_join_requests(7) # accepts ALL pending | ||
| client.groups.pay_out(7, user_id=1234, amount=500) | ||
| ``` | ||
| ## Catalog | ||
| --- | ||
| ```python | ||
| page = client.catalog.search(keyword="fedora", category="Accessories", sort_type="Sales") | ||
| item = client.catalog.get_asset(1028606) | ||
| item = client.catalog.get_bundle(192) | ||
| resale = client.catalog.get_resale_data(1028606) | ||
| ``` | ||
| <div align="center"> | ||
| --- | ||
| ## ๐ฐ Marketplace & Economy | ||
| ## Marketplace Tools | ||
| </div> | ||
@@ -246,10 +327,17 @@ ```python | ||
| # Full limited data โ RAP, trend, remaining supply | ||
| data = market.get_limited_data(1365767) | ||
| print(data.recent_average_price, data.price_trend) | ||
| print(f"{data.name} RAP: {data.recent_average_price:,}R$ Trend: {data.price_trend}") | ||
| # Profit estimator โ includes Roblox 30% fee | ||
| profit = market.estimate_resale_profit(1365767, purchase_price=12000) | ||
| deals = market.find_underpriced_limiteds([1365767, 1028606]) | ||
| print(f"Net profit: {profit.estimated_profit:,}R$ ROI: {profit.roi_percent}%") | ||
| # Find underpriced limiteds (below 85% of RAP) | ||
| deals = market.find_underpriced_limiteds([1365767, 1028606, 19027209]) | ||
| # RAP tracker โ snapshot and diff over time | ||
| tracker = market.create_rap_tracker([1365767, 1028606]) | ||
| tracker.snapshot() | ||
| # ... wait some time ... | ||
| tracker.snapshot() | ||
@@ -259,70 +347,32 @@ print(tracker.summary()) | ||
| ## Social Graph | ||
| --- | ||
| ```python | ||
| from roboat.social import SocialGraph | ||
| <div align="center"> | ||
| sg = SocialGraph(client) | ||
| ## ๐ค Social Graph | ||
| mutuals = sg.mutual_friends(156, 261) | ||
| is_following = sg.does_follow(156, 261) | ||
| snap = sg.presence_snapshot([156, 261, 1234]) | ||
| online_ids = sg.who_is_online([156, 261, 1234]) | ||
| nodes = sg.most_followed_in_group([156, 261, 1234]) | ||
| suggestions = sg.follow_suggestions(156, limit=10) | ||
| ``` | ||
| </div> | ||
| ## Notifications | ||
| ```python | ||
| from roboat.notifications import NotificationsAPI | ||
| from roboat.social import SocialGraph | ||
| notif = NotificationsAPI(api_key="roblox-KEY-xxxx") | ||
| sg = SocialGraph(client) | ||
| result = notif.send( | ||
| universe_id=123456789, | ||
| user_id=1234, | ||
| message_id="daily-reward", | ||
| attributes={"coins": "500"}, | ||
| join_experience={"launchData": "reward"}, | ||
| ) | ||
| results = notif.send_bulk(123456789, [1234, 5678], "daily-reward") | ||
| quota = notif.get_quota(123456789) | ||
| mutuals = sg.mutual_friends(156, 261) | ||
| is_following = sg.does_follow(follower_id=156, target_id=261) | ||
| snap = sg.presence_snapshot([156, 261, 1234]) | ||
| online_ids = sg.who_is_online([156, 261, 1234]) | ||
| nodes = sg.most_followed_in_group([156, 261, 1234]) # parallel fetch | ||
| suggestions = sg.follow_suggestions(156, limit=10) | ||
| ``` | ||
| ## Asset Publishing | ||
| --- | ||
| ```python | ||
| from roboat.publish import PublishAPI | ||
| <div align="center"> | ||
| pub = PublishAPI(api_key="roblox-KEY-xxxx", creator_id=156, creator_type="User") | ||
| ## ๐ง Open Cloud โ Developer Tools | ||
| asset = pub.upload_image("thumbnail.png", name="Game Icon") | ||
| asset = pub.upload_audio("bgm.ogg", name="Background Music") | ||
| asset = pub.upload_model("char.fbx", name="Character Model") | ||
| asset = pub.upload_auto("file.png", name="Auto Detected") | ||
| </div> | ||
| final = pub.wait_for_asset(asset.operation_id, max_wait=30) | ||
| ``` | ||
| ## Moderation | ||
| ```python | ||
| from roboat.moderation import ModerationAPI | ||
| mod = ModerationAPI(client) | ||
| standing = mod.get_account_standing(user_id=1234) | ||
| mod.report_user(1234, reason="Spam") | ||
| mod.report_asset(11111, reason="Inappropriate") | ||
| mod.block_user(1234) | ||
| result = mod.filter_text("Hello!", user_id=1234) | ||
| ``` | ||
| --- | ||
| ## Open Cloud โ Developer Tools | ||
| ```python | ||
| API_KEY = "roblox-KEY-xxxxx" | ||
@@ -332,6 +382,5 @@ UNIVERSE = 123456789 | ||
| # DataStores | ||
| client.develop.set_datastore_entry(UNIVERSE, "PlayerData", "player_1234", {"coins": 500}, API_KEY) | ||
| client.develop.get_datastore_entry(UNIVERSE, "PlayerData", "player_1234", API_KEY) | ||
| client.develop.set_datastore_entry(UNIVERSE, "PlayerData", "player_1", {"coins": 500}, API_KEY) | ||
| client.develop.get_datastore_entry(UNIVERSE, "PlayerData", "player_1", API_KEY) | ||
| client.develop.increment_datastore_entry(UNIVERSE, "Stats", "deaths", 1, API_KEY) | ||
| client.develop.delete_datastore_entry(UNIVERSE, "Sessions", "key", API_KEY) | ||
| client.develop.list_datastore_keys(UNIVERSE, "PlayerData", API_KEY) | ||
@@ -341,23 +390,12 @@ | ||
| client.develop.list_ordered_datastore(UNIVERSE, "Leaderboard", API_KEY, max_page_size=10) | ||
| client.develop.set_ordered_datastore_entry(UNIVERSE, "Leaderboard", "player_1234", 9500, API_KEY) | ||
| client.develop.increment_ordered_datastore(UNIVERSE, "Leaderboard", "player_1234", 100, API_KEY) | ||
| client.develop.set_ordered_datastore_entry(UNIVERSE, "Leaderboard", "player_1", 9500, API_KEY) | ||
| # MessagingService โ broadcast to all live servers | ||
| client.develop.publish_message(UNIVERSE, "Announcements", "Event in 5 minutes!", API_KEY) | ||
| client.develop.announce(UNIVERSE, API_KEY, "Server restart soon!") | ||
| # MessagingService โ reaches all live servers instantly | ||
| client.develop.announce(UNIVERSE, API_KEY, "Double XP starts now!") | ||
| client.develop.broadcast_shutdown(UNIVERSE, API_KEY) | ||
| # Bans | ||
| client.develop.ban_user(UNIVERSE, 1234, API_KEY, duration_seconds=86400, display_reason="Banned.") | ||
| client.develop.ban_user(UNIVERSE, 5678, API_KEY, duration_seconds=None) # permanent | ||
| client.develop.ban_user(UNIVERSE, 1234, API_KEY, duration_seconds=86400, | ||
| display_reason="Temporarily banned.") | ||
| client.develop.unban_user(UNIVERSE, 1234, API_KEY) | ||
| client.develop.list_bans(UNIVERSE, API_KEY) | ||
| # Team Create | ||
| client.develop.update_team_create(UNIVERSE, is_enabled=True) | ||
| client.develop.add_team_create_member(UNIVERSE, user_id=1234) | ||
| client.develop.get_team_create_members(UNIVERSE) | ||
| # Game Stats | ||
| stats = client.develop.get_game_stats(UNIVERSE, stat_type="Visits", granularity="Daily") | ||
| ``` | ||
@@ -367,4 +405,8 @@ | ||
| ## Database Layer | ||
| <div align="center"> | ||
| ## ๐๏ธ Database Layer | ||
| </div> | ||
| ```python | ||
@@ -378,6 +420,2 @@ from roboat import SessionDatabase | ||
| user_data = db.get_user(156) | ||
| all_users = db.get_all_users() | ||
| all_games = db.get_all_games() | ||
| db.set("tracked_ids", [156, 261, 1234]) | ||
@@ -388,3 +426,2 @@ val = db.get("tracked_ids") | ||
| # {'users': 10, 'games': 5, 'session_keys': 3, 'log_entries': 42} | ||
| db.close() | ||
@@ -395,4 +432,8 @@ ``` | ||
| ## Events | ||
| <div align="center"> | ||
| ## โก Events | ||
| </div> | ||
| ```python | ||
@@ -405,7 +446,7 @@ from roboat import EventPoller | ||
| def on_online(user): | ||
| print(f"{user.display_name} came online!") | ||
| print(f"๐ข {user.display_name} came online!") | ||
| @poller.on_new_friend | ||
| def on_friend(user): | ||
| print(f"New friend: {user.display_name}") | ||
| print(f"๐ค New friend: {user.display_name}") | ||
@@ -416,5 +457,5 @@ poller.track_game(2753915549, milestone_step=1_000_000) | ||
| def on_milestone(game, count): | ||
| print(f"{game.name} hit {count:,} visits!") | ||
| print(f"๐ {game.name} hit {count:,} visits!") | ||
| poller.start(interval=30) | ||
| poller.start(interval=30) # background thread | ||
| ``` | ||
@@ -424,16 +465,25 @@ | ||
| ## Pagination | ||
| <div align="center"> | ||
| ```python | ||
| from roboat.utils import Paginator | ||
| ## ๐ ๏ธ CLI Tools | ||
| for follower in Paginator( | ||
| lambda cursor: client.friends.get_followers(156, limit=100, cursor=cursor) | ||
| ): | ||
| print(follower) | ||
| </div> | ||
| top_500 = Paginator( | ||
| lambda c: client.friends.get_followers(156, limit=100, cursor=c), | ||
| max_items=500, | ||
| ).collect() | ||
| ```bash | ||
| # Bulk user lookup โ CSV or JSON | ||
| python tools/bulk_lookup.py --ids 1 156 261 --format csv | ||
| python tools/bulk_lookup.py --usernames Roblox builderman --format json | ||
| # RAP snapshot and diff | ||
| python tools/rap_snapshot.py --user 156 | ||
| python tools/rap_snapshot.py --user 156 --diff # compare to last run | ||
| # Live game monitor with milestone alerts | ||
| python tools/game_monitor.py --universe 2753915549 --interval 60 | ||
| # Environment health check | ||
| python scripts/check_env.py | ||
| # Generate .pyi type stubs | ||
| python scripts/generate_stub.py | ||
| ``` | ||
@@ -443,6 +493,26 @@ | ||
| ## Error Handling | ||
| <div align="center"> | ||
| ## ๐ Integrations | ||
| </div> | ||
| | Integration | File | Description | | ||
| |:---|:---|:---| | ||
| | Discord Bot | `integrations/discord_bot.py` | Slash commands: `/user`, `/game`, `/status` | | ||
| | Flask REST API | `integrations/flask_api.py` | REST endpoints for all major resources | | ||
| --- | ||
| <div align="center"> | ||
| ## ๐จ Error Handling | ||
| </div> | ||
| ```python | ||
| from roboat import UserNotFoundError, RateLimitedError, RoboatAPIError | ||
| from roboat import ( | ||
| UserNotFoundError, GameNotFoundError, | ||
| RateLimitedError, NotAuthenticatedError, RoboatAPIError, | ||
| ) | ||
| from roboat.utils import retry | ||
@@ -457,7 +527,9 @@ | ||
| except UserNotFoundError: | ||
| print("Not found") | ||
| print("User not found") | ||
| except RateLimitedError: | ||
| print("Rate limited") | ||
| except NotAuthenticatedError: | ||
| print("Need OAuth token") | ||
| except RoboatAPIError as e: | ||
| print(f"Error: {e}") | ||
| print(f"API error: {e}") | ||
| ``` | ||
@@ -467,13 +539,22 @@ | ||
| ## Analytics | ||
| <div align="center"> | ||
| ## ๐ Pagination | ||
| </div> | ||
| ```python | ||
| from roboat.analytics import Analytics | ||
| from roboat.utils import Paginator | ||
| an = Analytics(client) | ||
| # Lazily iterate ALL followers โ auto-fetches every page | ||
| for follower in Paginator( | ||
| lambda cursor: client.friends.get_followers(156, limit=100, cursor=cursor) | ||
| ): | ||
| print(follower) | ||
| print(an.user_report(156)) | ||
| print(an.compare_games([2753915549, 286090429])) | ||
| print(an.group_report(7)) | ||
| print(an.rich_leaderboard_str([2753915549, 286090429], by="visits")) | ||
| # Collect first 500 | ||
| top_500 = Paginator( | ||
| lambda c: client.friends.get_followers(156, limit=100, cursor=c), | ||
| max_items=500, | ||
| ).collect() | ||
| ``` | ||
@@ -483,16 +564,22 @@ | ||
| ## Terminal Commands | ||
| <div align="center"> | ||
| ## ๐ Terminal Commands | ||
| </div> | ||
| <div align="center"> | ||
| | Command | Description | | ||
| |:---|:---| | ||
| | `start <userid>` | Begin session (required first) | | ||
| | `auth` | OAuth login via browser | | ||
| | `auth` | OAuth login โ browser opens, 120s | | ||
| | `whoami` | Current session info | | ||
| | `newdb / loaddb / listdb` | Database management | | ||
| | `user <id>` | User profile | | ||
| | `game <id>` | Game stats | | ||
| | `friends / followers <id>` | Social info | | ||
| | `likes <id>` | Vote stats | | ||
| | `game <id>` | Game stats + votes | | ||
| | `friends / followers <id>` | Social counts | | ||
| | `likes <id>` | Vote breakdown | | ||
| | `search user/game <kw>` | Search | | ||
| | `presence / avatar <id>` | Status and avatar | | ||
| | `presence / avatar <id>` | Status + avatar | | ||
| | `servers <placeid>` | Active servers | | ||
@@ -502,6 +589,6 @@ | `badges <id>` | Game badges | | ||
| | `trades` | Trade list | | ||
| | `inventory / rap <id>` | Limiteds and RAP | | ||
| | `inventory / rap <id>` | Limiteds + RAP | | ||
| | `messages` | Private messages | | ||
| | `owns <uid> <assetid>` | Ownership check | | ||
| | `universe <id>` | Dev universe info | | ||
| | `universe <id>` | Developer universe info | | ||
| | `save user/game <id>` | Save to DB | | ||
@@ -512,8 +599,34 @@ | `cache [clear]` | Cache stats | | ||
| </div> | ||
| --- | ||
| ## License | ||
| <div align="center"> | ||
| MIT | ||
| ## โ๏ธ License | ||
| </div> | ||
| ``` | ||
| MIT License | ||
| Copyright (c) 2024 roboat contributors | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in | ||
| all copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| ``` | ||
| Full license: [LICENSE](LICENSE) | ||
| --- | ||
@@ -523,8 +636,8 @@ | ||
| <img src="https://capsule-render.vercel.app/api?type=waving&color=0:A78BFA,100:6EE7F7&height=120§ion=footer" width="100%"/> | ||
| <img src="https://capsule-render.vercel.app/api?type=waving&color=0:991B1B,100:E3342F&height=140§ion=footer" width="100%"/> | ||
| **Built for the Roblox developer community** | ||
| **[roboat.pro](https://roboat.pro)** ยท **[GitHub](https://github.com/Addi9000/roboat)** ยท **[Issues](https://github.com/Addi9000/roboat/issues)** | ||
| [](https://github.com/valeoncehadadream/roboat) | ||
| *Built with โค๏ธ for the Roblox developer community* | ||
| </div> |
+328
-215
| Metadata-Version: 2.4 | ||
| Name: roboat | ||
| Version: 1.1.0 | ||
| Version: 2.1.0 | ||
| Summary: The best Python wrapper for the Roblox API โ OAuth, async, typed models, datastores, events, marketplace tools | ||
| Home-page: https://github.com/Addi9000/roboat | ||
| Home-page: https://roboat.pro | ||
| Author: roboat contributors | ||
@@ -54,16 +54,17 @@ Author-email: bwesttwink@gmail.com | ||
| <img src="https://capsule-render.vercel.app/api?type=waving&color=0:6EE7F7,100:A78BFA&height=200§ion=header&text=roboat&fontSize=80&fontColor=ffffff&animation=fadeIn&fontAlignY=38&desc=The%20best%20Python%20wrapper%20for%20the%20Roblox%20API&descAlignY=60&descAlign=50" width="100%"/> | ||
| <img src="https://capsule-render.vercel.app/api?type=waving&color=0:E3342F,100:991B1B&height=220§ion=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%"/> | ||
| <br/> | ||
| [](https://python.org) | ||
| [](LICENSE) | ||
| [](https://github.com/valeoncehadadream/roboat) | ||
| [](https://github.com/valeoncehadadream/roboat) | ||
| <img src="https://readme-typing-svg.demolab.com?font=Fira+Code&weight=600&size=20&pause=800&color=E3342F¢er=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" /> | ||
| <br/> | ||
| <br/><br/> | ||
| <img src="https://readme-typing-svg.demolab.com?font=Fira+Code&size=22&pause=1000&color=6EE7F7¢er=true&vCenter=true&width=600&lines=OAuth+2.0+Authentication;Typed+Models+for+Every+Response;Async+%2B+Sync+Clients;Built-in+SQLite+Database;Open+Cloud+%2B+DataStores;Real-time+Event+System;Marketplace+%26+Economy+Tools;Interactive+Terminal+REPL" alt="Typing SVG" /> | ||
| [](https://python.org) | ||
| [](LICENSE) | ||
| [](https://github.com/Addi9000/roboat) | ||
| [](https://roboat.pro) | ||
| [](https://github.com/Addi9000/roboat/stargazers) | ||
| <br/><br/> | ||
| <br/> | ||
@@ -74,8 +75,12 @@ </div> | ||
| ## Install | ||
| <div align="center"> | ||
| ## โก Install | ||
| </div> | ||
| ```bash | ||
| pip install . | ||
| pip install ".[async]" # with aiohttp for async support | ||
| pip install ".[all]" # everything including tests | ||
| pip install roboat | ||
| pip install "roboat[async]" # async support via aiohttp | ||
| pip install "roboat[all]" # everything + test tools | ||
| ``` | ||
@@ -85,4 +90,8 @@ | ||
| ## Quick Start | ||
| <div align="center"> | ||
| ## ๐ Quick Start | ||
| </div> | ||
| ```python | ||
@@ -93,7 +102,15 @@ from roboat import RoboatClient | ||
| # User lookup | ||
| user = client.users.get_user(156) | ||
| print(user) # Builderman (@builderman) [ID: 156] | ||
| print(user) | ||
| # Builderman (@builderman) โ [ID: 156] | ||
| # Game stats | ||
| game = client.games.get_game(2753915549) | ||
| print(f"{game.name} โ {game.visits:,} visits") | ||
| print(f"{game.name} โ {game.visits:,} visits | {game.playing:,} playing") | ||
| # Catalog search | ||
| items = client.catalog.search(keyword="fedora", category="Accessories", sort_type="Sales") | ||
| for item in items: | ||
| print(f"{item.name} โ {item.price}R$") | ||
| ``` | ||
@@ -103,8 +120,11 @@ | ||
| ## Interactive Terminal | ||
| <div align="center"> | ||
| ## ๐ฅ๏ธ Interactive Terminal | ||
| </div> | ||
| ```bash | ||
| roboat | ||
| # or | ||
| python -m roboat | ||
| # or: python -m roboat | ||
| ``` | ||
@@ -119,3 +139,3 @@ | ||
| roboat v2.1.0 โ type 'help' to begin | ||
| roboat v2.1.0 โ roboat.pro โ type 'help' to begin | ||
@@ -126,10 +146,17 @@ ยป start 156 | ||
| ยป inventory 156 | ||
| ยป rap 156 | ||
| ยป likes 2753915549 | ||
| ยป friends 156 | ||
| ``` | ||
| Every command requires `start <userid>` first. The terminal will remind you if you forget. | ||
| --- | ||
| ## OAuth Authentication | ||
| <div align="center"> | ||
| ## ๐ OAuth Authentication | ||
| </div> | ||
| roboat uses **Roblox OAuth 2.0** โ no cookie extraction, no browser DevTools. | ||
| ```python | ||
@@ -139,58 +166,50 @@ from roboat import OAuthManager, RoboatClient | ||
| manager = OAuthManager( | ||
| on_success=lambda token: print("Authenticated!"), | ||
| on_failure=lambda err: print(f"Failed: {err}"), | ||
| on_success=lambda token: print("โ Authenticated!"), | ||
| on_failure=lambda err: print(f"โ Failed: {err}"), | ||
| timeout=120, | ||
| ) | ||
| token = manager.authenticate() # opens browser, waits 120s | ||
| token = manager.authenticate() # opens browser, 120s countdown | ||
| if token: | ||
| client = RoboatClient(oauth_token=token) | ||
| print(f"Logged in as {client.username()}") | ||
| ``` | ||
| In the terminal, type `auth`. Your browser opens automatically and you have **120 seconds** to complete login with a live countdown shown. | ||
| 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. | ||
| --- | ||
| ## All Modules | ||
| <div align="center"> | ||
| | Module | What It Does | | ||
| |:---|:---| | ||
| | `client.py` | Main sync client with caching and rate limiting | | ||
| | `async_client.py` | Full async mirror using aiohttp | | ||
| | `oauth.py` | OAuth 2.0 with 120s timeout | | ||
| | `session.py` | Interactive terminal REPL | | ||
| | `database.py` | Local SQLite persistence | | ||
| | `models.py` | 20+ typed dataclass response models | | ||
| | `exceptions.py` | Typed exceptions per HTTP status | | ||
| | `events.py` | Background polling event system | | ||
| | `analytics.py` | Parallel report aggregation | | ||
| | `marketplace.py` | Limited items, RAP tracking, profit estimator | | ||
| | `social.py` | Friend graph, mutual analysis, presence tools | | ||
| | `notifications.py` | Experience push notifications | | ||
| | `publish.py` | Asset upload (images, audio, models) | | ||
| | `moderation.py` | Reports, blocks, chat filter | | ||
| | `opencloud.py` | Open Cloud API key client | | ||
| | `develop.py` | Universe, DataStores, bans, Team Create | | ||
| | `users.py` | User lookup, search, bulk fetch | | ||
| | `games.py` | Games, visits, servers, votes | | ||
| | `catalog.py` | Avatar shop search and item details | | ||
| | `groups.py` | Full group management and payouts | | ||
| | `friends.py` | Friends, followers, followings | | ||
| | `thumbnails.py` | All thumbnail types as {id: url} | | ||
| | `badges.py` | Badges and award dates | | ||
| | `economy.py` | Transactions and resale data | | ||
| | `presence.py` | Online and in-game status | | ||
| | `avatar.py` | Avatar assets and outfits | | ||
| | `trades.py` | Trade list, send, accept, decline | | ||
| | `messages.py` | Private messages and chat | | ||
| | `inventory.py` | Ownership, RAP, collectibles | | ||
| | `utils/cache.py` | Thread-safe TTL LRU cache | | ||
| | `utils/ratelimit.py` | Token bucket and @retry decorator | | ||
| | `utils/paginator.py` | Lazy auto-pagination iterator | | ||
| ## ๐ฆ Repository Structure | ||
| </div> | ||
| ``` | ||
| roboat/ | ||
| โโโ roboat/ Source package (35 modules) | ||
| โ โโโ utils/ Cache, rate limiter, paginator | ||
| โ โโโ *.py All API modules | ||
| โโโ examples/ 8 ready-to-run example scripts | ||
| โโโ tests/ Unit tests โ no network required | ||
| โโโ benchmarks/ Performance benchmarks | ||
| โโโ docs/ Architecture, endpoints, models, FAQ | ||
| โโโ tools/ CLI utilities (bulk lookup, RAP snapshot, game monitor) | ||
| โโโ integrations/ Discord bot, Flask REST API | ||
| โโโ typestubs/ .pyi type stubs for IDE autocomplete | ||
| โโโ scripts/ Dev scripts (env check, stub generator) | ||
| โโโ .github/ CI/CD workflows, issue templates | ||
| ``` | ||
| --- | ||
| ## Client Setup | ||
| <div align="center"> | ||
| ## ๐ง Clients | ||
| </div> | ||
| ### Sync โ `RoboatClient` | ||
| ```python | ||
@@ -205,7 +224,6 @@ from roboat import RoboatClient, ClientBuilder | ||
| ClientBuilder() | ||
| .set_oauth_token("YOUR_TOKEN") | ||
| .set_oauth_token("TOKEN") | ||
| .set_timeout(15) | ||
| .set_cache_ttl(60) | ||
| .set_cache_size(512) | ||
| .set_rate_limit(10) | ||
| .set_cache_ttl(60) # cache responses for 60 seconds | ||
| .set_rate_limit(10) # max 10 requests/second | ||
| .set_proxy("http://proxy:8080") | ||
@@ -216,3 +234,3 @@ .build() | ||
| ## Async Client | ||
| ### Async โ `AsyncRoboatClient` | ||
@@ -225,3 +243,5 @@ ```python | ||
| async with AsyncRoboatClient() as client: | ||
| game, votes, icon = await asyncio.gather( | ||
| # Parallel fetch โ all at once | ||
| game, votes, icons = await asyncio.gather( | ||
| client.games.get_game(2753915549), | ||
@@ -231,3 +251,6 @@ client.games.get_votes([2753915549]), | ||
| ) | ||
| # Bulk fetch 500 users โ auto-chunked into batches of 100 | ||
| users = await client.users.get_users_by_ids(list(range(1, 501))) | ||
| print(f"Fetched {len(users)} users") | ||
@@ -237,33 +260,97 @@ asyncio.run(main()) | ||
| ### Open Cloud โ `RoboatCloudClient` | ||
| ```python | ||
| from roboat import RoboatCloudClient | ||
| cloud = RoboatCloudClient(api_key="roblox-KEY-xxxxx") | ||
| cloud.datastores.set(universe_id, "PlayerData", "player_1", {"coins": 500}) | ||
| ``` | ||
| --- | ||
| ## Users | ||
| <div align="center"> | ||
| ## ๐ก API Coverage | ||
| </div> | ||
| <div align="center"> | ||
| | API | Endpoint | Methods | | ||
| |:---|:---|:---:| | ||
| | Users | `users.roblox.com` | 7 | | ||
| | Games | `games.roblox.com` | 15 | | ||
| | Catalog | `catalog.roblox.com` | 8 | | ||
| | Groups | `groups.roblox.com` | 22 | | ||
| | Friends | `friends.roblox.com` | 11 | | ||
| | Thumbnails | `thumbnails.roblox.com` | 7 | | ||
| | Badges | `badges.roblox.com` | 4 | | ||
| | Economy | `economy.roblox.com` | 5 | | ||
| | Presence | `presence.roblox.com` | 3 | | ||
| | Avatar | `avatar.roblox.com` | 5 | | ||
| | Trades | `trades.roblox.com` | 7 | | ||
| | Messages | `privatemessages.roblox.com` | 7 | | ||
| | Inventory | `inventory.roblox.com` | 8 | | ||
| | Develop | `develop.roblox.com` | 14 | | ||
| | DataStores | `apis.roblox.com/datastores` | 7 | | ||
| | Ordered DS | `apis.roblox.com/ordered-data-stores` | 5 | | ||
| | Messaging | `apis.roblox.com/messaging-service` | 3 | | ||
| | Bans | `apis.roblox.com/cloud/v2` | 4 | | ||
| | Notifications | `apis.roblox.com/cloud/v2` | 3 | | ||
| | Asset Upload | `apis.roblox.com/assets` | 5 | | ||
| </div> | ||
| --- | ||
| <div align="center"> | ||
| ## ๐ค Users | ||
| </div> | ||
| ```python | ||
| user = client.users.get_user(156) | ||
| users = client.users.get_users_by_ids([1, 156, 261]) | ||
| users = client.users.get_users_by_ids([1, 156, 261]) # up to 100 at once | ||
| users = client.users.get_users_by_usernames(["Roblox", "builderman"]) | ||
| page = client.users.search_users("builderman", limit=10) | ||
| page = client.users.get_username_history(156) | ||
| ok = client.users.validate_username("mycoolname") | ||
| ``` | ||
| ## Games | ||
| --- | ||
| <div align="center"> | ||
| ## ๐ฎ Games | ||
| </div> | ||
| ```python | ||
| game = client.games.get_game(2753915549) | ||
| game = client.games.get_game_from_place(6872265039) | ||
| visits = client.games.get_visits([2753915549, 286090429]) | ||
| votes = client.games.get_votes([2753915549]) | ||
| page = client.games.get_servers(6872265039, limit=10) | ||
| page = client.games.get_user_games(156) | ||
| page = client.games.get_group_games(2868472) | ||
| page = client.games.search_games("obby", limit=20) | ||
| game = client.games.get_game(2753915549) | ||
| game = client.games.get_game_from_place(6872265039) | ||
| visits = client.games.get_visits([2753915549, 286090429]) # {id: count} | ||
| votes = client.games.get_votes([2753915549]) | ||
| servers = client.games.get_servers(6872265039, limit=10) | ||
| page = client.games.search_games("obby", limit=20) | ||
| page = client.games.get_user_games(156) | ||
| page = client.games.get_group_games(2868472) | ||
| count = client.games.get_favorite_count(2753915549) | ||
| ``` | ||
| ## Groups | ||
| --- | ||
| <div align="center"> | ||
| ## ๐ฅ Groups | ||
| </div> | ||
| ```python | ||
| group = client.groups.get_group(7) | ||
| roles = client.groups.get_roles(7) | ||
| role = client.groups.get_role_by_name(7, "Member") | ||
| members = client.groups.get_members(7, limit=100) | ||
| is_in = client.groups.is_member(7, user_id=156) | ||
@@ -273,20 +360,14 @@ # Management (auth required) | ||
| client.groups.kick_member(7, user_id=1234) | ||
| client.groups.post_shout(7, "Message here") | ||
| client.groups.post_to_wall(7, "Wall post") | ||
| client.groups.accept_all_join_requests(7) | ||
| client.groups.post_shout(7, "Welcome everyone!") | ||
| client.groups.accept_all_join_requests(7) # accepts ALL pending | ||
| client.groups.pay_out(7, user_id=1234, amount=500) | ||
| ``` | ||
| ## Catalog | ||
| --- | ||
| ```python | ||
| page = client.catalog.search(keyword="fedora", category="Accessories", sort_type="Sales") | ||
| item = client.catalog.get_asset(1028606) | ||
| item = client.catalog.get_bundle(192) | ||
| resale = client.catalog.get_resale_data(1028606) | ||
| ``` | ||
| <div align="center"> | ||
| --- | ||
| ## ๐ฐ Marketplace & Economy | ||
| ## Marketplace Tools | ||
| </div> | ||
@@ -298,10 +379,17 @@ ```python | ||
| # Full limited data โ RAP, trend, remaining supply | ||
| data = market.get_limited_data(1365767) | ||
| print(data.recent_average_price, data.price_trend) | ||
| print(f"{data.name} RAP: {data.recent_average_price:,}R$ Trend: {data.price_trend}") | ||
| # Profit estimator โ includes Roblox 30% fee | ||
| profit = market.estimate_resale_profit(1365767, purchase_price=12000) | ||
| deals = market.find_underpriced_limiteds([1365767, 1028606]) | ||
| print(f"Net profit: {profit.estimated_profit:,}R$ ROI: {profit.roi_percent}%") | ||
| # Find underpriced limiteds (below 85% of RAP) | ||
| deals = market.find_underpriced_limiteds([1365767, 1028606, 19027209]) | ||
| # RAP tracker โ snapshot and diff over time | ||
| tracker = market.create_rap_tracker([1365767, 1028606]) | ||
| tracker.snapshot() | ||
| # ... wait some time ... | ||
| tracker.snapshot() | ||
@@ -311,70 +399,32 @@ print(tracker.summary()) | ||
| ## Social Graph | ||
| --- | ||
| ```python | ||
| from roboat.social import SocialGraph | ||
| <div align="center"> | ||
| sg = SocialGraph(client) | ||
| ## ๐ค Social Graph | ||
| mutuals = sg.mutual_friends(156, 261) | ||
| is_following = sg.does_follow(156, 261) | ||
| snap = sg.presence_snapshot([156, 261, 1234]) | ||
| online_ids = sg.who_is_online([156, 261, 1234]) | ||
| nodes = sg.most_followed_in_group([156, 261, 1234]) | ||
| suggestions = sg.follow_suggestions(156, limit=10) | ||
| ``` | ||
| </div> | ||
| ## Notifications | ||
| ```python | ||
| from roboat.notifications import NotificationsAPI | ||
| from roboat.social import SocialGraph | ||
| notif = NotificationsAPI(api_key="roblox-KEY-xxxx") | ||
| sg = SocialGraph(client) | ||
| result = notif.send( | ||
| universe_id=123456789, | ||
| user_id=1234, | ||
| message_id="daily-reward", | ||
| attributes={"coins": "500"}, | ||
| join_experience={"launchData": "reward"}, | ||
| ) | ||
| results = notif.send_bulk(123456789, [1234, 5678], "daily-reward") | ||
| quota = notif.get_quota(123456789) | ||
| mutuals = sg.mutual_friends(156, 261) | ||
| is_following = sg.does_follow(follower_id=156, target_id=261) | ||
| snap = sg.presence_snapshot([156, 261, 1234]) | ||
| online_ids = sg.who_is_online([156, 261, 1234]) | ||
| nodes = sg.most_followed_in_group([156, 261, 1234]) # parallel fetch | ||
| suggestions = sg.follow_suggestions(156, limit=10) | ||
| ``` | ||
| ## Asset Publishing | ||
| --- | ||
| ```python | ||
| from roboat.publish import PublishAPI | ||
| <div align="center"> | ||
| pub = PublishAPI(api_key="roblox-KEY-xxxx", creator_id=156, creator_type="User") | ||
| ## ๐ง Open Cloud โ Developer Tools | ||
| asset = pub.upload_image("thumbnail.png", name="Game Icon") | ||
| asset = pub.upload_audio("bgm.ogg", name="Background Music") | ||
| asset = pub.upload_model("char.fbx", name="Character Model") | ||
| asset = pub.upload_auto("file.png", name="Auto Detected") | ||
| </div> | ||
| final = pub.wait_for_asset(asset.operation_id, max_wait=30) | ||
| ``` | ||
| ## Moderation | ||
| ```python | ||
| from roboat.moderation import ModerationAPI | ||
| mod = ModerationAPI(client) | ||
| standing = mod.get_account_standing(user_id=1234) | ||
| mod.report_user(1234, reason="Spam") | ||
| mod.report_asset(11111, reason="Inappropriate") | ||
| mod.block_user(1234) | ||
| result = mod.filter_text("Hello!", user_id=1234) | ||
| ``` | ||
| --- | ||
| ## Open Cloud โ Developer Tools | ||
| ```python | ||
| API_KEY = "roblox-KEY-xxxxx" | ||
@@ -384,6 +434,5 @@ UNIVERSE = 123456789 | ||
| # DataStores | ||
| client.develop.set_datastore_entry(UNIVERSE, "PlayerData", "player_1234", {"coins": 500}, API_KEY) | ||
| client.develop.get_datastore_entry(UNIVERSE, "PlayerData", "player_1234", API_KEY) | ||
| client.develop.set_datastore_entry(UNIVERSE, "PlayerData", "player_1", {"coins": 500}, API_KEY) | ||
| client.develop.get_datastore_entry(UNIVERSE, "PlayerData", "player_1", API_KEY) | ||
| client.develop.increment_datastore_entry(UNIVERSE, "Stats", "deaths", 1, API_KEY) | ||
| client.develop.delete_datastore_entry(UNIVERSE, "Sessions", "key", API_KEY) | ||
| client.develop.list_datastore_keys(UNIVERSE, "PlayerData", API_KEY) | ||
@@ -393,23 +442,12 @@ | ||
| client.develop.list_ordered_datastore(UNIVERSE, "Leaderboard", API_KEY, max_page_size=10) | ||
| client.develop.set_ordered_datastore_entry(UNIVERSE, "Leaderboard", "player_1234", 9500, API_KEY) | ||
| client.develop.increment_ordered_datastore(UNIVERSE, "Leaderboard", "player_1234", 100, API_KEY) | ||
| client.develop.set_ordered_datastore_entry(UNIVERSE, "Leaderboard", "player_1", 9500, API_KEY) | ||
| # MessagingService โ broadcast to all live servers | ||
| client.develop.publish_message(UNIVERSE, "Announcements", "Event in 5 minutes!", API_KEY) | ||
| client.develop.announce(UNIVERSE, API_KEY, "Server restart soon!") | ||
| # MessagingService โ reaches all live servers instantly | ||
| client.develop.announce(UNIVERSE, API_KEY, "Double XP starts now!") | ||
| client.develop.broadcast_shutdown(UNIVERSE, API_KEY) | ||
| # Bans | ||
| client.develop.ban_user(UNIVERSE, 1234, API_KEY, duration_seconds=86400, display_reason="Banned.") | ||
| client.develop.ban_user(UNIVERSE, 5678, API_KEY, duration_seconds=None) # permanent | ||
| client.develop.ban_user(UNIVERSE, 1234, API_KEY, duration_seconds=86400, | ||
| display_reason="Temporarily banned.") | ||
| client.develop.unban_user(UNIVERSE, 1234, API_KEY) | ||
| client.develop.list_bans(UNIVERSE, API_KEY) | ||
| # Team Create | ||
| client.develop.update_team_create(UNIVERSE, is_enabled=True) | ||
| client.develop.add_team_create_member(UNIVERSE, user_id=1234) | ||
| client.develop.get_team_create_members(UNIVERSE) | ||
| # Game Stats | ||
| stats = client.develop.get_game_stats(UNIVERSE, stat_type="Visits", granularity="Daily") | ||
| ``` | ||
@@ -419,4 +457,8 @@ | ||
| ## Database Layer | ||
| <div align="center"> | ||
| ## ๐๏ธ Database Layer | ||
| </div> | ||
| ```python | ||
@@ -430,6 +472,2 @@ from roboat import SessionDatabase | ||
| user_data = db.get_user(156) | ||
| all_users = db.get_all_users() | ||
| all_games = db.get_all_games() | ||
| db.set("tracked_ids", [156, 261, 1234]) | ||
@@ -440,3 +478,2 @@ val = db.get("tracked_ids") | ||
| # {'users': 10, 'games': 5, 'session_keys': 3, 'log_entries': 42} | ||
| db.close() | ||
@@ -447,4 +484,8 @@ ``` | ||
| ## Events | ||
| <div align="center"> | ||
| ## โก Events | ||
| </div> | ||
| ```python | ||
@@ -457,7 +498,7 @@ from roboat import EventPoller | ||
| def on_online(user): | ||
| print(f"{user.display_name} came online!") | ||
| print(f"๐ข {user.display_name} came online!") | ||
| @poller.on_new_friend | ||
| def on_friend(user): | ||
| print(f"New friend: {user.display_name}") | ||
| print(f"๐ค New friend: {user.display_name}") | ||
@@ -468,5 +509,5 @@ poller.track_game(2753915549, milestone_step=1_000_000) | ||
| def on_milestone(game, count): | ||
| print(f"{game.name} hit {count:,} visits!") | ||
| print(f"๐ {game.name} hit {count:,} visits!") | ||
| poller.start(interval=30) | ||
| poller.start(interval=30) # background thread | ||
| ``` | ||
@@ -476,16 +517,25 @@ | ||
| ## Pagination | ||
| <div align="center"> | ||
| ```python | ||
| from roboat.utils import Paginator | ||
| ## ๐ ๏ธ CLI Tools | ||
| for follower in Paginator( | ||
| lambda cursor: client.friends.get_followers(156, limit=100, cursor=cursor) | ||
| ): | ||
| print(follower) | ||
| </div> | ||
| top_500 = Paginator( | ||
| lambda c: client.friends.get_followers(156, limit=100, cursor=c), | ||
| max_items=500, | ||
| ).collect() | ||
| ```bash | ||
| # Bulk user lookup โ CSV or JSON | ||
| python tools/bulk_lookup.py --ids 1 156 261 --format csv | ||
| python tools/bulk_lookup.py --usernames Roblox builderman --format json | ||
| # RAP snapshot and diff | ||
| python tools/rap_snapshot.py --user 156 | ||
| python tools/rap_snapshot.py --user 156 --diff # compare to last run | ||
| # Live game monitor with milestone alerts | ||
| python tools/game_monitor.py --universe 2753915549 --interval 60 | ||
| # Environment health check | ||
| python scripts/check_env.py | ||
| # Generate .pyi type stubs | ||
| python scripts/generate_stub.py | ||
| ``` | ||
@@ -495,6 +545,26 @@ | ||
| ## Error Handling | ||
| <div align="center"> | ||
| ## ๐ Integrations | ||
| </div> | ||
| | Integration | File | Description | | ||
| |:---|:---|:---| | ||
| | Discord Bot | `integrations/discord_bot.py` | Slash commands: `/user`, `/game`, `/status` | | ||
| | Flask REST API | `integrations/flask_api.py` | REST endpoints for all major resources | | ||
| --- | ||
| <div align="center"> | ||
| ## ๐จ Error Handling | ||
| </div> | ||
| ```python | ||
| from roboat import UserNotFoundError, RateLimitedError, RoboatAPIError | ||
| from roboat import ( | ||
| UserNotFoundError, GameNotFoundError, | ||
| RateLimitedError, NotAuthenticatedError, RoboatAPIError, | ||
| ) | ||
| from roboat.utils import retry | ||
@@ -509,7 +579,9 @@ | ||
| except UserNotFoundError: | ||
| print("Not found") | ||
| print("User not found") | ||
| except RateLimitedError: | ||
| print("Rate limited") | ||
| except NotAuthenticatedError: | ||
| print("Need OAuth token") | ||
| except RoboatAPIError as e: | ||
| print(f"Error: {e}") | ||
| print(f"API error: {e}") | ||
| ``` | ||
@@ -519,13 +591,22 @@ | ||
| ## Analytics | ||
| <div align="center"> | ||
| ## ๐ Pagination | ||
| </div> | ||
| ```python | ||
| from roboat.analytics import Analytics | ||
| from roboat.utils import Paginator | ||
| an = Analytics(client) | ||
| # Lazily iterate ALL followers โ auto-fetches every page | ||
| for follower in Paginator( | ||
| lambda cursor: client.friends.get_followers(156, limit=100, cursor=cursor) | ||
| ): | ||
| print(follower) | ||
| print(an.user_report(156)) | ||
| print(an.compare_games([2753915549, 286090429])) | ||
| print(an.group_report(7)) | ||
| print(an.rich_leaderboard_str([2753915549, 286090429], by="visits")) | ||
| # Collect first 500 | ||
| top_500 = Paginator( | ||
| lambda c: client.friends.get_followers(156, limit=100, cursor=c), | ||
| max_items=500, | ||
| ).collect() | ||
| ``` | ||
@@ -535,16 +616,22 @@ | ||
| ## Terminal Commands | ||
| <div align="center"> | ||
| ## ๐ Terminal Commands | ||
| </div> | ||
| <div align="center"> | ||
| | Command | Description | | ||
| |:---|:---| | ||
| | `start <userid>` | Begin session (required first) | | ||
| | `auth` | OAuth login via browser | | ||
| | `auth` | OAuth login โ browser opens, 120s | | ||
| | `whoami` | Current session info | | ||
| | `newdb / loaddb / listdb` | Database management | | ||
| | `user <id>` | User profile | | ||
| | `game <id>` | Game stats | | ||
| | `friends / followers <id>` | Social info | | ||
| | `likes <id>` | Vote stats | | ||
| | `game <id>` | Game stats + votes | | ||
| | `friends / followers <id>` | Social counts | | ||
| | `likes <id>` | Vote breakdown | | ||
| | `search user/game <kw>` | Search | | ||
| | `presence / avatar <id>` | Status and avatar | | ||
| | `presence / avatar <id>` | Status + avatar | | ||
| | `servers <placeid>` | Active servers | | ||
@@ -554,6 +641,6 @@ | `badges <id>` | Game badges | | ||
| | `trades` | Trade list | | ||
| | `inventory / rap <id>` | Limiteds and RAP | | ||
| | `inventory / rap <id>` | Limiteds + RAP | | ||
| | `messages` | Private messages | | ||
| | `owns <uid> <assetid>` | Ownership check | | ||
| | `universe <id>` | Dev universe info | | ||
| | `universe <id>` | Developer universe info | | ||
| | `save user/game <id>` | Save to DB | | ||
@@ -564,8 +651,34 @@ | `cache [clear]` | Cache stats | | ||
| </div> | ||
| --- | ||
| ## License | ||
| <div align="center"> | ||
| MIT | ||
| ## โ๏ธ License | ||
| </div> | ||
| ``` | ||
| MIT License | ||
| Copyright (c) 2024 roboat contributors | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in | ||
| all copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| ``` | ||
| Full license: [LICENSE](LICENSE) | ||
| --- | ||
@@ -575,8 +688,8 @@ | ||
| <img src="https://capsule-render.vercel.app/api?type=waving&color=0:A78BFA,100:6EE7F7&height=120§ion=footer" width="100%"/> | ||
| <img src="https://capsule-render.vercel.app/api?type=waving&color=0:991B1B,100:E3342F&height=140§ion=footer" width="100%"/> | ||
| **Built for the Roblox developer community** | ||
| **[roboat.pro](https://roboat.pro)** ยท **[GitHub](https://github.com/Addi9000/roboat)** ยท **[Issues](https://github.com/Addi9000/roboat/issues)** | ||
| [](https://github.com/valeoncehadadream/roboat) | ||
| *Built with โค๏ธ for the Roblox developer community* | ||
| </div> |
+2
-2
@@ -8,3 +8,3 @@ from setuptools import setup, find_packages | ||
| name="roboat", | ||
| version="1.1.0", | ||
| version="2.1.0", | ||
| author="roboat contributors", | ||
@@ -18,3 +18,3 @@ author_email="bwesttwink@gmail.com", | ||
| long_description_content_type="text/markdown", | ||
| url="https://github.com/Addi9000/roboat", | ||
| url="https://roboat.pro", | ||
| project_urls={ | ||
@@ -21,0 +21,0 @@ "Bug Tracker": "https://github.com/Addi9000/roboat/issues", |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
328367
1.97%