feat(plugins): allow message introspection (#6353)

This allows users to introspect messages (i.e. look at the elements they're made up of).

Reviewed-by: Mm2PL <mm2pl+gh@kotmisia.pl>
This commit is contained in:
nerix
2025-12-07 15:52:59 +01:00
committed by GitHub
parent 0aff6fbe83
commit 74b4674c6b
12 changed files with 1836 additions and 210 deletions
+125 -1
View File
@@ -160,8 +160,24 @@ declare namespace c2 {
var WebSocket: WebSocketConstructor;
interface Message {
__dummy: void; // avoid being an empty interface
flags: MessageFlag;
id: string;
parse_time: number;
search_text: string;
message_text: string;
login_name: string;
display_name: string;
localized_name: string;
user_id: string;
channel_name: string;
username_color: string;
server_received_time: number;
highlight_color: string | null;
frozen: boolean;
elements(): MessageElement[];
append_element(init: MessageElementInit): void;
}
interface MessageConstructor {
new: (this: void, init: MessageInit) => Message;
}
@@ -184,6 +200,14 @@ declare namespace c2 {
elements?: MessageElementInit[];
}
interface MessageElementBase {
flags: MessageElementFlag;
tooltip: string;
trailing_space: boolean;
link: Link;
add_flags(flags: MessageElementFlag): void;
}
interface MessageElementInitBase {
tooltip?: string;
trailing_space?: boolean;
@@ -192,6 +216,25 @@ declare namespace c2 {
type MessageColor = "text" | "link" | "system" | string;
type MessageElement =
| TextElement
| SingleLineTextElement
| MentionElement
| TimestampElement
| TwitchModerationElement
| LinebreakElement
| ReplyCurveElement
| LinkElement
| EmoteElement
| LayeredEmoteElement
| ImageElement
| CircularImageElement
| ScalingImageElement
| BadgeElement
| ModBadgeElement
| VipBadgeElement
| FfzBadgeElement;
type MessageElementInit =
| TextElementInit
| SingleLineTextElementInit
@@ -201,6 +244,13 @@ declare namespace c2 {
| LinebreakElementInit
| ReplyCurveElementInit;
interface TextElement extends MessageElementBase {
type: "text";
words: string[];
color: string;
style: c2.FontStyle;
}
interface TextElementInit extends MessageElementInitBase {
type: "text";
text: string;
@@ -209,6 +259,13 @@ declare namespace c2 {
style?: FontStyle;
}
interface SingleLineTextElement extends MessageElementBase {
type: "single-line-text";
words: string[];
color: string;
style: c2.FontStyle;
}
interface SingleLineTextElementInit extends MessageElementInitBase {
type: "single-line-text";
text: string;
@@ -217,6 +274,14 @@ declare namespace c2 {
style?: FontStyle;
}
interface MentionElement extends Omit<TextElement, "type"> {
type: "mention";
display_name: string;
login_name: string;
fallback_color: string;
user_color: string;
}
interface MentionElementInit extends MessageElementInitBase {
type: "mention";
display_name: string;
@@ -225,24 +290,83 @@ declare namespace c2 {
user_color: MessageColor;
}
interface TimestampElement extends MessageElementBase {
type: "timestamp";
time: number;
}
interface TimestampElementInit extends MessageElementInitBase {
type: "timestamp";
time?: number;
}
interface TwitchModerationElement extends MessageElementBase {
type: "twitch-moderation";
}
interface TwitchModerationElementInit extends MessageElementInitBase {
type: "twitch-moderation";
}
interface LinebreakElement extends MessageElementBase {
type: "linebreak";
}
interface LinebreakElementInit extends MessageElementInitBase {
type: "linebreak";
flags?: MessageElementFlag;
}
interface ReplyCurveElement extends MessageElementBase {
type: "reply-curve";
}
interface ReplyCurveElementInit extends MessageElementInitBase {
type: "reply-curve";
}
interface LinkElement extends Omit<TextElement, "type"> {
type: "link";
lowercase: string;
original: string;
}
interface EmoteElement extends MessageElementBase {
type: "emote";
}
interface LayeredEmoteElement extends MessageElementBase {
type: "layered-emote";
}
interface ImageElement extends MessageElementBase {
type: "image";
}
interface CircularImageElement extends MessageElementBase {
type: "circular-image";
}
interface ScalingImageElement extends MessageElementBase {
type: "scaling-image";
}
interface BadgeElement extends MessageElementBase {
type: "badge";
}
interface ModBadgeElement extends Omit<BadgeElement, "type"> {
type: "mod-badge";
}
interface VipBadgeElement extends Omit<BadgeElement, "type"> {
type: "ffz-badge";
}
interface FfzBadgeElement extends Omit<BadgeElement, "type"> {
type: "ffz-badge";
}
interface Link {
type: LinkType;
value: string;
+150 -50
View File
@@ -299,10 +299,160 @@ function c2.HTTPRequest.create(method, url) end
-- Begin src/controllers/plugins/api/Message.hpp
---@class c2.MessageElementBase
---@field flags c2.MessageElementFlag The element's flags
---@field tooltip string The tooltip (if any)
---@field trailing_space boolean Whether to add a trailing space after the element
---@field link c2.Link An action when clicking on this element. Mention and Link elements don't support this. They manage the link themselves.
c2.MessageElementBase = {}
-- ^^^ this is kinda fake - this table doesn't exist in Lua, we only declare it to add methods
--- Add flags to this element
---
---@param flags c2.MessageElementFlag
function c2.MessageElementBase:add_flags(flags) end
---A base table to initialize a new message element
---@class MessageElementInitBase
---@field tooltip? string Tooltip text
---@field trailing_space? boolean Whether to add a trailing space after the element (default: true)
---@field link? c2.Link An action when clicking on this element. Mention and Link elements don't support this. They manage the link themselves.
---@class c2.TextElement : c2.MessageElementBase
---@field type "text"
---@field words string[] The words of this element
---@field color string The color of the text
---@field style c2.FontStyle The font style of the text
---A table to initialize a new message text element
---@class TextElementInit : MessageElementInitBase
---@field type "text" The type of the element
---@field text string The text of this element
---@field flags? c2.MessageElementFlag Message element flags (see `c2.MessageElementFlags`)
---@field color? MessageColor The color of the text
---@field style? c2.FontStyle The font style of the text
---@class c2.SingleLineTextElement : c2.MessageElementBase
---@field type "single-line-text"
---@field words string[] The words of this element
---@field color string The color of the text
---@field style c2.FontStyle The font style of the text
---A table to initialize a new message single-line text element
---@class SingleLineTextElementInit : MessageElementInitBase
---@field type "single-line-text" The type of the element
---@field text string The text of this element
---@field flags? c2.MessageElementFlag Message element flags (see `c2.MessageElementFlags`)
---@field color? MessageColor The color of the text
---@field style? c2.FontStyle The font style of the text
---@class c2.MentionElement : c2.TextElement
---@field type "mention"
---@field login_name string The login name of the mentioned user
---@field fallback_color MessageColor The color of the element in case the "Colorize @usernames" is disabled
---@field user_color MessageColor The color of the element in case the "Colorize @usernames" is enabled
---A table to initialize a new mention element
---@class MentionElementInit : MessageElementInitBase
---@field type "mention" The type of the element
---@field display_name string The display name of the mentioned user
---@field login_name string The login name of the mentioned user
---@field fallback_color MessageColor The color of the element in case the "Colorize @usernames" is disabled
---@field user_color MessageColor The color of the element in case the "Colorize @usernames" is enabled
---@class c2.TimestampElement : c2.MessageElementBase
---@field type "timestamp"
---@field time number The time of the timestamp (in milliseconds since epoch).
---A table to initialize a new timestamp element
---@class TimestampElementInit : MessageElementInitBase
---@field type "timestamp" The type of the element
---@field time number? The time of the timestamp (in milliseconds since epoch). If not provided, the current time is used.
---@class c2.TwitchModerationElement : c2.MessageElementBase
---@field type "twitch-moderation"
---A table to initialize a new Twitch moderation element (all the custom moderation buttons)
---@class TwitchModerationElementInit : MessageElementInitBase
---@field type "twitch-moderation" The type of the element
---@class c2.LinebreakElement : c2.MessageElementBase
---@field type "linebreak"
---A table to initialize a new linebreak element
---@class LinebreakElementInit : MessageElementInitBase
---@field type "linebreak" The type of the element
---@field flags? c2.MessageElementFlag Message element flags (see `c2.MessageElementFlags`)
---@class c2.ReplyCurveElement : c2.MessageElementBase
---@field type "reply-curve"
---A table to initialize a new reply curve element
---@class ReplyCurveElementInit : MessageElementInitBase
---@field type "reply-curve" The type of the element
---@class c2.LinkElement : c2.TextElement
---@field type "link"
---@class c2.EmoteElement : c2.MessageElementBase
---@field type "emote"
---@class c2.LayeredEmoteElement : c2.MessageElementBase
---@field type "layered-emote"
---@class c2.ImageElement : c2.MessageElementBase
---@field type "image"
---@class c2.CircularImageElement : c2.MessageElementBase
---@field type "circular-image"
---@class c2.ScalingImageElement : c2.MessageElementBase
---@field type "scaling-image"
---@class c2.BadgeElement : c2.MessageElementBase
---@field type "badge"
---@class c2.ModBadgeElement : c2.BadgeElement
---@field type "mod-badge"
---@class c2.VipBadgeElement : c2.BadgeElement
---@field type "vip-badge"
---@class c2.FfzBadgeElement : c2.BadgeElement
---@field type "ffz-badge"
---@alias MessageElement c2.TextElement|c2.SingleLineTextElement|c2.MentionElement|c2.TimestampElement|c2.TwitchModerationElement|c2.LinebreakElement|c2.ReplyCurveElement|c2.LinkElement|c2.EmoteElement|c2.LayeredEmoteElement|c2.ImageElement|c2.CircularImageElement|c2.ScalingImageElement|c2.BadgeElement|c2.ModBadgeElement|c2.VipBadgeElement|c2.FfzBadgeElement
---@alias MessageElementInit TextElementInit|SingleLineTextElementInit|MentionElementInit|TimestampElementInit|TwitchModerationElementInit|LinebreakElementInit|ReplyCurveElementInit
---A chat message
---@class c2.Message
---@field flags c2.MessageFlag The message's flags
---@field parse_time number Time the message was parsed (in milliseconds since epoch)
---@field id string The message ID
---@field search_text string Text to check when searching for messages
---@field message_text string Text content of this message (used for filters for example)
---@field login_name string The login name of the sender
---@field display_name string The dispay name of the sender
---@field localized_name string The localized name of the sender (this is used for CJK names, otherwise it's empty)
---@field user_id string The ID of the sender
---@field channel_name string The name of the channel this message appeared in
---@field username_color string The color of the username
---@field server_received_time number The time the server received the message (in milliseconds since epoch)
---@field highlight_color string The color of the highlight or empty
---@field frozen boolean If this is set, Lua plugins can't modify this message (as it's visible to the user).
c2.Message = {}
--- The elements this message is made up of
---
---@return MessageElement[] elements
function c2.Message:elements() end
--- Add an element to this message.
---
---@param init MessageElementInit The element to add
function c2.Message:append_element(init) end
---A table to initialize a new message
---@class MessageInit
---@field flags? c2.MessageFlag Message flags (see `c2.MessageFlags`)
@@ -320,58 +470,8 @@ c2.Message = {}
---@field highlight_color? string|nil The color of the highlight (if any)
---@field elements? MessageElementInit[] The elements of the message
---A base table to initialize a new message element
---@class MessageElementInitBase
---@field tooltip? string Tooltip text
---@field trailing_space? boolean Whether to add a trailing space after the element (default: true)
---@field link? c2.Link An action when clicking on this element. Mention and Link elements don't support this. They manage the link themselves.
---@alias MessageColor "text"|"link"|"system"|string A color for a text element - "text", "link", and "system" are special values that take the current theme into account
---A table to initialize a new message text element
---@class TextElementInit : MessageElementInitBase
---@field type "text" The type of the element
---@field text string The text of this element
---@field flags? c2.MessageElementFlag Message element flags (see `c2.MessageElementFlags`)
---@field color? MessageColor The color of the text
---@field style? c2.FontStyle The font style of the text
---A table to initialize a new message single-line text element
---@class SingleLineTextElementInit : MessageElementInitBase
---@field type "single-line-text" The type of the element
---@field text string The text of this element
---@field flags? c2.MessageElementFlag Message element flags (see `c2.MessageElementFlags`)
---@field color? MessageColor The color of the text
---@field style? c2.FontStyle The font style of the text
---A table to initialize a new mention element
---@class MentionElementInit : MessageElementInitBase
---@field type "mention" The type of the element
---@field display_name string The display name of the mentioned user
---@field login_name string The login name of the mentioned user
---@field fallback_color MessageColor The color of the element in case the "Colorize @usernames" is disabled
---@field user_color MessageColor The color of the element in case the "Colorize @usernames" is enabled
---A table to initialize a new timestamp element
---@class TimestampElementInit : MessageElementInitBase
---@field type "timestamp" The type of the element
---@field time number? The time of the timestamp (in milliseconds since epoch). If not provided, the current time is used.
---A table to initialize a new Twitch moderation element (all the custom moderation buttons)
---@class TwitchModerationElementInit : MessageElementInitBase
---@field type "twitch-moderation" The type of the element
---A table to initialize a new linebreak element
---@class LinebreakElementInit : MessageElementInitBase
---@field type "linebreak" The type of the element
---@field flags? c2.MessageElementFlag Message element flags (see `c2.MessageElementFlags`)
---A table to initialize a new reply curve element
---@class ReplyCurveElementInit : MessageElementInitBase
---@field type "reply-curve" The type of the element
---@alias MessageElementInit TextElementInit|SingleLineTextElementInit|MentionElementInit|TimestampElementInit|TwitchModerationElementInit|LinebreakElementInit|ReplyCurveElementInit
--- Creates a new message
---
---@param init MessageInit The message initialization table
+72 -1
View File
@@ -620,7 +620,26 @@ Closes the WebSocket connection.
#### `Message`
Allows creation of rich chat messages. This is currently limited but is expected to be expanded soon.
Allows creation and modification of rich chat messages. A `Message` is
Chatterino's representation of a chat message or any system message.
The interface to Lua is currently limited but is expected to be expanded soon.
Messages can be added to a [`Channel`](#channel). Once a message is added to a
channel, it can't be modified anymore (except for its `flags`). These messages
are termed "frozen" (immutable). You can check for this using the `frozen`
property:
```lua
local my_msg = c2.Message.new({ id = "foobar" })
assert(not my_msg.frozen)
my_channel:add_message(my_msg)
assert(my_msg.frozen)
```
A message has the same properties that it takes in its constructor (e.g.
`msg.id` or `msg.flags`). If the message is not frozen (mutable), these
properties can be modified. New elements can be added with
[`append_element`](#messageappend_elementdata).
##### `Message.new(data)`
@@ -654,6 +673,58 @@ end)
The full range of options can be found in the typing files ([LuaLS](./lua-meta/globals.lua), [TypeScript](./chatterino.d.ts)).
##### `Message:elements()`
Gets a reference to the message's elements. This can be indexed or iterated
though. Through the return value, you can also remove elements. Note that
currently, new elements can't be added here (use `Message:append_element`).
The return value can be thought of as a table of `MessageElement`s.
```lua
local my_msg = c2.Message.new({
id = "foo",
elements = {
{ type = "text", text = "foo" },
{ type = "text", text = "bar" },
{ type = "text", text = "baz" },
}
})
local elements = my_msg:elements()
assert(#elements == 3)
assert(elements[1].words[1] == "foo")
assert(elements[2].words[1] == "bar")
elements:erase(2) -- erase "bar"
assert(#elements == 2)
assert(elements[1].words[1] == "foo")
assert(elements[2].words[1] == "baz")
```
##### `Message:append_element(data)`
Creates a message element from a table (`data`) and appends it to the message.
The structure of the table matches the one taken in `Message.new`'s `elements`.
```lua
local my_msg = c2.Message.new({ id = "foo" })
my_msg:append_element({
type = "text",
text = "My text element",
})
```
#### `MessageElement`
A reference to an element inside a `Message` (essentially an in index into the
elements). Note that modifications to the elements of the parent `Message` might
change the actual element this is referring to.
To distinguish different types of elements, check the `type` property. This
takes the same values as `element.type` in the `Message` constructor.
The full range of properties can be found in the typing files ([LuaLS](./lua-meta/globals.lua), [TypeScript](./chatterino.d.ts)).
#### `LinkType` enum
This table describes links available to plugins.