Create Discord-to-Roblox bots with ro.py!

This tutorial is part of the ro.py tutorial set.
To view more ro.py tutorials and learn more about ro.py,
visit the Discord server, main topic, documentation, or GitHub repository.

In this tutorial, I’ll teach you how to add Roblox features to your discord.py bots with ro.py, the Roblox Web API wrapper for Python 3.
When you finish the tutorial, you’ll have made whois, shout, exile, promote, demote, and setrank commands.

We’ll start with my discord.py bot template, but you’ll be able to follow this tutorial with your own bots (which is recommended, as this is a ro.py tutorial and I won’t be going over discord.py!)

If you are cloning this template, place a file named .env in the same directory as the template, and place your token in it like so:

DISCORDTOKEN=tokengoeshere
discord.py - Bot Template
from discord.ext.commands import Bot, has_permissions
from discord import Embed
from discord.utils import escape_markdown, escape_mentions  # We will use this later!
from dotenv import load_dotenv
import os

load_dotenv()  # Load environment variables from .env file.

bot = Bot(
    command_prefix="r!",  # r! will be our prefix
    case_insensitive=True  # e.g. r!help is the same as r!HELP or r!hElP
)


@bot.event
async def on_ready():
    print(f"Logged in as {bot.user}")


@bot.command()
async def test(ctx):
    await ctx.send("Hello World")  # Replies with "Hello World"

bot.run(os.getenv("DISCORDTOKEN"))  # Grab the DISCORDTOKEN env from our .env file

To begin, just start with Part 1 below and keep going! Don’t worry, it’s not hard.

Installation/Setup

To begin, you’ll need to install ro.py. You can install it from PyPI (pip), but I suggest cloning from GitHub to get the latest features.

We’ll use this command to install ro.py:

pip install roblox

(Use pip3 instead of pip to ensure you’re using the Python 3.x version of pip if you have Python 2.x installed.)

Next, import the ro.py client at the top of your file, right where the rest of the imports are:

from ro_py import Client

Then, right above the line where you create your Bot, add this line:

roblox = Client()

The area where you generate your bot will now look like this:

bot = Bot(...)
roblox = Client()

This generates a ro.py client named roblox that we will use later.

This tutorial will go over some commands that do require authentication, which means you’ll need to create a new Roblox account (or use your own, but it’s not recommended) and grab your ROBLOSECURITY cookie and pass it to the client like so:

roblox = Client("ROBLOSECURITY HERE")

You can grab it in the padlock menu on Chromium-based browsers or through the Developer Tools > Settings menu. You can also grab it though the Developer Tools on Firefox.

Do not share this cookie! It can give an attacker access to your account.

Please note that once you log into your alt and grab the cookie, logging out will invalidate that cookie and it won’t work again.
To get around this, you can open an incognito window and grab the cookie there, then close that window, or just delete the cookie once you’re done without pressing the Logout button.

whois command

At this point, things start to differ from a normal ro.py application because we won’t need to grab the event loop and call run_until_complete (if you don’t know what that means, don’t worry!)

To start, we’ll add a whois command that grabs a Roblox user and their information. We’ll start by adding the command:

@bot.command()
async def whois(ctx, username):

We’ll then grab the user using get_user_by_username like so:

@bot.command()
async def whois(ctx, username):
    user = await client.get_user_by_username(username)

We can now generate an embed or send a message containing information about our user. I’ll start with an embed, but you can get as fancy as you want!

@bot.command()
async def whois(ctx, username):
    user = await roblox.get_user_by_username(username)
    embed = Embed(title=f"Info for {user.name}")
    embed.add_field(
        name="Username",
        value="`" + user.name + "`"
    )
    embed.add_field(
        name="Display Name",
        value="`" + user.display_name + "`"
    )
    embed.add_field(
        name="User ID",
        value="`" + str(user.id) + "`"
    )
    embed.add_field(
        name="Description",
        value="```" + (escape_markdown(user.description or "No description")) + "```"
    )
    await ctx.send(embed=embed)

We’re using escape_mentions and escape_markdown to prevent users from setting their status to @everyone or placing Discord message markdown in a message.

This command will look something like this:


It works, but it’s a bit boring. Let’s add the user’s headshot in the top right corner of the embed!

To do this, we’ll need to import some extra things at the top of our file. Add this to your imports:

from ro_py.thumbnails import ThumbnailSize, ThumbnailType

Add this right before your ctx.send:

avatar_image = await user.thumbnails.get_avatar_image(
    shot_type=ThumbnailType.avatar_headshot,  # headshot
    size=ThumbnailSize.size_420x420,  # high resolution thumbnail
    is_circular=False  # square thumbnail
)
embed.set_thumbnail(
    url=avatar_image
)

Now our embed will look like this:


Looks pretty neat! You are free to advance to the next commands.

shout command

Warning: make sure to lock this command behind some sort of permission check or others may be able to shout in your group without your permission!

This one’s fairly simple. All we have to do is store a group somewhere and call await group.shout(newshout) when the time comes.

client.get_group caches objects, so it’s okay to grab it in a command.

@bot.command()
@has_permissions(manage_guild=True)  # Guild managers only.
async def shout(ctx, *, shout_text):
    group = await roblox.get_group(7819173)  # Group ID here
    await group.shout(shout_text)
    await ctx.send("Sent shout.")

If we want to be able to shout in any group the client has access to, we can also take in a group ID as a parameter, like so:

@bot.command()
@has_permissions(manage_guild=True)  # Guild managers only.
async def shout(ctx, group_id: int, *, shout_text):
    group = await roblox.get_group(group_id)
    await group.shout(shout_text)
    await ctx.send("Sent shout.")

An error will be raised on the shout event if something goes wrong, which your error handler should catch.

exile command

Again, not a hard one! All we have to do is take in a username and exile that user from the group. This time, instead of grabbing a User, we’ll grab a Member.

@bot.command()
@has_permissions(manage_guild=True)  # Guild managers only.
async def exile(ctx, username):
    group = await roblox.get_group(7819173)  # Group ID here
    member = await group.get_member_by_username(username)
    await member.exile()
    await ctx.send("Exiled user.")

Similar to the shout command, we can also exile users on a specific group:

@bot.command()
@has_permissions(manage_guild=True)  # Guild managers only.
async def exile(ctx, group_id: int, username):
    group = await roblox.get_group(group_id)  # Group ID here
    member = await group.get_member_by_username(username)
    await member.exile()
    await ctx.send("Exiled user.")
promote/demote/setrank commands

Now we’re getting fancy! We’ll add a promote and demote command (which will go up a rank an down a rank number respectively) and also a setrank command (which will set the user to a specific rank ID)

We’ll start with promote and demote:

@bot.command()
@has_permissions(manage_guild=True)  # Guild managers only.
async def promote(ctx, username):
    group = await roblox.get_group(7819173)  # Group ID here
    member = await group.get_member_by_username(username)
    await member.promote()
    await ctx.send("Promoted user.")

@bot.command()
@has_permissions(manage_guild=True)  # Guild managers only.
async def demote(ctx, username):
    group = await roblox.get_group(7819173)  # Group ID here
    member = await group.get_member_by_username(username)
    await member.demote()
    await ctx.send("Promoted user.")

You can also add a group_id parameter and pass it to get_group if your workflow requires managing multiple groups.

Next, we’ll add a setrank command for specific ranks. This will use the same ID system used by ranks in your group settings:
image

@bot.command()
@has_permissions(manage_guild=True)  # Guild managers only.
async def setrank(ctx, username, rank: int):
    if 255 >= rank >= 1:  # Make sure rank is in allowed range
        group = await roblox.get_group(7819173)  # Group ID here
        member = await group.get_member_by_username(username)
        await member.setrole(rank)  # Sets the rank
        await ctx.send("Promoted user.")
    else:
        await ctx.send("Rank must be at least 1 and at most 255.")

If you want more control over promotions, you can grab a list of roles (note the difference) with await group.get_roles().

Continue with ro.py

The same things that I taught you here can be used to create very advanced Discord-Roblox bots! I use ro.py for FiveFive, an advanced Discord bot:


I encourage you to keep learning about ro.py and to continue adding features to your bot! You can join our Discord server if you have more questions (or reply to this topic, I’ll be happy to help you with that!) I encourage you to continue with ro.py, and if you’re feeling nice you can also contribute to the project by implementing more APIs that aren’t currently part of the project.

Thank you for following this tutorial, and please let me know if you’d like me to write any more ro.py tutorials as part of this tutorial set!

This tutorial goes nowhere near the extent of what ro.py can do, and there are many other commands I plan to add to this tutorial in the future! Please let me know if there’s anything you’d like me to add.

51 Likes

Brilliant tutorial you got there, I prefer to make bots myself rather than rely on open-source systems so I’ll definitely be giving this a try.

5 Likes

I’m glad you found it useful! I have plans to add more commands to this tutorial in the future as needed. There are a lot of features in ro.py that I haven’t implemented here at the moment, and I would like to try to fit these features into Discord commands in the future.

I’ve always enjoyed writing bots like this from the ground up, and I just didn’t find it as fun when I used outside APIs or just cloned other bots. I hope to write more tutorials like these in the future, given this is only scratching the surface on what libraries like ro.py can do.

2 Likes

When I tried it, It worked good, but when I added the exile, shout, promote/demote I got that error message:
Ignoring exception in command shout: Traceback (most recent call last): File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 85, in wrapped ret = await coro(*args, **kwargs) File "main.py", line 28, in shout await group.shout(shout_text) File "/opt/virtualenvs/python3/lib/python3.8/site-packages/ro_py/groups.py", line 63, in __call__ shout_req = await self.requests.patch( File "/opt/virtualenvs/python3/lib/python3.8/site-packages/ro_py/utilities/requests.py", line 99, in patch return await self.request("patch", *args, **kwargs) File "/opt/virtualenvs/python3/lib/python3.8/site-packages/ro_py/utilities/requests.py", line 78, in request raise request_exception(f"[{this_request.status_code}] {get_request_error[0]['message']}") ro_py.utilities.errors.Unauthorized: [401] Authorization has been denied for this request.
How can I fix it?

2 Likes

Have you followed the steps in “Installation/Setup”? It looks like you haven’t authenticated properly.

2 Likes

I followed it, but It isn’t working for me

1 Like

Great tutorial besides the fact that I use discord.is an noblox lol. But I understand that not everyone uses Js so I give :star::star::star::star::star:

3 Likes

im trying to add the promote command but when i use it, it keeps giving me this error:

Ignoring exception in on_message
Traceback (most recent call last):
File “/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/client.py”, line 343, in _run_event
await coro(*args, **kwargs)
File “main.py”, line 41, in on_message
await promote(trainees)
File “/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py”, line 374, in call
return await self.callback(*args, **kwargs)
File “main.py”, line 17, in promote
group = await roblox.getgroup(10201866)
AttributeError: ‘Client’ object has no attribute ‘getgroup’

2 Likes

Instead of ‘await roblox.getgroup(10201866)’, it should be ‘await roblox.get_group(10201866)’
You missed the underscore

1 Like

im trying to let me be able to promote multiple people at once and i am confused this is what i have so far do you have any ideas on it?

@bot.command()
@has_permissions(manage_messages=True)
async def promote(ctx, *username):
    group = await roblox.get_group(10201866)
    for member in username:
      member = await group.get_member_by_username(username)
      await member.promote()
    await ctx.send("Successfully promoted.")

also the command only runs when i remove my @bot.event but i need that for it to work in the way i want

this is the error i get when it use it:

Ignoring exception in command promote:
Traceback (most recent call last):
  File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 85, in wrapped
    ret = await coro(*args, **kwargs)
  File "main.py", line 19, in promote
    member = await group.get_member_by_username(username)
  File "/opt/virtualenvs/python3/lib/python3.8/site-packages/ro_py/groups.py", line 276, in get_member_by_username
    user = await self.cso.client.get_user_by_username(name)
  File "/opt/virtualenvs/python3/lib/python3.8/site-packages/ro_py/client.py", line 131, in get_user_by_username
    raise UserDoesNotExistError
ro_py.utilities.errors.UserDoesNotExistError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/bot.py", line 939, in invoke
    await ctx.command.invoke(ctx)
  File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 863, in invoke
    await injected(*ctx.args, **ctx.kwargs)
  File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 94, in wrapped
    raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: UserDoesNotExistError:

EDIT: i got the promote command working but i have no idea how to fixed it so it works with @bot.event on

EDIT 2: nvm i got it to work just a simple typo

async def promote(ctx, *username):

This * will store all arguments to the command in a list called username. This will promote every user that you pass to the command, which means running a command like !promote apple banana would promote both the users apple and banana.

You then grab the group and then loop through the username list and then get each member, then promote that member. This code is correct, so I believe you actually have given it an invalid username.

I suggest printing out the username list to make sure you haven’t given it an invalid argument.

1 Like

thanks but i now have a problem. im trying to make a “rank lock” meaning it wont promote someone if they are a certain rank. ex: if rank number = 4 then do not promote
if you could help that would be nice
i tried printing the rank and it gave me a big string then i tried checking that with an if statement by doing
if member.role == [long string here]:
(got that by printing member.role of the person while they were on the rank)
i tried doing if member.role == 4 (number of the rank) still didnt work
got any ideas
if i could get this working i would be using this for as long as my group stands

member.role is a Role object - if you want to check if the rank (the role’s number), you can use member.role.rank:

if member.role.rank >= 4:
    # Role is greater than or equal to 4
1 Like

thank you very much it works flawlessly now

1 Like

This is absolutely amazing! I’ve set it up and turned it into a Cog to use with my instance of RED. Roblox verification when?

2 Likes

Hello, I have a question. How did you make the avatar command displayed in the video?

Yep,
Going to “Rare Nuggets” Bookmarks.

This is really cool but I have one question how would I go about making an authentication system similar to RoVer. I am trying to make a general purpose discord to roblox bot

I found a tiny error within your coding. Specifically in the “promote/demote/setrank commands” category, where you await ctx.send('Promoted user.') in the demote command. Demoted user* :stuck_out_tongue:

This is cool! So it can just find your Roblox profile and stuff? :grinning:

1 Like