Chat Projects |
This topic covers the scripting API for chat projects. The chat-specific data lives on a separate object model accessed through ChatProjectItem, while the standard document loop still applies for iterating and committing changes.
Building custom chat connectors — ReadySuite has built-in connectors for common platforms, but scripts can import data from any chat format. A script reads the source data (CSV, JSON, database, proprietary export), creates participants, conversations, and events through the API, and produces a fully structured chat project. This is the primary way to add support for new or proprietary messaging formats.
Enriching and overlaying existing chat data — Scripts can iterate over an existing chat project to classify messages, flag content, assign importance, update metadata, or populate custom fields. Because each chat event is backed by a DocumentItem, you can combine standard field operations with chat-specific modifications in the same document loop.
Data cleanup and validation — Scripts can audit chat data for integrity issues like missing senders, orphaned threads, or incomplete metadata, and fix or report problems automatically.
Use ProjectItem.GetChatProject() to access the chat API. Always check IsChatProject first — calling GetChatProject() on a document project throws a NotSupportedException:
var project = script.GetProject(); if (project.IsChatProject) { var chatProject = project.GetChatProject(); // Work with chat data }
Chat projects are organized as a hierarchy:
ChatProjectItem
├── Participants (ChatParticipantItem)
└── Sources (ChatSourceItem)
└── Conversations (ChatConversationItem)
└── Events (ChatEventItem) ── tied to a DocumentItem
├── Reactions (ChatEventReactionItem)
├── Attachments (ChatEventAttachmentItem)
├── Edits (ChatEventEditItem)
└── Read Receipts (ChatEventReadReceiptItem)Participants are global to the project — the same participant can appear in multiple conversations across multiple sources.
Sources are containers of conversations. A project may have one or many sources (e.g. one per data export or platform).
Conversations represent a channel or direct message thread. Each conversation has a type, platform, and its own set of participants.
Events are the individual messages, joins, leaves, or other activities. Each event is tied to a DocumentItem, so you can still use the document loop to iterate them.
Participants represent the people in a chat project. Each has a unique ID across the entire project.
var chatProject = script.GetProject().GetChatProject(); var alice = chatProject.CreateParticipant( id: "user-001", displayName: "Alice Smith", email: "alice@example.com"); var bob = chatProject.CreateParticipant( id: "user-002", displayName: "Bob Jones");
All parameters are optional — omitting id auto-generates a GUID.
var participants = chatProject.GetParticipants(); var alice = chatProject.GetParticipant("user-001");
| Property | Type | Description |
|---|---|---|
| Id | string | Unique identifier |
| DisplayName | string? | Display name |
| string? | Email address | |
| AvatarPath | string? | Path to avatar image |
| OtherProperties | IDictionary<string, string> | Custom metadata |
var source = chatProject.CreateChatSource(); var conversation = source.CreateConversation( id: "general", displayName: "General", platform: "Slack", type: ChatConversationItemType.Channel);
| Value | Description |
|---|---|
| Channel | A channel conversation (e.g. Slack channel, Teams channel) |
| Direct | A direct or group message |
| Other | Other or unknown type |
Conversations track which participants are involved. Add participants to the Participants collection:
conversation.Participants.Add(alice); conversation.Participants.Add(bob);
Each conversation can have a custodian — the person whose perspective the conversation is captured from:
conversation.Custodian = alice;
| Property | Type | Description |
|---|---|---|
| Id | string | Unique within the source |
| DisplayName | string? | Conversation name |
| Platform | string | Platform identifier (e.g. "Slack", "Teams") |
| Type | ChatConversationItemType | Channel, Direct, or Other |
| Participants | ICollection<ChatParticipantItem> | Conversation participants |
| Custodian | ChatParticipantItem? | Custodian of the conversation |
| OtherProperties | IDictionary<string, string> | Custom metadata |
Events are the core data in a chat project — messages, joins, leaves, and other activity. Each event is tied to a DocumentItem.
When iterating documents in a chat project, use GetChatEvent() to access the event tied to each document:
while (script.ReadDocument()) { var document = script.Document; var chatEvent = document.GetChatEvent(); if (chatEvent != null) { var message = chatEvent.Message; var sender = chatEvent.Sender; var timestamp = chatEvent.Timestamp; } }
GetChatEvent() returns null if the document has no associated chat event.
To create a new event, call CreateChatEvent() on a document. The event is automatically tied to that document:
var newDoc = script.GetNewDocument("MSG-0001"); script.CreateDocument(newDoc); var chatEvent = newDoc.CreateChatEvent( conversation: conversation, sender: alice, timestamp: DateTime.UtcNow); chatEvent.Message = "Hello, world!"; script.UpdateDocument(newDoc);
A document can only have one chat event — calling CreateChatEvent() on a document that already has one throws an InvalidOperationException.
Event properties are mutable. Update them directly and call UpdateDocument():
while (script.ReadDocument()) { var document = script.Document; var chatEvent = document.GetChatEvent(); if (chatEvent != null && chatEvent.Type == ChatEventItemType.Message) { chatEvent.Importance = ChatEventItemImportance.High; script.UpdateDocument(document); } }
| Property | Type | Description |
|---|---|---|
| Id | string | Unique event identifier |
| Conversation | ChatConversationItem | The conversation this event belongs to |
| Parent | ChatEventItem? | Parent event (for threaded replies) |
| Message | string? | Message content |
| Type | ChatEventItemType | Message, Disclaimer, Join, Leave, or Other |
| Direction | ChatEventItemDirection | Incoming, Outgoing, or Other |
| Sender | ChatParticipantItem? | Who sent the event |
| Recipients | IReadOnlyCollection<ChatParticipantItem> | All conversation participants |
| Timestamp | DateTime | When the event was sent |
| Importance | ChatEventItemImportance | Normal, High, or Other |
| IsDeleted | bool | Whether the event was deleted |
| Value | Description |
|---|---|
| Message | A chat message |
| Disclaimer | A disclaimer event |
| Join | A participant joined |
| Leave | A participant left |
| Other | Other or unknown event |
Events can be nested using the Parent property. This is used for threaded replies (e.g. Slack threads):
var reply = newDoc.CreateChatEvent(conversation, bob, DateTime.UtcNow); reply.Message = "This is a reply."; reply.Parent = parentEvent;
The parent event must belong to the same conversation. Changing an event's conversation resets Parent to null.
Events can have emoji reactions:
var reaction = chatEvent.CreateReaction("👍"); reaction.Count = 3; reaction.Participants.Add(alice); reaction.Participants.Add(bob);
var reactions = chatEvent.GetReactions();
chatEvent.RemoveReaction(reaction);
chatEvent.ClearReactions();Events can have file attachments or external resource links:
// Local file attachment var attachment = chatEvent.CreateAttachment("C:\\files\\report.pdf"); // External resource (URL or network path) var extAttachment = chatEvent.CreateExternalResourceAttachment("https://example.com/file.pdf");
The copyFileToChatSource parameter (default true) controls whether the file is copied into the chat source directory:
var attachment = chatEvent.CreateAttachment(filePath, copyFileToChatSource: false);
| Property | Type | Description |
|---|---|---|
| Id | string | Unique identifier |
| DisplayName | string? | Display name |
| Path | string? | Local file path (null for external resources) |
| IsExternalResource | bool | Whether this is an external resource |
| ExternalResourcePath | string? | External resource path |
Attachments can be copied or moved:
attachment.CopyTo("C:\\output\\report.pdf"); attachment.MoveTo("C:\\output\\report.pdf");
Track message edits:
var edit = chatEvent.CreateEdit( timestamp: DateTime.UtcNow, newMessage: "Updated message", oldMessage: "Original message", participant: alice);
var edits = chatEvent.GetEdits();
chatEvent.ClearEdits();Track when participants read or received a message:
var receipt = chatEvent.CreateReadReceipt(
participant: bob,
timestamp: DateTime.UtcNow,
type: ChatEventReadReceiptItemType.Read);| Receipt Type | Description |
|---|---|
| Read | The message was read |
| Delivered | The message was delivered but may not be read |
| Other | Other or unknown type |
Most chat types expose an OtherProperties dictionary for storing platform-specific or custom metadata that doesn't fit the standard schema:
participant.OtherProperties["sourceSystem"] = "LegacyChat v2.1"; conversation.OtherProperties["originalId"] = "slack-C123456";
OtherProperties is available on ChatParticipantItem, ChatConversationItem, ChatEventReactionItem, ChatEventAttachmentItem, ChatEventEditItem, and ChatEventReadReceiptItem.
The chat API enforces several constraints:
This example builds a chat project from scratch — the pattern used when writing a custom connector for a format ReadySuite doesn't natively support. The script reads source data, creates the project structure, and ties each message to a document:
public override void Run(IScriptContext script) { base.Run(script); var chatProject = script.GetProject().GetChatProject(); // Create participants var alice = chatProject.CreateParticipant( id: "user-001", displayName: "Alice Smith", email: "alice@example.com"); var bob = chatProject.CreateParticipant( id: "user-002", displayName: "Bob Jones"); // Create source and conversation var source = chatProject.CreateChatSource(); var conversation = source.CreateConversation( id: "general", displayName: "General", platform: "CustomChat", type: ChatConversationItemType.Channel); conversation.Participants.Add(alice); conversation.Participants.Add(bob); conversation.Custodian = alice; // Create a document and event for each message var newDoc = script.GetNewDocument("MSG-0001"); script.CreateDocument(newDoc); var chatEvent = newDoc.CreateChatEvent(conversation, alice, DateTime.UtcNow); chatEvent.Message = "Hello, team!"; chatEvent.Direction = ChatEventItemDirection.Outgoing; script.UpdateDocument(newDoc); }
This example iterates over an existing chat project and flags messages that match a keyword — a common overlay pattern for classification or review workflows:
public override void Run(IScriptContext script) { base.Run(script); var project = script.GetProject(); if (!project.IsChatProject) { script.AddWarning("This script only works with chat projects."); return; } while (script.ReadDocument()) { var document = script.Document; var chatEvent = document.GetChatEvent(); if (chatEvent == null || chatEvent.Type != ChatEventItemType.Message) continue; if (!string.IsNullOrEmpty(chatEvent.Message) && chatEvent.Message.Contains(options.Keyword, StringComparison.OrdinalIgnoreCase)) { chatEvent.Importance = ChatEventItemImportance.High; document.SetFieldData(options.FlagField, "true"); script.UpdateDocument(document); } } }