Message structure ================= In order for another plug to process your incoming messages, the :class:`.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 :class:`.User` class provides a similar amount of flexibility for message senders. The :attr:`.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 :class:`.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 :class:`.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 :attr:`.Receipt.id` but a new :attr:`.Receipt.revision`. If the source message is deleted, treat it as a new revision with the :attr:`.Receipt.deleted` flag set. Replies and forwarding ---------------------- For networks with threading, :attr:`.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 :attr:`.Message.attachments`. These may take the form of a :class:`.SentMessage`, which acts as a pointer to another message, or a plain :class:`.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 :class:`.RichText` class, which is itself just a list of :class:`.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: .. code-block:: python RichText([Segment('Some '), Segment('bold', bold=True), Segment(' and '), Segment('emphasis', italic=True)]) For plugs that don't support formatting, calling :class:`str() ` on a :class:`.RichText` object produces a string with formatting removed. Raw format ~~~~~~~~~~ If you need to persist formatting in plain-text storage (such as in the :ref:`Database`), rich text can be (de)serialised via :meth:`.RichText.raw` and :meth:`.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: .. code-block:: none Some bold and emphasis Valid tags match the :class:`.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: .. code-block:: none Qualified link: Example site Implied link: http://example.com Mentions can be constructed using the plug name, user identifier and user name: .. code-block:: none Mentioning @John Note that tags do not nest, and always represent the break between message segments. For example: .. code-block:: none Plain Bold Both 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 :meth:`.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 :attr:`.File.source`, and the default implementation of :meth:`.File.get_content` will download it.