ZeroKnight / ZeroBot

My personal IRC/Multi-protocol Bot created (and re-created) for education and amusement.
MIT License
1 stars 0 forks source link

Interface for sending formatted messages #52

Open ZeroKnight opened 3 months ago

ZeroKnight commented 3 months ago

Sending unformatted messages is easy and an interface exists for it: just send some text through module_message or similar. Sending a message with formatting (bold, italics, code spans/blocks, etc.) is theoretically still easy, but as usual becomes a bit more complicated in our generalized multi-protocol environment. Bolding a span of text is different on IRC than on Discord, some markdown-capable protocols could support different subsets of markdown, and some may not support formatting at all. An interface for this is necessary.

Implementation Ideas

Markdown as agnostic formatting

Since many things support Markdown formatting, feature modules can use it as a lingua franca of sorts. Feature modules will format with Markdown, and it is up to the protocol to transform it appropriately (if at all).

One hiccup with this is colored text; there's no way to specify color in any markdown flavor that I'm aware of. How common is this in recent protocols, though? Is it enough to bother with a generalization?

To nick part of the brainstorming ideas, I could have helper classes for more complicated formatting, e.g. code blocks:

@dataclass
class CodeBlock:
    body: str | list[str]
    lang: str

    def __str__(self) -> str:
        # join lines, wrap in backticks, slot in language

Though that's just for markdown, so presumably protocols would need to fill in an implementation somewhere...

Custom or existing markup

Unsure exactly how this would look without just reinventing an existing markup...

Brainstorming from #13

Later Thought: I'm not sure that this will really work, as the logging design deals more with inserting discrete chunks of text into places rather than wrapping. I could do something vaguely like {bold} placeholders, but at that point, I may as well use something more purpose-made.

The thought occurred to me that I could take inspiration from the designs used in Python's logging module--and by extension, loguru; more specifically, their Formatter interfaces. I could do something similar and have Feature Modules opt-in to specify various formatters for their outputs that can take advantage of different protocol capabilities. The formatters can be added to a "message" data structure (think of it like a logging Record) that defines the discrete parts of a plain message, embed, etc. Then, which formatter to use can be selected based on what the receiving protocol supports, falling back to others if necessary. For example, falling back to a simple message body if the target protocol doesn't support embeds.

IDEA: Could maybe do something like this but strictly for "formatting" in the sense of bold, italic, code blocks, etc.

As part of this design language, it will more than likely be necessary to create "generic" message element interfaces distilled down to some common intersection of attributes across different protocols (and maybe some kind of extra_args escape hatch for protocol-specific behavior) that these "message" structures can use. To start with, many protocols support at least having Embeds and Code Blocks for example, and naturally all of them support plain message bodies. An Embed could have fields like:

Then, Protocols would be responsible for hydrating their specific Embed objects by translating from our generic via our "Formatters" or whatever.

Locus of Responsibility: Feature or Protocol?

I think that a big part of this design will come down to deciding whether the Protocol or the Feature should provide the interface. Or even whether the responsibility should be shared, and to what degree. If Protocols provide the interface then that removes the burden from the Feature modules to account for how it could format output on each protocol. However, this may impose restrictions on how such output can be constructed. On the other hand, Features could theoretically have full control over formatting but then must be aware of the Protocol they're sending to.