Scripting

Yoake has a scripting engine that closely follows real mIRC's syntax, applied to Telegram and WhatsApp chats. Opens from Tools > Aliases.../Popups.../Remote... (Script Editor), with four tabs: Aliases, Remote (events), Popups, Variables.

Everything is saved as real .ini files inside userData/scripts/ (aliases.ini, remote.ini, popups.ini, variables.ini) - also editable with an external text editor; the "Load..." button in the editor imports an external .ini file into the open tab.

Aliases

Custom commands, callable as /name:

hello {
  say Hi $1
}
As in real mIRC: inside aliases.ini you only write name { ... }, NEVER alias name { ... } - that prefix is required only inside Remote below, where it distinguishes an alias from an on event.

Remote (events)

Every event runs BEFORE the corresponding default action (as in real mIRC): it can observe it and, where indicated, suppress it with haltdef (or stop it entirely with halt, only inside "on message").

Line visible in chat: as in real mIRC, join/part/kick/ op/deop/topic/nick always show a system line in chat (e.g. --> Mario has joined, * Mario is now an admin) - regardless of any script you've written, exactly as mIRC does by default. The script still runs, in addition, not instead of that line.

Besides the simplified form on start { ... } (no level, no condition: Yoake has no access levels and its events don't filter by nick/channel, they always fire), the real mIRC syntax on *:START:{ ... } is also accepted (with a level and any extra conditions, e.g. on *:TEXT:*:#channel:{ ... }) - handy for pasting a ready-made mIRC script without translating it by hand. The level and condition are read and discarded (no real matching against them), only the event name matters.
EventWhen it fires$nick$chan$1-$9 / argshaltdef
on startOnce, when Yoake starts.You--no
on loadWhen you save/close the Script Editor (scripts get recompiled) - not at startup, unlike real mIRC where "on load" fires when an .mrc file is loaded.You--no
on connectSuccessful connection to a network (normal login or QR).your name--no
on connectfailFailed connection attempt.You-the error message, as separate wordsno
on disconnectDisconnected from a network (manual with /disconnect, or forced e.g. device unlinked from the phone).You--no
on messageAn incoming message (never for your own sent messages).who wrote itthe channel/group, empty in privatemessage textyes (doesn't show the message) - halt does the same and also stops the script
on noticeSynthetic notification (screenshot, pin, missed call...) - see Notifications and system events for the full list of $noticetype values.who caused itwhere (if known)the notice textyes
on whoisRight before printing the result of /whois - our own addition: real mIRC has no dedicated event, it uses the protocol's numeric RAW events instead (301/311/312/317/318/319...), one per piece of data. Our /whois already returns everything together, so a single event with haltdef makes more sense than faking 5-6 separate raw events.the person looked up-$1 = name, $2 = username, $3 = phone, $4 = on/off/empty (online status, empty if unknown), $5 = bio, $6 = profile photo URL, $7 = read-receipts-seen date (both networks, empty if unknown), $8 = last seen (an exact date, or on Telegram sometimes a vague range like "recently"/"last week"/"last month" when Telegram doesn't give an exact time - empty if unknown or if online right now)yes - suppresses the default lines, the script redraws them however it likes
on callA call (audio or video) is coming in - our own addition: notification only, GramJS/Baileys don't implement the actual audio/video at all, you can't really "answer" from here. See /rejectcall in Commands to decline it (optionally with a message to whoever called).who's calling-$1 = video or audioyes - suppresses the default "Incoming call" line
on joinSomeone joins a group.who joinedthe group-no
on partSomeone leaves a group.who leftthe group-no
on kickSomeone gets removed from a group.who removed themthe group$1 = who was removedno
on opSomeone becomes admin of a group (with /op, or because another admin did it).who became adminthe group-no
on deopSomeone loses admin status (with /deop, or because another admin did it).who lost adminthe group-no
on nickA contact/user changes their name - fires in Status and in every open window where it's relevant (a private chat with them, or a group they're part of).the name BEFORE the change-$1 = the new nameno
on notifyA contact on your Notify list (see Address Book) connects. Fires in that network's Status.the contact's name--no
on unotifyA contact on your Notify list disconnects.the contact's name--no
on inviteTwo distinct cases, both covered: (1) you receive a message with a real invite link (t.me/joinchat, t.me/+..., chat.whatsapp.com/...) - the case closest to IRC, the notification arrives BEFORE you join; (2) you were added directly to a new group with no link at all (an admin who has your contact can do this on both networks) - here you find out "after the fact", there's no way to know beforehand.who sent the link (case 1) or empty (case 2)who sent the message (case 1) or the group (case 2)$1 = the link (case 1 only)no
on topicA group's topic/name changes.-the group$1 = the old titleno
on openAny window opens (channel, group, query, status, or a @window created with /window).Youthe window's title (for a @window: its name, e.g. @log)-no
on closeAny window closes (same, including @windows).Youthe window's title-no
on queryopenA private chat (query) opens specifically.You--no
on querycloseA private chat (query) closes.You--no
on inputEvery line sent in any window (command or message), before it's even processed.You-the typed text, as separate wordsyes - /halt//haltdef here really do cancel the default send (command or message), as in real mIRC: the script has to send something itself with say/msg if it still wants to send modified text
on filesentA /dcc send transfer has finished.the recipient-$1 = file nameno
on filercvdAn incoming media/document has finished downloading (photo/video/audio/document clicked, or automatically loaded in HTML mode).--$1 = file name (empty for photo/video/audio)no
on videoendOur own addition (real mIRC never had video) - a video open in the viewer (double click, or /playmedia) finishes playing on its own. The window doesn't close on its own: if the script wants that, it decides with /close $wid.You--no
on sockopenA sockopen connected successfully - see Commands (Sockets section).---no
on socklistenAn incoming connection on a socklisten - accept it with sockaccept, otherwise it's lost.---no
on sockreadData arrived on an open socket - use sockread to read it, otherwise it stays queued (lost when the next data arrives if not drained).---no
on sockcloseA socket closed (from the other side, or because the connection failed/dropped) - $sockerr is set if there was an error.---no
on runOur addition (real /run never returns output - see Commands, External programs section). A program started with /run has exited.--$1- = the output (split into words) - $runoutput for the full text, line breaks includedno
on reactionOur own addition (real mIRC has no reactions - see /react in Commands). Someone reacts with an emoji to one of your messages or to your Story/Status, on Telegram or WhatsApp. Only fires for reactions that are new/changed since Yoake connected, not for reactions already present on old messages.who reactedthe channel/group (empty in private, or for a reaction to a Story/Status)$1 = the emoji, $2 = message id (empty for a reaction to a Story/Status)yes - suppresses the default line, the script redraws it as it prefers
on join/on part/on kick/on topic/ on op/on deop/on invite work on Telegram and WhatsApp (on invite doesn't make sense on IRC, which has no invite links). On Telegram there's a limit from the platform itself though (not Yoake): the protocol only sends these updates to channel/supergroup admins, or to everyone in small groups - if you don't see them arriving in a large Telegram group you're not an admin of, it's because Telegram itself isn't sending them to you. On IRC, on join/on part/on kick/ on topic/on op/on deop work without this limit (the server always sends these events to everyone in the channel, not just admins) - see IRC.
on op {
  echo $nick became admin of $chan
}

on nick {
  echo $nick is now called $1
}

on invite {
  echo ⚠️ $nick sent you an invite link: $1
}
on message {
  echo $nick wrote in $chan
}

on notice {
  if ($noticetype == missedcall) haltdef
}

Custom whois - replaces the default lines with a format of your own:

on whois {
  haltdef
  echo -a ── $nick ──
  echo -a Name: $1
  if ($2 != $null) echo -a Username: $2
  if ($4 == on) echo -a Status: 🟢 online
  elseif ($4 == off) echo -a Status: ⚫ offline
}

Block all calls - decline them right away and let the caller know instead of showing "Incoming call":

on call {
  haltdef
  rejectcall I don't take calls, message me instead!
}

Automatic video playlist - walks through a channel/group (e.g. a series' episodes) looking for videos with $msg() (see Identifiers) and moves to the next one on its own with on videoend:

alias playlist {
  set %pl.chan $1
  set %pl.idx 0
  playlist.next
}
alias playlist.next {
  inc %pl.idx
  while (%pl.idx <= $msg(%pl.chan,0)) && ($msg(%pl.chan,%pl.idx).type != video) {
    inc %pl.idx
  }
  if (%pl.idx > $msg(%pl.chan,0)) {
    echo -a Playlist finished.
    return
  }
  playmedia %pl.chan $msg(%pl.chan,%pl.idx).id
}
on videoend {
  playlist.next
}

Then just /playlist #ChannelName (or the window id) to start it. Note: $msg() only sees the messages already loaded in that window (open the channel and scroll up to load more, if needed).

As in real mIRC, inside Remote you can also write alias name { ... } - it works the same as writing it on the Aliases tab, mIRC doesn't separate aliases by file.

Variables

On the Variables tab, one declaration per line: %name value. Usable as %name inside aliases/events/popups, anywhere in a script's text.

%greeting Hi from my alias!

hello {
  say %greeting $1
}
Persistence: as in real mIRC, global variables are permanent - they survive between restarts, inside variables.ini. This applies both to the ones declared by hand on the Variables tab and to the ones created/modified at runtime with set/inc/dec while a script runs: they're written to disk automatically (with a short delay, so as not to write on every single loop iteration - the same principle real mIRC uses, saving periodically instead of on every /set).

Example - a counter that survives restarts, with no need to declare it by hand on the Variables tab:

on message {
  set %counter $calc(%counter + 1)
  echo Messages received so far: %counter
}

Popups

Context menus (right-click) - same syntax as real mIRC, separate sections for each context:

SectionWhere it appears
[status]Right-click in the Status window.
[channel]Right-click in the middle of a group/channel window.
[query]Right-click in the middle of a private chat.
[nicklist]Right-click a nick in the user list.
[menubar]Popup attachable to the menu bar.

On the Popups tab, the "View" dropdown (or the View menu at the top of the editor) chooses which section you're editing - each has its own text, as in the real mIRC editor. The "Popup" menu at the top shows a live preview of how the menu will be drawn as you write it.

Syntax: Label:command one entry per line, - for a separator, nested submenus with one extra dot per level. In [nicklist] popups, $1 and $nick are the nick you right-clicked:

Greet everyone:say Hi everyone!
-
More commands
.Who am I:whois $1
.Kick:kick $1
See Commands for the full list of commands available inside a script, and Identifiers and functions for $nick/$chan/$1-$9/functions.