# 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; } channelOrig = channel; channel = strlower(channel); 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 " + channelOrig); 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 = strlower(params[1]); if (channel == void) return; if (inProgress[channel] != true) return; hndl = chansethndl[channel]; # No topic is set if (code == "331") { topicDone[channel] = true; currentTopic[channel] = ""; } # Topic received else if (code == "332") { dlgsetwidgetattr(hndl, "topic", "text", message); topicDone[channel] = true; currentTopic[channel] = message; } # Topic-by message is always a bit late to the party so must be unmuted separately. 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[channel]; 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[channel]; 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[channel]; 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) { target = strlower(target); 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) { channel = strlower(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) { channel = strlower(channel); 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 newTopic = dlggetwidgetattr(handle, "topic", "text"); if (newTopic != currentTopic[channel]) { if (newTopic == "") /topic -c %channel else /topic %channel %newTopic } 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) { channel = strlower(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"); }