πŸƒπŸΌβ€β™‚οΈ Follow (Butler Function)

πŸƒπŸΌβ€β™‚οΈ Follow (Butler Function)

Concept

Do you ever wondered how to make your bot follow you? Well, now you can do that with simple commands and this way you can get your own private Butler Bot!
Video preview
To do that we will use the
πŸ“¬ Command Handler
, note that this use is optional, you can implement this function directly on your main code or directly on your on_chat method.

Methods/Functions used:

Required Libraries

  • highrise-bot-sdk
  • asyncio

Code

First of all, let’s create a new file called follow.py inside of our functions folder:
File structure example in the workspace.
File structure example in the workspace.
Here’s what we will need to import into our follow.py file:
from highrise import * from highrise.models import * import asyncio
Modules to import in the follow.py file.
Let’s proceed to our functions definitions, since we’re using the command handler for this example, your function name will be the command that you will use in the chat to call it, I will name mines as follow and stop.
Since we want that the bot continuously follows us, this function has to be an loop and be asynchronously executed. For that, we will be using the buit-in Highrise taskgroup, and we will make a function called following_loop to be inserted in this taskgroup
from highrise import * from highrise.models import * import asyncio async def follow(self: BaseBot, user: User, message: str) -> None: async def following_loop(self: BaseBot, user: User, message: str) -> None: pass pass async def stop(self: BaseBot, user: User, message: str) -> None: pass
Example of how the function will be defined in the follow.py file.
Finally, let’s implement the functions and after it you will see a detailed step by step guide on how the code works:
from highrise import * from highrise.models import * import asyncio from asyncio import Task async def follow(self: BaseBot, user: User, message: str) -> None: async def following_loop(self: BaseBot, user: User, message: str) -> None: if message.startswith("/following_loop"): await self.highrise.chat("Invalid command, please use /follow") return while True: #gets the user position room_users = (await self.highrise.get_room_users()).content for room_user, position in room_users: if room_user.id == user.id: user_position = position break print(user_position) if type(user_position) != AnchorPosition: await self.highrise.walk_to(Position(user_position.x + 1, user_position.y, user_position.z)) await asyncio.sleep(0.5) taskgroup = self.highrise.tg task_list = list(taskgroup._tasks) for task in task_list: if task.get_name() == "following_loop": await self.highrise.chat("Already following someone") return #checks if this function is already in the Highrise class tg (task group). taskgroup.create_task(coro=following_loop(self, user, message)) task_list : list[Task] = list(taskgroup._tasks) # Sets the name of the task who has the following_loop function to "following_loop" for task in task_list: if task.get_coro().__name__ == "following_loop": task.set_name("following_loop") await self.highrise.chat(f"Following {user.username}") async def stop(self: BaseBot, user: User, message: str) -> None: taskgroup = self.highrise.tg task_list = list(taskgroup._tasks) for task in task_list: if task.get_name() == "following_loop": task.cancel() await self.highrise.chat(f"Stopping following {user.username}") return await self.highrise.chat("Not following anyone") return
Function implementation in the follow.py file.
Now let’s understand how the code works:
  1. from highrise import * and from highrise.models import *:
      • These lines import the necessary modules and classes required for interacting with the game's API.
  1. async def follow(self: BaseBot, user: User, message: str) -> None::
      • This is an asynchronous function called follow.
      • It takes four parameters: self (a reference to the instance of the class this method belongs to), user (an object representing the user to follow), and message (a string representing the chat message sent by the user).
  1. taskgroup = self.highrise.tg and task_list = list(taskgroup._tasks):
      • The code fetches the task group (tg) from self.highrise and creates a list of tasks (task_list) in that task group.
  1. for task in task_list: if task.get_name() == "following_loop": await self.highrise.chat("Already following someone") return:
      • The code checks if there is already a task named "following_loop" in the task group.
      • If such a task is found, it means the bot is already following someone.
      • In that case, the function sends a message to the chat using self.highrise.chat() to inform the user that the bot is already following someone, and then returns, ending the function.
  1. taskgroup.create_task(coro=following_loop(self, user, message), name="following_loop"):
      • If the bot is not already following someone, the code creates a new task named "following_loop" in the task group (taskgroup).
      • It uses following_loop(self, user, message) as the coroutine to execute for the task.
  1. await self.highrise.chat(f"Following {user.username}"):
      • After creating the "following_loop" task, the function sends a message to the chat using self.highrise.chat() to inform the user that the bot is now following the specified user.
  1. async def stop(self: BaseBot, user: User, message: str) -> None::
      • This is an asynchronous function called stop.
      • It takes four parameters: self (a reference to the instance of the class this method belongs to), user (an object representing the user the bot is following), and message (a string representing the chat message sent by the user).
  1. The stop function has similar initial code as the follow function to get the task group and task list.
  1. for task in task_list: if task.get_name() == "following_loop": task.cancel() await self.highrise.chat(f"Stopping following {user.username}") return:
      • The code checks if there is a task named "following_loop" in the task group (taskgroup).
      • If such a task is found, it means the bot is following someone, and it cancels the task using task.cancel().
      • The function then sends a message to the chat using self.highrise.chat() to inform the user that the bot is stopping following the specified user, and then returns, ending the function.
  1. await self.highrise.chat("Not following anyone") return:
      • If the bot is not following anyone (no "following_loop" task found), the function sends a message to the chat to inform the user that the bot is not following anyone, and then returns, ending the function.
  1. async def following_loop(self: BaseBot, user: User, message: str) -> None::
      • This is an asynchronous function called following_loop.
      • It takes four parameters: self (a reference to the instance of the class this method belongs to), user (an object representing the user the bot is following), and message (a string representing the chat message sent by the user).
  1. if message.startswith("/following_loop"): await self.highrise.chat("Invalid command, please use /follow") return:
      • The code checks if the message starts with "/following_loop". If it does, it means the user entered an invalid command directly for the following_loop function.
      • The function sends a message to the chat using self.highrise.chat() to inform the user that they should use "/follow" instead of "/following_loop", and then returns, ending the function.
  1. while True::
      • The code enters an infinite loop to keep the bot following the user indefinitely until the task is canceled.
  1. (await self.highrise.get_room_users()).content:
      • The code calls an asynchronous method get_room_users() on self.highrise to fetch a list of users in the room where this chat event occurred.
      • The result is awaited using await.
      • The method seems to return some kind of response object, and content appears to be the actual list of room users.
  1. The code then iterates through the room users and their corresponding positions to find the user_position of the specified user.
  1. if type(user_position) != AnchorPosition: await self.highrise.walk_to(Position(user_position.x + 1, user_position.y, user_position.z)):
      • The code checks if the user_position is not an AnchorPosition type.
      • If it is not, it means the user is not anchored (in a fixed position) and can be moved.
      • The bot then calls self.highrise.walk_to(Position(...)) to move the bot to the right of the user by increasing the x coordinate by 1 (it follows the user from the right side).
      • The walk_to method seems to be used for moving the bot in the game.
  1. await asyncio.sleep(0.5):
      • After following the user, the function awaits for 0.5 seconds using asyncio.sleep(0.5) before the next iteration of the loop.
      • This is to avoid unnecessary CPU load and to give some time between bot movements.
Built with Potion.so