This guide covers outgoing communication actions and the incoming friend/MOTD features. CommunicationPlugin owns the friend and character-entry subscriptions; CommunicationTestCommandSuite owns the admin diagnostic command. All sends use CharacterCommunicationApi and its journaled actions over the shared BcClient queue.
classDiagram
direction LR
class CommunicationPlugin {
+start() void
+stop() void
-friendReciprocation: FriendReciprocationAction
-motd: MotdService
}
class CommunicationTestCommandSuite {
+register() Dispose
-execute(sender, args) Promise~void~
}
class CharacterCommunicationApi {
+chat(text) ActionOutcome
+chatRedacted(text) ActionOutcome
+emote(text) ActionOutcome
+action(text, dictionary) ActionOutcome
+activity(text, dictionary) ActionOutcome
+status(text, dictionary) ActionOutcome
+hidden(member, text, dictionary) ActionOutcome
+whisper(member, text) ActionOutcome
+motd(member, text) ActionOutcome
+beep(member, text) ActionOutcome
+testAllChannels(member, text) ChannelResult[]
}
class SendRoomMessageAction {
+execute(type, content, target, dictionary) ActionOutcome
}
class SendWhisperAction {
+execute(member, text) ActionOutcome
}
class SendBeepAction {
+execute(member, text) ActionOutcome
}
class FriendReciprocationAction {
+handleMessage(message) Promise~ActionOutcome?~
+resetConnectionState() void
}
class MotdService {
+onCharacterEntered(member) Promise~ActionOutcome?~
+resetConnectionState() void
}
class BcClient
class ActionCoordinator
class ActionJournal
CommunicationPlugin --> BcClient : message/presence
CommunicationPlugin --> FriendReciprocationAction
CommunicationPlugin --> MotdService
CommunicationPlugin --> CommunicationTestCommandSuite
CommunicationTestCommandSuite --> CharacterCommunicationApi
CharacterCommunicationApi --> SendRoomMessageAction
CharacterCommunicationApi --> SendWhisperAction
CharacterCommunicationApi --> SendBeepAction
SendRoomMessageAction --> BcClient : ChatRoomChat
SendWhisperAction --> BcClient : targeted Whisper
SendBeepAction --> BcClient : AccountBeep
FriendReciprocationAction --> BcClient : AccountUpdate / Hidden / OnlineFriends
MotdService --> CharacterCommunicationApi
SendRoomMessageAction --> ActionCoordinator
SendWhisperAction --> ActionCoordinator
SendBeepAction --> ActionCoordinator
ActionCoordinator --> ActionJournal
stateDiagram-v2
[*] --> Requested
Requested --> Journaled: ActionCoordinator starts record
Journaled --> Queued: action validates and enqueues packet
Queued --> Failed: validation, disconnected transport, or emit error
Queued --> DispatchedUnconfirmed: local Socket.IO emit succeeds
DispatchedUnconfirmed --> DeliveryUnknown: no sender-side receipt exists
DeliveryUnknown --> [*]
Failed --> [*]
Room messages share BC’s rate-limited ChatRoomChat transport. Chat, Emote, Action, Activity, and Hidden are available through SendRoomMessageAction; Whisper has a dedicated action because it needs a target and BC’s map-room wrapping; Beep uses AccountBeep and requires the target to be a bot friend. These sends generally end at dispatched_unconfirmed; a successful emit does not prove display or delivery.
CharacterCommunicationApi.chatRedacted() uses the same journaled room-chat
action but replaces any dispatch exception with a fixed error before it reaches
the action journal. Use it for sensitive one-shot text whose content must not
appear in an error string; action metadata still records only its length and
dispatch status.
Action and Activity content is not arbitrary prose in BC’s UI. Action content must be a localized key available in BC asset/interface text, with matching substitutions in the dictionary. Activity needs an ActivityDictionary.csv key and activity metadata. The prototype’s channel test uses existing localized examples and substitutes the supplied text into the nickname-change Action. Status is declared by the wire type but is not displayed by the current BC chat renderer, so the diagnostic probe reports it skipped. Hidden is delivered as a targeted packet but intentionally does not appear in the room transcript.
stateDiagram-v2
[*] --> IncomingHidden
IncomingHidden --> Ignored: wrong type/content/target/sender
IncomingHidden --> AlreadyFriend: sender already in bot friend state
IncomingHidden --> Pending: valid ChatRoomFriendRequestAdd
Pending --> Deduplicated: same sender request already pending
Pending --> AccountUpdate: add sender to bot FriendList
AccountUpdate --> NotifyPeer: emit reciprocal Hidden request
NotifyPeer --> ReadBack: query OnlineFriends
ReadBack --> PersistedObserved: online result says Type=Friend
ReadBack --> PersistenceNotObserved: absent/offline/query failure
AlreadyFriend --> RecordedSatisfied
Ignored --> [*]
Deduplicated --> [*]
PersistedObserved --> [*]
PersistenceNotObserved --> [*]
RecordedSatisfied --> [*]
The read-back is an online-friends subset. If the sender is offline or missing from the response, the feature records persistence as unobserved rather than claiming rejection or success.
stateDiagram-v2
[*] --> CharacterEntered
CharacterEntered --> Skipped: invalid member / empty message / already greeted
CharacterEntered --> ResolveMessage: first valid entry this connection
ResolveMessage --> WhisperRequested: matching scheduled preset or MOTD_TEXT fallback
WhisperRequested --> DispatchedUnconfirmed: local emit succeeds
WhisperRequested --> Failed: send action fails
DispatchedUnconfirmed --> [*]
Failed --> RetryEligible: remove greeting marker
Skipped --> [*]
RetryEligible --> [*]
The greeting set is reset on each BC ready event. A sent Whisper has no delivery receipt, so it remains dispatched_unconfirmed.
The message is selected when the character enters. The bot reads the current
motd.schedule value from the live prototypeGameConfig snapshot; writes from
the admin web are picked up by the existing game-config watcher. The schedule
contains named message presets and ordered rules, using an explicit IANA time
zone (UTC by default). A rule can match weekdays, a time range, or both; empty
weekdays mean every day, and an overnight range belongs to its starting weekday.
The first matching rule selects its preset. If the config is absent or invalid,
or no rule matches, the bot uses MOTD_TEXT. Delete the stored schedule to
return to that environment fallback. Messages are limited to 500 characters.
Administrators can create, edit, reorder, and delete presets and schedule rules from the MOTD tab in the admin web. Those edits use the global-config revision check and audit path; they do not change live BC state.
Room admins can run !testcomms <memberId> <text> or /bot testcomms <memberId> <text>. Chat/Emote/Action/Activity are room-wide; Hidden/Whisper/Beep target the supplied member. The command privately reports each action status and records only safe summaries such as target, channel, and text length. Use non-sensitive test text because several variants are visible in the room.