mirror of
				https://github.com/SikongJueluo/cc-utils.git
				synced 2025-11-04 19:27:50 +08:00 
			
		
		
		
	feature: ChatManager
feature: - multi chatboxes manage - chatbox message queue
This commit is contained in:
		@@ -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
									
								
							
							
						
						
									
										637
									
								
								src/lib/ChatManager.ts
									
									
									
									
									
										Normal 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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user