Get Started - Learn How To Make Your Bot!GuidesCode SnippetsGet methodsPost methodsWeb APIUseful linksList of all currently Free ItemsList of EmotesHighrise 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:
Β
Β
Required Libraries
- highrise-bot-sdk
- os
- importlib
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β:
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!
- The function is defined with the name
command_handler
and takes two arguments:self
,user
,andmessage
. Theself
parameter implies that this function is defined within a class.
- The
message
parameter is a string containing a command that the code needs to handle.
parts = message.split(" ")
: The inputmessage
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.
command = parts[0][1:]
: After splitting, the first part of the message will be the command with the prefix. Thecommand
variable is then assigned the value of the command without the prefix, removing the first character.
functions_folder = "functions"
: A variablefunctions_folder
is assigned the value "functions", which is the name of the folder where the command functions are stored.
- The code iterates over all files in the
functions
folder to find a module that contains the appropriate command function.
for file_name in os.listdir(functions_folder):
: This loop iterates over the list of files in thefunctions
folder.
if file_name.endswith(".py"):
: The code checks if the file is a Python file (ends with ".py").
module_name = file_name[:-3]
: It removes the last 3 characters (".py") from the file name to get the module name.
module_path = os.path.join(functions_folder, file_name)
: The full path of the module is obtained by joining thefunctions_folder
andfile_name
.
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.
module = importlib.util.module_from_spec(spec)
: A new module is created using the module specification.
spec.loader.exec_module(module)
: The module is executed, which means the code in the module is run, and it becomes available for use.
- 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).
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.
function = getattr(module, command)
: The command function is obtained from the module using thegetattr
function.
await function(self, user, message)
: The command function is called withself
user
, andmessage
as arguments. Theawait
keyword indicates that this function is an asynchronous function, and it will wait for the command function to complete its execution.
- 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.Β