Get Started - Learn How To Make Your Bot!

Guides

Code Snippets

Get methods

Post methods

Web API

Useful links

List of all currently Free Items

List of Emotes

Highrise Bot SDK Changelog


Concept

Here we will implement a Command Handler, this will help us to organize better our bot’s code.

The Command Handler will receive a message and check if it’s a valid function within our files, to do that we will use the 🛄 Teleporter code snippet as an example and we will modify it a little bit to fit in our Command Handler.

Here’s a quick video showing the Teleporter function working through this Command Handler:

Highrise 2023-07-24 23-11-39.mp4

Required Libraries

Code

Here’s what we will need to import into our bot’s main file:

from highrise import *
from highrise.models import *
import os
import importlib.util

Now we will make a new folder called “functions” where we will store our functions, see that inside of our new folder I created a file called “teleporter.py”:

Files in the workspace.

Files in the workspace.

Now let’s make some changes on our “teleporter function” to make sure it works from outside of the “Bot class” (outside of the main file):

from highrise import *
from highrise.models import *

async def teleport(self: BaseBot, user: User, message: str)-> None:
        """
            Teleports the user to the specified user or coordinate
            Usage: /teleport <username> <x,y,z>
                                                                """
        #separates the message into parts
        #part 1 is the command "/teleport"
        #part 2 is the name of the user to teleport to (if it exists)
        #part 3 is the coordinates to teleport to (if it exists)
        try:
            command, username, coordinate = message.split(" ")
        except:
            await self.highrise.chat("Incorrect format, please use /teleport <username> <x,y,z>")
            return
        
        #checks if the user is in the room
        room_users = (await self.highrise.get_room_users()).content
        for user, _ in room_users:
            if user.username.lower() == username.lower():
                user_id = user.id
                break
        #if the user_id isn't defined, the user isn't in the room
        if "user_id" not in locals():
            await self.highrise.chat("User not found, please specify a valid user and coordinate")
            return
            
        #checks if the coordinate is in the correct format (x,y,z)
        try:
            x, y, z = coordinate.split(",")
        except:
            await self.highrise.chat("Coordinate not found or incorrect format, please use x,y,z")
            return
        
        #teleports the user to the specified coordinate
        await self.highrise.teleport(user_id = user_id, dest = Position(float(x), float(y), float(z)))

Perfect! Now we can start making our handler, to do that we will first going to create a new function called “command_handler” inside of our main file that will be triggered if the user says a message that starts with “/”:

from highrise import *
from highrise.models import *
import os
import importlib.util

class Bot(BaseBot):
    async def on_start(self, SessionMetadata: SessionMetadata)-> None:
        print (f"Starting: {SessionMetadata}")
        
    async def on_chat(self, user: User, message: str)-> None:
        print (f"Received: {message} from {user.username}")
        if message.startswith("/"):
            await self.command_handler(user, message)        
        
    async def command_handler(self, user: User, message: str):
			pass

Ok, now we will implement the operation of the command_handler in the main file:

async def command_handler(self, user: User, message: str):
        parts = message.split(" ")
        command = parts[0][1:]
        functions_folder = "functions"
        # Check if the function exists in the module
        for file_name in os.listdir(functions_folder):
            if file_name.endswith(".py"):
                module_name = file_name[:-3]  # Remove the '.py' extension
                module_path = os.path.join(functions_folder, file_name)
                
                # Load the module
                spec = importlib.util.spec_from_file_location(module_name, module_path)
                module = importlib.util.module_from_spec(spec)
                spec.loader.exec_module(module)
                
                # Check if the function exists in the module
                if hasattr(module, command) and callable(getattr(module, command)):
                    function = getattr(module, command)
                    await function(self, user, message)
                    return
        # If no matching function is found
        return

The command handler will perform any functions that take an User and a Message as a parameter, the function has to be in the functions folder and to use it the user only has to say “/<name of the function”.

Our code is done, now let’s summarize how this code works!

  1. The function is defined with the name command_handler and takes two arguments: self , user,and message. The self parameter implies that this function is defined within a class.
  2. The message parameter is a string containing a command that the code needs to handle.
  3. parts = message.split(" "): The input message is split into parts using space (" ") as the delimiter. It assumes that the command is prefixed with a character, like "!command". So, this line separates the prefix from the actual command and any additional arguments.
  4. command = parts[0][1:]: After splitting, the first part of the message will be the command with the prefix. The command variable is then assigned the value of the command without the prefix, removing the first character.
  5. functions_folder = "functions": A variable functions_folder is assigned the value "functions", which is the name of the folder where the command functions are stored.
  6. The code iterates over all files in the functions folder to find a module that contains the appropriate command function.
  7. for file_name in os.listdir(functions_folder):: This loop iterates over the list of files in the functions folder.
  8. if file_name.endswith(".py"):: The code checks if the file is a Python file (ends with ".py").
  9. module_name = file_name[:-3]: It removes the last 3 characters (".py") from the file name to get the module name.
  10. module_path = os.path.join(functions_folder, file_name): The full path of the module is obtained by joining the functions_folder and file_name.
  11. spec = importlib.util.spec_from_file_location(module_name, module_path): A module specification is created using the module name and the path of the module.
  12. module = importlib.util.module_from_spec(spec): A new module is created using the module specification.
  13. spec.loader.exec_module(module): The module is executed, which means the code in the module is run, and it becomes available for use.
  14. The code checks if the loaded module (module) has the specified command (command) as an attribute and if that attribute is callable (meaning it's a function).
  15. if hasattr(module, command) and callable(getattr(module, command)):: If the command function is found in the module, the code proceeds to the next step.
  16. function = getattr(module, command): The command function is obtained from the module using the getattr function.
  17. await function(self, user, message): The command function is called with self user, and message as arguments. The await keyword indicates that this function is an asynchronous function, and it will wait for the command function to complete its execution.
  18. Finally, if no matching function is found, the function returns without doing anything (since the code is not raising an error or performing any other action in case no command is found).

This code is designed to dynamically load and execute command functions from separate Python files located in the "functions" folder based on the input command passed to the command_handler function. The assumption is that each command function has a corresponding Python file with the same name as the command in the "functions" folder.