Skip to content

Models: the messenger, tickets and help

The messenger’s, tickets’ and help center’s models, from package:crm_sdk/wire.dart.

import 'package:crm_sdk/wire.dart';
NameWhat it is
ArticleOne published article, whole.
ArticleRatingBodyThe guest saying whether an article helped.
ArticleSummaryAn article in a list.
ArticlesThe answer to a search: the best matches first; with nothing searched for, the featured articles.
AttachmentA picture in a part.
AttachmentBodyAsks for somewhere to upload a picture.
AttachmentUploadWhere to upload a picture: a form POST to url with fields first and the file last, as a field named “file”.
AuthorWho wrote a part, as the guest is shown it: a member of staff by their first name, never their account or their address.
BotButtonOne of a bot part’s buttons.
BotPartWhat a help-bot part asks of the guest, beside its words.
BotReplyBodyThe guest’s answer to the bot part partId: one of its buttons, or the data it asked for.
CollectionA group of help articles.
CollectionArticlesOne collection and its articles, in staff’s order.
CollectionsEvery collection with an article in it, in staff’s order.
ConversationOne conversation, with every part the guest sees, oldest first.
ConversationPageA page of the guest’s conversations, the most recently changed first.
ConversationSummaryA conversation in a list.
ConversationUpdateThe answer to a wait: the parts after the one asked after that the guest sees, and the conversation as it is now. parts is empty when the wait ran out with nothing new.
CrmUnreadCountPurrCRM’s answer to a ReadBody: how many staff parts are still unread across every one of the guest’s conversations.
ExportArticleRatingThe guest saying whether an article helped, in their export.
ExportConversationOne of the guest’s conversations, in their export: every part they could see, and their rating.
ExportPartOne part of an exported conversation.
ExportTicketOne of the guest’s tickets, in their export.
HomeCardOne card of the messenger’s home.
MessageBodyThe guest writing: in a new conversation, in one of theirs, or on a ticket.
MessengerHomeThe messenger’s first screen.
MessengerLayoutThe messenger’s home as the app’s staff published it, for one guest at one moment: the greeting with their name in it, the faces, what it says about a reply, the cards they are to see now, in order (those not for them, a status out of its dates and the help center’s while it is off already left out by PurrCRM), and what they may do.
MessengerOpeningA help bot’s first words to a new conversation, as the guest would be shown them now: the bot’s parts up to its first question, numbered from 1 like a conversation’s, and nothing kept.
OpeningRefNames a MessengerOpening in a StartBody: its workflow, and the version it was worked out from.
PartOne part of a conversation: a message, a reply or a change.
RatingBodyThe guest’s answer to a rating request.
ReadBodyThe guest has seen a conversation up to a part.
ReplyExpectationHow soon the guest can expect an answer.
StartBodyThe guest starting a conversation: what they wrote (text and attachmentIds, as a MessageBody), or, under the help bot’s opening, a tap on one of its question’s buttons (button) instead.
SupportTicketOne of the guest’s tickets: the fields staff let the guest see, and the conversation about it, oldest first.
SupportTicketPageThe guest’s tickets, the most recently changed first.
SupportTicketSummaryA ticket in a list.
TicketFieldOne field of a ticket and its value: text for a text or a list field, number, boolean or date (YYYY-MM-DD).
attachmentTypesThe only content types a picture may be.
checkCollectedWhat the bot would take for field, as the service reads it (CheckCollected): trimmed, an email lower-cased, an order reference as the app’s orderRef keeps it; null when it would refuse it.
maxAttachmentBytesThe largest picture: five megabytes.
maxAttachmentsThe most pictures one message carries.
maxCollectRunesThe longest answer the help bot collects.
maxMessageRunesThe longest message a guest may write.
maxRatingThe best rating; 1 is the worst.
maxRemarkRunesThe longest remark with a rating.
messengerWaitThe longest a wait is held open with nothing new.
validEmailWhether s is an email address the service takes (ValidEmail).
class Article

One published article, whole.

factory Article.fromJson(Map<String, Object?> json)
const Article({required String id, required String slug, required String title, required List<ContentBlock> blocks, required DateTime updatedAt, String summary = '', Collection? collection, List<ArticleSummary> related = const <ArticleSummary>[], String language = '', List<String> languages = const <String>[]})
final List<ContentBlock> blocks
final Collection? collection

The collection it is in, when it is in one.

final String id
final String language

The language it is in (a BCP 47 tag): PurrCRM picked it for the guest, their own where the article is written in it and the app’s default where it is not. The package shows what it is given; the tag is for a host that says so.

final List<String> languages

Every language it is written in, the app’s default first.

final List<ArticleSummary> related

Other articles in the same collection, at most three.

final String slug
final String summary
final String title
final DateTime updatedAt
class ArticleRatingBody

The guest saying whether an article helped. Asked again, the newer answer is kept.

factory ArticleRatingBody.fromJson(Map<String, Object?> json)
const ArticleRatingBody({required bool helpful})
final bool helpful
Map<String, Object?> toJson()
class ArticleSummary

An article in a list.

factory ArticleSummary.fromJson(Map<String, Object?> json)
const ArticleSummary({required String id, required String slug, required String title, required DateTime updatedAt, String summary = '', String collection = ''})
final String collection

The slug of the collection it is in; empty for none.

final String id
final String slug
final String summary
final String title
final DateTime updatedAt
class Articles

The answer to a search: the best matches first; with nothing searched for, the featured articles.

factory Articles.fromJson(Map<String, Object?> json)
const Articles({required List<ArticleSummary> items})
final List<ArticleSummary> items
class Attachment

A picture in a part.

factory Attachment.fromJson(Map<String, Object?> json)
const Attachment({required String id, required String contentType, required int bytes, required String url, required DateTime expiresAt})
final int bytes
final String contentType
final DateTime expiresAt
final String id
final String url

Where to fetch it, until expiresAt; the conversation asked for again has a new one after that.

class AttachmentBody

Asks for somewhere to upload a picture.

factory AttachmentBody.fromJson(Map<String, Object?> json)
const AttachmentBody({required String contentType, required int bytes})
final int bytes

The picture’s size, at most maxAttachmentBytes.

final String contentType

One of attachmentTypes.

Map<String, Object?> toJson()
class AttachmentUpload

Where to upload a picture: a form POST to url with fields first and the file last, as a field named “file”. The picture is sent by naming id in a MessageBody before expiresAt.

factory AttachmentUpload.fromJson(Map<String, Object?> json)
const AttachmentUpload({required String id, required String url, required Map<String, String> fields, required int maxBytes, required DateTime expiresAt})
final DateTime expiresAt
final Map<String, String> fields
final String id
final int maxBytes
final String url
class Author

Who wrote a part, as the guest is shown it: a member of staff by their first name, never their account or their address.

factory Author.fromJson(Map<String, Object?> json)
const Author({required AuthorKind kind, String name = ''})
final AuthorKind kind
final String name

A teammate’s first name or the bot’s name; empty for the guest and the messenger.

bool get isGuest

Written by the guest, rather than by staff, the bot or the messenger.

class BotButton

One of a bot part’s buttons.

factory BotButton.fromJson(Map<String, Object?> json)
const BotButton({required String id, required String label})
final String id

What the answer names.

final String label

What the guest reads, and what their answer says they said.

class BotPart

What a help-bot part asks of the guest, beside its words.

factory BotPart.fromJson(Map<String, Object?> json)
const BotPart({required BotKind kind, List<BotButton> buttons = const <BotButton>[], CollectField collect = CollectField.unknown, List<ArticleSummary> articles = const <ArticleSummary>[], ReplyExpectation? reply, CrmOrderRef? orderRef})
final List<ArticleSummary> articles

A suggestion’s, each opened in the help center.

final List<BotButton> buttons

An ask’s, and a suggestion’s yes and no.

final CollectField collect

What a collecting part asks for.

final BotKind kind
final CrmOrderRef? orderRef
final ReplyExpectation? reply

A hand-off’s: how soon a person answers.

class BotReplyBody

The guest’s answer to the bot part partId: one of its buttons, or the data it asked for. Exactly one of button and value.

factory BotReplyBody.fromJson(Map<String, Object?> json)
const BotReplyBody({required String partId, String button = '', String value = ''})
final String button
final String partId
final String value
Map<String, Object?> toJson()
class Collection

A group of help articles.

factory Collection.fromJson(Map<String, Object?> json)
const Collection({required String id, required String slug, required String title, required int articles, String description = ''})
final int articles

How many published articles the guest can read in it.

final String description
final String id
final String slug
final String title
class CollectionArticles

One collection and its articles, in staff’s order.

factory CollectionArticles.fromJson(Map<String, Object?> json)
const CollectionArticles({required Collection collection, required List<ArticleSummary> articles})
final List<ArticleSummary> articles
final Collection collection
class Collections

Every collection with an article in it, in staff’s order.

factory Collections.fromJson(Map<String, Object?> json)
const Collections({required List<Collection> items})
final List<Collection> items
class Conversation

One conversation, with every part the guest sees, oldest first.

factory Conversation.fromJson(Map<String, Object?> json)
const Conversation({required String id, required ConversationState state, required List<Part> parts, required int lastSeq, required int unread, required ReplyExpectation reply, required DateTime createdAt, required DateTime updatedAt, Author? assignee, String ticketId = '', String awaiting = ''})
final Author? assignee

The member of staff answering, when one is.

final String awaiting

The bot part the help bot waits on the guest to answer; empty when it waits on nothing. Only that part’s buttons or box are live.

final DateTime createdAt
final String id
final int lastSeq

The newest part’s seq, including those the guest is not shown, so that a wait asked after it waits for the next.

final List<Part> parts
final ReplyExpectation reply
final ConversationState state

Open or closed; snoozed is shown as open.

final String ticketId

The ticket this conversation is about; empty when there is none.

final int unread

How many staff parts the guest has not read.

final DateTime updatedAt
class ConversationPage

A page of the guest’s conversations, the most recently changed first.

factory ConversationPage.fromJson(Map<String, Object?> json)
const ConversationPage({required List<ConversationSummary> items, String nextBefore = ''})
final List<ConversationSummary> items
final String nextBefore

The conversation to ask for older ones before; empty when there are none.

class ConversationSummary

A conversation in a list.

factory ConversationSummary.fromJson(Map<String, Object?> json)
const ConversationSummary({required String id, required ConversationState state, required String preview, required Author previewAuthor, required int unread, required DateTime createdAt, required DateTime updatedAt})
final DateTime createdAt
final String id
final String preview

The newest part the guest sees, as a line of text.

final Author previewAuthor
final ConversationState state
final int unread
final DateTime updatedAt
class ConversationUpdate

The answer to a wait: the parts after the one asked after that the guest sees, and the conversation as it is now. parts is empty when the wait ran out with nothing new.

factory ConversationUpdate.fromJson(Map<String, Object?> json)
const ConversationUpdate({required List<Part> parts, required ConversationState state, required int lastSeq, required int unread, String awaiting = ''})
final String awaiting

The bot part the help bot waits on now (Conversation.awaiting).

final int lastSeq
final List<Part> parts
final ConversationState state
final int unread
class CrmUnreadCount

PurrCRM’s answer to a ReadBody: how many staff parts are still unread across every one of the guest’s conversations. PurrCRM answers with the guest routes’ own unread count, which a host’s own bell can show too; the Dart name is the package’s, so that a host with a bell of its own keeps its name for it.

factory CrmUnreadCount.fromJson(Map<String, Object?> json)
const CrmUnreadCount({required int unread})
final int unread
class ExportArticleRating

The guest saying whether an article helped, in their export.

factory ExportArticleRating.fromJson(Map<String, Object?> json)
const ExportArticleRating({required String article, required bool helpful, required DateTime ratedAt})
final String article

The article’s title, as it was when rated.

final bool helpful
final DateTime ratedAt
class ExportConversation

One of the guest’s conversations, in their export: every part they could see, and their rating.

factory ExportConversation.fromJson(Map<String, Object?> json)
const ExportConversation({required String id, required ConversationState state, required List<ExportPart> parts, required DateTime createdAt, int? rating, String remark = ''})
final DateTime createdAt
final String id
final List<ExportPart> parts
final int? rating
final String remark
final ConversationState state
class ExportPart

One part of an exported conversation. A picture is named by its content type and size: the file is sent on request, not in the export.

factory ExportPart.fromJson(Map<String, Object?> json)
const ExportPart({required PartKind kind, required Author author, required DateTime createdAt, String text = '', List<ContentBlock> blocks = const <ContentBlock>[], List<String> attachments = const <String>[]})
final List<String> attachments
final Author author
final List<ContentBlock> blocks
final DateTime createdAt
final PartKind kind
final String text
class ExportTicket

One of the guest’s tickets, in their export.

factory ExportTicket.fromJson(Map<String, Object?> json)
const ExportTicket({required String ref, required String title, required String type, required TicketState state, required List<TicketField> fields, required DateTime createdAt, DateTime? resolvedAt})
final DateTime createdAt
final List<TicketField> fields
final String ref
final DateTime? resolvedAt
final TicketState state
final String title
final String type
class HomeCard

One card of the messenger’s home. Only what its kind draws is set: a link card’s title, text and link; a status card’s text, and its title and link when it has them; an articles card’s articles.

factory HomeCard.fromJson(Map<String, Object?> json)
const HomeCard({required String id, required HomeCardKind kind, String title = '', String text = '', String link = '', List<ArticleSummary> articles = const <ArticleSummary>[]})
final List<ArticleSummary> articles
final String id

The card’s own, the same on every read.

final HomeCardKind kind
final String link

A path in the app or an https link; empty for none.

final String text
final String title
class MessageBody

The guest writing: in a new conversation, in one of theirs, or on a ticket. Text, pictures, or both.

factory MessageBody.fromJson(Map<String, Object?> json)
const MessageBody({required String text, List<String> attachmentIds = const []})
final List<String> attachmentIds

Pictures uploaded through an AttachmentUpload and not yet sent.

final String text
Map<String, Object?> toJson()
class MessengerHome

The messenger’s first screen.

factory MessengerHome.fromJson(Map<String, Object?> json)
const MessengerHome({required List<ConversationSummary> open, required int unread, required ReplyExpectation reply, List<ArticleSummary> articles = const <ArticleSummary>[], List<SupportTicketSummary> tickets = const <SupportTicketSummary>[], MessengerOpening? opening, MessengerLayout? layout})
final List<ArticleSummary> articles

The help center’s featured articles; empty while it is off.

final MessengerLayout? layout

The home as the app’s staff published it, worked out for this guest now; null until they publish one (and from a CRM before 0.4.0’s), when the messenger draws the home it always drew.

final List<ConversationSummary> open

The guest’s open conversations, the most recently changed first, at most three.

final MessengerOpening? opening

What the help bot says to a new conversation before the guest writes; null when no help bot greets them first (it then speaks once they have written), when they may not start one now, and from a CRM before openings.

final ReplyExpectation reply
final List<SupportTicketSummary> tickets

The guest’s tickets not yet resolved, and those resolved in the last week.

final int unread

Every staff part the guest has not read, in every conversation.

bool get picturesAllowed

Whether the guest may send pictures: as the published home says, and yes without one.

class MessengerLayout

The messenger’s home as the app’s staff published it, for one guest at one moment: the greeting with their name in it, the faces, what it says about a reply, the cards they are to see now, in order (those not for them, a status out of its dates and the help center’s while it is off already left out by PurrCRM), and what they may do. The help bot’s opening is not part of it: the home greets, and the bot speaks first in the conversation the send card starts.

factory MessengerLayout.fromJson(Map<String, Object?> json)
const MessengerLayout({String greeting = '', String intro = '', TeamFaces faces = TeamFaces.auto, List<Author> team = const <Author>[], bool names = false, String replyText = '', String awayText = '', List<HomeCard> cards = const <HomeCard>[], NewConversations newConversation = NewConversations.always, bool attachments = true, bool helpCenter = true})
final bool attachments

The guest may send pictures.

final String awayText

Said instead while the team is away; empty for “We’re back …”.

final List<HomeCard> cards

The guest’s cards, in order, those of a kind this version does not know left out.

final TeamFaces faces
final String greeting

The heading, personalised; empty for the package’s own words.

final bool helpCenter

The home points to the help center.

final String intro

A line under it; empty for none.

final bool names

Write the chosen teammates’ names under their faces.

final NewConversations newConversation
final String replyText

How soon a reply comes, in staff’s words; empty for the line worked out from the reply expectation.

final List<Author> team

The teammates to show when faces is chosen, by first name.

class MessengerOpening

A help bot’s first words to a new conversation, as the guest would be shown them now: the bot’s parts up to its first question, numbered from 1 like a conversation’s, and nothing kept. A new-chat screen shows them as the bot’s; the conversation is made when the guest taps one of the awaiting part’s buttons or writes, with MessengerApi.start given this opening, and its parts then are the conversation’s own, with ids of their own.

factory MessengerOpening.fromJson(Map<String, Object?> json)
const MessengerOpening({required String workflow, required int version, required List<Part> parts, String awaiting = ''})
final String awaiting

The id of the part whose question the bot waits on, as Conversation.awaiting: its buttons are live, or the composer asks for what it collects. Empty when it asks nothing.

final List<Part> parts

The bot’s parts, oldest first.

final int version
final String workflow

The help bot’s workflow, and the version the opening is of.

Part? get question

The part the bot waits on, when it waits on one.

class OpeningRef

Names a MessengerOpening in a StartBody: its workflow, and the version it was worked out from.

factory OpeningRef.fromJson(Map<String, Object?> json)
const OpeningRef({required String workflow, required int version})
OpeningRef.of(MessengerOpening o)

The opening o names.

final int version
final String workflow
Map<String, Object?> toJson()
class Part

One part of a conversation: a message, a reply or a change. Only the fields its kind names are set.

factory Part.fromJson(Map<String, Object?> json)
const Part({required String id, required int seq, required PartKind kind, required Author author, required DateTime createdAt, String text = '', List<ContentBlock> blocks = const <ContentBlock>[], List<Attachment> attachments = const <Attachment>[], ConversationState state = ConversationState.unknown, int? rating, BotPart? bot})
final List<Attachment> attachments
final Author author
final List<ContentBlock> blocks

A reply’s or the bot’s words.

final BotPart? bot

What a help-bot part asks of the guest; null for any other part.

final DateTime createdAt
final String id
final PartKind kind
final int? rating

The guest’s answer to a rating request, 1 to maxRating, once given.

final int seq

Orders the conversation’s parts, from 1; what a wait is asked after.

final ConversationState state

Where a state part moved the conversation to.

final String text

What the guest wrote, as they wrote it.

class RatingBody

The guest’s answer to a rating request.

factory RatingBody.fromJson(Map<String, Object?> json)
const RatingBody({required int rating, String remark = ''})
final int rating

1 (bad) to maxRating (great).

final String remark

Anything they want to add; at most maxRemarkRunes.

Map<String, Object?> toJson()
class ReadBody

The guest has seen a conversation up to a part.

factory ReadBody.fromJson(Map<String, Object?> json)
const ReadBody({required int seq})
final int seq
Map<String, Object?> toJson()
class ReplyExpectation

How soon the guest can expect an answer.

factory ReplyExpectation.fromJson(Map<String, Object?> json)
const ReplyExpectation({required ReplyTime within, required bool open, DateTime? opensAt})
final bool open

Staff are at work now, by the team’s office hours.

final DateTime? opensAt

When they are next at work, when they are not now.

final ReplyTime within
class StartBody

The guest starting a conversation: what they wrote (text and attachmentIds, as a MessageBody), or, under the help bot’s opening, a tap on one of its question’s buttons (button) instead.

With an opening the bot’s parts come first in the conversation, and the tap or the words are its answer; without one the guest’s words come first, and the bot after them.

factory StartBody.fromJson(Map<String, Object?> json)
StartBody.message(MessageBody body, {OpeningRef? opening})

The guest’s words under opening (or with none), as written.

const StartBody({String text = '', List<String> attachmentIds = const [], OpeningRef? opening, String button = ''})
final List<String> attachmentIds

Pictures uploaded through an AttachmentUpload and not yet sent.

final String button

The id of the opening’s button the guest tapped, with no words or pictures; empty when they wrote.

final OpeningRef? opening

The opening the guest was shown when they wrote or tapped; null for none.

final String text
Map<String, Object?> toJson()
class SupportTicket

One of the guest’s tickets: the fields staff let the guest see, and the conversation about it, oldest first.

factory SupportTicket.fromJson(Map<String, Object?> json)
const SupportTicket({required String id, required String ref, required String title, required String type, required TicketState state, required DateTime createdAt, required DateTime updatedAt, List<ContentBlock> description = const <ContentBlock>[], List<TicketField> fields = const <TicketField>[], String conversationId = '', List<Part> parts = const <Part>[], DateTime? resolvedAt})
final String conversationId

The conversation the ticket’s parts are in; empty when there is none.

final DateTime createdAt
final List<ContentBlock> description

What staff wrote the ticket is for.

final List<TicketField> fields
final String id
final List<Part> parts
final String ref
final DateTime? resolvedAt
final TicketState state
final String title
final String type
final DateTime updatedAt
class SupportTicketPage

The guest’s tickets, the most recently changed first.

factory SupportTicketPage.fromJson(Map<String, Object?> json)
const SupportTicketPage({required List<SupportTicketSummary> items})
final List<SupportTicketSummary> items
class SupportTicketSummary

A ticket in a list.

factory SupportTicketSummary.fromJson(Map<String, Object?> json)
const SupportTicketSummary({required String id, required String ref, required String title, required String type, required TicketState state, required DateTime updatedAt})
final String id
final String ref

How the guest and staff name it: “T-1042”.

final TicketState state
final String title
final String type

The ticket’s kind, in staff’s words (“Order issue”).

final DateTime updatedAt
class TicketField

One field of a ticket and its value: text for a text or a list field, number, boolean or date (YYYY-MM-DD). A field with no value has none of them.

factory TicketField.fromJson(Map<String, Object?> json)
const TicketField({required String key, required String label, required FieldType type, String text = '', double? number, bool? boolean, String date = ''})
final bool? boolean
final String date
final String key
final String label
final double? number
final String text
final FieldType type
bool get hasValue

Whether the field has a value to show.

const List<String> attachmentTypes

The only content types a picture may be. The Go variable is AttachmentTypes.

String? checkCollected(CollectField field, String value, [CrmOrderRef? orderRef])

What the bot would take for field, as the service reads it (CheckCollected): trimmed, an email lower-cased, an order reference as the app’s orderRef keeps it; null when it would refuse it. With no order reference on the collecting part, any words are taken as they are and PurrCRM decides: the package has no format of its own to hold them to.

const int maxAttachmentBytes

The largest picture: five megabytes. The Go constant is MaxAttachmentBytes.

const int maxAttachments

The most pictures one message carries. The Go constant is MaxAttachments.

const int maxCollectRunes

The longest answer the help bot collects. The Go constant is MaxCollectRunes.

const int maxMessageRunes

The longest message a guest may write. The Go constant is MaxMessageRunes.

const int maxRating

The best rating; 1 is the worst. The Go constant is MaxRating.

const int maxRemarkRunes

The longest remark with a rating. The Go constant is MaxRemarkRunes.

const Duration messengerWait

The longest a wait is held open with nothing new. The Go constant is MessengerWait.

bool validEmail(String s)

Whether s is an email address the service takes (ValidEmail).