API Reference

Bot

class osmium_chat.bot.Bot(prefix, client_id, *, logger=None)[source]

Bases: object

The main entry point for an Osmium bot.

Holds connection state, the registered event listeners, and the authenticated User once connected.

Commands and event listeners are defined by subclassing Commands and registering the subclass with add_commands().

Events

The built-in events (used with listen()) are:

  • connect — fired with no arguments once the bot has authorized.

  • message — fired with a Message for every inbound message, regardless of where it came from.

  • guild_message — fired with a Message when the message was sent in a community (guild) channel.

  • dm_message — fired with a Message when the message was a direct message to the bot.

  • command_error — fired with (ctx, error) when a command lookup or invocation fails.

from osmium_chat import Bot, Context, Message, commands

class MyCommands(commands.Commands):
    @commands.listen("message")
    async def on_message(self, message: Message) -> None:
        ...

    @commands.command("ping")
    async def ping(self, ctx: Context) -> None:
        await ctx.channel.send("pong")

bot = Bot(prefix="!", client_id=12345)
bot.add_commands(MyCommands)
bot.run(token="...")
Parameters:
prefix
user: User | None
async dispatch(event, *args, **kwargs)[source]

Invoke every listener registered for event.

Handler errors are logged and swallowed so one faulty listener can’t take down the connection or block the others.

Parameters:
Return type:

None

async lookup_invite(code)[source]

Fetch an invite by code and return its full metadata.

Parameters:

code (str) – The invite code to resolve.

Returns:

The InvitePreview for the invite.

Raises:

RequestError – If the gateway cannot find the invite.

Return type:

InvitePreview

add_commands(cls, *args, **kwargs)[source]

Instantiate a Commands subclass and register all its decorated commands and listeners.

The bot calls cls(self, *args, **kwargs), so any extra arguments are forwarded directly to the subclass __init__ after bot. This lets command collections accept configuration at registration time without needing globals or post-init setters:

class Greeter(commands.Commands):
    def __init__(self, bot: Bot, greeting: str) -> None:
        super().__init__(bot)
        self.greeting = greeting

    @commands.command("hi")
    async def hi(self, ctx: Context) -> None:
        await ctx.channel.send(self.greeting)

bot.add_commands(Greeter, greeting="Howdy!")
Parameters:
  • cls (type[Commands]) – An uninitialised Commands subclass.

  • args (Any) – Extra positional arguments passed to cls.__init__ after bot.

  • kwargs (Any) – Extra keyword arguments passed to cls.__init__.

Raises:

ValueError – If any command name or alias is already registered.

Return type:

None

remove_commands(cls)[source]

Remove all commands and listeners that were registered from cls.

Parameters:

cls (type[Commands]) – The same Commands subclass that was passed to add_commands().

Return type:

None

get_command(name)[source]

Look up a command by name or alias.

Parameters:

name (str) – The name or alias to resolve.

Return type:

Command | None

async process_commands(update)[source]

Turn an inbound message into a command invocation.

Builds the Context, fires the message event, and — if the message starts with the prefix and names a known command — parses its arguments and invokes it. Command failures are surfaced through the command_error event.

Parameters:

update (UpdateMessageCreated) – The decoded message_created payload from the gateway.

Return type:

None

async use_invite(invite_code)[source]

Redeem an invite code on behalf of the bot.

Parameters:

invite_code (str) – The invite code to redeem.

Return type:

None

async connect(token)[source]

Connect to Osmium and run the bot until the connection closes.

This authenticates with token, fires the connect event, then blocks processing incoming messages.

Parameters:

token (str) – The authorization token for this bot.

Return type:

None

run(token)[source]

Start the bot’s event loop and connect, blocking until it closes.

A synchronous convenience wrapper around connect() for use as a program’s entry point.

Parameters:

token (str) – The authorization token for this bot.

Return type:

None

Client

class osmium_chat.client.Client(client_id, bot, *, logger=None)[source]

Bases: object

Low-level WebSocket transport between a Bot and the Osmium gateway.

Handles connecting, the initialize/authorize handshake, sending protobuf messages, and reading the inbound message stream.

Parameters:
WS_URL: str = 'wss://ws-0.osmium.chat'
bot: Bot
id: int
async send_pb(message)[source]

Wrap, serialize, and send a protobuf message over the connection.

This is fire-and-forget: it sends with request id 0 and does not wait for a reply. Use request() when you need the server’s response.

Parameters:

message (Any) – The protobuf message to send.

Return type:

None

async request(payload, *, timeout=60.0)[source]

Send a request and wait for the gateway’s matching RpcResult.

Tags the outbound frame with a unique request id, registers a future for it, and resolves once the server replies with a result carrying the same id. This requires the read loop (_handle_ws()) to be running, which it is for the whole lifetime of a connected bot.

Parameters:
  • payload (Any) – The request protobuf to send.

  • timeout (float) – How long to wait for the response, in seconds.

Returns:

The PB_RpcResult for this request.

Raises:
Return type:

RpcResult

async upload_file(data, filename, mimetype='application/octet-stream', send_as_file=False)[source]

Upload data to the Osmium media service in chunks.

Splits data into parts of up to _UPLOAD_CHUNK_SIZE bytes, sends each part sequentially, and returns the server-side ref together with a ready-to-use PB_MediaRef for attaching to a PB_SendMessage.

Parameters:
  • data (bytes) – The raw file bytes to upload.

  • filename (str) – The file name that will be shown to recipients.

  • mimetype (str) – The MIME type of the file; defaults to application/octet-stream.

  • send_as_file (bool)

Returns:

A (PB_UploadedFileRef, PB_MediaRef) pair.

Raises:

RequestError – If the gateway rejects any part.

Return type:

tuple[UploadedFileRef, MediaRef]

async upload_emoji_image(data, name, mimetype, community_id)[source]

Upload emoji image bytes and return a ref ready for PB_AddStickerToPack.

Splits data into chunks, uploads them, and wraps the result in a PB_MediaRefUploadedFile carrying custom-emoji metadata pointing at community_id’s sticker pack.

Parameters:
  • data (bytes) – Raw image bytes (PNG or WebP recommended).

  • name (str) – The emoji short name stored in the file metadata.

  • mimetype (str) – The MIME type of the image.

  • community_id (int) – The community whose emoji pack this image will be added to; used to populate the pack metadata field.

Returns:

A PB_MediaRefUploadedFile ready to pass as the sticker argument of PB_AddStickerToPack.

Raises:

RequestError – If the gateway rejects any part.

Return type:

MediaRefUploadedFile

async connect(token)[source]

Open the connection, run the handshake, and process messages.

Performs the initialize/authorize exchange, dispatches the bot’s connect event once authorized, then blocks reading messages until the connection closes.

Parameters:

token (str) – The authorization token for the bot.

Raises:

ConnectionError – If the connection closes before authorization.

Return type:

None

Commands

Command parsing and argument conversion.

Commands are defined by subclassing Commands and decorating methods with command(), guild_command(), or dm_command(). Event listeners are decorated with listen(). Register the subclass with add_commands().

Basic types

The built-in converters handle the most common cases:

  • str — raw token, unchanged.

  • int / float — numeric conversion.

  • bool — accepts true/false, yes/no, on/off, y/n, 1/0, enable/disable (case-insensitive).

  • UnicodeEmoji — wraps the raw token.

Any other annotation is called with the raw token (annotation(token)), so a type whose constructor accepts a single string just works. Custom types can be registered by inserting into CONVERTERS.

Mention types

These types are resolved from message entities and community data:

  • User — from a user_mention entity, @username/@<id> text, or a bare username/id.

  • Role — from &<name-or-id> text or a bare name/id.

  • Channel — from #<name-or-id> text or a bare name/id.

  • Category — from #<name-or-id> text or a bare name/id (resolved against categories).

  • CustomEmoji — from a custom_emoji entity or :name: text.

Multi-type (union) parameters

Annotating a parameter as A | B (or Union[A, B]) tells the parser to try each candidate type in declaration order, accepting the first one that succeeds:

class Targeting(commands.Commands):
    @commands.command("target")
    async def target(self, ctx: Context, who: User | Channel) -> None:
        ...

# !target @alice   → who is a User
# !target #general → who is a Channel

When a message entity at the argument’s position unambiguously identifies a type (e.g. a custom_emoji entity for CustomEmoji), the parser skips the fallback candidates and raises BadArgument immediately if conversion fails.

Optional[T] / T | None is treated as the type T with an implicit None default — the None arm is never tried as a conversion candidate.

Quoting

Arguments are split on whitespace. To pass a value containing spaces as a single argument, wrap it in single or double quotes. A backslash escapes the next character inside quotes:

# !echo "hello world"   →   word == "hello world"

Consume-rest and variadic parameters

A keyword-only parameter (declared after a bare *) consumes the entire remaining message as one unsplit string:

class SayCommands(commands.Commands):
    @commands.command("say")
    async def say(self, ctx: Context, *, words: str) -> None:
        await ctx.channel.send(words)

# !say hello there world   →   words == "hello there world"

A variadic *args parameter collects every remaining token:

class MathCommands(commands.Commands):
    @commands.command("sum")
    async def sum_(self, ctx: Context, *numbers: int) -> None:
        await ctx.channel.send(str(sum(numbers)))

# !sum 1 2 3   →   numbers == (1, 2, 3)
class osmium_chat.commands.Command(callback, *, name=None, aliases=(), restriction=CommandRestriction.NONE)[source]

Bases: object

A registered command: a name, optional aliases, and a parsed callback.

The callback’s signature drives argument parsing. The first parameter always receives the Context; each parameter after it becomes a command argument, read from the message text following the command name and converted according to the rules below.

Automatic type conversion

Each argument is converted to its annotated type before the callback runs. The built-in converters are:

  • str (or an un-annotated parameter) — the raw token, unchanged.

  • int — parsed with int.

  • float — parsed with float.

  • bool — accepts true/false, yes/no, y/n, on/off, 1/0, enable/disable (case-insensitive).

  • UnicodeEmoji — wraps the raw token as a UnicodeEmoji.

Mention types (resolved via message entities and community lookups)

  • User — decoded from a user_mention entity; the text at that span must be the user’s numeric id (with or without a leading @).

  • Role — decoded from &<name-or-id> text.

  • Channel — decoded from #<name-or-id> text.

  • Category — decoded from #<name-or-id> text (resolved in categories).

  • CustomEmoji — decoded from a custom_emoji entity (by emoji_id) or from :name: text as a fallback.

Any other annotation is called with the raw token (annotation(token)), so a type whose constructor takes a single string just works. Conversion failures raise BadArgument. New types can be registered by adding to CONVERTERS.

Optional arguments

A parameter with a default value is optional; if the invoker omits it, the default is used. Otherwise a missing argument raises MissingRequiredArgument. An annotation of T | None (i.e. Optional[T]) is converted as T.

Quoting ("...")

Arguments are split on whitespace, so each parameter normally receives a single word. To pass a value that contains spaces as one argument, wrap it in single or double quotes; the surrounding quotes are stripped and a backslash escapes the next character inside them:

@bot.command("echo")
async def echo(ctx: Context, word: str) -> None:
    await ctx.channel.send(word)

# !echo "hello world"   ->   word == "hello world"

Consume-rest (*) and variadic (*args)

A keyword-only parameter — one declared after a bare * — consumes the entire remaining message as a single, unsplit string (quotes are kept verbatim here). This is the idiomatic way to accept free-form text:

@bot.command("say")
async def say(ctx: Context, *, words: str) -> None:
    await ctx.channel.send(words)

# !say hello there world   ->   words == "hello there world"

A variadic *args parameter instead collects every remaining token, converting each one to the annotated element type:

@bot.command("sum")
async def sum_(ctx: Context, *numbers: int) -> None:
    await ctx.channel.send(str(sum(numbers)))

# !sum 1 2 3   ->   numbers == (1, 2, 3)

Any leftover text after all parameters are filled raises TooManyArguments.

Parameters:
callback
name
aliases
restriction
params
async parse_arguments(ctx, view, entity_by_pos, prefix_utf16)[source]

Convert the argument string into call arguments for the callback.

Returns the positional args and keyword-only kwargs to pass to the callback alongside the context.

Parameters:
  • ctx (Context) – The invocation context, used to resolve mention-type arguments.

  • view (StringView) – A view over the message text following the command name.

  • entity_by_pos (dict[int, Any]) – Mapping of UTF-16 start position → message entity, built from the message’s entity list.

  • prefix_utf16 (int) – The UTF-16 length of the command prefix, used to translate view-relative positions back to full-content positions.

Raises:
Return type:

tuple[list[Any], dict[str, Any]]

async invoke(ctx, view)[source]

Parse arguments from view and run the command callback.

Parameters:
  • ctx (Context) – The invocation context, passed as the first argument.

  • view (StringView) – A view over the message text following the command name.

Return type:

None

class osmium_chat.commands.CommandRestriction(*values)[source]

Bases: Enum

Where a command is allowed to be invoked.

NONE = 'none'
DM_ONLY = 'dm_only'
COMMUNITY_ONLY = 'community_only'
class osmium_chat.commands.Commands(bot)[source]

Bases: object

Base class for a collection of related bot commands and listeners.

Subclass this, decorate methods with command(), guild_command(), or dm_command(), and add event listeners with listen(). Then register the subclass (uninitialised) with add_commands().

The default __init__ accepts only bot. Override it to receive additional arguments — they are forwarded from the add_commands() call:

from osmium_chat import Bot, Context, Message, commands

class MyCommands(commands.Commands):
    # No extra args — use the inherited __init__.

    @commands.listen("connect")
    async def on_connect(self) -> None:
        print("connected!")

    @commands.listen("message")
    async def on_message(self, message: Message) -> None:
        print(message.content)

    @commands.command("ping")
    async def ping(self, ctx: Context) -> None:
        await ctx.channel.send("pong")

    @commands.guild_command("info")
    async def info(self, ctx: Context) -> None:
        await ctx.reply(f"Community: {ctx.community.name}")

    @commands.dm_command("help")
    async def help(self, ctx: Context) -> None:
        await ctx.channel.send("Commands: ping, info, help")

class GreetCommands(commands.Commands):
    # Extra constructor argument supplied via add_commands.
    def __init__(self, bot: Bot, greeting: str = "Hello") -> None:
        super().__init__(bot)
        self.greeting = greeting

    @commands.command("greet")
    async def greet(self, ctx: Context) -> None:
        await ctx.channel.send(self.greeting)

bot = Bot(prefix="!", client_id=12345)
bot.add_commands(MyCommands)
bot.add_commands(GreetCommands, greeting="Howdy")
bot.run(token="...")
Parameters:

bot (Bot)

class osmium_chat.commands.Parameter(name, annotation, kind, default)[source]

Bases: object

A single command argument, resolved from the callback’s signature.

Parameters:
  • name (str)

  • annotation (Any)

  • kind (_ParameterKind)

  • default (Any)

name
annotation
kind
default
optional
property required: bool

Whether a value must be supplied for this parameter.

convert(value)[source]

Convert a raw token to this parameter’s annotated type.

Tries each candidate type in declaration order, returning the first successful result.

Parameters:

value (str) – The raw token from the message.

Raises:

BadArgument – If no candidate type can convert the token.

Return type:

Any

async resolve(ctx, value, *, entity=None)[source]

Convert a raw token, using the invocation context for mention types.

Tries each candidate type in declaration order, returning the first successful result. Falls back to convert() for types that don’t need context.

Parameters:
  • ctx (Context) – The invocation context (used to look up mentions).

  • value (str) – The raw token from the message.

  • entity (Any) – The PB_MessageEntity at this argument’s position in the message, or None if absent.

Raises:

BadArgument – If no candidate type can convert the token.

Return type:

Any

class osmium_chat.commands.StringView(text)[source]

Bases: object

A cursor over a command’s argument string.

Hands out whitespace-delimited words one at a time (respecting single and double quotes so multi-word arguments can be passed), or the entire remaining string for “consume rest” parameters.

Parameters:

text (str)

text
index
word_start
property eof: bool

Whether the cursor has reached the end of the string.

skip_whitespace()[source]

Advance the cursor past any run of whitespace.

Return type:

None

rest()[source]

Consume and return the remaining string, stripped of surrounding space.

Return type:

str

get_word()[source]

Consume and return the next word, or None if none remain.

A word is a run of non-whitespace characters, unless it is wrapped in matching quotes, in which case everything up to the closing quote is returned as a single word (with backslash escaping inside the quotes).

word_start is updated to the position in text where the returned word began, so callers can correlate it with message entities.

Return type:

str | None

osmium_chat.commands.command(name=None, *, aliases=(), restriction=None)[source]

Mark a Commands method as a bot command.

Parameters:
  • name (str | None) – The command name; defaults to the method name.

  • aliases (tuple[str, ...]) – Additional names the command responds to.

  • restriction (CommandRestriction) – Where the command may be invoked.

Return type:

Callable[[Callable[[…], Awaitable[None]]], Callable[[…], Awaitable[None]]]

osmium_chat.commands.dm_command(name=None, *, aliases=())[source]

Shorthand for @command(..., restriction=CommandRestriction.DM_ONLY).

Parameters:
Return type:

Callable[[Callable[[…], Awaitable[None]]], Callable[[…], Awaitable[None]]]

osmium_chat.commands.guild_command(name=None, *, aliases=())[source]

Shorthand for @command(..., restriction=CommandRestriction.COMMUNITY_ONLY).

Parameters:
Return type:

Callable[[Callable[[…], Awaitable[None]]], Callable[[…], Awaitable[None]]]

osmium_chat.commands.listen(event)[source]

Mark a Commands method as an event listener.

Parameters:

event (str) – The event name to listen for (e.g. "connect", "message", "guild_message", "dm_message", "command_error").

Return type:

Callable[[Callable[[…], Awaitable[None]]], Callable[[…], Awaitable[None]]]

Context

class osmium_chat.context.Context(*, bot, message, author, channel, community=None, prefix, command=None, invoked_with=None, args=None)[source]

Bases: object

The invocation context handed to a command callback.

Bundles everything a command needs: who sent the message (author), where it came from (channel), the underlying message, and the resolved command metadata. Reply with await ctx.channel.send(...) or the send() shortcut.

Parameters:
bot
message
author: Member | User | None
channel
community
prefix
command
invoked_with
args: list[Any]
async send(content, *, reply_to=None)[source]

Send a message to the channel this command was invoked in.

A shortcut for ctx.channel.send(...).

Parameters:
  • content (str | Content) – The message text, either a plain string or a Content object.

  • reply_to (int | None) – Optional id of a message this should reply to.

Returns:

The newly created message.

Return type:

Message

async reply(content)[source]

Reply to the invoking message, threading it as a reply.

Parameters:

content (str | Content) – The message text, either a plain string or a Content object.

Returns:

The newly created message.

Return type:

Message

Channels

class osmium_chat.channel.ChannelType(*values)[source]

Bases: IntEnum

The kind of a community channel.

Mirrors PB_ChannelType so callers can use a readable name (ChannelType.TEXT) instead of a raw integer when creating channels.

TEXT = 0
VOICE = 1
CATEGORY = 2
class osmium_chat.channel.Channel(chat_ref, client, *, id=None, name=None, type=None, community_id=None, position=None, parent_id=None, community=None)[source]

Bases: object

A conversation a message can be sent to.

Wraps the PB_ChatRef that identifies a destination (a user DM, community channel, group, or self) together with the client used to deliver outbound messages, so callers can simply await channel.send(...).

When the channel was resolved from a community (see from_pb()) the descriptive fields — id, name, type, community_id, position, and parent_id — are populated; for an ad-hoc DM or group ref they are None. Only community channels can be edited or deleted, since those operations need a PB_ChannelRef.

Parameters:
id
name
type
community_id
position
parent_id
category: Category | None
community: Community | None
classmethod from_pb(channel, client)[source]

Build a channel from a community’s PB_Channel.

Constructs the addressing ref from the channel’s community and id and copies across the descriptive metadata, so the returned channel can be both sent to and edited/deleted.

Parameters:
  • channel (Channel) – The raw PB_Channel describing a community channel.

  • client (Client) – The client used to deliver messages and edits.

Return type:

Channel

async send(content, *, reply_to=None)[source]

Send a message to this channel and return the created message.

Waits for the gateway to acknowledge the send so the returned Message carries the server-assigned id, ready to edit() or delete().

Parameters:
  • content (str | Content) – The message text, either a plain string or a Content object.

  • reply_to (int | None) – Optional id of a message this should reply to.

Returns:

The newly created message.

Return type:

Message

async send_file(data, filename, *, mimetype='application/octet-stream', reply_to=None)[source]

Upload data and send it as a file attachment to this channel.

Parameters:
  • data (bytes) – The raw file bytes to upload and attach.

  • filename (str) – The file name shown to recipients.

  • mimetype (str) – The MIME type of the file; defaults to application/octet-stream.

  • reply_to (int | None) – Optional id of a message this should reply to.

Returns:

The newly created message carrying the file attachment.

Raises:

RequestError – If the gateway rejects the upload or send.

Return type:

Message

async edit(*, name=None, position=None, parent_id=None, explicit=None)[source]

Edit this community channel’s attributes.

Only the arguments you pass are changed; anything left as None is kept as-is by the server. Waits for the gateway to confirm the edit and updates the local metadata to match.

Parameters:
  • name (str | None) – A new name for the channel.

  • position (int | None) – A new sort position within the community.

  • parent_id (int | None) – A new parent category id (or None to leave it).

  • explicit (bool | None) – Whether the channel is flagged as explicit.

Returns:

This channel, with its local metadata updated.

Raises:
  • TypeError – If this channel is not a community channel.

  • RequestError – If the gateway rejects the edit.

Return type:

Channel

async delete()[source]

Delete this community channel.

Raises:

TypeError – If this channel is not a community channel.

Return type:

None

async create_invite(*, expires_at=None, max_uses=None)[source]

Create an invite link for this channel and return the full preview.

Creates the invite then immediately fetches its metadata so the returned InvitePreview carries creator and target info.

Parameters:
  • expires_at (int | None) – Optional Unix timestamp (seconds) at which the invite expires.

  • max_uses (int | None) – Optional maximum number of times the invite can be used.

Returns:

The newly created invite with full metadata.

Raises:

RequestError – If the gateway rejects the request.

Return type:

InvitePreview

async get_invites()[source]

Fetch all active invite links for this channel.

Returns:

The channel’s active invites with full metadata.

Raises:

RequestError – If the gateway rejects the request.

Return type:

list[InvitePreview]

Categories

Category channels that group other channels together.

class osmium_chat.category.Category(*args, **kwargs)[source]

Bases: Channel

A category channel that groups other channels under it.

Subclasses Channel — it can be sent to, edited, and deleted via the inherited methods. The extra channels attribute holds the channels nested under this category.

channels: list[Channel]
classmethod from_pb(channel, client, *, channels=None)[source]

Build a category from a protobuf channel and its child channels.

Parameters:
  • channel (Channel) – The raw PB_Channel with type=CATEGORY.

  • client (Client) – The client used for subsequent operations.

  • channels (list[Channel] | None) – The channels nested under this category.

Returns:

The built Category.

Return type:

Category

Messages

class osmium_chat.message.Message(message, client, *, author=None, channel=None, community=None)[source]

Bases: object

A chat message, parsed from its protobuf representation.

Holds where the message came from (chat_ref) so replies, edits, and deletes can be routed back to the same conversation, alongside its text content, the resolved author, and other metadata.

Parameters:
id: int
content_raw: Content
content: str
author_id: int
author: Member | User | None
chat_ref: PB_ChatRef | None
reply_to: int | None
attachments: list[File]
reactions: list[Reaction]
channel: Channel | None
community: Community | None
async edit(content)[source]

Edit this message’s text content.

Waits for the gateway to confirm the edit and updates content and content_raw to match.

Parameters:

content (str | Content) – The new message text, either a plain string or a Content object.

Returns:

This message, with its content updated.

Raises:
  • ValueError – If the message has no chat ref to route the edit to.

  • RequestError – If the gateway rejects the edit.

Return type:

Message

async delete()[source]

Delete this message.

Raises:

ValueError – If the message has no chat ref to route the delete to.

Return type:

None

async add_reaction(emoji)[source]

Add a reaction to this message.

Accepts a CustomEmoji, a UnicodeEmoji, or a plain Unicode emoji string (e.g. "🎉"). A user may only add one reaction of each emoji type to a message.

Parameters:

emoji (CustomEmoji | UnicodeEmoji | str) – The emoji to react with.

Raises:
Return type:

None

async remove_reaction(emoji)[source]

Remove a previously added reaction from this message.

Accepts a CustomEmoji, a UnicodeEmoji, or a plain Unicode emoji string (e.g. "🎉").

Parameters:

emoji (CustomEmoji | UnicodeEmoji | str) – The emoji reaction to remove.

Raises:
Return type:

None

async reply_file(data, filename, *, mimetype='application/octet-stream')[source]

Upload data and send it as a file reply to this message.

Parameters:
  • data (bytes) – The raw file bytes to upload and attach.

  • filename (str) – The file name shown to recipients.

  • mimetype (str) – The MIME type of the file; defaults to application/octet-stream.

Returns:

The newly created reply message carrying the file attachment.

Raises:
  • ValueError – If the message has no chat ref to reply into.

  • RequestError – If the gateway rejects the upload or send.

Return type:

Message

async reply(content)[source]

Send a message to this message’s conversation, threaded as a reply.

Waits for the gateway to acknowledge the send so the returned message carries the server-assigned id.

Parameters:

content (str | Content) – The reply text, either a plain string or a Content object.

Returns:

The newly created reply message.

Raises:
  • ValueError – If the message has no chat ref to reply into.

  • RequestError – If the gateway rejects the send.

Return type:

Message

Reactions

Message reaction model.

A Reaction groups every user who reacted to a message with the same emoji into one object. The emoji is either a UnicodeEmoji (standard Unicode) or a CustomEmoji stub (community emoji).

Reading reactions

Reactions arrive in reactions after a message is parsed from the gateway:

for reaction in message.reactions:
    print(reaction.emoji, reaction.count, reaction.users)

Adding and removing reactions

Use add_reaction() and remove_reaction() to react to a message. Both accept a plain Unicode string, a UnicodeEmoji, or a CustomEmoji:

await message.add_reaction("👍")
await message.add_reaction(UnicodeEmoji("🎉"))
await message.add_reaction(custom_emoji)
await message.remove_reaction("👍")

Note

The users list is a gateway-provided preview and may be truncated for reactions with many participants. count is always the authoritative total.

class osmium_chat.reaction.Reaction(emoji, count, users)[source]

Bases: object

A single reaction type on a message.

Groups all users who reacted with the same emoji under one Reaction. The emoji is either a UnicodeEmoji (for standard Unicode emoji) or a CustomEmoji stub carrying only the server id (for community custom emoji received from the gateway). users holds the preview list of user ids the gateway provided — it may be truncated for reactions with many participants.

Parameters:
  • emoji (CustomEmoji | UnicodeEmoji) – The emoji this reaction represents.

  • count (int) – Total number of users who added this reaction.

  • users (list[int]) – Preview list of user ids who added this reaction.

emoji: CustomEmoji | UnicodeEmoji
count: int
users: list[int]

Formatting

Rich text formatting for outbound messages.

Formatting nodes — Bold, Italic, Underline, Strikethrough, Code, CodeBlock, Spoiler, TextUrl, and CustomEmoji — can be nested freely and embedded inside a Content using either the constructor or f-string interpolation:

from osmium_chat.content import Bold, Content, Italic, UnicodeEmoji

# constructor form
msg = Content("Hello, ", Bold("world"), "!")

# f-string form — identical result
msg = Content(f"Hello, {Bold('world')}!")

# nesting
msg = Content(f"{Bold(Italic('important'))}")

# unicode emoji (rendered as plain text)
msg = Content("Congrats! ", UnicodeEmoji("🎉"))

Each node tree is serialized to a flat text string plus a matching list of PB_MessageEntity offset/length spans — the wire format the Osmium gateway expects.

class osmium_chat.content.Bold(*parts)[source]

Bases: _FormattingNode

Renders its contents in bold.

Parameters:

parts (str | _FormattingNode)

class osmium_chat.content.Code(*parts)[source]

Bases: _FormattingNode

Renders its contents as inline code.

Parameters:

parts (str | _FormattingNode)

class osmium_chat.content.CodeBlock(*parts, language='')[source]

Bases: _FormattingNode

Renders its contents as a fenced code block with optional syntax highlighting.

CodeBlock("print('hello')", language="python")
Parameters:
  • parts (str | _FormattingNode) – The text content of the block.

  • language (str) – An optional language hint for syntax highlighting (e.g. "python").

class osmium_chat.content.Content(*parts)[source]

Bases: object

A complete message payload: plain text plus formatting entity spans.

Construct by passing any mix of str and formatting nodes:

content = Content("Price: ", Bold("$9.99"), " — limited time!")

Or use f-string embedding:

content = Content(f"Price: {Bold('$9.99')} — limited time!")

The text property returns the plain-text string; entities returns the matching PB_MessageEntity list ready for the wire.

Parameters:

parts (str | _FormattingNode | UnicodeEmoji)

property text: str
property entities: list[MessageEntity]
class osmium_chat.content.Italic(*parts)[source]

Bases: _FormattingNode

Renders its contents in italic.

Parameters:

parts (str | _FormattingNode)

class osmium_chat.content.Spoiler(*parts)[source]

Bases: _FormattingNode

Hides its contents behind a spoiler that must be clicked to reveal.

Parameters:

parts (str | _FormattingNode)

class osmium_chat.content.Strikethrough(*parts)[source]

Bases: _FormattingNode

Renders its contents with a ~~strikethrough~~.

Parameters:

parts (str | _FormattingNode)

class osmium_chat.content.TextUrl(*parts, url)[source]

Bases: _FormattingNode

Renders its contents as a hyperlink.

TextUrl("osmium.chat", url="https://osmium.chat")
Parameters:
  • parts (str | _FormattingNode) – The visible link text.

  • url (str) – The URL the link points to.

class osmium_chat.content.Underline(*parts)[source]

Bases: _FormattingNode

Renders its contents with an underline.

Parameters:

parts (str | _FormattingNode)

class osmium_chat.content.UnicodeEmoji(emoji)[source]

Bases: object

A standard Unicode emoji for use in Content.

When passed to Content, it is treated as plain text — the emoji character is embedded in the message string without any formatting entity.

Content("Congrats! ", UnicodeEmoji("🎉"))
Parameters:

emoji (str) – The Unicode emoji character(s).

emoji: str
osmium_chat.content.parse_content(text, entities=None)[source]

Build a Content from a plain-text string and optional entity list.

Parameters:
  • text (str)

  • entities (list[MessageEntity] | None)

Return type:

Content

osmium_chat.content.plain_text(content)[source]

Return the plain text of content with all formatting stripped.

Parameters:

content (Content)

Return type:

str

Communities

class osmium_chat.community.Community(community, client, *, channels=None, categories=None, roles=None)[source]

Bases: object

An Osmium community (a server), parsed from its protobuf representation.

A community owns a list of channels and roles. Those lists are populated when the community is built from data that includes them (see from_pb()); since the gateway delivers channels and roles as separate updates, fetch_channels() and fetch_roles() ask the server to (re)send them. Use edit(), create_channel(), and create_role() to manage the community.

Parameters:
id: int
name: str
username: str | None
owner: bool
permissions: CommunityPermission
photo: Photo | None
channels: list[Channel]
categories: list[Category]
roles: list[Role]
custom_emojis: list[CustomEmoji]
classmethod from_id(community_id, client)[source]

Build a minimal community stub from a bare id.

Only id is meaningful; all other descriptive fields are their zero/default values until you call fetch_channels(), fetch_roles(), or similar methods that hit the gateway.

Parameters:
  • community_id (int) – The community’s snowflake id.

  • client (Client) – The client used for subsequent operations.

Return type:

Community

classmethod from_pb(community, client, *, channels=None, roles=None)[source]

Build a community, wrapping any raw channel/role protobufs supplied.

Parameters:
  • community (Community) – The raw PB_Community describing the community.

  • client (Client) – The client used for subsequent operations.

  • channels (list | None) – Raw PB_Channel objects to wrap as Channel.

  • roles (list | None) – Raw PB_CommunityRole objects to wrap as Role.

Return type:

Community

async edit(*, name=None, username=None)[source]

Edit this community’s attributes.

Only the arguments you pass are changed; anything left as None is kept as-is. The local attributes are updated to match.

Parameters:
  • name (str | None) – A new display name for the community.

  • username (str | None) – A new public username (handle) for the community.

Returns:

This community, with its local attributes updated.

Raises:

RequestError – If the gateway rejects the edit.

Return type:

Community

async delete()[source]

Delete this community.

Return type:

None

async create_channel(name, *, type=ChannelType.TEXT, parent_id=None)[source]

Create a new channel in this community and return it.

The gateway’s create RPC doesn’t echo the new channel, so this snapshots the existing channel ids, creates the channel, refetches, and returns the newly appeared one (with its server-assigned id). channels is refreshed as a side effect.

Parameters:
  • name (str) – The name of the new channel.

  • type (ChannelType) – The ChannelType to create.

  • parent_id (int | None) – The id of the category channel to nest it under, if any.

Returns:

The newly created channel.

Raises:
  • RequestError – If the gateway rejects the request.

  • OsmiumChatError – If the channel was created but cannot be located in the refetched channel list.

Return type:

Channel

async create_category(name)[source]

Create a new category channel in this community and return it.

Snapshots existing channel ids, creates the category, refetches, and returns the newly appeared Category with an empty channels list (child channels can be assigned by creating channels with the category’s id as parent_id). channels is refreshed as a side effect.

Parameters:

name (str) – The name of the new category.

Returns:

The newly created category.

Raises:
  • RequestError – If the gateway rejects the request.

  • OsmiumChatError – If the category was created but cannot be located in the refetched channel list.

Return type:

Category

async create_role(name, *, permissions=<CommunityPermission.NO_PERMISSION: 0>, priority=0, color=0, separated=False, public=False)[source]

Create a new role in this community and return it.

The gateway’s create RPC doesn’t echo the new role, so this snapshots the existing role ids, creates the role, refetches, and returns the newly appeared one (with its server-assigned id). roles is refreshed as a side effect.

Parameters:
  • name (str) – The name of the new role.

  • permissions (CommunityPermission | int) – The permission bitfield to grant.

  • priority (int) – The role priority (higher sorts above lower).

  • color (int) – The role’s display color.

  • separated (bool) – Whether members with this role are shown separately.

  • public (bool) – Whether the role is publicly visible.

Returns:

The newly created role.

Raises:
  • RequestError – If the gateway rejects the request.

  • OsmiumChatError – If the role was created but cannot be located in the refetched role list.

Return type:

Role

async fetch_channels()[source]

Fetch this community’s channels from the gateway.

The result is also cached on channels, replacing whatever was there before.

Returns:

The community’s channels.

Return type:

list[Channel]

async fetch_roles()[source]

Fetch this community’s roles from the gateway.

The result is also cached on roles, replacing whatever was there before.

Returns:

The community’s roles.

Return type:

list[Role]

async fetch_custom_emojis()[source]

Fetch this community’s custom emojis from the gateway.

Requests the community’s emoji sticker pack, then fetches full file metadata via a second request to resolve emoji names. The result is cached on custom_emojis, replacing whatever was there before.

Returns:

The community’s custom emojis.

Raises:

RequestError – If the gateway rejects either request.

Return type:

list[CustomEmoji]

async create_custom_emoji(image, name, *, mimetype='image/png')[source]

Upload an image and add it as a custom emoji for this community.

Snapshots the existing emoji ids, uploads image, adds it to the community’s emoji sticker pack, re-fetches, and returns the newly created CustomEmoji with its server-assigned id.

Parameters:
  • image (bytes) – Raw image bytes (PNG or WebP recommended).

  • name (str) – Short name for the emoji, used as :<name>: in messages.

  • mimetype (str) – MIME type of the image; defaults to "image/png".

Returns:

The newly created custom emoji.

Raises:
  • RequestError – If the gateway rejects the upload or pack add.

  • OsmiumChatError – If the emoji was created but cannot be located in the re-fetched emoji list.

Return type:

CustomEmoji

Custom Emoji

Custom community emoji.

A CustomEmoji represents a community-specific emoji uploaded to an Osmium sticker pack. It can be embedded directly in a Content message, used as a reaction, or accepted as a command argument.

Fetching

Custom emojis for a community are retrieved with fetch_custom_emojis():

emojis = await ctx.community.fetch_custom_emojis()
emoji  = next(e for e in emojis if e.name == "wave")

Embedding in messages

Pass a CustomEmoji directly to Content — it renders as the emoji name in plain text client-side and produces a custom_emoji entity span on the wire:

await ctx.channel.send(Content("Hello! ", emoji))

As a command argument

Annotating a command parameter as CustomEmoji makes the parser accept either a :name: mention or a custom_emoji message entity at the argument’s position:

@bot.command("react")
async def react(ctx: Context, emoji: CustomEmoji) -> None:
    await ctx.message.add_reaction(emoji)

Creating and deleting

Upload a new emoji with create_custom_emoji(), and remove one with CustomEmoji.delete():

emoji = await ctx.community.create_custom_emoji(image_bytes, "wave")
await emoji.delete()
class osmium_chat.emoji.CustomEmoji(emoji_id, name, community_id, pack_id, client)[source]

Bases: _FormattingNode

A custom emoji belonging to a community’s emoji pack.

Can be embedded directly in a Content message — it renders as the emoji name in plain text and produces a custom_emoji entity span on the wire:

content = Content("Look at this! ", emoji)

Use delete() to remove it from its community, and rename() to update its local display name.

Parameters:
  • emoji_id (int) – The server-assigned snowflake id of this emoji.

  • name (str) – The emoji’s short name (without colons).

  • community_id (int) – The id of the community this emoji belongs to.

  • pack_id (int) – The id of the sticker pack that holds this emoji.

  • client (Client) – The client used to manage the emoji.

id: int
name: str
community_id: int
async delete()[source]

Remove this emoji from its community’s emoji pack.

Raises:

RequestError – If the gateway rejects the request.

Return type:

None

rename(name)[source]

Update this emoji’s local display name.

The Osmium gateway does not expose a rename endpoint, so this only updates the local name and the placeholder text used when the emoji is embedded in a Content. The server-side name (set at upload time) is unchanged.

Parameters:

name (str) – The new short name (without colons).

Returns:

This emoji, with name updated.

Return type:

CustomEmoji

Invites

Invite links for joining communities and channels.

class osmium_chat.invite.Invite(invite, client)[source]

Bases: object

A channel invite handle that can be revoked.

Holds the invite code returned by the gateway. Call delete() to revoke it. For the full hydrated metadata use InvitePreview instead.

Parameters:
  • invite (CreatedInvite)

  • client (Client)

code: str
async delete()[source]

Revoke this invite.

Return type:

None

class osmium_chat.invite.InvitePreview(preview, client)[source]

Bases: object

A hydrated invite including creator and target metadata.

Returned by create_invite() and get_invites(). Call delete() to revoke it.

Parameters:
  • preview (InvitePreview)

  • client (Client)

code: str
creator_id: int
target_id: int
target_type: InviteType
expires_at: int | None
async delete()[source]

Revoke this invite.

Return type:

None

class osmium_chat.invite.InviteType(*values)[source]

Bases: IntEnum

The type of entity an invite links to.

Mirrors PB_InviteType.

UNKNOWN = 0
COMMUNITY = 1
GROUP = 2
USER = 3

Members

Community member: a user viewed through the lens of a specific community.

class osmium_chat.member.Member(member, user, client, *, community)[source]

Bases: User

A user as a member of a specific community.

Extends User with community-specific data: a nickname, a list of role_ids, and helpers to manage roles.

Note

When a Member is built from an incoming message event the gateway does not include full member metadata, so nickname starts as None and role_ids starts empty. Call fetch_members() (once available) if you need the current roles.

Parameters:
  • member (CommunityMember)

  • user (User)

  • client (Client)

  • community (Community)

community: Community
nickname: str | None
role_ids: list[int]
property display_name: str

Display name: the community nickname if set, otherwise the user’s name.

property roles: list[Role]

The member’s roles, resolved from the community’s cached role list.

Call fetch_roles() first if you need up-to-date role data.

async add_role(role)[source]

Add a role to this member.

No-ops if the member already has the role. Updates role_ids on success.

Parameters:

role (Role) – The role to add.

Raises:

RequestError – If the gateway rejects the request.

Return type:

None

async remove_role(role)[source]

Remove a role from this member.

No-ops if the member does not have the role. Updates role_ids on success.

Parameters:

role (Role) – The role to remove.

Raises:

RequestError – If the gateway rejects the request.

Return type:

None

Roles

Community roles controlling member permissions and display.

class osmium_chat.role.Role(role, client)[source]

Bases: object

A role within a community, parsed from its protobuf representation.

Roles bundle a set of CommunityPermission grants, a display color, and a priority that orders them. The edit() and delete() helpers let you manage a role in place.

Parameters:
  • role (CommunityRole)

  • client (Client)

id: int
community_id: int
name: str
permissions: CommunityPermission
priority: int
color: int
separated: bool
public: bool
async edit(*, name=None, permissions=None, priority=None, color=None, separated=None, public=None)[source]

Edit this role’s attributes.

The edit endpoint replaces the whole role, so any argument left as None keeps the role’s current value. The local attributes are updated to match, and the call waits for the gateway to confirm the edit.

Parameters:
  • name (str | None) – A new name for the role.

  • permissions (CommunityPermission | int | None) – A new permission bitfield.

  • priority (int | None) – A new priority (higher sorts above lower).

  • color (int | None) – A new display color.

  • separated (bool | None) – Whether members with this role are shown separately.

  • public (bool | None) – Whether the role is publicly visible.

Returns:

This role, with its local attributes updated.

Raises:

RequestError – If the gateway rejects the edit.

Return type:

Role

async delete()[source]

Delete this role from its community.

Return type:

None

Permissions

class osmium_chat.permissions.CommunityPermission(*values)[source]

Bases: IntFlag

A bitfield of the permissions a role can grant within a community.

Mirrors PB_CommunityPermission. Being an enum.IntFlag, members combine with | and can be tested with in, so a role’s permissions integer reads naturally:

perms = CommunityPermission.VIEW_CHANNEL | CommunityPermission.SEND_MESSAGES
await community.create_role("member", permissions=perms)

if CommunityPermission.ADMINISTRATOR in CommunityPermission(role.permissions):
    ...
NO_PERMISSION = 0
ADMINISTRATOR = 1
VIEW_CHANNEL = 2
SEND_MESSAGES = 4
CONNECT_VOICE = 8
MODIFY_CHANNEL = 16
SEND_MEDIA = 32
DELETE_MESSAGES = 64
PIN_MESSAGES = 128
SPEAK_VOICE = 256
MODIFY_COMMUNITY = 512
MODIFY_ROLES = 1024
REMOVE_MEMBERS = 2048
ADD_REACTIONS = 4096
MODIFY_LINKED_STICKERS = 8192

Utilities

osmium_chat.utils.locate_created(before_ids, after, name)[source]

Pick the entity just created out of a freshly fetched list.

Several Osmium create RPCs don’t echo the new entity back, so the caller snapshots the ids that existed beforehand, performs the create, refetches, and uses this to find what appeared. Among the ids that are new we prefer a name match and take the highest id (the most recently created); if nothing is new we fall back to the newest name match across the whole list.

Parameters:
  • before_ids (Container[object]) – The set of entity ids that existed before the create.

  • after (Sequence[_T]) – The entities fetched after the create.

  • name (str) – The name the new entity was created with.

Returns:

The created entity, or None if it could not be located.

Return type:

_T | None

Errors

Exceptions raised while parsing and invoking commands.

exception osmium_chat.errors.OsmiumChatError[source]

Bases: Exception

Base class for every exception raised by osmium_chat.

exception osmium_chat.errors.RequestError(code, message)[source]

Bases: OsmiumChatError

The gateway returned an error in response to a request.

Raised from request() when the matching RpcResult carries an error instead of a result payload.

Parameters:
Return type:

None

exception osmium_chat.errors.CommandError[source]

Bases: OsmiumChatError

Base class for errors that occur while handling a command.

exception osmium_chat.errors.CommandNotFound(name)[source]

Bases: CommandError

No command was registered under the invoked name or alias.

Parameters:

name (str)

Return type:

None

exception osmium_chat.errors.CommandRestrictionError(name, restriction)[source]

Bases: CommandError

A command was invoked in a context it does not allow.

Parameters:
  • name (str)

  • restriction (Any)

Return type:

None

exception osmium_chat.errors.ArgumentError[source]

Bases: CommandError

Base class for problems converting or supplying command arguments.

exception osmium_chat.errors.MissingRequiredArgument(name)[source]

Bases: ArgumentError

A required argument was not supplied by the invoker.

Parameters:

name (str)

Return type:

None

exception osmium_chat.errors.BadArgument(name, value, expected)[source]

Bases: ArgumentError

An argument could not be converted to the parameter’s annotated type.

Parameters:
Return type:

None

exception osmium_chat.errors.TooManyArguments(extra)[source]

Bases: ArgumentError

The invoker supplied more arguments than the command accepts.

Parameters:

extra (str)

Return type:

None

Users

class osmium_chat.user.user.User(user, client)[source]

Bases: object

An Osmium user, parsed from its protobuf representation.

Parameters:
id: int
name: str
username: str | None
status: UserStatus | None
photo: Photo | None
icon: int | None
color: int | None
dm_channel: Channel
profile: Profile | None
async fetch_profile()[source]

Fetch this user’s extended profile and cache it on profile.

Calls users.getProfile and wraps the result in a Profile, which carries the user’s bio, premium status, and image banner.

Returns:

The fetched Profile.

Raises:

RequestError – If the gateway rejects the request.

Return type:

Profile

class osmium_chat.user.status.UserStatusStatus(*values)[source]

Bases: IntEnum

A user’s availability state.

ONLINE = 0
IDLE = 1
class osmium_chat.user.status.UserStatus(status)[source]

Bases: object

A user’s presence: online flag, status state, and activities.

Parameters:

status (UserStatus)

online: bool
status: UserStatusStatus | None
activities: list[UserStatusActivity]
class osmium_chat.user.activity.UserActivityType(*values)[source]

Bases: IntEnum

The kind of activity a user is engaged in.

GAME = 0
STREAMING = 1
LISTENING = 2
WATCHING = 3
class osmium_chat.user.activity.UserStatusActivity(activity)[source]

Bases: object

A single activity shown in a user’s status (e.g. a game being played).

Parameters:

activity (UserStatusActivity)

title: str
type: UserActivityType
start_time: int
end_time: int | None
state: str | None

Media

class osmium_chat.photo.Photo(photo, client)[source]

Bases: object

A chat photo: its file id and an inline preview blob.

Parameters:
  • photo (ChatPhoto)

  • client (Client)

file_id: int
preview: bytes
download()[source]

Fetch the full photo contents as bytes.

The return value can be awaited directly or used as an async context manager; both yield the complete photo bytes:

data = await photo.download()

async with photo.download() as data:
    ...
Returns:

An awaitable / async context manager resolving to the bytes.

Raises:

RequestError – If the gateway rejects any part request.

Return type:

_PhotoDownload

class osmium_chat.file.File(file, client)[source]

Bases: object

A file attachment on a received message.

Wraps the PB_File metadata the server sends alongside a message. Call download() to fetch the raw bytes.

Parameters:
file_id: int
region: int
size: int
mimetype: str
filename: str | None
async download()[source]

Fetch and return the full file contents as bytes.

Requests the file in _DOWNLOAD_CHUNK_SIZE-byte windows and reassembles them in order.

Returns:

The complete file bytes.

Raises:

RequestError – If the gateway rejects any part request.

Return type:

bytes

async save(path=None)[source]

Download the file and write it to disk.

Parameters:

path (str | Path | None) – Destination path. Pass a directory to write <dir>/<filename> using the server-provided name, or a full file path to control the exact location. Defaults to the current working directory with the server-provided name (or file_<id> when the server did not supply one).

Returns:

The Path the file was written to.

Raises:

RequestError – If the gateway rejects any part request.

Return type:

Path