Metadata-Version: 2.4
Name: roboat
Version: 2.0.0
Summary: The best Roblox API wrapper for Python — typed models, async, Open Cloud, events, analytics, trades, datastores
License: MIT License
        
        Copyright (c) 2024 robloxapi 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. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/yourname/roboat
Project-URL: Repository, https://github.com/yourname/roboat
Project-URL: Bug Tracker, https://github.com/yourname/roboat/issues
Project-URL: Documentation, https://github.com/yourname/roboat#readme
Keywords: roblox,api,wrapper,games,catalog,roblox-api,robux,trades,opencloud,datastore
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Environment :: Console
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.28.0
Provides-Extra: async
Requires-Dist: aiohttp>=3.8.0; extra == "async"
Provides-Extra: test
Requires-Dist: pytest>=7.0; extra == "test"
Provides-Extra: all
Requires-Dist: aiohttp>=3.8.0; extra == "all"
Requires-Dist: pytest>=7.0; extra == "all"
Dynamic: license-file

# roboat

> The best Roblox API wrapper for Python — typed models, interactive session terminal, and local SQLite database.

[![Python](https://img.shields.io/badge/python-3.8%2B-blue)](https://python.org)
[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)

---

## Installation

```bash
pip install roboat
```

**Requirements:** Python 3.8+, `requests`

---

## Interactive Terminal

The fastest way to explore the Roblox API:

```bash
# Launch after installing
roboat

# Or with Python
python -m roboat
```

```
  ____       _     _            _    ____ ___
 |  _ \ ___ | |__ | | _____  __/ \  |  _ \_ _|
 | |_) / _ \| '_ \| |/ _ \ \/ / _ \ | |_) | |
 |  _ < (_) | |_) | | (_) >  < ___ \|  __/| |
 |_| \_\___/|_.__/|_|\___/_/\_/_/  \_|_|  |___|

  roboat v2.0.0  —  type 'help' to begin
```

### Session Commands

| Command | Description |
|---|---|
| `start <userid>` | Begin session as a Roblox user |
| `cookie <token>` | Set `.ROBLOSECURITY` for authenticated requests |
| `whoami` | Show current session user |
| `newdb <name>` | Create a new local database |
| `loaddb <name>` | Load an existing database |
| `listdb` | List all local databases |
| `user <userid>` | Fetch user profile |
| `game <universeid>` | Fetch game info + visit stats |
| `friends <userid>` | Get friends list |
| `followers <userid>` | Get follower count & list |
| `likes <universeid>` | Get game upvote/downvote stats |
| `search user <kw>` | Search users by keyword |
| `search game <kw>` | Search games by keyword |
| `presence <userid>` | Get user online status |
| `avatar <userid>` | Get avatar details + thumbnail |
| `servers <placeid>` | List active game servers |
| `badges <universeid>` | List badges in a game |
| `catalog <keyword>` | Search the avatar catalog |
| `robux` | Show your Robux balance (needs cookie) |
| `save user <id>` | Save user to current DB |
| `save game <id>` | Save game to current DB |
| `history` | Show command history |
| `clear` | Clear the screen |
| `exit` | Close the session |

**Example session:**

```
» start 156
» game 2753915549
» friends 156
» likes 2753915549
» newdb mydata
» save user 156
» loaddb mydata
```

---

## Programmatic Usage

### Client setup

```python
from roboat import RoboatClient, ClientBuilder

# Simple
client = RoboatClient()
client = RoboatClient(cookie="YOUR_.ROBLOSECURITY")

# Builder pattern
client = (
    ClientBuilder()
    .set_cookie("YOUR_.ROBLOSECURITY")
    .set_timeout(15)
    .set_proxy("http://proxy:8080")
    .build()
)
```

---

### Users

```python
# Get user by ID — returns a User object
user = client.users.get_user(156)
print(user)          # builderman (@builderman) [ID: 156]
print(user.name)     # builderman
print(user.visits)   # attribute access on typed model

# Bulk lookup
users = client.users.get_users_by_ids([1, 156, 261])
users = client.users.get_users_by_usernames(["Roblox", "builderman"])

# Search
page = client.users.search_users("builderman", limit=10)
for user in page:
    print(user)

# Username history
page = client.users.get_username_history(user_id=156)

# Authenticated user (requires cookie)
me = client.users.get_authenticated_user()
uid = client.user_id()
name = client.username()
```

---

### Games

```python
# Get game by universe ID — returns a Game object
game = client.games.get_game(2753915549)
print(game)
print(game.visits)    # int
print(game.playing)   # int

# Resolve from a place ID
game = client.games.get_game_from_place(place_id=6872265039)

# Visit counts as a dict
visits = client.games.get_visits([2753915549, 286090429])
# {2753915549: 12345678, 286090429: 987654}

# Vote stats
votes = client.games.get_votes([2753915549])
print(votes[0])        # 👍 12,345  👎 678  (94.8% positive)
print(votes[0].ratio)  # 94.8

# Search
page = client.games.search_games("obby", limit=20)
for game in page:
    print(f"{game.name} — {game.visits:,} visits")

# Games by user / group
page = client.games.get_user_games(user_id=156)
page = client.games.get_group_games(group_id=2868472)

# Active servers
page = client.games.get_servers(place_id=6872265039, limit=10)
for server in page:
    print(server)  # [abc123...] Players: 8/12  FPS: 60.0  Ping: 45ms

# Game passes
page = client.games.get_game_passes(universe_id=2753915549)

# Favorite count
count = client.games.get_favorite_count(universe_id=2753915549)
```

---

### Catalog

```python
# Search avatar shop — returns Page of CatalogItem
page = client.catalog.search(
    keyword="fedora",
    category="Accessories",
    sort_type="Sales",
    price_max=500,
    limit=30,
)
for item in page:
    print(item)

# Single asset or bundle
item = client.catalog.get_asset(asset_id=1028606)
item = client.catalog.get_bundle(bundle_id=192)

# Resale data for limiteds
resale = client.catalog.get_resale_data(asset_id=1028606)
print(resale)  # 📈 RAP: 12,500R$  Sales: 3,421  ...

# Current resellers
page = client.catalog.get_resellers(asset_id=1028606)
```

---

### Groups

```python
# Group info — returns Group object
group = client.groups.get_group(2868472)
print(group)

# Roles — returns list of GroupRole
roles = client.groups.get_roles(2868472)
for role in roles:
    print(role)  # [Rank 255] Owner    (1 members)

# Members
page = client.groups.get_members(2868472, limit=100)
page = client.groups.get_members(2868472, role_id=12345)

# User's groups
groups = client.groups.get_user_groups(user_id=156)

# Wall
page = client.groups.get_wall(2868472, limit=10)

# Search
page = client.groups.search("builders")
```

---

### Friends

```python
# Friends list — returns list of Friend objects
friends = client.friends.get_friends(user_id=156)
for f in friends:
    print(f)  # 🟢 Builderman (@builderman) [ID: 156]

# Counts
fc  = client.friends.get_friend_count(156)
flc = client.friends.get_follower_count(156)
fgc = client.friends.get_following_count(156)

# Paginated followers / followings
page = client.friends.get_followers(156, limit=100)
page = client.friends.get_followings(156, limit=100)

# Actions (require cookie)
client.friends.send_friend_request(user_id=12345)
client.friends.accept_friend_request(user_id=12345)
client.friends.decline_friend_request(user_id=12345)
client.friends.unfriend(user_id=12345)

# Pending requests (requires cookie)
page = client.friends.get_friend_requests(limit=20)
```

---

### Presence

```python
# Single user
presence = client.presence.get_presence(user_id=156)
print(presence.status)        # "In Game", "Online", "Offline", "In Studio"
print(presence.last_location) # "Natural Disaster Survival"

# Bulk
presences = client.presence.get_presences([1, 156, 261])
for p in presences:
    print(f"{p.user_id}: {p.status}")
```

---

### Thumbnails

```python
# All return {id: url} dicts
avatars = client.thumbnails.get_user_avatars([1, 156], size="420x420")
heads   = client.thumbnails.get_user_headshots([1, 156])
icons   = client.thumbnails.get_game_icons([2753915549])
shots   = client.thumbnails.get_game_thumbnails([2753915549], count=3)
assets  = client.thumbnails.get_asset_thumbnails([1028606])
groups  = client.thumbnails.get_group_icons([2868472])

# Single convenience method
url = client.thumbnails.get_avatar_url(user_id=156)
```

---

### Economy

```python
# Robux balance (requires cookie)
balance = client.economy.get_robux_balance(user_id=156)
print(balance)  # 💎 12,345 Robux
# shortcut:
robux = client.robux()

# Resale data
resale = client.economy.get_asset_resale_data(asset_id=1028606)
page   = client.economy.get_asset_resellers(asset_id=1028606)

# Group funds (requires cookie + admin)
balance = client.economy.get_group_funds(group_id=2868472)
```

---

### Badges

```python
# Badge info
badge = client.badges.get_badge(badge_id=2124445228)
print(badge)

# All badges in a game
page = client.badges.get_universe_badges(universe_id=2753915549)

# Badges a user has earned
page = client.badges.get_user_badges(user_id=156)

# When specific badges were awarded
dates = client.badges.get_awarded_dates(
    user_id=156,
    badge_ids=[2124445228, 2124445229],
)
# {2124445228: "2022-01-15T10:30:00Z", ...}
```

---

### Avatar

```python
# Full avatar info
avatar = client.avatar.get_user_avatar(user_id=156)
print(avatar.avatar_type)   # "R15"
print(avatar.scales)        # {"height": 1.0, "width": 1.0, ...}
print(len(avatar.assets))   # number of equipped items

# Outfits
outfits = client.avatar.get_user_outfits(user_id=156)
```

---

## Local Database

```python
from roboat import SessionDatabase

# Create / load
db = SessionDatabase.create("myproject")
db = SessionDatabase.load("myproject")
db = SessionDatabase.load_or_create("myproject")

# List all local databases
names = SessionDatabase.list_databases()

# Save API objects
db.save_user(user)
db.save_game(game)

# Retrieve cached data
user_dict = db.get_user(156)
game_dict = db.get_game(2753915549)
all_users = db.get_all_users()
all_games = db.get_all_games()

# Key-value store
db.set("last_run", "2024-01-01")
val = db.get("last_run")

# Stats
print(db.stats())
# {'users': 10, 'games': 5, 'session_keys': 3, 'log_entries': 42}

db.close()
```

---

## Pagination

```python
cursor = None
all_followers = []

while True:
    page = client.friends.get_followers(user_id=156, limit=100, cursor=cursor)
    all_followers.extend(page.data)
    cursor = page.next_cursor
    if not cursor:
        break

print(f"Total: {len(all_followers)}")
```

---

## Error Handling

```python
from roboat import (
    RobloxAPIError,
    UserNotFoundError,
    NotAuthenticatedError,
    RateLimitedError,
    InvalidCookieError,
)

try:
    user = client.users.get_user(99999999999)
except UserNotFoundError:
    print("User not found")
except RateLimitedError:
    print("Rate limited — slow down")
except NotAuthenticatedError as e:
    print(f"Need cookie: {e}")
except RobloxAPIError as e:
    print(f"API error: {e}")
```

---

## Authentication

Get your `.ROBLOSECURITY` cookie by logging into roblox.com and copying it from browser DevTools → Application → Cookies.

> ⚠️ **Never share your `.ROBLOSECURITY` cookie.** It gives full account access.

```python
client = RoboatClient(cookie="_|WARNING:-DO-NOT-SHARE-THIS-...")
```

---

## License

MIT
