feature: ChatManager

feature:
- multi chatboxes manage
- chatbox message queue
This commit is contained in:
2025-10-30 12:58:53 +08:00
parent e680ef0263
commit 959ec0c424
2 changed files with 1033 additions and 5 deletions

View File

@@ -1,16 +1,19 @@
/**
* Example CLI application demonstrating the ccCLI framework
* This example shows how to create a calculator CLI with global context injection
* and ChatManager integration for Minecraft chat functionality
*/
import { Command, createCli, CliError } from "../lib/ccCLI/index";
import { Ok, Result } from "../lib/thirdparty/ts-result-es";
import { ChatManager, ChatMessage, ChatToast } from "../lib/ChatManager";
// 1. Define global context type
interface AppContext {
appName: string;
log: (message: string) => void;
debugMode: boolean;
chatManager?: ChatManager;
}
// 2. Define individual commands
@@ -153,10 +156,346 @@ const configCommand: Command<AppContext> = {
]),
};
// ChatManager commands
const chatSendCommand: Command<AppContext> = {
name: "send",
description: "Send a chat message",
args: [
{ name: "message", description: "The message to send", required: true },
],
options: new Map([
[
"player",
{
name: "player",
shortName: "p",
description: "Target player for private message",
defaultValue: undefined,
},
],
[
"prefix",
{
name: "prefix",
description: "Message prefix",
defaultValue: "CC",
},
],
]),
action: ({ args, options, context }): Result<void, CliError> => {
if (!context.chatManager) {
print(
"Error: ChatManager not initialized. No chatbox peripherals found.",
);
return Ok.EMPTY;
}
const message: ChatMessage = {
message: args.message as string,
targetPlayer: options.player as string | undefined,
prefix: options.prefix as string,
};
const result = context.chatManager.sendMessage(message);
if (result.isOk()) {
print(`Message queued: "${String(args.message)}"`);
const targetPlayer = options.player;
if (
targetPlayer !== undefined &&
targetPlayer !== null &&
typeof targetPlayer === "string"
) {
print(`Target: ${targetPlayer}`);
} else {
print("Target: Global chat");
}
} else {
print(`Failed to queue message: ${result.error.reason}`);
}
return Ok.EMPTY;
},
};
const chatToastCommand: Command<AppContext> = {
name: "toast",
description: "Send a toast notification to a player",
args: [
{ name: "player", description: "Target player username", required: true },
{ name: "title", description: "Toast title", required: true },
{ name: "message", description: "Toast message", required: true },
],
options: new Map([
[
"prefix",
{
name: "prefix",
description: "Message prefix",
defaultValue: "CC",
},
],
]),
action: ({ args, options, context }): Result<void, CliError> => {
if (!context.chatManager) {
print(
"Error: ChatManager not initialized. No chatbox peripherals found.",
);
return Ok.EMPTY;
}
const toast: ChatToast = {
username: args.player as string,
title: args.title as string,
message: args.message as string,
prefix: options.prefix as string,
};
const result = context.chatManager.sendToast(toast);
if (result.isOk()) {
print(
`Toast queued for ${String(args.player)}: "${String(args.title)}" - "${String(args.message)}"`,
);
} else {
print(`Failed to queue toast: ${result.error.reason}`);
}
return Ok.EMPTY;
},
};
const chatStatusCommand: Command<AppContext> = {
name: "status",
description: "Show ChatManager status and queue information",
action: ({ context }): Result<void, CliError> => {
if (!context.chatManager) {
print("ChatManager: Not initialized (no chatbox peripherals found)");
return Ok.EMPTY;
}
print("=== ChatManager Status ===");
print(`Pending messages: ${context.chatManager.getPendingMessageCount()}`);
print(`Pending toasts: ${context.chatManager.getPendingToastCount()}`);
print(
`Buffered received: ${context.chatManager.getBufferedMessageCount()}`,
);
const chatboxStatus = context.chatManager.getChatboxStatus();
print(`Chatboxes: ${chatboxStatus.length} total`);
for (let i = 0; i < chatboxStatus.length; i++) {
const status = chatboxStatus[i] ? "idle" : "busy";
print(` Chatbox ${i + 1}: ${status}`);
}
return Ok.EMPTY;
},
};
const chatReceiveCommand: Command<AppContext> = {
name: "receive",
description: "Check for received chat messages",
options: new Map([
[
"count",
{
name: "count",
shortName: "c",
description: "Number of messages to retrieve",
defaultValue: 1,
},
],
]),
action: ({ options, context }): Result<void, CliError> => {
if (!context.chatManager) {
print(
"Error: ChatManager not initialized. No chatbox peripherals found.",
);
return Ok.EMPTY;
}
const count = tonumber(options.count as string) ?? 1;
let retrieved = 0;
print("=== Received Messages ===");
for (let i = 0; i < count; i++) {
const result = context.chatManager.getReceivedMessage();
if (result.isOk()) {
const event = result.value;
print(`[${event.username}]: ${event.message}`);
if (event.uuid !== undefined) {
print(` UUID: ${event.uuid}`);
}
retrieved++;
} else {
// Buffer is empty
break;
}
}
if (retrieved === 0) {
print("No messages in buffer");
} else {
print(`Retrieved ${retrieved} message(s)`);
}
return Ok.EMPTY;
},
};
const chatSendImmediateCommand: Command<AppContext> = {
name: "send-immediate",
description: "Send a chat message immediately (bypass queue)",
args: [
{ name: "message", description: "The message to send", required: true },
],
options: new Map([
[
"player",
{
name: "player",
shortName: "p",
description: "Target player for private message",
defaultValue: undefined,
},
],
[
"prefix",
{
name: "prefix",
description: "Message prefix",
defaultValue: "CC",
},
],
]),
action: ({ args, options, context }): Result<void, CliError> => {
if (!context.chatManager) {
print(
"Error: ChatManager not initialized. No chatbox peripherals found.",
);
return Ok.EMPTY;
}
const message: ChatMessage = {
message: args.message as string,
targetPlayer: options.player as string | undefined,
prefix: options.prefix as string,
};
const result = context.chatManager.sendMessageImmediate(message);
if (result.isOk()) {
print(`Message sent immediately: "${String(args.message)}"`);
} else {
print(`Failed to send message: ${result.error.reason}`);
if (result.error.kind === "NoIdleChatbox") {
print("All chatboxes are currently busy. Try queuing instead.");
}
}
return Ok.EMPTY;
},
};
const chatStopCommand: Command<AppContext> = {
name: "stop",
description: "Stop the ChatManager",
action: ({ context }): Result<void, CliError> => {
if (!context.chatManager) {
print("Error: ChatManager not initialized.");
return Ok.EMPTY;
}
const result = context.chatManager.stop();
if (result.isOk()) {
print("ChatManager stopped successfully.");
} else {
print(`Failed to stop ChatManager: ${result.error.reason}`);
}
return Ok.EMPTY;
},
};
const chatClearCommand: Command<AppContext> = {
name: "clear",
description: "Clear queues and buffer",
options: new Map([
[
"queues",
{
name: "queues",
shortName: "q",
description: "Clear message and toast queues",
defaultValue: false,
},
],
[
"buffer",
{
name: "buffer",
shortName: "b",
description: "Clear received message buffer",
defaultValue: false,
},
],
]),
action: ({ options, context }): Result<void, CliError> => {
if (!context.chatManager) {
print("Error: ChatManager not initialized.");
return Ok.EMPTY;
}
const clearQueues = options.queues as boolean;
const clearBuffer = options.buffer as boolean;
if (!clearQueues && !clearBuffer) {
print("Specify --queues or --buffer (or both) to clear.");
return Ok.EMPTY;
}
const results: string[] = [];
if (clearQueues) {
const result = context.chatManager.clearQueues();
if (result.isOk()) {
results.push("Queues cleared");
} else {
results.push(`Failed to clear queues: ${result.error.reason}`);
}
}
if (clearBuffer) {
const result = context.chatManager.clearBuffer();
if (result.isOk()) {
results.push("Buffer cleared");
} else {
results.push(`Failed to clear buffer: ${result.error.reason}`);
}
}
results.forEach((msg) => print(msg));
return Ok.EMPTY;
},
};
const chatCommand: Command<AppContext> = {
name: "chat",
description: "Chat management commands using ChatManager",
subcommands: new Map([
["send", chatSendCommand],
["send-immediate", chatSendImmediateCommand],
["toast", chatToastCommand],
["status", chatStatusCommand],
["receive", chatReceiveCommand],
["stop", chatStopCommand],
["clear", chatClearCommand],
]),
};
// 3. Define root command
const rootCommand: Command<AppContext> = {
name: "calculator",
description: "A feature-rich calculator program",
description: "A feature-rich calculator and chat management program",
options: new Map([
[
"debug",
@@ -172,6 +511,7 @@ const rootCommand: Command<AppContext> = {
["math", mathCommand],
["greet", greetCommand],
["config", configCommand],
["chat", chatCommand],
]),
action: ({ options, context }): Result<void, CliError> => {
// Update debug mode from command line option
@@ -183,20 +523,58 @@ const rootCommand: Command<AppContext> = {
print(`Welcome to ${context.appName}!`);
print("Use --help to see available commands");
if (context.chatManager) {
print("ChatManager initialized and ready!");
} else {
print("Note: No chatbox peripherals found - chat commands unavailable");
}
return Ok.EMPTY;
},
};
// 4. Create global context instance
// 4. Initialize ChatManager if chatbox peripherals are available
function initializeChatManager(): ChatManager | undefined {
// Find all available chatbox peripherals
const peripheralNames = peripheral.getNames();
const chatboxPeripherals: ChatBoxPeripheral[] = [];
for (const name of peripheralNames) {
const peripheralType = peripheral.getType(name);
if (peripheralType[0] === "chatBox") {
const chatbox = peripheral.wrap(name) as ChatBoxPeripheral;
chatboxPeripherals.push(chatbox);
}
}
if (chatboxPeripherals.length === 0) {
return undefined;
}
const chatManager = new ChatManager(chatboxPeripherals);
// Start ChatManager in async mode so it doesn't block the CLI
const runResult = chatManager.runAsync();
if (runResult.isErr()) {
print(`Warning: Failed to start ChatManager: ${runResult.error.reason}`);
return undefined;
}
return chatManager;
}
// 5. Create global context instance
const appContext: AppContext = {
appName: "MyAwesomeCalculator",
appName: "MyAwesome Calculator & Chat Manager",
debugMode: false,
log: (message) => {
print(`[LOG] ${message}`);
},
chatManager: initializeChatManager(),
};
// 5. Create and export CLI handler
// 6. Create and export CLI handler
const cli = createCli(rootCommand, { globalContext: appContext });
const args = [...$vararg];
cli(args);
@@ -215,11 +593,24 @@ cli(['greet', '-n', 'World', '-t', '3']); // Output: Hello, World! (3 times)
cli(['config', 'show']); // Shows current config
cli(['config', 'set', 'theme', 'dark']); // Sets config
// Chat management (requires chatbox peripherals)
cli(['chat', 'status']); // Shows ChatManager status
cli(['chat', 'send', 'Hello World!']); // Sends global message (queued)
cli(['chat', 'send', 'Hi there!', '--player', 'Steve']); // Private message (queued)
cli(['chat', 'send-immediate', 'Urgent!', '--player', 'Admin']); // Immediate send
cli(['chat', 'toast', 'Steve', 'Alert', 'Server restart in 5 minutes']); // Toast notification
cli(['chat', 'receive', '--count', '5']); // Check for received messages
cli(['chat', 'clear', '--queues']); // Clear pending queues
cli(['chat', 'clear', '--buffer']); // Clear received buffer
cli(['chat', 'stop']); // Stop ChatManager
// Help examples
cli(['--help']); // Shows root help
cli(['math', '--help']); // Shows math command help
cli(['config', 'set', '--help']); // Shows config set help
cli(['chat', '--help']); // Shows chat command help
cli(['chat', 'send', '--help']); // Shows chat send help
// Debug mode
cli(['--debug', 'math', 'add', '1', '2']); // Enables debug logging
cli(['--debug', 'chat', 'status']); // Debug mode with chat status
*/

637
src/lib/ChatManager.ts Normal file
View File

@@ -0,0 +1,637 @@
import { Queue } from "./datatype/Queue";
import { ChatBoxEvent, pullEventAs } from "./event";
import { Result, Ok, Err } from "./thirdparty/ts-result-es";
/**
* Chat manager error types
*/
export interface ChatManagerError {
kind: "ChatManager";
reason: string;
chatboxIndex?: number;
}
export interface NoIdleChatboxError {
kind: "NoIdleChatbox";
reason: "All chatboxes are busy";
}
export interface SendFailureError {
kind: "SendFailure";
reason: string;
chatboxIndex: number;
}
export interface EmptyBufferError {
kind: "EmptyBuffer";
reason: "No messages in buffer";
}
export type ChatError =
| ChatManagerError
| NoIdleChatboxError
| SendFailureError
| EmptyBufferError;
/**
* Base interface for chat messages and toasts
*/
interface ChatBasicMessage {
message: string | MinecraftTextComponent;
prefix?: string;
brackets?: string;
bracketColor?: string;
range?: number;
utf8Support?: boolean;
}
/**
* Interface for chat toast notifications
*/
export interface ChatToast extends ChatBasicMessage {
/** Target player username to send the toast to */
username: string;
/** Title of the toast notification */
title: string;
}
/**
* Interface for regular chat messages
*/
export interface ChatMessage extends ChatBasicMessage {
/** Optional target player username for private messages */
targetPlayer?: string;
}
/**
* ChatManager class for managing multiple ChatBox peripherals
* Handles message queuing, sending with cooldown management, and event receiving
* Uses Result types for robust error handling
*/
export class ChatManager {
/** Array of all available ChatBox peripherals */
private chatboxes: ChatBoxPeripheral[];
/** Queue for pending chat messages */
private messageQueue = new Queue<ChatMessage>();
/** Queue for pending toast notifications */
private toastQueue = new Queue<ChatToast>();
/** Buffer for received chat events */
private chatBuffer = new Queue<ChatBoxEvent>();
/** Array tracking which chatboxes are currently idle (not in cooldown) */
private idleChatboxes: boolean[];
/** Flag
to control the running state of loops */
private isRunning = false;
/**
* Constructor - initializes the ChatManager with available ChatBox peripherals
* @param peripherals Array of ChatBox peripherals to manage
*/
constructor(peripherals: ChatBoxPeripheral[]) {
if (peripherals.length === 0) {
throw new Error("ChatManager requires at least one ChatBox peripheral");
}
this.chatboxes = peripherals;
// Initially all chatboxes are idle
this.idleChatboxes = peripherals.map(() => true);
}
/**
* Adds a chat message to the sending queue
* @param message The chat message to send
* @returns Result indicating success or failure
*/
public sendMessage(message: ChatMessage): Result<void, ChatManagerError> {
try {
this.messageQueue.enqueue(message);
return new Ok(undefined);
} catch (error) {
return new Err({
kind: "ChatManager",
reason: `Failed to enqueue message: ${String(error)}`,
});
}
}
/**
* Adds a toast notification to the sending queue
* @param toast The toast notification to send
* @returns Result indicating success or failure
*/
public sendToast(toast: ChatToast): Result<void, ChatManagerError> {
try {
this.toastQueue.enqueue(toast);
return new Ok(undefined);
} catch (error) {
return new Err({
kind: "ChatManager",
reason: `Failed to enqueue toast: ${String(error)}`,
});
}
}
/**
* Retrieves and removes the next received chat event from the buffer
* @returns Result containing the chat event or an error if buffer is empty
*/
public getReceivedMessage(): Result<ChatBoxEvent, EmptyBufferError> {
const event = this.chatBuffer.dequeue();
if (event === undefined) {
return new Err({
kind: "EmptyBuffer",
reason: "No messages in buffer",
});
}
return new Ok(event);
}
/**
* Finds the first available (idle) chatbox
* @returns Result containing chatbox index or error if none available
*/
private findIdleChatbox(): Result<number, NoIdleChatboxError> {
for (let i = 0; i < this.idleChatboxes.length; i++) {
if (this.idleChatboxes[i]) {
return new Ok(i);
}
}
return new Err({
kind: "NoIdleChatbox",
reason: "All chatboxes are busy",
});
}
/**
* Marks a chatbox as busy and sets up a timer to mark it as idle after cooldown
* @param chatboxIndex Index of the chatbox to mark as busy
* @returns Result indicating success or failure
*/
private setChatboxBusy(chatboxIndex: number): Result<void, ChatManagerError> {
if (chatboxIndex < 0 || chatboxIndex >= this.idleChatboxes.length) {
return new Err({
kind: "ChatManager",
reason: "Invalid chatbox index",
chatboxIndex,
});
}
this.idleChatboxes[chatboxIndex] = false;
try {
// Set timer to mark chatbox as idle after 1 second cooldown
const timerId = os.startTimer(1);
// Start a coroutine to wait for the timer and mark chatbox as idle
coroutine.resume(
coroutine.create(() => {
while (true) {
const [_eventName, id] = os.pullEvent("timer");
if (id === timerId) {
this.idleChatboxes[chatboxIndex] = true;
break;
}
}
}),
);
return new Ok(undefined);
} catch (error) {
// Revert chatbox state if timer setup fails
this.idleChatboxes[chatboxIndex] = true;
return new Err({
kind: "ChatManager",
reason: `Failed to set chatbox timer: ${String(error)}`,
chatboxIndex,
});
}
}
/**
* Attempts to send a chat message using an available chatbox
* @param message The message to send
* @returns Result indicating success or failure with error details
*/
private trySendMessage(message: ChatMessage): Result<void, ChatError> {
const chatboxResult = this.findIdleChatbox();
if (chatboxResult.isErr()) {
return chatboxResult;
}
const chatboxIndex = chatboxResult.value;
const chatbox = this.chatboxes[chatboxIndex];
try {
let success: boolean;
let errorMsg: string | undefined;
// Determine the appropriate sending method based on message properties
if (message.targetPlayer !== undefined) {
// Send private message to specific player
if (typeof message.message === "string") {
[success, errorMsg] = chatbox.sendMessageToPlayer(
message.message,
message.targetPlayer,
message.prefix,
message.brackets,
message.bracketColor,
message.range,
message.utf8Support,
);
} else {
// Handle MinecraftTextComponent for private message
[success, errorMsg] = chatbox.sendFormattedMessageToPlayer(
JSON.stringify(message.message),
message.targetPlayer,
message.prefix,
message.brackets,
message.bracketColor,
message.range,
message.utf8Support,
);
}
} else {
// Send global message
if (typeof message.message === "string") {
[success, errorMsg] = chatbox.sendMessage(
message.message,
message.prefix,
message.brackets,
message.bracketColor,
message.range,
message.utf8Support,
);
} else {
// Handle MinecraftTextComponent for global message
[success, errorMsg] = chatbox.sendFormattedMessage(
JSON.stringify(message.message),
message.prefix,
message.brackets,
message.bracketColor,
message.range,
message.utf8Support,
);
}
}
if (success) {
// Mark chatbox as busy for cooldown period
const busyResult = this.setChatboxBusy(chatboxIndex);
if (busyResult.isErr()) {
return busyResult;
}
return new Ok(undefined);
} else {
return new Err({
kind: "SendFailure",
reason: errorMsg ?? "Unknown send failure",
chatboxIndex,
});
}
} catch (error) {
return new Err({
kind: "SendFailure",
reason: `Exception during send: ${String(error)}`,
chatboxIndex,
});
}
}
/**
* Attempts to send a toast notification using an available chatbox
* @param toast The toast to send
* @returns Result indicating success or failure with error details
*/
private trySendToast(toast: ChatToast): Result<void, ChatError> {
const chatboxResult = this.findIdleChatbox();
if (chatboxResult.isErr()) {
return chatboxResult;
}
const chatboxIndex = chatboxResult.value;
const chatbox = this.chatboxes[chatboxIndex];
try {
let success: boolean;
let errorMsg: string | undefined;
// Send toast notification
if (
typeof toast.message === "string" &&
typeof toast.title === "string"
) {
[success, errorMsg] = chatbox.sendToastToPlayer(
toast.message,
toast.title,
toast.username,
toast.prefix,
toast.brackets,
toast.bracketColor,
toast.range,
toast.utf8Support,
);
} else {
// Handle MinecraftTextComponent for toast
const messageJson =
typeof toast.message === "string"
? toast.message
: JSON.stringify(toast.message);
const titleJson =
typeof toast.title === "string"
? toast.title
: JSON.stringify(toast.title);
[success, errorMsg] = chatbox.sendFormattedToastToPlayer(
messageJson,
titleJson,
toast.username,
toast.prefix,
toast.brackets,
toast.bracketColor,
toast.range,
toast.utf8Support,
);
}
if (success) {
// Mark chatbox as busy for cooldown period
const busyResult = this.setChatboxBusy(chatboxIndex);
if (busyResult.isErr()) {
return busyResult;
}
return new Ok(undefined);
} else {
return new Err({
kind: "SendFailure",
reason: errorMsg ?? "Unknown toast send failure",
chatboxIndex,
});
}
} catch (error) {
return new Err({
kind: "SendFailure",
reason: `Exception during toast send: ${String(error)}`,
chatboxIndex,
});
}
}
/**
* Main sending loop - continuously processes message and toast queues
* Runs in a separate coroutine to handle sending with proper timing
*/
private sendLoop(): void {
while (this.isRunning) {
let sentSomething = false;
// Try to send a message if queue is not empty
if (this.messageQueue.size() > 0) {
const message = this.messageQueue.peek();
if (message) {
const result = this.trySendMessage(message);
if (result.isOk()) {
this.messageQueue.dequeue(); // Remove from queue only if successfully sent
sentSomething = true;
} else if (result.error.kind === "SendFailure") {
// Log send failures but keep trying
print(`Failed to send message: ${result.error.reason}`);
this.messageQueue.dequeue(); // Remove failed message to prevent infinite retry
}
// For NoIdleChatbox errors, we keep the message in queue and try again later
}
}
// Try to send a toast if queue is not empty
if (this.toastQueue.size() > 0) {
const toast = this.toastQueue.peek();
if (toast) {
const result = this.trySendToast(toast);
if (result.isOk()) {
this.toastQueue.dequeue(); // Remove from queue only if successfully sent
sentSomething = true;
} else if (result.error.kind === "SendFailure") {
// Log send failures but keep trying
print(`Failed to send toast: ${result.error.reason}`);
this.toastQueue.dequeue(); // Remove failed toast to prevent infinite retry
}
// For NoIdleChatbox errors, we keep the toast in queue and try again later
}
}
// Small sleep to prevent busy waiting and allow other coroutines to run
if (!sentSomething) {
sleep(0.1);
}
}
}
/**
* Main receiving loop - continuously listens for chat events
* Runs in a separate coroutine to handle incoming messages
*/
private receiveLoop(): void {
while (this.isRunning) {
try {
// Listen for chatbox_message events (note: event name might be "chat" based on event.ts)
const event = pullEventAs(ChatBoxEvent, "chat");
if (event) {
// Store received event in buffer for user processing
this.chatBuffer.enqueue(event);
}
} catch (error) {
// Log receive errors but continue running
print(`Error in receive loop: ${String(error)}`);
sleep(0.1); // Brief pause before retrying
}
}
}
/**
* Starts the ChatManager's main operation loops
* Launches both sending and receiving coroutines in parallel
* @returns Result indicating success or failure of startup
*/
public run(): Result<void, ChatManagerError> {
if (this.isRunning) {
return new Err({
kind: "ChatManager",
reason: "ChatManager is already running",
});
}
try {
this.isRunning = true;
// Start both send and receive loops in parallel
parallel.waitForAll(
() => this.sendLoop(),
() => this.receiveLoop(),
);
return new Ok(undefined);
} catch (error) {
this.isRunning = false;
return new Err({
kind: "ChatManager",
reason: `Failed to start ChatManager: ${String(error)}`,
});
}
}
/**
* Starts the ChatManager asynchronously without blocking
* Useful when you need to run other code alongside the ChatManager
* @returns Result indicating success or failure of async startup
*/
public runAsync(): Result<void, ChatManagerError> {
if (this.isRunning) {
return new Err({
kind: "ChatManager",
reason: "ChatManager is already running",
});
}
try {
this.isRunning = true;
// Start the run method in a separate coroutine
coroutine.resume(
coroutine.create(() => {
const result = this.run();
if (result.isErr()) {
print(`ChatManager async error: ${result.error.reason}`);
}
}),
);
return new Ok(undefined);
} catch (error) {
this.isRunning = false;
return new Err({
kind: "ChatManager",
reason: `Failed to start ChatManager async: ${String(error)}`,
});
}
}
/**
* Stops the ChatManager loops gracefully
* @returns Result indicating success or failure of shutdown
*/
public stop(): Result<void, ChatManagerError> {
if (!this.isRunning) {
return new Err({
kind: "ChatManager",
reason: "ChatManager is not running",
});
}
try {
this.isRunning = false;
return new Ok(undefined);
} catch (error) {
return new Err({
kind: "ChatManager",
reason: `Failed to stop ChatManager: ${String(error)}`,
});
}
}
/**
* Gets the number of pending messages in the queue
* @returns Number of pending messages
*/
public getPendingMessageCount(): number {
return this.messageQueue.size();
}
/**
* Gets the number of pending toasts in the queue
* @returns Number of pending toasts
*/
public getPendingToastCount(): number {
return this.toastQueue.size();
}
/**
* Gets the number of received messages in the buffer
* @returns Number of buffered received messages
*/
public getBufferedMessageCount(): number {
return this.chatBuffer.size();
}
/**
* Gets the current status of all chatboxes
* @returns Array of boolean values indicating which chatboxes are idle
*/
public getChatboxStatus(): boolean[] {
return [...this.idleChatboxes]; // Return a copy to prevent external modification
}
/**
* Gets the running state of the ChatManager
* @returns true if ChatManager is currently running
*/
public isManagerRunning(): boolean {
return this.isRunning;
}
/**
* Clears all pending messages and toasts from queues
* Does not affect the received message buffer
* @returns Result indicating success or failure
*/
public clearQueues(): Result<void, ChatManagerError> {
try {
this.messageQueue.clear();
this.toastQueue.clear();
return new Ok(undefined);
} catch (error) {
return new Err({
kind: "ChatManager",
reason: `Failed to clear queues: ${String(error)}`,
});
}
}
/**
* Clears the received message buffer
* @returns Result indicating success or failure
*/
public clearBuffer(): Result<void, ChatManagerError> {
try {
this.chatBuffer.clear();
return new Ok(undefined);
} catch (error) {
return new Err({
kind: "ChatManager",
reason: `Failed to clear buffer: ${String(error)}`,
});
}
}
/**
* Tries to send a message immediately, bypassing the queue
* @param message The message to send immediately
* @returns Result indicating success or failure with error details
*/
public sendMessageImmediate(message: ChatMessage): Result<void, ChatError> {
return this.trySendMessage(message);
}
/**
* Tries to send a toast immediately, bypassing the queue
* @param toast The toast to send immediately
* @returns Result indicating success or failure with error details
*/
public sendToastImmediate(toast: ChatToast): Result<void, ChatError> {
return this.trySendToast(toast);
}
}