-
Notifications
You must be signed in to change notification settings - Fork 1
1. How To Use
Here we will be talking about how to use the interface of the QalibContext
This is a simple XML file that contains a barebones embed
<discord>
<message key="test_key">
<embed>
<title>Hello World</title>
<colour>cyan</colour>
<fields>
<field>
<name>Field 1</name>
<value>This is the first field in the embed that is mandatory</value>
</field>
</fields>
</embed>
</message>
</discord>
Let us dissect this, piece by piece. First off, we have the <discord>
tag which is the container (ElementTree) for all the <message>
elements. We can have a variable number of <message>
elements and they are uniquely identified by their key
attribute such as <message key="test_key">
, which is used to identify this message, in this case test_key
uniquely identifies this message.
The tag has a key attribute which is used to identify the message, and various tags. You can use the tag to create a custom embed that is attached to the message as seen above.
There are other tags that allow you to control how the message is used, such as the true tag which is a boolean value that determines whether the message is sent as text-to-speech or not, and the <delete_after>15.0</delete_after> tag which is a float value that deletes the message after the specified number of seconds. The full list can be seen here
We then use the components of the embed class, so the 3 mandatory elements of an embed that is required to render it is the <title>
, <colour>
/<color>
, and at least one <field>
in the <fields>
container, which contain a <name>
, and a <value>
.
Using more of the attributes that can associated with an embed
Each embed can be extended to leverage more the things that an embed can offer such as:
- Description
- Type
- Timestamp (with format attribute defaulting to "%Y-%m-%d %H:%M:%S.%f")
- Url
- Footer
- Text
- Icon
- Thumbnail
- Image
- Author
- Name
- Icon
- Url
So an example of fully using all the features an embed could offer is as such:
<discord>
<message key="test_key">
<embed>
<title>Test</title>
<description>Test Description</description>
<type>rich</type>
<colour>magenta</colour>
<timestamp format="%d-%m%-Y">22-04-2023</timestamp>
<url>https://www.discord.com</url>
<fields>
<field>
<name>Test Field</name>
<text>Test Text</text>
</field>
</fields>
<footer>
<text>Test Footer</text>
<icon>https://cdn.discordapp.com/embed/avatars/0.png</icon>
</footer>
<thumbnail>https://cdn.discordapp.com/embed/avatars/0.png</thumbnail>
<image>https://cdn.discordapp.com/embed/avatars/0.png</image>
<author>
<name>Test Author</name>
<icon>https://cdn.discordapp.com/embed/avatars/0.png</icon>
<url>https://discordapp.com</url>
</author>
</embed>
</message>
</discord>
You can choose your TemplateEngine
. The ones that are currently defined is Formatter
which uses python's format method, and the Jinja2
template engine, which is an adapter over Jinja2. The TemplateEngine
is used in the form of a Dependency Injection into the Renderer
instance via its constructor. The Renderer
templates and deserializes the files into a Display
. The Renderer
instance is injected into the QalibContext
which extends the discord.ext.commands.Context
You can use the qalib_context
decorator over a bot.command so that it overrides the discord.ext.commands.Context
with its subclass QalibContext.
<discord>
<message key="bank">
<embed>
<title>Balance {player.name}</title>
<colour>cyan</colour>
<fields>
<field>
<name>{player.bank_name}</name>
<value>Has π΅{player.funds} EGP in the bank</value>
</field>
</fields>
</embed>
</message>
</discord>
so if we have this sample file called player.xml
that is in the templates/
directory:
<discord>
<message key="army">
<embed>
<title>Army of {player.name}</title>
<colour>cyan</colour>
<fields>
<field>
<name>{player.army_name}</name>
<value>Has π{player.soldiers} defending the nation</value>
</field>
</fields>
</embed>
</message>
<message key="bank">
<embed>
<title>Hello {player.name}</title>
<colour>cyan</colour>
<fields>
<field>
<name>{player.bank_name}</name>
<value>Has π΅{player.funds} EGP in the bank</value>
</field>
</fields>
</embed>
</message>
</discord>
and then can be used by an QalibContext
instance
from dataclasses import dataclass
from typing import Literal
import discord
from discord.ext import commands
import qalib
from qalib.template_engines.formatter import Formatter
bot = commands.AutoShardedBot(command_prefix="!", intents=discord.Intents.all())
Messages = Literal["balance", "army"]
@dataclass
class Player:
name: str
bank: str
funds: float
army_name: str
soldiers: int
def fetch_player(name: str) -> Player:
...
@bot.command()
@qalib.qalib_context(Formatter(), "templates/player.xml")
async def test(ctx: qalib.QalibContext[Messages]):
await ctx.rendered_send("army", keywords={
"player": fetch_player(name)
})
@bot.event
async def on_ready():
await bot.tree.sync()
There is also a QalibInteraction
, so that it extends the discord.Interaction
instance. Using the same .xml
file as the previous section we are able to use the QalibInteraction
with a slash command as such:
from dataclasses import dataclass
from typing import Literal
import discord
from discord.ext import commands
import qalib
from qalib.template_engines.formatter import Formatter
bot = commands.AutoShardedBot(command_prefix="!", intents=discord.Intents.all())
Messages = Literal["balance", "army"]
@dataclass
class Player:
name: str
bank: str
funds: float
army_name: str
soldiers: int
def fetch_player(name: str) -> Player:
...
@bot.tree.command(name="test")
@discord.app_commands.describe(player_name="What is the name of the player?")
@qalib.qalib_interaction(Formatter(), "templates/player.xml")
async def test(interaction: qalib.QalibInteraction[Messages], name):
await interaction.rendered_send("army", keywords={
"player": fetch_player(name)
})
QalibInteractions
are also able to render Modals from their documents using the .respond_with_modal(key, methods, keywords)
method.
We also support the rendering of views, and the different UI Components for each embed.
<discord>
<message key="welcome">
<embed>
<title>Greet</title>
<colour>cyan</colour>
<fields>
<field>
<name>Hello!</name>
<value>Welcome!</value>
</field>
</fields>
</embed>
<view>
<button key="greet">
<label>Click Me!</label>
<style>success</style>
</button>
</view>
</message>
</discord>
This is stored as templates/button.xml
There are other components that can be rendered, and is shown in the XML View Section
and then can be rendered using the EmbedManager
from typing import Literal
import discord
from discord.ext import commands
import qalib
from qalib.template_engines.formatter import Formatter
bot = commands.AutoShardedBot(command_prefix="!", intents=discord.Intents.all())
Messages = Literal["welcome"]
async def acknowledged(interaction: discord.Interaction):
await interaction.response.send_message("Acknowledged", ephemeral=True)
@bot.command()
@qalib.qalib_context(Formatter(), "templates/button.xml")
async def greet(ctx: qalib.QalibContext[Messages]):
await ctx.rendered_send("welcome", callables={"greet":acknowledged})
There is also in-built menus, that you can make easily. You just have to define the embeds in a <menu>
tag, with a unique key
attribute, and you can use that to make a sequential menu (order appears as defined).
<discord>
<menu key="menu1">
<message key="1">
<embed>
<title>Page 1</title>
<colour>magenta</colour>
<fields>
<field>
<name>This is the first page</name>
<value>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod</value>
</field>
</fields>
</embed>
</message>
<message key="2">
<embed>
<title>Page 2</title>
<colour>magenta</colour>
<fields>
<field>
<name>This is the second page</name>
<value>tempor incididunt ut labore et dolore magna aliqua.</value>
</field>
</fields>
</embed>
</message>
<message key="3">
<embed>
<title>Page 3</title>
<colour>magenta</colour>
<fields>
<field>
<name>This is the third page</name>
<value> Eu non diam phasellus vestibulum lorem sed risus.</value>
</field>
</fields>
</embed>
</message>
</menu>
</discord>
Then you can use them in two ways. The first is passing the key menu key as the second argument to the embed manger, and it will automatically set that menu as the root.
from typing import Literal
import discord
from discord.ext import commands
import qalib
from qalib.template_engines.formatter import Formatter
bot = commands.AutoShardedBot(command_prefix="!", intents=discord.Intents.all())
Menus = Literal["menu1"]
@bot.command()
@qalib.qalib_context(Formatter(), "templates/player.xml")
async def test(ctx: qalib.QalibContext[Menus]):
await ctx.menu("menu1")
or you can manually define it as so:
from typing import Literal
import discord
from discord.ext import commands
import qalib
from qalib.template_engine.formatter import Formatter
bot = commands.AutoShardedBot(command_prefix="!", intents=discord.Intents.all())
Menus = Literal["menu1"]
@bot.command()
async def test(ctx):
manager: qalib.QalibContext[Menus] = qalib.QalibContext(ctx, qalib.Renderer(Formatter(), "templates/player.xml")
await manager.menu("menu1")
its also possible to set the menu, by using the set_root
method which sets the root to a menu (instead of the default <discord>
tag).
π Discord-Qalib 2023