commit 856b18d60cee8d9c952b32437789e5982d81e2a7 Author: Tomatix Date: Sat Jul 25 01:26:33 2020 +0200 Added ChannelSettings and Essentials script diff --git a/ChannelSettings.iis b/ChannelSettings.iis new file mode 100644 index 0000000..e2466ba --- /dev/null +++ b/ChannelSettings.iis @@ -0,0 +1,843 @@ +# IdealIRC channel settings v1.0 + +# All of the maps below are indexed by a channel name. + +# Map of channel settings dialog handles. +global chansethndl; + +# Map of booleans if the instance supports these. +global hasInvites, hasExceptions; + +# Map of current channel settings. Limit, Key and currentTopic might not be set. +global chanmodes, modeLimit, modeKey, currentTopic; + +# Map of what's done receiving from the server. +global topicDone, modesDone, bansDone, invitesDone, exceptionsDone; + +# Map of which channels we're currently receiving for. +global inProgress; + +######################################################################## + +# Date/time format to use in ban/invite/except list. See hook load() for definition. +global DateTimeFormat; + +hook load() +{ + # Again, the key is always a channel name, for each one of the maps below. + chansethndl = map(); + hasInvites = map(); + hasExceptions = map(); + chanmodes = map(); + modeLimit = map(); + modeKey = map(); + currentTopic = map(); + topicDone = map(); + modesDone = map(); + bansDone = map(); + invitesDone = map(); + exceptionsDone = map(); + inProgress = map(); + + DateTimeFormat = "ddd MMMM dd yyyy, HH:mm:ss"; +} + +menu channel +{ + "Channel settings" => { + ctx = context(); + if (ctx["online"] == false) { + showmessage("Not connected", "You are not connected to the server", "warn", "o"); + return; + } + + if (chansethndl[channel] != void && dlgvalidhndl(chansethndl[channel])) + dlgclose(chansethndl[channel]); + + chansethndl[channel] = dialog("channelsettings"); + hndl = chansethndl[channel]; + + is = strsplit(isupport("CHANMODES"), ","); + hasInvites[channel] = strfind(&is[0], "I") >= 0; + hasExceptions[channel] = strfind(&is[0], "e") >= 0; + + topicDone[channel] = false; + modesDone[channel] = false; + bansDone[channel] = false; + invitesDone[channel] = hasInvites[channel] == false; + exceptionsDone[channel] = hasExceptions[channel] == false; + inProgress[channel] = true; + + # Custom attribute to keep track of which channel the dialog has an effect to + dlgsetattr(hndl, "channel", channel); + + dlgsetattr(hndl, "title", "Settings for " + channel); + + dlgsetwidgetattr(hndl, "bantable", "header", list("Mask", "Set by", "Date")); + + if (hasInvites[channel]) + dlgsetwidgetattr(hndl, "invitetable", "header", list("Mask", "Set by", "Date")); + else { + dlgsetwidgetattr(hndl, "invitetable", "header", list("Server does not support invite list")); + dlgsetwidgetattr(hndl, "inviteadd", "enabled", false); + dlgsetwidgetattr(hndl, "inviteremove", "enabled", false); + } + + if (hasExceptions[channel]) + dlgsetwidgetattr(hndl, "exceptiontable", "header", list("Mask", "Set by", "Date")); + else { + dlgsetwidgetattr(hndl, "exceptiontable", "header", list("Server does not support exception list")); + dlgsetwidgetattr(hndl, "exceptionadd", "enabled", false); + dlgsetwidgetattr(hndl, "exceptionremove", "enabled", false); + } + + /muteresp 331 332 333 324 329 367 368 346 347 348 349 + /mode %channel + /mode %channel +b + if (hasInvites[channel]) + /mode %channel +I + if (hasExceptions[channel]) + /mode %channel +e + /topic %channel + } +} + +hook numeric(code, params, message) +{ + channel = params[1]; + if (channel == void) + return; + + if (inProgress[channel] != true) + return; + + hndl = chansethndl[channel]; + + # No topic is set. + if (code == "331") { + topicDone[channel] = true; + } + + # Topic received + else if (code == "332") { + dlgsetwidgetattr(hndl, "topic", "text", message); + topicDone[channel] = true; + currentTopic[channel] = message; + } + + else if (code == "333") { + /unmuteresp 333 + } + + # Mode list received + else if (code == "324") { + args = list(); + i = 3; # 3rd position starts the possible first argument (key, limit) + while (i < listlen(¶ms)) { + listpush(&args, ¶ms[i]); + i = i + 1; + } + chanmodes[channel] = params[2]; + parseChannelModes(channel, params[2], args); + modesDone[channel] = true; + } + + # Ban list item received + else if (code == "367") { + handle = chansethndl[params[1]]; + + mask = params[2]; + setby = params[3]; + ts = toint(params[4]); + + dlgtableinsert(handle, "bantable", list(mask, setby, unixts(ts, &DateTimeFormat))); + } + + # Ban list end received + else if (code == "368") { + bansDone[channel] = true; + } + + # Invites list item received + else if (code == "346") { + handle = chansethndl[params[1]]; + + mask = params[2]; + setby = params[3]; + ts = toint(params[4]); + + dlgtableinsert(handle, "invitetable", list(mask, setby, unixts(ts, &DateTimeFormat))); + } + + # Invites list end received + else if (code == "347") { + invitesDone[channel] = true; + } + + # Exceptions list item received + else if (code == "348") { + handle = chansethndl[params[1]]; + + mask = params[2]; + setby = params[3]; + ts = toint(params[4]); + + dlgtableinsert(handle, "exceptiontable", list(mask, setby, unixts(ts, &DateTimeFormat))); + } + + # Exceptions list end received + else if (code == "349") { + exceptionsDone[channel] = true; + } + + checkAndHandleAllDone(channel); +} + +# +# Hooking onto 'mode' to receive any new/deleted entries in +# the ban/invite/except list for the given channel. +# +hook mode(sender, target, modes) +{ + if (chansethndl[target] == void) + return; + handle = chansethndl[target]; + + len = strlen(&modes); + i = 0; + add = false; + pidx = 0; + params = strsplit(&modes, " "); + while (i < len) { + c = modes[i]; + if (c == "+" || c == "-") { + add = c == "+"; + i = i + 1; + continue; + } + + if (c == "b") { + pidx = pidx + 1; + if (add) { + row = list(params[pidx], sender, now(DateTimeFormat)); + dlgtableinsert(handle, "bantable", row); + } + else + findAndRemoveFromTable(handle, "bantable", params[pidx]); + } + else if (c == "I") { + pidx = pidx + 1; + if (add) { + row = list(params[pidx], sender, now(DateTimeFormat)); + dlgtableinsert(handle, "invitetable", row); + } + else + findAndRemoveFromTable(handle, "invitetable", params[pidx]); + } + else if (c == "e") { + pidx = pidx + 1; + if (add) { + row = list(params[pidx], sender, now(DateTimeFormat)); + dlgtableinsert(handle, "exceptiontable", row); + } + else + findAndRemoveFromTable(handle, "exceptiontable", params[pidx]); + } + else + break; + + i = i + 1; + } +} + +# +# Delete a hostmask from a table widget. +# Valid table widgets are obviously; bantable, invitetable, exceptiontable. +# +function findAndRemoveFromTable(handle, table, mask) +{ + rowcount = dlgtablecount(handle, table); + i = 0; + while (i < rowcount) { + row = dlgtablerow(handle, table, i); + if (row[0] == mask) { + dlgtableremove(handle, table, i); + return; + } + i = i + 1; + } +} + +# +# Checks if receiving all modes and lists is done. +# Side-effect, if done, clean up the receiving process. +# +function checkAndHandleAllDone(channel) +{ + if ((topicDone[channel] == false) + || (modesDone[channel] == false) + || (bansDone[channel] == false) + || (invitesDone[channel] == false) + || (exceptionsDone[channel] == false)) + { + return false; + } + + /unmuteresp 331 332 324 329 367 368 346 347 348 349 + inProgress[channel] = false; + return true; +} + +# +# Parse channel modes and set the checkboxes for the supported modes. +# +function parseChannelModes(channel, modes, args) +{ + hndl = chansethndl[channel]; + i = 0; + argi = 0; # Index of 'args', increments on group B and C. + while (i < strlen(&modes)) { + c = modes[i]; + + if (c == "n") { + dlgsetwidgetattr(hndl, "mode_n", "checked", true); + } + else if (c == "t") { + dlgsetwidgetattr(hndl, "mode_t", "checked", true); + } + else if (c == "i") { + dlgsetwidgetattr(hndl, "mode_i", "checked", true); + } + else if (c == "p") { + dlgsetwidgetattr(hndl, "mode_p", "checked", true); + } + else if (c == "s") { + dlgsetwidgetattr(hndl, "mode_s", "checked", true); + } + else if (c == "m") { + dlgsetwidgetattr(hndl, "mode_m", "checked", true); + } + else if (c == "k") { + dlgsetwidgetattr(hndl, "mode_k", "checked", true); + dlgsetwidgetattr(hndl, "channel_key", "text", args[argi]); + modeKey[channel] = args[argi]; + argi = argi + 1; + } + else if (c == "l") { + dlgsetwidgetattr(hndl, "mode_l", "checked", true); + dlgsetwidgetattr(hndl, "channel_limit", "value", toint(args[argi])); + modeLimit[channel] = args[argi]; + argi = argi + 1; + } + + i = i + 1; + } +} + +dialog channelsettings +{ + width = 500; + height = 380; + icon = ":/Icons/options.png"; + + button btncommon { + text = "Common"; + checkable = true; + checked = true; + width = 80; + height = 30; + hook clicked => chanset_change_page(handle, "btncommon"); + } + + button btnbans { + text = "Bans"; + checkable = true; + x = 85; + width = 80; + height = 30; + hook clicked => chanset_change_page(handle, "btnbans"); + } + + button btnexceptions { + text = "Exceptions"; + checkable = true; + x = 170; + width = 80; + height = 30; + hook clicked => chanset_change_page(handle, "btnexceptions"); + } + + button btninvites { + text = "Invites"; + checkable = true; + x = 255; + width = 80; + height = 30; + hook clicked => chanset_change_page(handle, "btninvites"); + } + + + ###################### + # Common # + ###################### + + label labeltopic { + text = "Topic"; + x = 5; + y = 40; + width = 60; + height = 20; + } + + textline topic { + x = 45; + y = 40; + width = 450; + height = 25; + } + + groupbox modesbox { + text = "Channel modes"; + x = 5; + y = 65; + width = 490; + height = 280; + } + + checkbox mode_n { + x = 15; + y = 90; + height = 25; + width = 200; + text = "(n) No external messages"; + } + checkbox mode_t { + x = 15; + y = 120; + height = 25; + width = 210; + text = "(t) Only operators can set topic"; + } + checkbox mode_i { + x = 15; + y = 150; + height = 25; + width = 110; + text = "(i) Invite only"; + } + checkbox mode_p { + x = 15; + y = 180; + height = 25; + width = 100; + text = "(p) Private"; + } + checkbox mode_s { + x = 15; + y = 210; + height = 25; + width = 100; + text = "(s) Secret"; + } + checkbox mode_m { + x = 15; + y = 240; + height = 25; + width = 350; + text = "(m) Moderated, only privileged can send messages"; + } + checkbox mode_k { + x = 250; + y = 90; + height = 25; + width = 210; + text = "(k) Channel key"; + } + textline channel_key { + x = 270; + y = 120; + height = 25; + width = 100; + } + checkbox mode_l { + x = 250; + y = 150; + height = 25; + width = 210; + text = "(l) User limit"; + } + spinbox channel_limit { + x = 270; + y = 180; + height = 25; + width = 100; + min = 1; + max = 999999; + } + + ###################### + # Bans # + ###################### + + groupbox bansbox { + text = "Ban list"; + x = 5; + y = 40; + width = 490; + height = 305; + visible = false; + } + + table bantable { + x = 15; + y = 70; + width = 470; + height = 240; + visible = false; + } + + button banremove { + text = "Remove"; + x = 15; + y = 315; + width = 100; + height = 25; + visible = false; + hook clicked => { + selectedRow = dlgtableselected(handle, "bantable"); + if (selectedRow > -1) { + row = dlgtablerow(handle, "bantable", selectedRow); + mask = row[0]; + channel = dlggetattr(handle, "channel"); + /mode %channel -b %mask + } + } + } + + button banadd { + text = "New ..."; + x = 120; + y = 315; + width = 100; + height = 25; + visible = false; + hook clicked => { + channel = dlggetattr(handle, "channel"); + mask = input("New ban", "Add a new ban mask:"); + if (mask != void && strlen(mask) > 0) + /mode %channel +b %mask + } + } + + ###################### + # Invites # + ###################### + + groupbox invitesbox { + text = "Invites list"; + x = 5; + y = 40; + width = 490; + height = 305; + visible = false; + } + + table invitetable { + x = 15; + y = 70; + width = 470; + height = 240; + visible = false; + } + + button inviteremove { + text = "Remove"; + x = 15; + y = 315; + width = 100; + height = 25; + visible = false; + hook clicked => { + selectedRow = dlgtableselected(handle, "invitetable"); + if (selectedRow > -1) { + row = dlgtablerow(handle, "invitetable", selectedRow); + mask = row[0]; + channel = dlggetattr(handle, "channel"); + /mode %channel -I %mask + } + } + } + + button inviteadd { + text = "New ..."; + x = 120; + y = 315; + width = 100; + height = 25; + visible = false; + hook clicked => { + channel = dlggetattr(handle, "channel"); + mask = input("New invite", "Add a new invite mask:"); + if (mask != void && strlen(mask) > 0) + /mode %channel +I %mask + } + } + + ###################### + # Exceptions # + ###################### + + groupbox exceptionsbox { + text = "Exceptions list"; + x = 5; + y = 40; + width = 490; + height = 305; + visible = false; + } + + table exceptiontable { + x = 15; + y = 70; + width = 470; + height = 240; + visible = false; + } + + button exceptionremove { + text = "Remove"; + x = 15; + y = 315; + width = 100; + height = 25; + visible = false; + hook clicked => { + selectedRow = dlgtableselected(handle, "exceptiontable"); + if (selectedRow > -1) { + row = dlgtablerow(handle, "exceptiontable", selectedRow); + mask = row[0]; + channel = dlggetattr(handle, "channel"); + /mode %channel -e %mask + } + } + } + + button exceptionadd { + text = "New ..."; + x = 120; + y = 315; + width = 100; + height = 25; + visible = false; + hook clicked => { + channel = dlggetattr(handle, "channel"); + mask = input("New exception", "Add a new exception mask:"); + if (mask != void && strlen(mask) > 0) + /mode %channel +e %mask + } + } + + ###################### + # Cancel / save # + ###################### + + button btncancel { + text = "Cancel"; + x = 290; + y = 350; + width = 100; + height = 25; + hook clicked => dlgclose(handle); + } + + button btnsave { + text = "Save && Close"; + x = 395; + y = 350; + width = 100; + height = 25; + hook clicked => { + channel = dlggetattr(handle, "channel"); + newModes = createModeDiff(channel); + if (strlen(newModes) > 0) + /mode %channel %newModes + dlgclose(handle); + } + } +} + +# +# Returns a string of modes that reflect the changes in the dialog. +# Note that bans/invite/except lists are handled on-the-fly. +# +function createModeDiff(channel) +{ + hndl = chansethndl[channel]; + + unsetmodes = ""; + setmodes = ""; + params = ""; + + # Find which modes to unset + m = &chanmodes[channel]; + i = 0; + while (i < strlen(m)) { + c = m[i]; + if ( (c == "n" && dlggetwidgetattr(hndl, "mode_n", "checked") == false) + || (c == "t" && dlggetwidgetattr(hndl, "mode_t", "checked") == false) + || (c == "i" && dlggetwidgetattr(hndl, "mode_i", "checked") == false) + || (c == "p" && dlggetwidgetattr(hndl, "mode_p", "checked") == false) + || (c == "s" && dlggetwidgetattr(hndl, "mode_s", "checked") == false) + || (c == "m" && dlggetwidgetattr(hndl, "mode_m", "checked") == false) + || (c == "l" && dlggetwidgetattr(hndl, "mode_l", "checked") == false) ) + { + unsetmodes = unsetmodes + c; + } + else if (c == "k" && dlggetwidgetattr(hndl, "mode_k", "checked") == false) { + unsetmodes = unsetmodes + c; + params = modeKey[channel]; + } + i = i + 1; + } + + # Find new modes to set + if (strfind(m, "n") < 0 && dlggetwidgetattr(hndl, "mode_n", "checked") == true) { + setmodes = setmodes + "n"; + } + if (strfind(m, "t") < 0 && dlggetwidgetattr(hndl, "mode_t", "checked") == true) { + setmodes = setmodes + "t"; + } + if (strfind(m, "i") < 0 && dlggetwidgetattr(hndl, "mode_i", "checked") == true) { + setmodes = setmodes + "i"; + } + if (strfind(m, "p") < 0 && dlggetwidgetattr(hndl, "mode_p", "checked") == true) { + setmodes = setmodes + "p"; + } + if (strfind(m, "s") < 0 && dlggetwidgetattr(hndl, "mode_s", "checked") == true) { + setmodes = setmodes + "s"; + } + if (strfind(m, "m") < 0 && dlggetwidgetattr(hndl, "mode_m", "checked") == true) { + setmodes = setmodes + "m"; + } + if (strfind(m, "k") < 0 && dlggetwidgetattr(hndl, "mode_k", "checked") == true) { + setmodes = setmodes + "k"; + newkey = dlggetwidgetattr(hndl, "channel_key", "text"); + if (newkey == "") { + showmessage("Missing key", "The field for channel key is empty, yet marked as set", "warn", "o"); + return void; + } + params = newkey; + } + else if (strfind(m, "k") > -1 && dlggetwidgetattr(hndl, "mode_k", "checked") == true) { + newkey = dlggetwidgetattr(hndl, "channel_key", "text"); + oldkey = modeKey[channel]; + if (newkey == "") { + showmessage("Missing key", "The field for channel key is empty, yet marked as set", "warn", "o"); + return void; + } + if (newkey != oldkey) { + unsetmodes = unsetmodes + "k"; + setmodes = setmodes + "k"; + params = oldkey + " " + newkey; + } + } + if (strfind(m, "l") < 0 && dlggetwidgetattr(hndl, "mode_l", "checked") == true) { + setmodes = setmodes + "l"; + newlimit = dlggetwidgetattr(hndl, "channel_limit", "value"); + if (strlen(params) > 0) + params = params + " " + newlimit; + else + params = tostr(newlimit); + } + else if (strfind(m, "l") > -1 && dlggetwidgetattr(hndl, "mode_l", "checked") == true) { + newlimit = tostr( dlggetwidgetattr(hndl, "channel_limit", "value") ); + mapdump(&modeLimit); + if (newlimit != modeLimit[channel]) { + setmodes = setmodes + "l"; + if (strlen(params) > 0) + params = params + " " + newlimit; + else + params = newlimit; + } + } + + if (unsetmodes != "") + unsetmodes = "-" + unsetmodes; + if (setmodes != "") + setmodes = "+" + setmodes; + if (params != "") + params = " " + params; + return unsetmodes + setmodes + params; +} + +# +# Change to the "Common" view +# +function chanset_show_common(handle, visible) +{ + dlgsetwidgetattr(handle, "btncommon", "checked", visible); + + dlgsetwidgetattr(handle, "labeltopic", "visible", visible); + dlgsetwidgetattr(handle, "topic", "visible", visible); + dlgsetwidgetattr(handle, "modesbox", "visible", visible); + dlgsetwidgetattr(handle, "mode_n", "visible", visible); + dlgsetwidgetattr(handle, "mode_t", "visible", visible); + dlgsetwidgetattr(handle, "mode_i", "visible", visible); + dlgsetwidgetattr(handle, "mode_p", "visible", visible); + dlgsetwidgetattr(handle, "mode_s", "visible", visible); + dlgsetwidgetattr(handle, "mode_m", "visible", visible); + dlgsetwidgetattr(handle, "mode_k", "visible", visible); + dlgsetwidgetattr(handle, "channel_key", "visible", visible); + dlgsetwidgetattr(handle, "mode_l", "visible", visible); + dlgsetwidgetattr(handle, "channel_limit", "visible", visible); +} + +# +# Change to the "Bans" view +# +function chanset_show_bans(handle, visible) +{ + dlgsetwidgetattr(handle, "btnbans", "checked", visible); + + dlgsetwidgetattr(handle, "bansbox", "visible", visible); + dlgsetwidgetattr(handle, "bantable", "visible", visible); + dlgsetwidgetattr(handle, "banadd", "visible", visible); + dlgsetwidgetattr(handle, "banremove", "visible", visible); +} + +# +# Change to the "Invites" view +# +function chanset_show_invites(handle, visible) +{ + dlgsetwidgetattr(handle, "btninvites", "checked", visible); + + dlgsetwidgetattr(handle, "invitesbox", "visible", visible); + dlgsetwidgetattr(handle, "invitetable", "visible", visible); + dlgsetwidgetattr(handle, "inviteadd", "visible", visible); + dlgsetwidgetattr(handle, "inviteremove", "visible", visible); +} + +# +# Change to the "Exceptions" view +# +function chanset_show_exceptions(handle, visible) +{ + dlgsetwidgetattr(handle, "btnexceptions", "checked", visible); + + dlgsetwidgetattr(handle, "exceptionsbox", "visible", visible); + dlgsetwidgetattr(handle, "exceptiontable", "visible", visible); + dlgsetwidgetattr(handle, "exceptionadd", "visible", visible); + dlgsetwidgetattr(handle, "exceptionremove", "visible", visible); +} + +# +# Called when clicking either of the buttons Common, Bans, Invites or Exceptions. +# +function chanset_change_page(handle, button) +{ + chanset_show_common(handle, button == "btncommon"); + chanset_show_bans(handle, button == "btnbans"); + chanset_show_invites(handle, button == "btninvites"); + chanset_show_exceptions(handle, button == "btnexceptions"); +} diff --git a/Essentials.iis b/Essentials.iis new file mode 100644 index 0000000..d66bb87 --- /dev/null +++ b/Essentials.iis @@ -0,0 +1,303 @@ +# IdealIRC Essentials script v1.0 + +# (Map) Used if we try to ban someone but we haven't registered their hostname yet. +global banqueue, kickqueue; + +hook start() +{ + # Queue for host lookup if a ban is issued, but hostname is unknown. + # Key: nickname, Value: channel to issue ban. + banqueue = map(); + + # Same as above, but value is the kick reason. + kickqueue = map(); +} + +menu status { + "Server stats" => /lusers + + "Join channel" => { + channel = input("Join channel", "Enter a channel name:"); + if (channel == void || channel == "") return; + + # Todo: Implement new function "ischannelprefix" or something. + if (channel[0] != "#") + channel = "#" + channel; + + /join %channel + } + + "Change nickname" => { + nickname = input("Set nickname", "Enter nickname:"); + if (nickname == void || nickname == "") return; + /nick %nickname + } +} + +menu private { + "Whois" => /whois %nickname + divide + "CTCP" { + "Ping" => /ctcp %nickname PING + "Version" => /ctcp %nickname VERSION + "Time" => /ctcp %nickname TIME + divide + "Custom..." => { + msg = input("Custom CTCP message", "Message:"); + if (msg == void) return; + if (msg == "") return; + /ctcp %nickname %msg + } + } +} + + +menu memberlist { + "Whois" => { + if (maplen(selected) > 1) { + button = showmessage("Multiple members selected", + "You have selected " + maplen(selected) + + " members to show /whois for. Continue?", + "question", "yn"); + if (button == "n") return; + } + i = 0; + while (selected[i] != void) { + nick = selected[i]; + /whois %nick + i = i + 1; + } + } + + "Query" => { + if (maplen(selected) > 1) { + button = showmessage("Multiple members selected", + "You have selected " + maplen(selected) + + " members to open a query dialog for. Continue?", + "question", "yn"); + if (button == "n") return; + } + i = 0; + while (selected[i] != void) { + nick = selected[i]; + /query %nick + i = i + 1; + } + } + + divide + + "Privileges" { + "Give op" => privilege(channel, "+", "o", selected); + "Take op" => privilege(channel, "-", "o", selected); + divide + "Give half-op" => privilege(channel, "+", "h", selected); + "Take half-op" => privilege(channel, "-", "h", selected); + divide + "Give voice" => privilege(channel, "+", "v", selected); + "Take voice" => privilege(channel, "-", "v", selected); + } + + divide + + "Kick..." => { + if (maplen(selected) > 1) { + button = showmessage("Multiple members selected", + "You have selected " + maplen(selected) + + " members for kicking. Continue?", + "question", "yn"); + if (button == "n") return; + } + i = 0; + while (selected[i] != void) { + nick = selected[i]; + reason = input("Kick " + nick + " from " + channel, + "Enter a reason for " + nick + ":"); + if (reason != void) + /kick %channel %nick %reason + i = i + 1; + } + } + + "Ban" => { + if (maplen(selected) > 1) { + button = showmessage("Multiple members selected", + "You have selected " + maplen(selected) + + " members for ban. Continue?", + "question", "yn"); + if (button == "n") return; + } + i = 0; + while (selected[i] != void) { + nick = selected[i]; + host = userhost(nick); + if (host == void) { + banqueue[nick] = channel; + /userhost %nick + } + else { + /mode %channel +b %host + } + i = i + 1; + } + } + + "Ban + kick..." => { + if (maplen(selected) > 1) { + button = showmessage("Multiple members selected", + "You have selected " + maplen(selected) + + " members for ban + kick. Continue?", + "question", "yn"); + if (button == "n") return; + } + i = 0; + while (selected[i] != void) { + nick = selected[i]; + reason = input("Ban + kick " + nick + " from " + channel, + "Enter a reason for " + nick + ":"); + if (reason != void) { + host = userhost(nick); + if (host == void) { + banqueue[nick] = channel; + kickqueue[nick] = reason; + /userhost %nick + } + else { + /mode %channel +b %host + /kick %channel %nick %reason + } + } + i = i + 1; + } + } + + divide + + "CTCP" { + "Ping" => { + if (maplen(selected) > 1) { + button = showmessage("Multiple members selected", + "You have selected " + maplen(selected) + + " members for CTCP PING. Continue?", + "question", "yn"); + if (button == "n") return; + } + i = 0; + while (selected[i] != void) { + nick = selected[i]; + /ctcp %nick PING + i = i + 1; + } + } + + "Version" => { + if (maplen(selected) > 1) { + button = showmessage("Multiple members selected", + "You have selected " + maplen(selected) + + " members for CTCP VERSION. Continue?", + "question", "yn"); + if (button == "n") return; + } + i = 0; + while (selected[i] != void) { + nick = selected[i]; + /ctcp %nick VERSION + i = i + 1; + } + } + + "Time" => { + if (maplen(selected) > 1) { + button = showmessage("Multiple members selected", + "You have selected " + maplen(selected) + + " members for CTCP TIME. Continue?", + "question", "yn"); + if (button == "n") return; + } + i = 0; + while (selected[i] != void) { + nick = selected[i]; + /ctcp %nick TIME + i = i + 1; + } + } + + divide + + "Custom..." => { + if (maplen(selected) > 1) { + button = showmessage("Multiple members selected", + "You have selected " + maplen(selected) + + " members for custom CTCP. Continue?", + "question", "yn"); + if (button == "n") return; + } + msg = input("Custom CTCP message", "Message:"); + if (msg == void || msg == "") return; + i = 0; + while (selected[i] != void) { + nick = selected[i]; + /ctcp %nick %msg + i = i + 1; + } + } + } +} + +hook numeric(code, args, msg) +{ + if (code == "302") { + # ::= ['*'] '=' <'+'|'-'> + # The '*' indicates whether the client has registered + # as an Operator. The '-' or '+' characters represent + # whether the client has set an AWAY message or not + # respectively. + + nickend = strfind(msg, "="); + if (msg[nickend - 1] == "*") nickend = nickend - 1; + nickname = strmid(msg, 0, nickend); + + if (banqueue[nickname] != void) { + channel = banqueue[nickname]; + mapremove(&banqueue, nickname); + + host = userhost(nickname); + /mode %channel +b %host + + if (kickqueue[nickname] != void) { + reason = kickqueue[nickname]; + mapremove(&kickqueue, nickname); + + /kick %channel %nickname %reason + } + } + } +} + +# +# Generate a /MODE message that can handle all selected nicknames in +# a nickname list, based on the amount of concurrent modes the server supports. +# +function privilege(channel, action, mode, members) +{ + maxmodes = toint(isupport("MODES")); + totalmodes = maplen(&members); + i = 0; + while (i < totalmodes) { + modelen = maxmodes; + if ((totalmodes - i) < maxmodes) + modelen = totalmodes - i; + + j = i; + nicks = ""; + while (j < (i + modelen)) { + nicks = nicks + members[j] + " "; + j = j + 1; + } + + modestr = action + strrep(mode, modelen); + /mode %channel %modestr %nicks + i = i + modelen; + } + +}