The Lifecycle of a Message
A Message Is Born
First, a user sends a message to the chat. This could be through any supported messaging platform, such as IRC, Discord, or Slack:
# Example on IRC
<goos> | !weather 12345
Your Voice Is Heard
The message is heard by the appropriate module, which is responsible for handling the incoming messages from specific platforms (e.g., irc-connector@thegooscloud
). This module formats the message into a standardized JSON payload and forwards it to the NATS message broker:
# Topic format: chat.message.incoming.$platform.$network.$instance.$channel.$user
# Example topic: chat.message.incoming.irc.thegooscloud.eevee.#general.goos
{
"type": "message.incoming",
"trace": "c4f8f2e5-0fbe-4511-a398-cb43393c2eed", # Unique trace ID for the message
"platform": "irc", # Platform the message came from
"network": "thegooscloud", # Network identifier within the platform
"instance": "eevee", # Instance identifier within the network
"channel": "#general", # Channel where the message was posted
"user": "goos", # User who sent the message
"text": "!weather 12345", # Raw text of the message
"raw_event": {}, # Placeholder for raw event data from the platform
}
We Seek to Understand
The router
module consumes the incoming message from NATS. The router performs several tasks:
- Common Prefix Check: It verifies if the message starts with a recognized prefix (e.g.,
!
). - Regex Parsing: The router attempts to match the message text against any registered regular expressions.
- Filtering and Ratelimiting: It checks the message against any filtering or ratelimiting rules.
- Emitting a Parsed Message: After processing, the router emits a structured message back into NATS with additional metadata:
# Topic format: command.request.$command
# Example topic: command.request.weather
{
"argv": [ "!weather", "12345" ], # Argument vector with command and parameters
"channel": "#general", # Channel where the message originated
"command": "weather", # Command extracted from the message
"commandUUID": "d462389d-a4f5-4d38-b738-3fa2ae89c2ad", # Unique UUID for the command request
"network": "thegooscloud", # Network identifier within the platform
"instance": "eevee", # Instance identifier within the network
"platform": "irc", # Platform where the message was received
"prefix": "!", # Command prefix used
"raw": {}, # Raw input data for reference
"regexCaptured": ["12345"], # Any captured groups from regex matching
"targetModule": "weather", # Target module for processing the command
"text": "!weather 12345", # Original message text
"trace": "c4f8f2e5-0fbe-4511-a398-cb43393c2eed", # Trace ID inherited from the original message
"type": "command.request", # Type of message being processed
"user": "goos@foo.bar.baz", # Full user identifier including network information
"ratelimited": true, # Was this message held due to ratelimiting rules
}
We Want to Help
The parsed command.request
message is now delivered to the target module, which in this case is the weather
module. This module performs the necessary action to fulfill the command, such as querying an external weather API. If the command requires a response, the module will construct and send a command.response
message back to NATS:
# Topic: command.response.$trace
{
"channel": "#general", # Channel where the response should be sent
"fromModule": "weather", # Module that processed the command
"network": "thegooscloud", # Network identifier within the platform
"instance": "eevee", # Instance identifier within the network
"platform": "irc", # Platform where the response should be sent
"text": "goos: the weather for 12345 is foo bar baz", # Response text to be sent to the user
"trace": "c4f8f2e5-0fbe-4511-a398-cb43393c2eed", # Trace ID inherited from the original message
"type": "command.response", # Type of message being sent
}
Hear Our Voice
The router
module consumes the command.response
message from NATS. Similar to its role in understanding incoming messages, the router may apply additional filtering or ratelimiting rules to the response. Once processed, it emits a chat.message.outgoing
message, which is picked up by the appropriate connector module:
# Topic format: chat.message.outgoing.$platform.$network.$channel
# Example topic: chat.message.outgoing.irc.thegooscloud.#general
{
"channel": "#general", # Channel where the message should be posted
"network": "thegooscloud", # Network identifier within the platform
"instance": "eevee", # Instance identifier within the network
"platform": "irc", # Platform where the message should be sent
"text": "goos: the weather for 12345 is foo bar baz", # Text of the message to be sent
"trace": "c4f8f2e5-0fbe-4511-a398-cb43393c2eed", # Trace ID inherited from the original message
"type": "message.outgoing", # Type of message being sent
}
This outgoing message is then consumed by the irc-connector@thegooscloud
module, which is responsible for sending the message to the intended destination on the IRC network:
# Example on IRC
<eevee> | goos: the weather for 12345 is foo bar baz
Notes
A module may register .*
with no prefix
specified to listen to all messages in a channel. This is useful for modules that need to react to specific content regardless of whether it starts with a command prefix. Notable examples include:
tell
: This module listens to all messages to identify any commands for delayed delivery.urltitle
: This module listens to all messages to fetch and display titles of URLs posted in channels.