diff --git a/examples/basic_bot_example.py b/examples/basic_bot_example.py
index 9524a2a9..53569a37 100644
--- a/examples/basic_bot_example.py
+++ b/examples/basic_bot_example.py
@@ -1,57 +1,74 @@
# -*- coding: utf-8 -*-
-# Copyright © tandemdude 2020-present
+# Copyright (c) 2023-present tandemdude
#
-# This file is part of Lightbulb.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
#
-# Lightbulb is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
#
-# Lightbulb is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with Lightbulb. If not, see .
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
import hikari
import lightbulb
-bot = lightbulb.BotApp(prefix="!", token="YOUR_TOKEN", intents=hikari.Intents.ALL_UNPRIVILEGED)
+bot = hikari.GatewayBot(token="...")
+client = lightbulb.client_from_app(bot)
+
+bot.subscribe(hikari.StartingEvent, client.start)
-@bot.listen(hikari.ShardReadyEvent)
-async def ready_listener(_):
- print("The bot is ready!")
+@client.register()
+class Ping(
+ lightbulb.SlashCommand,
+ name="ping",
+ description="Checks that the bot is alive",
+):
+ @lightbulb.invoke
+ async def invoke(self, ctx: lightbulb.Context) -> None:
+ """Checks that the bot is alive"""
+ await ctx.respond("Pong!")
-@bot.command()
-@lightbulb.command("ping", "Checks that the bot is alive")
-@lightbulb.implements(lightbulb.PrefixCommand)
-async def ping(ctx: lightbulb.Context) -> None:
- """Checks that the bot is alive"""
- await ctx.respond("Pong!")
+@client.register()
+class Echo(
+ lightbulb.SlashCommand,
+ name="echo",
+ description="Repeats the user's input",
+):
+ text = lightbulb.string("text", "Text to repeat")
+ @lightbulb.invoke
+ async def invoke(self, ctx: lightbulb.Context) -> None:
+ """Repeats the user's input"""
+ await ctx.respond(self.text)
-@bot.command()
-@lightbulb.option("num2", "Second number", int)
-@lightbulb.option("num1", "First number", int)
-@lightbulb.command("add", "Adds the two given numbers together")
-@lightbulb.implements(lightbulb.PrefixCommand)
-async def add(ctx: lightbulb.Context) -> None:
- """Adds the two given numbers together"""
- num1, num2 = ctx.options.num1, ctx.options.num2
- await ctx.respond(f"{num1} + {num2} = {num1 + num2}")
+@client.register()
+class Add(
+ lightbulb.SlashCommand,
+ name="add",
+ description="Adds the two given numbers together",
+):
+ # Order of options go from top to bottom
+ num1 = lightbulb.integer("num1", "First number")
+ num2 = lightbulb.integer("num2", "Second number")
-@bot.command()
-@lightbulb.option("user", "User to greet", hikari.User)
-@lightbulb.command("greet", "Greets the specified user")
-@lightbulb.implements(lightbulb.PrefixCommand)
-async def greet(ctx: lightbulb.Context) -> None:
- await ctx.respond(f"Hello {ctx.options.user.mention}!")
+ @lightbulb.invoke
+ async def invoke(self, ctx: lightbulb.Context) -> None:
+ """Adds the two given numbers together"""
+ await ctx.respond(f"{self.num1} + {self.num2} = {self.num1 + self.num2}")
bot.run()
diff --git a/examples/basic_slash_command_bot_example.py b/examples/basic_slash_command_bot_example.py
deleted file mode 100644
index 8caf5378..00000000
--- a/examples/basic_slash_command_bot_example.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright © tandemdude 2020-present
-#
-# This file is part of Lightbulb.
-#
-# Lightbulb is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Lightbulb is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with Lightbulb. If not, see .
-import lightbulb
-
-bot = lightbulb.BotApp(prefix="!", token="YOUR_TOKEN")
-
-
-@bot.command()
-@lightbulb.command("ping", "Checks that the bot is alive")
-@lightbulb.implements(lightbulb.SlashCommand)
-async def ping(ctx: lightbulb.Context) -> None:
- """Checks that the bot is alive"""
- await ctx.respond("Pong!")
-
-
-@bot.command()
-@lightbulb.option("text", "Text to repeat")
-@lightbulb.command("echo", "Repeats the user's input")
-@lightbulb.implements(lightbulb.SlashCommand)
-async def echo(ctx: lightbulb.Context) -> None:
- await ctx.respond(ctx.options.text)
-
-
-bot.run()
diff --git a/examples/extension_example.py b/examples/extension_example.py
index a28ec1f9..2ba1fa89 100644
--- a/examples/extension_example.py
+++ b/examples/extension_example.py
@@ -1,36 +1,38 @@
# -*- coding: utf-8 -*-
-# Copyright © tandemdude 2020-present
+# Copyright (c) 2023-present tandemdude
#
-# This file is part of Lightbulb.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
#
-# Lightbulb is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
#
-# Lightbulb is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with Lightbulb. If not, see .
-import lightbulb
-
-example_plugin = lightbulb.Plugin("ExamplePlugin")
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+import lightbulb
-@example_plugin.command()
-@lightbulb.command("ping", "Checks that the bot is alive")
-@lightbulb.implements(lightbulb.PrefixCommand, lightbulb.SlashCommand)
-async def ping(ctx: lightbulb.Context) -> None:
- """Checks that the bot is alive"""
- await ctx.respond("Pong!")
-
+loader = lightbulb.Loader()
-def load(bot):
- bot.add_plugin(example_plugin)
+@loader.command
+class Greet(
+ lightbulb.SlashCommand,
+ name="greet",
+ description="Greets the specified user",
+):
+ user = lightbulb.user("user", "User to greet")
-def unload(bot):
- bot.remove_plugin(example_plugin)
+ @lightbulb.invoke
+ async def invoke(self, ctx: lightbulb.Context) -> None:
+ """Greets the specified user"""
+ await ctx.respond(f"Hello, {self.user.mention}!")
diff --git a/examples/in_depth_component_example.py b/examples/in_depth_component_example.py
deleted file mode 100644
index 1c79e92c..00000000
--- a/examples/in_depth_component_example.py
+++ /dev/null
@@ -1,209 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright © tandemdude 2020-present
-#
-# This file is part of Lightbulb.
-#
-# Lightbulb is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Lightbulb is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with Lightbulb. If not, see .
-
-import typing as t
-
-import hikari
-from hikari.impl import MessageActionRowBuilder
-
-import lightbulb
-
-########################################################################
-# Helper functions and data.
-########################################################################
-
-# Mapping of color names to hex literal and a fact about the color.
-COLORS: t.Mapping[str, t.Tuple[int, str]] = {
- "Red": (
- 0xFF0000,
- "Due to it's long wavelength, red is the first color a baby sees!",
- ),
- "Green": (
- 0x00FF00,
- "Plants green color help them use photosynthesis!",
- ),
- "Blue": (
- 0x0000FF,
- "Globally, blue is the most common favorite color!",
- ),
- "Orange": (0xFFA500, "The color orange is named after its fruity counterpart, the orange!"),
- "Purple": (
- 0xA020F0,
- "Purple is the hardest color for human eyes to distinguish!",
- ),
- "Yellow": (
- 0xFFFF00,
- "Taxi's and school buses are yellow because it's so easy to see!",
- ),
- "Black": (0x000000, "Black is a color which results from the absence of visible light!"),
- "White": (0xFFFFFF, "White objects fully reflect and scatter all visible light!"),
-}
-
-
-async def generate_rows(bot: lightbulb.BotApp) -> t.Iterable[MessageActionRowBuilder]:
- """Generate 2 action rows with 4 buttons each."""
-
- # This will hold our action rows of buttons. The limit
- # imposed by Discord is 5 rows with 5 buttons each. We
- # will not use that many here, however.
- rows: t.List[MessageActionRowBuilder] = []
-
- # Build the first action row
- row = bot.rest.build_message_action_row()
-
- # Here we iterate len(COLORS) times.
- for i in range(len(COLORS)):
- if i % 4 == 0 and i != 0:
- # If i is evenly divided by 4, and not 0 we want to
- # append the first row to rows and build the second
- # action row. (Gives a more even button layout)
- rows.append(row)
- row = bot.rest.build_message_action_row()
-
- # Extract the current color from the mapping and assign
- # to this label var for later.
- label = list(COLORS)[i]
-
- # We use an enclosing scope here so that we can easily chain
- # method calls of the action row.
- (
- # Adding the buttons into the action row.
- row.add_button(
- # Gray button style, see also PRIMARY, and DANGER.
- hikari.ButtonStyle.SECONDARY,
- # Set the buttons custom ID to the label.
- label,
- )
- # Set the actual label.
- .set_label(label)
- # Finally add the button to the container.
- .add_to_container()
- )
-
- # Append the second action row to rows after the for loop.
- rows.append(row)
-
- # Return the action rows from the function.
- return rows
-
-
-async def handle_responses(
- bot: lightbulb.BotApp,
- author: hikari.User,
- message: hikari.Message,
-) -> None:
- """Watches for events, and handles responding to them."""
-
- # Now we need to check if the user who ran the command interacts
- # with our buttons, we stop watching after 120 seconds (2 mins) of
- # inactivity.
- with bot.stream(hikari.InteractionCreateEvent, 120).filter(
- # Here we filter out events we don't care about.
- lambda e: (
- # A component interaction is a button interaction.
- isinstance(e.interaction, hikari.ComponentInteraction)
- # Make sure the command author hit the button.
- and e.interaction.user == author
- # Make sure the button was attached to our message.
- and e.interaction.message == message
- )
- ) as stream:
- async for event in stream:
- # If we made it through the filter, the user has clicked
- # one of our buttons, so we grab the custom ID.
- cid = event.interaction.custom_id
-
- # Create new embed with info on the color they selected
- embed = hikari.Embed(
- # The color name.
- title=cid,
- # The hex literal we stored earlier.
- color=COLORS[cid][0],
- # The fact about the color.
- description=COLORS[cid][1],
- )
-
- # If we haven't responded to the interaction yet, we
- # need to create the initial response. Otherwise, we
- # need to edit the initial response.
- try:
- # NOTE: We don't have to add the buttons again as they
- # are already on the message. So we don't have to
- # pass components here. If we wanted to update the
- # buttons we would pass a new list of action rows.
- await event.interaction.create_initial_response(
- # The response type is required when creating
- # the initial response. We use MESSAGE_UPDATE
- # because we are updating a message we previously
- # sent. NOTE: even though the message was already
- # sent, this is still the **INITIAL RESPONSE** to
- # the interaction event (button click).
- hikari.ResponseType.MESSAGE_UPDATE,
- embed=embed,
- )
- except hikari.NotFoundError:
- # This error is raised if we have already sent the
- # initial response. Notice no response type is needed
- # here, so we just edit the initial response with the
- # new embed.
- await event.interaction.edit_initial_response(
- embed=embed,
- )
-
- # Once were back outside the stream loop, it's been 2 minutes since
- # the last interaction and it's time now to remove the buttons from
- # the message to prevent further interaction.
- await message.edit(
- # Set components to an empty list to get rid of them.
- components=[]
- )
-
-
-########################################################################
-# Create the bot.
-########################################################################
-
-
-# Instantiate the bot.
-bot = lightbulb.BotApp(token="YOUR_TOKEN", prefix="!")
-
-
-# Create the message command.
-@bot.command()
-@lightbulb.command("rgb", "Get facts on different colors!", guilds=[1234])
-@lightbulb.implements(lightbulb.PrefixCommand, lightbulb.SlashCommand)
-async def rgb_command(ctx: lightbulb.Context) -> None:
- """Get facts on different colors!"""
-
- # Generate the action rows.
- rows = await generate_rows(ctx.bot)
-
- # Send the initial response with our action rows, and save the
- # message for handling interaction responses.
- response = await ctx.respond(
- hikari.Embed(title="Pick a color"),
- components=rows,
- )
- message = await response.message()
-
- # Handle interaction responses to the initial message.
- await handle_responses(ctx.bot, ctx.author, message)
-
-
-# Run the bot.
-bot.run()
diff --git a/examples/moderation_example.py b/examples/moderation_example.py
index ff58a86f..deab7978 100644
--- a/examples/moderation_example.py
+++ b/examples/moderation_example.py
@@ -1,71 +1,97 @@
# -*- coding: utf-8 -*-
-# Copyright © tandemdude 2020-present
+# Copyright (c) 2023-present tandemdude
#
-# This file is part of Lightbulb.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
#
-# Lightbulb is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
#
-# Lightbulb is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with Lightbulb. If not, see .
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
import datetime
import hikari
import lightbulb
-bot = lightbulb.BotApp(token="YOUR_TOKEN", intents=hikari.Intents.ALL_UNPRIVILEGED)
+bot = hikari.GatewayBot(token="...")
+client = lightbulb.client_from_app(bot)
+
+bot.subscribe(hikari.StartingEvent, client.start)
+
+
+@client.register()
+class Ban(
+ lightbulb.SlashCommand,
+ name="ban",
+ description="Bans a user from the server",
+ dm_enabled=False,
+ default_member_permissions=hikari.Permissions.BAN_MEMBERS,
+):
+ # Order of options go from top to bottom
+ user = lightbulb.user("user", "The user to ban")
+ # Give non-required options a default value (e.g. default=None)
+ # Non-required options MUST appear after required options
+ # Required options do not have a default value
+ reason = lightbulb.string("reason", "Reason for the ban", default=None)
+ @lightbulb.invoke
+ async def invoke(self, ctx: lightbulb.Context, rest: hikari.api.RESTClient) -> None:
+ """Ban a user from the server with an optional reason"""
+ if not ctx.guild_id:
+ await ctx.respond("This command can only be used in a guild.")
+ return
-@bot.command()
-@lightbulb.option("reason", "Reason for the ban", required=False)
-@lightbulb.option("user", "The user to ban.", type=hikari.User)
-@lightbulb.command("ban", "Ban a user from the server.")
-@lightbulb.implements(lightbulb.SlashCommand)
-async def ban(ctx: lightbulb.SlashContext) -> None:
- """Ban a user from the server with an optional reason."""
- if not ctx.guild_id:
- await ctx.respond("This command can only be used in a guild.")
- return
+ # Create a deferred response as the ban may take longer than 3 seconds
+ await ctx.respond(hikari.ResponseType.DEFERRED_MESSAGE_CREATE)
+ # Perform the ban
+ await rest.ban_user(ctx.guild_id, self.user.id, reason=self.reason or hikari.UNDEFINED)
+ # Provide feedback to the moderator
+ await ctx.respond(f"Banned {self.user.mention}.\n**Reason:** {self.reason or 'No reason provided.'}")
- # Create a deferred response as the ban may take longer than 3 seconds
- await ctx.respond(hikari.ResponseType.DEFERRED_MESSAGE_CREATE)
- # Perform the ban
- await ctx.app.rest.ban_user(ctx.guild_id, ctx.options.user.id, reason=ctx.options.reason or hikari.UNDEFINED)
- # Provide feedback to the moderator
- await ctx.respond(f"Banned {ctx.options.user.mention}.\n**Reason:** {ctx.options.reason or 'No reason provided.'}")
+@client.register()
+class Purge(
+ lightbulb.SlashCommand,
+ name="purge",
+ description="Purge a certain amount of messages from a channel",
+ dm_enabled=False,
+ default_member_permissions=hikari.Permissions.MANAGE_MESSAGES,
+):
+ count = lightbulb.integer("count", "The amount of messages to purge", max_value=100, min_value=1)
-@bot.command()
-@lightbulb.option("count", "The amount of messages to purge.", type=int, max_value=100, min_value=1)
-# You may also use pass_options to pass the options directly to the function
-@lightbulb.command("purge", "Purge a certain amount of messages from a channel.", pass_options=True)
-@lightbulb.implements(lightbulb.SlashCommand)
-async def purge(ctx: lightbulb.SlashContext, count: int) -> None:
- """Purge a certain amount of messages from a channel."""
- if not ctx.guild_id:
- await ctx.respond("This command can only be used in a server.")
- return
+ @lightbulb.invoke
+ async def invoke(self, ctx: lightbulb.Context, rest: hikari.api.RESTClient) -> None:
+ """Purge a certain amount of messages from a channel"""
+ if not ctx.guild_id:
+ await ctx.respond("This command can only be used in a server.")
+ return
- # Fetch messages that are not older than 14 days in the channel the command is invoked in
- # Messages older than 14 days cannot be deleted by bots, so this is a necessary precaution
- messages = (
- await ctx.app.rest.fetch_messages(ctx.channel_id)
- .take_until(lambda m: datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=14) > m.created_at)
- .limit(count)
- )
- if messages:
- await ctx.app.rest.delete_messages(ctx.channel_id, messages)
- await ctx.respond(f"Purged {len(messages)} messages.")
- else:
- await ctx.respond("Could not find any messages younger than 14 days!")
+ # Fetch messages that are not older than 14 days in the channel the command is invoked in
+ # Messages older than 14 days cannot be deleted by bots, so this is a necessary precaution
+ messages = (
+ await rest.fetch_messages(ctx.channel_id)
+ .take_until(
+ lambda m: datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=14) > m.created_at
+ )
+ .limit(self.count)
+ )
+ if messages:
+ await rest.delete_messages(ctx.channel_id, messages)
+ await ctx.respond(f"Purged {len(messages)} messages.")
+ else:
+ await ctx.respond("Could not find any messages younger than 14 days!")
bot.run()
diff --git a/examples/plugin_example.py b/examples/plugin_example.py
deleted file mode 100644
index d3e67e63..00000000
--- a/examples/plugin_example.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright © tandemdude 2020-present
-#
-# This file is part of Lightbulb.
-#
-# Lightbulb is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Lightbulb is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with Lightbulb. If not, see .
-import lightbulb
-
-bot = lightbulb.BotApp(prefix="!", token="YOUR_TOKEN")
-
-
-plugin = lightbulb.Plugin("Example Plugin")
-
-
-@plugin.command()
-@lightbulb.option("text", "Text to repeat", modifier=lightbulb.OptionModifier.CONSUME_REST)
-@lightbulb.command("echo", "Repeats the user's input")
-@lightbulb.implements(lightbulb.PrefixCommand)
-async def echo(ctx: lightbulb.Context) -> None:
- await ctx.respond(ctx.options.text)
-
-
-bot.add_plugin(plugin)
-bot.run()
diff --git a/fragments/436.doc.md b/fragments/436.doc.md
new file mode 100644
index 00000000..7985fec4
--- /dev/null
+++ b/fragments/436.doc.md
@@ -0,0 +1 @@
+Update code examples for version 3 from version 2.