Message structure¶
In order for another plug to process your incoming messages, the Message
class provides a
shared data structure containing many common instant messaging properties.
Of course, a given plug is unlikely to support everything, so it may ignore certain fields as required. It is expected that plugs provide a sensible plain text description of each message, so if a given plug doesn’t support e.g. joins, it can still show some text that says John has joined the channel.
The User
class provides a similar amount of flexibility for message senders. The
Message.user
field holds a standard representation of a network user, where different
properties can be used to give messages a native feel across each plug.
Abstract and concrete messages¶
The Message
class represents an abstract messages; that is, the content of a message,
regardless of the network it’s designed for or retrieved from.
When a real message arrives, often you’ll want to be able to reference it, or know the source
channel. This is where the Receipt
class comes in, with metadata about a concrete
message.
Edits and deletes¶
If the plug’s network supports editing messages, this can be represented by a new message object
with the same Receipt.id
but a new Receipt.revision
. If the source message is
deleted, treat it as a new revision with the Receipt.deleted
flag set.
Replies and forwarding¶
For networks with threading, Message.reply_to
allows linking a message to its parent. If
a message needs to forward or otherwise reference another message, it can be included in
Message.attachments
. These may take the form of a SentMessage
, which acts as a
pointer to another message, or a plain Message
to render any message regardless of its
presence in the target channel. If a plug receives a message containing references, it will
attempt a native use of the linked message, e.g. to reply or forward it.
Rich text¶
Message formatting is represented by the RichText
class, which is itself just a list of
Segment
objects. Each segment takes a substring of the message text, along with the
formatting that applies to it.
For example, the text “Some bold and emphasis” might be represented as follows:
RichText([Segment('Some '),
Segment('bold', bold=True),
Segment(' and '),
Segment('emphasis', italic=True)])
For plugs that don’t support formatting, calling str()
on a RichText
object produces a string with formatting removed.
Raw format¶
If you need to persist formatting in plain-text storage (such as in the Database), rich text
can be (de)serialised via RichText.raw()
and RichText.unraw()
. The raw format syntax
is loosely based on HTML, except that one tag holds a comma-separated list of modifiers.
The example above in raw format, with added underline:
Some <b>bold</> and <i,u>emphasis</>
Valid tags match the RichText
attributes, and may be shortened to their first character.
Links may be either be qualified, or use the segment text as their link target:
Qualified link: <l=http://example.com>Example site</>
Implied link: <l>http://example.com</>
Mentions can be constructed using the plug name, user identifier and user name:
Mentioning <m=demo/123456/John Smith>@John</>
Note that tags do not nest, and always represent the break between message segments. For example:
Plain <b>Bold <b,i>Both<i> Italic</> Plain
Attachments¶
Currently only image file attachments are supported. A plug may provide at least one of two methods to fetch a given file: a public URL, or opaque content retrieval.
In most cases, a plug will need its own implementation of File.get_content()
, which takes no
arguments and returns a file-like object of the attachment. This should handle any authentication
required by the network.
If the plug’s network provides publicly-accessible URLs for files, you may instead just set
File.source
, and the default implementation of File.get_content()
will download it.