Scripting - edgeof8/tIRC GitHub Wiki

tIRC features a powerful and extensible Python scripting system, allowing users to customize and automate virtually every aspect of the client. Scripts can add new commands, respond to IRC events, interact with the UI, and manage client state.

For a detailed list of API methods and events, refer to the Script API Reference and the API Documentation.

Overview

  • Language: Scripts are written in Python.
  • Location: Scripts are typically placed in the scripts/ directory in tIRC's root or user configuration directory.
  • Core Class: Scripts usually inherit from ScriptBase (found in tirc_core/scripting/script_base.py).
  • API Access: Scripts interact with tIRC through an instance of ScriptAPIHandler, commonly provided as self.api.
  • Discovery: The ScriptManager discovers and loads scripts at startup.

Script Structure

A typical tIRC script has the following structure:

from tirc_core.scripting.script_base import ScriptBase
from typing import Dict, Any # For type hinting event_data
import os
import json # Example for data persistence

class MyCustomScript(ScriptBase):
    def __init__(self, api_handler):
        super().__init__(api_handler)
        # Initialize script-specific attributes here
        self.my_data = {}
        self.script_metadata = self.load_metadata() # Optional: Load metadata

    def load_metadata(self):
        # Example: Load metadata from a JSON file in the script's directory
        metadata_path = os.path.join(self.get_script_dir(), "script_metadata.json")
        if os.path.exists(metadata_path):
            with open(metadata_path, 'r') as f:
                return json.load(f)
        return {}

    def load(self):
        """
        Called when the script is loaded by ScriptManager.
        Register commands, event handlers, and perform initial setup.
        """
        self.api.log_info(f"Loading script: {self.script_metadata.get('name', 'MyCustomScript')}")

        # Example: Register a command
        self.api.register_command(
            name="mycommand",
            handler=self.handle_my_command,
            help_text={ # Using the dictionary format for richer help
                "usage": "/mycommand <argument>",
                "description": "This is a custom command from MyCustomScript.",
                "aliases": ["mycmd"]
            }
        )

        # Example: Subscribe to an event
        self.api.subscribe_to_event("PRIVMSG", self.handle_privmsg) # Using new event name from README

        # Example: Load persistent data
        data_file = os.path.join(self.get_script_data_dir(), "my_script_data.json")
        if os.path.exists(data_file):
            with open(data_file, 'r') as f:
                self.my_data = json.load(f)

    def unload(self):
        """
        Called when the script is unloaded (e.g., on /script unload or client shutdown).
        Clean up resources, save persistent data.
        """
        self.api.log_info(f"Unloading script: {self.script_metadata.get('name', 'MyCustomScript')}")

        # Example: Save persistent data
        data_file = os.path.join(self.get_script_data_dir(), "my_script_data.json")
        os.makedirs(os.path.dirname(data_file), exist_ok=True) # Ensure directory exists
        with open(data_file, 'w') as f:
            json.dump(self.my_data, f, indent=4)

        # Unregister commands or event handlers if necessary, though ScriptManager might handle this
        # self.api.unregister_command("mycommand")
        # self.api.unsubscribe_from_event("PRIVMSG", self.handle_privmsg)


    async def handle_my_command(self, args_str: str):
        """
        Handler for the /mycommand. Must be async if it calls async API methods.
        """
        target_context = self.api.get_current_context_name() or "Status"
        await self.api.send_message(
            target_context,
            f"MyCommand executed with args: {args_str}"
        )
        self.my_data['last_command_args'] = args_str

    async def handle_privmsg(self, event_data: Dict[str, Any]):
        """
        Handler for PRIVMSG events. Must be async.
        """
        nick = event_data.get("nick")
        message = event_data.get("message")
        target = event_data.get("target")

        if message and "hello tirc" in message.lower():
            response_target = nick if not event_data.get("is_channel_msg") else target
            if response_target:
                await self.api.send_message(
                    response_target,
                    f"Hello to you too, {nick}!"
                )

# Required factory function for ScriptManager
def create_script(api_handler):
    return MyCustomScript(api_handler)

Script Metadata (script_metadata.json)

It's good practice to include a script_metadata.json file in your script's directory (e.g., scripts/my_custom_script/script_metadata.json). This file provides information about your script to the ScriptManager and users.

Example script_metadata.json:

{
  "name": "My Custom Script",
  "version": "1.0.1",
  "description": "An example script demonstrating tIRC scripting features.",
  "author": "Your Name",
  "dependencies": [], // List of other script names this script depends on
  "min_tirc_version": "1.0.0", // Optional: Minimum tIRC version required
  "is_enabled_by_default": true // Optional: Whether the script is enabled by default
}

The ScriptManager may use this metadata for dependency resolution, version checking, and display in UI elements (e.g., /script list).

Key Scripting Capabilities

Via the ScriptAPIHandler (self.api), scripts can:

  • Register Commands: Add new slash commands (e.g., /mycommand).
    self.api.register_command(name="greet", handler=self.greet_handler, help_text="Usage: /greet <name>")
  • Handle Events: Subscribe to a wide range of IRC and client events (e.g., PRIVMSG, JOIN, CLIENT_CONNECTED). Event handlers can be synchronous or async.
    self.api.subscribe_to_event("PRIVMSG", self.on_message_received)
  • Send Messages and Commands: Interact with the IRC server.
    await self.api.send_message("#channel", "Hello from my script!")
    await self.api.send_raw("NICK NewNick")
  • Access Client State: Read information about the current connection, channels, user settings, etc., via StateManager accessors if provided by the API.
    current_nick = self.api.get_client_nick()
  • Interact with the UI: Add messages to specific contexts (windows).
    await self.api.add_message_to_context("Status", "My script is now active!", "info")
  • Manage Persistent Data: Scripts can save and load their own data using self.get_script_data_dir() to get a dedicated directory.
  • Logging: Use self.api.log_info(), self.api.log_warning(), self.api.log_error() for script-specific logging, which will be integrated into tIRC's main logging system.

Event Handling

  • Events are dispatched by the EventManager. Refer to the README.md section "Key Script Events" or the Script API Reference for a list of available events and their event_data structures.
  • Event handlers can be regular functions or async coroutines. The EventManager will await async handlers.

Best Practices

  • Asynchronous Operations: If your script performs I/O (network requests, file operations), use async def for your handlers and await the I/O calls to avoid blocking tIRC's main event loop.
  • Error Handling: Wrap potentially failing operations in try...except blocks and use self.api.log_error() for reporting issues.
  • Resource Management: In the unload method, clean up any resources your script might be holding (e.g., open files, network connections, background tasks).
  • Modularity: Keep scripts focused on specific tasks. For complex functionality, consider breaking it into multiple smaller scripts or modules.
  • Dependency Management: If your script depends on others, declare them in script_metadata.json.
  • Configuration: For script-specific settings, consider allowing users to configure them via tIRC's /set command (if the API supports registering settings) or by reading from a dedicated section in tirc_config.ini.

For detailed API calls, event names, and data structures, always refer to the latest Script API Reference.

⚠️ **GitHub.com Fallback** ⚠️