Part 3 - Making a lightbulb extension#
Extensions are a useful way of separating 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.option(
12 "user", "The user to get information about.", hikari.User, required=False
13)
14@lightbulb.command("userinfo", "Get info on a server member.", pass_options=True)
15@lightbulb.implements(lightbulb.PrefixCommand, lightbulb.SlashCommand)
16async def userinfo(ctx: lightbulb.Context, user: Optional[hikari.User] = None) -> None:
17 if not (guild := ctx.get_guild()):
18 await ctx.respond("This command may only be used in servers.")
19 return
20
21 user = user or ctx.author
22 user = ctx.bot.cache.get_member(guild, user)
23
24 if not user:
25 await ctx.respond("That user is not in the server.")
26 return
27
28 created_at = int(user.created_at.timestamp())
29 joined_at = int(user.joined_at.timestamp())
30
31 roles = (await user.fetch_roles())[1:] # All but @everyone
32 roles = sorted(
33 roles, key=lambda role: role.position, reverse=True
34 ) # sort them by position, then reverse the order to go from top role down
35
36 embed = (
37 hikari.Embed(
38 title=f"User Info - {user.display_name}",
39 description=f"ID: `{user.id}`",
40 colour=0x3B9DFF,
41 timestamp=datetime.now().astimezone(),
42 )
43 .set_footer(
44 text=f"Requested by {ctx.author.username}",
45 icon=ctx.author.display_avatar_url,
46 )
47 .set_thumbnail(user.avatar_url)
48 .add_field(
49 "Bot?",
50 "Yes" if user.is_bot else "No",
51 inline=True,
52 )
53 .add_field(
54 "Created account on",
55 f"<t:{created_at}:d>\n(<t:{created_at}:R>)",
56 inline=True,
57 )
58 .add_field(
59 "Joined server on",
60 f"<t:{joined_at}:d>\n(<t:{joined_at}:R>)",
61 inline=True,
62 )
63 .add_field(
64 "Roles",
65 ", ".join(r.mention for r in roles),
66 inline=False,
67 )
68 )
69
70 await ctx.respond(embed)
71
72
73def load(bot: lightbulb.BotApp) -> None:
74 bot.add_plugin(info_plugin)
And in bot.py
we’ll need to make a little change. On line 17, 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-08-13 17:22:03,151 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 Line 10 - Decorator to attach the following command to the plugin
- Line 11-13 - 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.
” Line 14 - Decorator to create the command, setting the name to “
userinfo
” and the description to “Get info on a server member.
”Line 15 - Converts the decorated function into a prefix command and slash command
- Line 16 - The command’s function, which takes the parameters
ctx
anduser
- Line 17 - Get the guild (
ctx.get_guild()
) - Line 21-22 - If a user was not passed as an option (
user
will beNone
), we assignctx.author
touser
Then, get the member of the guildNote: This will returnNone
if the target is not found in the guild - Line 28-29 - Get the UNIX Timestamps for when the member created their account and joined the guildNote: The rounding with
int()
is necessary, as Discord timestamps only work with integers, not floats Line 31-34 - Get the member’s list of roles, excluding
@everyone
, then sort them from highest role to lowestLine 37-42 - Make a Discord embed setting the title, description, colour and timestamp
- Line 48-67 - 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 70 - respond to the interaction with the embed (Read the docs - Context.respond)
- Line 73-74 - the load function, to load the extension when the bot startsNote: This is required in each extension