Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/treeben77/rblx-open-cloud i…
Browse files Browse the repository at this point in the history
…nto engine
  • Loading branch information
treeben77 committed Dec 5, 2024
2 parents 0f2b903 + e17dcb4 commit 7c241ab
Show file tree
Hide file tree
Showing 33 changed files with 1,233 additions and 307 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
[![DevForum Post](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdevforum.roproxy.com%2Ft%2F1991959.json&query=%24.like_count&suffix=%20Likes&style=for-the-badge&logo=robloxstudio&logoColor=white&label=DevForum%20Post&labelColor=%23009fff&color=%23353535)](https://devforum.roblox.com/t/1991959)
[![Downloads](https://img.shields.io/pypi/dm/rblx-open-cloud?style=for-the-badge&logo=pypi&logoColor=white&label=PyPi%20Downloads&labelColor=%23006dad&color=%23353535)](https://pypi.org/project/rblx-open-cloud)

rblx-open-cloud is a Python API wrapper for [Roblox Open Cloud](https://create.roblox.com/docs/open-cloud/index). It supports all experience and creator APIs, OAuth2, and incoming webhooks.
rblx-open-cloud is a Python API wrapper for [Roblox Open Cloud](https://create.roblox.com/docs/cloud/open-cloud).

**Documentation: https://rblx-open-cloud.readthedocs.io**
### Key Features
- Support for most endpoints, including the OAuth2 flow and authentication.
- Allows both syncronous and asyncronous usage.
- Logic for operation polling and internal error retrying.

**Documentation: https://rblx-open-cloud.readthedocs.io/en/latest/**

## Quickstart

Expand All @@ -16,10 +21,10 @@ rblx-open-cloud is a Python API wrapper for [Roblox Open Cloud](https://create.r

```sh
# Stable (PyPi, recommended)
pip install rblx-open-cloud
pip install rblx-open-cloud~=2.0

# Development (GitHub)
pip install "rblx-open-cloud @ git+https://github.com/treeben77/rblx-open-cloud@v2"
pip install "rblx-open-cloud @ git+https://github.com/treeben77/rblx-open-cloud@main"
```

2. Create an API key from the [Creator Dashboard](https://create.roblox.com/credentials). You can read [Managing API Keys](https://create.roblox.com/docs/open-cloud/managing-api-keys) for help.
Expand Down
2 changes: 1 addition & 1 deletion docs/asynchronous.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ print(value, info)

async for version in datastore.list_versions("287113233"):
print(version)
```
```
52 changes: 26 additions & 26 deletions docs/guides/experience.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,109 +21,109 @@ Replace `00000000` with your experience/universe ID (NOT place ID), and `your-ap

You can use Open Cloud to access your experience's [Datastores](https://create.roblox.com/docs/cloud-services/Datastores) in Python, including Ordered Datastores. This could be useful if you're creating a Discord bot, or website to remotly manage user data, or to control certain settings.

To get started, you will need to create a [`rblxopencloud.Datastore`][rblxopencloud.Datastore] object. The following code example creates a [`rblxopencloud.Datastore`][rblxopencloud.Datastore] with the name 'ExampleStore' and the scope 'global' (global is the default scope in Lua):
To get started, you will need to create a [`rblxopencloud.DataStore`][rblxopencloud.DataStore] object. The following code example creates a [`rblxopencloud.DataStore`][rblxopencloud.DataStore] with the name 'ExampleStore' and the scope 'global' (global is the default scope in Lua):

```py
Datastore = experience.get_Datastore("ExampleStore", scope="global")
datastore = experience.get_datastore("ExampleStore", scope="global")
```

Great! Now that you've created a Datastore, you can now access it's entrys.
Great! Now that you've created a data store, you can now access it's entrys.

!!! tip
You can set the `scope` parameter to `None` if you want to access all scopes. You will be required to format keys in the `scope/key` syntax. For example, in `pets/user_287113233`, the scope is `pets`, and `user_287113233` is the key.

### Getting Keys

To get the value of a Datastore key, you can use [`Datastore.get_entry`][rblxopencloud.Datastore.get_entry]. The following code will get the value for `user_287113233` in the data store.
To get the value of a datastore key, you can use [`datastore.get_entry`][rblxopencloud.DataStore.get_entry]. The following code will get the value for `user_287113233` in the data store.

```py
value, info = Datastore.get_entry("user_287113233")
value, info = datastore.get_entry("user_287113233")
```

[`Datastore.get_entry`][rblxopencloud.Datastore.get_entry] returns a tuple of two items, the key's value, and an [`rblxopencloud.EntryInfo`][rblxopencloud.EntryInfo] object. The value can be either `str`, `int`, `float`, `list`, or `dict`, and is the equivalent value in Roblox Lua. The [`rblxopencloud.EntryInfo`][rblxopencloud.EntryInfo] object contains metadata about the Datastore key, such as the current version ID, when it was created and last updated, a list of user IDs for GDPR tracking, and the custom metadata, learn more about this on [Roblox's Datastore Guide](https://create.roblox.com/docs/cloud-services/Datastores#metadata).
[`datastore.get_entry`][rblxopencloud.DataStore.get_entry] returns a tuple of two items, the key's value, and an [`rblxopencloud.EntryInfo`][rblxopencloud.EntryInfo] object. The value can be either `str`, `int`, `float`, `list`, or `dict`, and is the equivalent value in Roblox Lua. The [`rblxopencloud.EntryInfo`][rblxopencloud.EntryInfo] object contains metadata about the datastore key, such as the current version ID, when it was created and last updated, a list of user IDs for GDPR tracking, and the custom metadata, learn more about this on [Roblox's Datastore Guide](https://create.roblox.com/docs/cloud-services/Datastores#metadata).

If the requested key does not exist, then this method will raise [`rblxopencloud.NotFound`][rblxopencloud.NotFound]. So, you should create a try block and deal with errors correctly, for example:

```py
try:
value, info = Datastore.get_entry("user_287113233")
value, info = datastore.get_entry("user_287113233")
except(rblxopencloud.NotFound):
print("the key doesn't exist!")
else:
print(f"the key's value is {value}.")
```

You can list all keys in a Data Store using [`Datastore.list_keys`][rblxopencloud.Datastore.list_keys], it will iterate all keys in the Datastore, and can be used like this:
You can list all keys in a Data Store using [`datastore.list_keys`][rblxopencloud.DataStore.list_keys], it will iterate all keys in the datastore, and can be used like this:

```py
for key in Datastore.list_keys():
for key in datastore.list_keys():
print(key.key, key.scope)
```

If the data store's scope is `None` this will return keys from every scope. You can also provide an optional `prefix`, and `limit`.

### Changing Keys

Datastore values can be changed with [`Datastore.set_entry`][rblxopencloud.Datastore.set_entry] and [`Datastore.increment_entry`][rblxopencloud.Datastore.increment_entry]. Both will need the 'Create Entry' permission to create new keys, and/or 'Update Entry' permission to update existing keys. First, here's an example using [`Datastore.set_entry`][rblxopencloud.Datastore.set_entry]:
Datastore values can be changed with [`datastore.set_entry`][rblxopencloud.DataStore.set_entry] and [`datastore.increment_entry`][rblxopencloud.DataStore.increment_entry]. Both will need the 'Create Entry' permission to create new keys, and/or 'Update Entry' permission to update existing keys. First, here's an example using [`datastore.set_entry`][rblxopencloud.DataStore.set_entry]:

```py
version = Datastore.set_entry("user_287113233", {"xp": 1337, "level": 7}, users=[287113233])
version = datastore.set_entry("user_287113233", {"xp": 1337, "level": 7}, users=[287113233])
```

This will set the key `user_287113233` to the dictionary `{"xp": 1337, "level": 7}`, with the user's ID in the list of users. The method returns [`rblxopencloud.EntryVersion`][rblxopencloud.EntryVersion], which contains metadata about the new version, such as it's version ID. The code above is equivalent to the the following lua code:

```lua
local version = Datastore:SetAsync("user_287113233", {["xp"] = 1337, ["level"] = 7}, {287113233})
local version = datastore:SetAsync("user_287113233", {["xp"] = 1337, ["level"] = 7}, {287113233})
```

If the current value of the key is an integer, or float you can use [`Datastore.increment_entry`][rblxopencloud.Datastore.increment_entry] to update the value, while guaranteeing you don't overwrite the old value. The following example will increment the key `user_score_287113233` by 70:
If the current value of the key is an integer, or float you can use [`datastore.increment_entry`][rblxopencloud.DataStore.increment_entry] to update the value, while guaranteeing you don't overwrite the old value. The following example will increment the key `user_score_287113233` by 70:

```py
value, info = Datastore.increment_entry("user_score_287113233", 70, users=[287113233])
value, info = datastore.increment_entry("user_score_287113233", 70, users=[287113233])
```

[`Datastore.increment_entry`][rblxopencloud.Datastore.increment_entry] actually returns the new value just like [`Datastore.get_entry`][rblxopencloud.Datastore.get_entry] instead.
[`datastore.increment_entry`][rblxopencloud.DataStore.increment_entry] actually returns the new value just like [`datastore.get_entry`][rblxopencloud.DataStore.get_entry] instead.

!!! warning
If an entry has `users` and `metadata`, you must provide them every time you set or increment the value, otherwise they will be removed.

### Removing Keys

You can remove a key from a Datastore with Open Cloud with the following code:
You can remove a key from a datastore with Open Cloud with the following code:

```py
version = Datastore.remove_entry("user_287113233")
version = datastore.remove_entry("user_287113233")
```

This will mark the key as deleted, and calls to get the key will fail. However, the old version of the key can still be accessed by listing versions (explained below), and listing keys will still return the key. These are limitations with Open Cloud, not one with the library.

### Key Versioning

Data Stores retain previous versions of the key for 30 days. You can list all previous versions with [`Datastore.list_versions`][rblxopencloud.Datastore.list_versions], like this:
Data Stores retain previous versions of the key for 30 days. You can list all previous versions with [`datastore.list_versions`][rblxopencloud.DataStore.list_versions], like this:

```py
for version in Datastore.list_versions("user_287113233"):
for version in datastore.list_versions("user_287113233"):
print(version, version.get_value())
```

This will iterate [`rblxopencloud.EntryVersion`][rblxopencloud.EntryVersion] for every version in the key. It contains information like the version ID, when the version was created, the content length, and wether it has been deleted. You can fetch a version's value with [`EntryVersion.get_value`][rblxopencloud.EntryVersion.get_value], but if you already have the version ID, and don't need to list versions you can use [`Datastore.get_version`][rblxopencloud.Datastore.get_version].
This will iterate [`rblxopencloud.EntryVersion`][rblxopencloud.EntryVersion] for every version in the key. It contains information like the version ID, when the version was created, the content length, and wether it has been deleted. You can fetch a version's value with [`EntryVersion.get_value`][rblxopencloud.EntryVersion.get_value], but if you already have the version ID, and don't need to list versions you can use [`datastore.get_version`][rblxopencloud.DataStore.get_version].

```py
value, info = Datastore.get_version("user_287113233", "VERSION_ID")
value, info = datastore.get_version("user_287113233", "VERSION_ID")
```

It returns the same as [`Datastore.get_entry`][rblxopencloud.Datastore.get_entry].
It returns the same as [`datastore.get_entry`][rblxopencloud.DataStore.get_entry].

### Ordered Data Stores

You can also access Ordered Data Stores with Open Cloud. There are a few differences between regular Data Stores and Ordered Data Stores:

- Ordered Data Store do not support versioning, therefore you can not use versioning functions.
- Ordered Data Stores also do not support user IDs or metadata, and since they also don't support versioning, there is no second parameter returned on get methods.
- [`OrderedDatastore.set_entry`][rblxopencloud.OrderedDatastore.set_entry] doesn't have the `previous_version` precondition, instead it has an `exclusive_update` precondition.
- Ordered Data Stores don't have a `list_keys` method, but instead [`OrderedDatastore.sort_keys`][rblxopencloud.OrderedDatastore.sort_keys]. They also iterate [`rblxopencloud.SortedEntry`][rblxopencloud.SortedEntry] which includes the key's value.
- [`OrderedDataStore.set_entry`][rblxopencloud.OrderedDataStore.set_entry] doesn't have the `previous_version` precondition, instead it has an `exclusive_update` precondition.
- Ordered Data Stores don't have a `list_keys` method, but instead [`OrderedDataStore.sort_keys`][rblxopencloud.OrderedDataStore.sort_keys]. They also iterate [`rblxopencloud.SortedEntry`][rblxopencloud.SortedEntry] which includes the key's value.

To create an Ordered Data Store, you can use the [`Experience.get_ordered_Datastore`][rblxopencloud.Experience.get_ordered_Datastore] method, which also supports `scope` being `None`:
To create an Ordered Data Store, you can use the [`Experience.get_ordered_datastore`][rblxopencloud.Experience.get_ordered_datastore] method, which also supports `scope` being `None`:

```py
Datastore = experience.get_ordered_datastore("ExampleStore", scope="global")
Expand Down Expand Up @@ -178,4 +178,4 @@ else:
print(f"The user is not banned right now!")
```

The previous methods can also be used on a place-level by using a them from a [`Place`][rblxopencloud.Place] object.
The previous methods can also be used on a place-level by using a them from a [`Place`][rblxopencloud.Place] object.
4 changes: 4 additions & 0 deletions docs/guides/v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ Can be shortened to:
asset = creator.upload_asset(...).wait()
```

### `user_id` parameter of [`list_members`][rblxopencloud.Group.list_members] removed

For simplicity, [`list_members`][rblxopencloud.Group.list_members] no longer accepts a `user_id` parameter. To fetch a specific member of a group, use [`fetch_member`][rblxopencloud.Group.fetch_member] instead.

### Exception changes

The following exceptions have been renamed/merged:
Expand Down
6 changes: 3 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ To install rblx-open-cloud from PyPI package, you can install it from pip:

=== "Windows"
```console
python -m pip install rblx-open-cloud --upgrade
python -m pip install rblx-open-cloud~=2.0 --upgrade
```

=== "Linux"
```console
python3 -m pip install rblx-open-cloud --upgrade
python3 -m pip install rblx-open-cloud~=2.0 --upgrade
```

## Getting Started
Expand All @@ -26,4 +26,4 @@ You can begin getting started with our [Authentication Guide](/docs/guides/authe

You can ask for help in the [Discord Server](https://discord.gg/4CSc9E5uQy) or the [DevForum Post](https://devforum.roblox.com/t/1991959), and you can report bugs on the [GitHub Repository](https://github.com/treeben77/rblx-open-cloud/issues).

**Thank you for using rblx-open-cloud!**
**Thank you for using rblx-open-cloud!**
10 changes: 10 additions & 0 deletions docs/reference/creator.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
# Creator

::: rblxopencloud.Asset
options:
merge_init_into_class: true

::: rblxopencloud.AssetVersion

::: rblxopencloud.CreatorStoreProduct
options:
merge_init_into_class: true

::: rblxopencloud.Creator

::: rblxopencloud.Money

::: rblxopencloud.AssetType

::: rblxopencloud.ModerationStatus

::: rblxopencloud.ProductRestriction
2 changes: 1 addition & 1 deletion docs/reference/experience.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@

::: rblxopencloud.PaymentProvider

::: rblxopencloud.Platform
::: rblxopencloud.Platform
2 changes: 1 addition & 1 deletion docs/reference/group.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@

::: rblxopencloud.GroupJoinRequest
options:
inherited_members: false
inherited_members: false
2 changes: 2 additions & 0 deletions docs/reference/library.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
::: rblxopencloud.send_request

::: rblxopencloud.Operation

::: rblxopencloud.ApiKey
2 changes: 1 addition & 1 deletion docs/reference/user.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@

::: rblxopencloud.UserExperienceFollowing
options:
merge_init_into_class: true
merge_init_into_class: true
3 changes: 2 additions & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ mkdocs-material-extensions==1.3
mkdocs-git-committers-plugin-2==2.3
mkdocstrings==0.23.0
mkdocstrings-python==1.7.1
mkdocs-git-revision-date-localized-plugin==1.2.5
mkdocs-git-revision-date-localized-plugin==1.2.5
griffe==0.48.0
2 changes: 1 addition & 1 deletion rblxopencloud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

import requests

VERSION: str = "2.0.0"
VERSION: str = "2.1.2"
VERSION_INFO: Literal["alpha", "beta", "final"] = "alpha"

user_agent: str = (
Expand Down
60 changes: 59 additions & 1 deletion rblxopencloud/apikey.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from .creator import Asset, AssetType, CreatorStoreProduct
from .experience import Experience
from .group import Group
from .http import send_request
from .user import User

from typing import Union

__all__ = ("ApiKey",)


Expand All @@ -38,7 +42,7 @@ class ApiKey:
[Creator Dashboard](https://create.roblox.com/credentials).
"""

def __init__(self, api_key) -> None:
def __init__(self, api_key: str) -> None:
self.__api_key = api_key

def get_experience(self, id: int, fetch_info: bool = False) -> Experience:
Expand All @@ -64,3 +68,57 @@ def get_user(self, id: int, fetch_info: bool = False) -> User:
obj.fetch_info()

return obj

def fetch_asset(self, asset_id: int) -> Asset:
"""
Fetches an asset uploaded to Roblox.
Args:
asset_id: The ID of the asset to fetch.
Returns:
An [`Asset`][rblxopencloud.Asset] representing the asset.
"""

_, data, _ = send_request(
"GET",
f"assets/v1/assets/{asset_id}",
authorization=self.__api_key,
expected_status=[200],
)

return Asset(data, self, self.__api_key)

def fetch_creator_store_product(
self, asset_type: Union[AssetType, str], product_id: int
) -> CreatorStoreProduct:
"""
Fetches information about an asset on the creator store.
Args:
asset_type: The type of asset the product is.
product_id: The ID of the asset to fetch.
Returns:
A [`CreatorStoreProduct`][rblxopencloud.CreatorStoreProduct] \
representing the asset.
Tip:
If the asset type is unknown or other information such as the \
description is required, use the \
[`fetch_asset`][rblxopencloud.ApiKey.fetch_asset].
"""

asset_type = (
asset_type.name if type(asset_type) == AssetType else asset_type
)

_, data, _ = send_request(
"GET",
"/creator-store-products/CreatorMarketplaceAsset"
f"-{asset_type}-{product_id}",
authorization=self.__api_key,
expected_status=[200],
)

return CreatorStoreProduct(data, self.__api_key)
Loading

0 comments on commit 7c241ab

Please sign in to comment.