Socket
Book a DemoSign in
Socket

roboat

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

roboat - pypi Package Compare versions

Comparing version
1.1.0
to
2.1.0
+328
-215
PKG-INFO
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&section=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&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%"/>
<br/>
[![Python](https://img.shields.io/badge/Python-3.8%2B-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://python.org)
[![License](https://img.shields.io/badge/License-MIT-A78BFA?style=for-the-badge)](LICENSE)
[![Version](https://img.shields.io/badge/Version-2.1.0-6EE7F7?style=for-the-badge)](https://github.com/valeoncehadadream/roboat)
[![Status](https://img.shields.io/badge/Status-Production%20Ready-22c55e?style=for-the-badge)](https://github.com/valeoncehadadream/roboat)
<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" />
<br/>
<br/><br/>
<img src="https://readme-typing-svg.demolab.com?font=Fira+Code&size=22&pause=1000&color=6EE7F7&center=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" />
[![Python](https://img.shields.io/badge/Python-3.8%2B-E3342F?style=for-the-badge&logo=python&logoColor=white)](https://python.org)
[![License](https://img.shields.io/badge/License-MIT-991B1B?style=for-the-badge)](LICENSE)
[![Version](https://img.shields.io/badge/Version-2.1.0-E3342F?style=for-the-badge)](https://github.com/Addi9000/roboat)
[![Website](https://img.shields.io/badge/Website-roboat.pro-991B1B?style=for-the-badge&logo=google-chrome&logoColor=white)](https://roboat.pro)
[![Stars](https://img.shields.io/github/stars/Addi9000/roboat?style=for-the-badge&color=E3342F&logo=github)](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&section=footer" width="100%"/>
<img src="https://capsule-render.vercel.app/api?type=waving&color=0:991B1B,100:E3342F&height=140&section=footer" width="100%"/>
**Built for the Roblox developer community**
**[roboat.pro](https://roboat.pro)** &nbsp;ยท&nbsp; **[GitHub](https://github.com/Addi9000/roboat)** &nbsp;ยท&nbsp; **[Issues](https://github.com/Addi9000/roboat/issues)**
[![GitHub](https://img.shields.io/badge/GitHub-valeoncehadadream%2Froboat-181717?style=for-the-badge&logo=github)](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&section=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&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%"/>
<br/>
[![Python](https://img.shields.io/badge/Python-3.8%2B-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://python.org)
[![License](https://img.shields.io/badge/License-MIT-A78BFA?style=for-the-badge)](LICENSE)
[![Version](https://img.shields.io/badge/Version-2.1.0-6EE7F7?style=for-the-badge)](https://github.com/valeoncehadadream/roboat)
[![Status](https://img.shields.io/badge/Status-Production%20Ready-22c55e?style=for-the-badge)](https://github.com/valeoncehadadream/roboat)
<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" />
<br/>
<br/><br/>
<img src="https://readme-typing-svg.demolab.com?font=Fira+Code&size=22&pause=1000&color=6EE7F7&center=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" />
[![Python](https://img.shields.io/badge/Python-3.8%2B-E3342F?style=for-the-badge&logo=python&logoColor=white)](https://python.org)
[![License](https://img.shields.io/badge/License-MIT-991B1B?style=for-the-badge)](LICENSE)
[![Version](https://img.shields.io/badge/Version-2.1.0-E3342F?style=for-the-badge)](https://github.com/Addi9000/roboat)
[![Website](https://img.shields.io/badge/Website-roboat.pro-991B1B?style=for-the-badge&logo=google-chrome&logoColor=white)](https://roboat.pro)
[![Stars](https://img.shields.io/github/stars/Addi9000/roboat?style=for-the-badge&color=E3342F&logo=github)](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&section=footer" width="100%"/>
<img src="https://capsule-render.vercel.app/api?type=waving&color=0:991B1B,100:E3342F&height=140&section=footer" width="100%"/>
**Built for the Roblox developer community**
**[roboat.pro](https://roboat.pro)** &nbsp;ยท&nbsp; **[GitHub](https://github.com/Addi9000/roboat)** &nbsp;ยท&nbsp; **[Issues](https://github.com/Addi9000/roboat/issues)**
[![GitHub](https://img.shields.io/badge/GitHub-valeoncehadadream%2Froboat-181717?style=for-the-badge&logo=github)](https://github.com/valeoncehadadream/roboat)
*Built with โค๏ธ for the Roblox developer community*
</div>
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&section=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&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%"/>
<br/>
[![Python](https://img.shields.io/badge/Python-3.8%2B-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://python.org)
[![License](https://img.shields.io/badge/License-MIT-A78BFA?style=for-the-badge)](LICENSE)
[![Version](https://img.shields.io/badge/Version-2.1.0-6EE7F7?style=for-the-badge)](https://github.com/valeoncehadadream/roboat)
[![Status](https://img.shields.io/badge/Status-Production%20Ready-22c55e?style=for-the-badge)](https://github.com/valeoncehadadream/roboat)
<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" />
<br/>
<br/><br/>
<img src="https://readme-typing-svg.demolab.com?font=Fira+Code&size=22&pause=1000&color=6EE7F7&center=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" />
[![Python](https://img.shields.io/badge/Python-3.8%2B-E3342F?style=for-the-badge&logo=python&logoColor=white)](https://python.org)
[![License](https://img.shields.io/badge/License-MIT-991B1B?style=for-the-badge)](LICENSE)
[![Version](https://img.shields.io/badge/Version-2.1.0-E3342F?style=for-the-badge)](https://github.com/Addi9000/roboat)
[![Website](https://img.shields.io/badge/Website-roboat.pro-991B1B?style=for-the-badge&logo=google-chrome&logoColor=white)](https://roboat.pro)
[![Stars](https://img.shields.io/github/stars/Addi9000/roboat?style=for-the-badge&color=E3342F&logo=github)](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&section=footer" width="100%"/>
<img src="https://capsule-render.vercel.app/api?type=waving&color=0:991B1B,100:E3342F&height=140&section=footer" width="100%"/>
**Built for the Roblox developer community**
**[roboat.pro](https://roboat.pro)** &nbsp;ยท&nbsp; **[GitHub](https://github.com/Addi9000/roboat)** &nbsp;ยท&nbsp; **[Issues](https://github.com/Addi9000/roboat/issues)**
[![GitHub](https://img.shields.io/badge/GitHub-valeoncehadadream%2Froboat-181717?style=for-the-badge&logo=github)](https://github.com/valeoncehadadream/roboat)
*Built with โค๏ธ for the Roblox developer community*
</div>

@@ -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",