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.
- 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 intirc_core/scripting/script_base.py
). -
API Access: Scripts interact with tIRC through an instance of
ScriptAPIHandler
, commonly provided asself.api
. -
Discovery: The
ScriptManager
discovers and loads scripts at startup.
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)
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
).
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 orasync
.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.
- Events are dispatched by the
EventManager
. Refer to theREADME.md
section "Key Script Events" or the Script API Reference for a list of available events and theirevent_data
structures. - Event handlers can be regular functions or
async
coroutines. TheEventManager
willawait
async handlers.
-
Asynchronous Operations: If your script performs I/O (network requests, file operations), use
async def
for your handlers andawait
the I/O calls to avoid blocking tIRC's main event loop. -
Error Handling: Wrap potentially failing operations in
try...except
blocks and useself.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 intirc_config.ini
.
For detailed API calls, event names, and data structures, always refer to the latest Script API Reference.