Part 3 - Making a lightbulb extension#
Extensions are a useful way to separate parts of your bot into different files, making it easier to manage.
So, let’s create an extension!
In your my_bot
folder make a new folder named extensions
.
Then in that folder create a file named info.py
.
Your file structure should look like this now:
my_bot
├─ bot.py
├─ requirements.txt
├─ .env
│
└─ extensions
├─ info.py
In info.py
paste the following:
1from datetime import datetime
2from typing import Optional
3
4import hikari
5import lightbulb
6
7info_plugin = lightbulb.Plugin("Info")
8
9
10@info_plugin.command
11@lightbulb.app_command_permissions(dm_enabled=False)
12@lightbulb.option(
13 "user", "The user to get information about.", hikari.User, required=False
14)
15@lightbulb.command("userinfo", "Get info on a server member.", pass_options=True)
16@lightbulb.implements(lightbulb.SlashCommand)
17async def userinfo(
18 ctx: lightbulb.SlashContext, user: Optional[hikari.User] = None
19) -> None:
20 assert ctx.guild_id is not None
21
22 user = user or ctx.author
23 user = ctx.bot.cache.get_member(ctx.guild_id, user)
24
25 if not user:
26 await ctx.respond("That user is not in this server.")
27 return
28
29 created_at = int(user.created_at.timestamp())
30 joined_at = int(user.joined_at.timestamp())
31
32 roles = [f"<@&{role}>" for role in user.role_ids if role != ctx.guild_id]
33
34 embed = (
35 hikari.Embed(
36 title=f"User Info - {user.display_name}",
37 description=f"ID: `{user.id}`",
38 colour=0x3B9DFF,
39 timestamp=datetime.now().astimezone(),
40 )
41 .set_footer(
42 text=f"Requested by {ctx.author}",
43 icon=ctx.author.display_avatar_url,
44 )
45 .set_thumbnail(user.avatar_url)
46 .add_field(
47 "Bot?",
48 "Yes" if user.is_bot else "No",
49 inline=True,
50 )
51 .add_field(
52 "Created account on",
53 f"<t:{created_at}:d>\n(<t:{created_at}:R>)",
54 inline=True,
55 )
56 .add_field(
57 "Joined server on",
58 f"<t:{joined_at}:d>\n(<t:{joined_at}:R>)",
59 inline=True,
60 )
61 .add_field(
62 "Roles",
63 ", ".join(roles) if roles else "No roles",
64 inline=False,
65 )
66 )
67
68 await ctx.respond(embed)
69
70
71def load(bot: lightbulb.BotApp) -> None:
72 bot.add_plugin(info_plugin)
And in bot.py
we’ll need to make a little change. On line 18, add:
bot.load_extensions_from("./extensions/")
So, now let’s run the bot with our new /userinfo
command!
You should see a new line in your output:
I 2022-12-24 13:24:36,782 lightbulb.app: Extension loaded 'extensions.info'
Now let’s go and try out the command:
Now to go through what everything does…
Line 7 - Create a plugin named
Info
which will be used to add our new command
Read the docs - PluginsLine 10 - Decorator to attach the following command to the plugin
Line 11 - Add some permissions to the command, disabling it from being accessible in DMs (we only want it to be run in servers)
Line 12-14 - Add a command option named “
user
” with a type ofhikari.User
that is not required and a description of “The user to get information about.
”
Read the docs - Converters and Slash Command Options TypesLine 15 - Decorator to create the command, setting the name to “
userinfo
” and the description to “Get info on a server member.
”Line 16 - Converts the decorated function into a slash command
Line 17-19 - The command’s function, which takes the parameters
ctx
and the optionuser
Read the docs - lightbulb.Context
Read the docs - hikari.UserLine 20 - Assert that
ctx.guild_id
is notNone
, because it never will beLine 22 - If a user was not passed as an option (
user
will beNone
), we assignctx.author
touser
Line 23 - Get the
Member
object for that user from the bot’s cacheNote
This will return
None
if the user is not in the guildLine 29-30 - Get the UNIX Timestamps for when the member created their account and joined the guild
Note
The rounding with
int()
is necessary, as Discord timestamps only work with integers, not floatsLine 32 - Get the member’s list of roles, excluding
@everyone
(the@everyone
role’s ID is the guild’s ID), and format it into a list of role mentionsLine 35-40 - Make a Discord embed setting the title, description, colour and timestamp
Line 46-65 - Add fields to the embed, stating:
whether the user is a bot or not
when their account was created & when they joined the server, using Discord Timestamps
a list of roles the member has
Line 68 - Respond to the interaction with the embed (Read the docs - Context.respond)
Line 71-72 - The load function, to load the extension when the bot starts
Note
This load function is required in each extension