Skip to main content

Scripts

Automate your client with JavaScript — custom /commands, message filters, auto-responders and web API integrations, in the spirit of mIRC and irssi scripting

How Scripts Work

A script is a small piece of JavaScript that reacts to what happens in the client: it can subscribe to events (messages, joins, parts, nick changes…), register its own slash commands, send messages, print local notes and fetch data from the web. Scripts run inside the client — there is nothing to install and no external process.

To create one:

  1. Click your avatar in the toolbar and choose Profile Settings
  2. Scroll to the Scripts section and click New script
  3. Write your code in the editor (it opens pre-filled with a commented example) and Save
  4. Flip the script's toggle to enable it — it starts immediately, no reconnect needed

Scripts are stored locally in your browser or app — nothing is uploaded anywhere. The same scripts work in the web app and the desktop builds.

Sandboxed by Design

Every script runs in its own isolated JavaScript sandbox (a QuickJS engine compiled to WebAssembly). A script only sees the sic API described below — it has no access to the page, your settings, your stored passwords or other scripts. On top of that:

The sic API

Scripts talk to the client through a single global object:

sic.on(event, handler)

Subscribes to an event (see the table below). Returns a function that unsubscribes the handler. Handlers for message and notice can call e.block() to hide the message before it renders, or assign to e.text to rewrite it.

sic.command(name, handler, options?)

Registers a slash command; the handler receives (args, channel) — the rest of the typed line and the window it was typed in. Optional { aliases: […] }. Script commands show up in Tab completion; names that collide with built-in commands are rejected.

sic.say(target, text) / sic.sendRaw(line)

say sends a normal message to a channel or nick. sendRaw sends a raw IRC line for anything the API does not cover (MODE, TOPIC, CTCP…).

sic.print(text, target?)

Prints a local informational line that is not sent to the server — to the current window, or to a named one (for example 'Status').

sic.fetch(url, options?)

HTTP request returning a Promise of { ok, status, headers, body, json() }. Options: method, headers, body. Limits: http(s) only, 10 second timeout, 1 MB response, 4 concurrent requests — and the target API must allow cross-origin requests (CORS), like any browser fetch.

sic.nick() / sic.currentChannel()

Your current nickname and the name of the active window.

Events

Event Handler receives Can block / rewrite
message nick, target, text, self, tags Yes — e.block() and e.text
notice nick, target, text, tags Yes — e.block() and e.text
join nick, channel Observe only
part nick, channel, reason Observe only
quit nick, reason Observe only
nick oldNick, newNick Observe only
connect / disconnect Observe only
raw line (the unparsed IRC line) e.block() — advanced, use with care

A blocked message is gone for good: it does not render, does not bump unread counters and does not trigger mention notifications. When several scripts are enabled they run in the order they were created — each sees the text as rewritten by the previous one, and the first block() wins.

Example Scripts

Copy any example, open Profile Settings → Scripts → New script, replace the pre-filled code, save and enable. Each one is self-contained — use them as they are or as starting points.

The Classic /slap

No IRC client is complete without it. Registers /slap (alias /trout) and sends the traditional action message.

// /slap <nick> - the IRC classic
sic.command('slap', (args, channel) => {
  const victim = args.trim() || 'someone';
  sic.sendRaw('PRIVMSG ' + channel +
    ' :\u0001ACTION slaps ' + victim +
    ' around a bit with a large trout\u0001');
}, { aliases: ['trout'] });

Ignore List and Word Filter

Hides messages from nicks you never want to hear from and anything containing blocked phrases — filtered messages never render and never trigger notifications.

// Personal ignore list + spam filter
const ignoredNicks = ['spammer42', 'flooder'];
const blockedPhrases = ['free crypto', 'casino bonus'];

sic.on('message', (e) => {
  if (ignoredNicks.includes(e.nick.toLowerCase())) {
    e.block();
    return;
  }
  const text = e.text.toLowerCase();
  if (blockedPhrases.some((phrase) => text.includes(phrase))) {
    e.block();
  }
});

Tame the Shouting

Rewrites incoming messages instead of hiding them: anything written in ALL CAPS arrives in calm lowercase.

// Lowercase ALL-CAPS messages
sic.on('message', (e) => {
  const letters = e.text.replace(/[^a-z]/gi, '');
  if (letters.length >= 8 && letters === letters.toUpperCase()) {
    e.text = e.text.toLowerCase();
  }
});

Mention Log

Collects every message that mentions your nick into the Status window — one place to catch up after being away, no matter how many channels you are in.

// Copy mentions to the Status window
sic.on('message', (e) => {
  if (e.self) { return; }
  if (e.text.toLowerCase().includes(sic.nick().toLowerCase())) {
    sic.print('[mention] ' + e.nick + ' in ' + e.target + ': ' + e.text, 'Status');
  }
});

Channel Greeter

Welcomes people joining your channel. The nick check keeps it from greeting you on reconnect.

// Greet joins in one channel
const myChannel = '#mychannel';

sic.on('join', (e) => {
  if (e.channel === myChannel && e.nick !== sic.nick()) {
    sic.say(myChannel, 'Welcome to ' + myChannel + ', ' + e.nick + '!');
  }
});

/weather — Web APIs from a Script

Fetches a one-line forecast from wttr.in and shares it with the channel. The same pattern works with any CORS-enabled JSON or text API.

// /weather <city> - share the forecast
sic.command('weather', (args, channel) => {
  const city = args.trim();
  if (!city) {
    sic.print('Usage: /weather <city>');
    return;
  }
  sic.fetch('https://wttr.in/' + encodeURIComponent(city) + '?format=3')
    .then((res) => sic.say(channel, res.body.trim()))
    .catch((err) => sic.print('weather: ' + err.message));
});

/github — JSON APIs

Looks up a repository on the GitHub API and posts a summary — a compact example of json(), error handling and status checks.

// /github owner/repo - repository summary
sic.command('github', (args, channel) => {
  const repo = args.trim();
  if (!repo.includes('/')) {
    sic.print('Usage: /github owner/repo');
    return;
  }
  sic.fetch('https://api.github.com/repos/' + repo)
    .then((res) => {
      if (!res.ok) {
        sic.print('github: HTTP ' + res.status);
        return;
      }
      const data = res.json();
      sic.say(channel, data.full_name + ' — ' +
        (data.description || 'no description') +
        ' | ⭐ ' + data.stargazers_count);
    })
    .catch((err) => sic.print('github: ' + err.message));
});

Auto-Join on Connect

Runs whenever registration with the server completes — handy for anything you would otherwise type by hand after connecting.

// Extra channels on every (re)connect
sic.on('connect', () => {
  sic.sendRaw('JOIN #linux,#security');
});

Try It Yourself

Open Simple IRC Client, head to Profile Settings and write your first script — it is just JavaScript.