# notify.irc: a module to detect the presence of nicknames
#
# written by nsx
#
# /set variables this script uses:
#
#   DO_NOTIFY_IMMEDIATELY -- if set on, the notify system will check for
#                            signons and signoffs immediately when a
#                            /notify command has been performed
#
#   NOTIFY -- turns notify system on or off
#
#   NOTIFY_INTERVAL -- how often (in seconds) to check for signons and signoffs
#
#   NOTIFY_USERHOST_AUTOMATIC -- if set on, userhosts will be displayed in
#                                the signon messages (this can lag you if you
#                                have a very large notify list)
#
#
# note: this script uses the serial number 556 for its serial hooks
#

if (word(2 $loadinfo()) != [pf]) { load -pf $word(1 $loadinfo()); return; };

package notify;

load ison;

# global config vars
@do_notify_immediately = [ON];
@notify = [ON];
@notify_interval = 30;
@notify_userhost_automatic = [ON];


# turn off epic's built-in notify system
on ^set -"NOTIFY *";
set notify off;

alias change_notify_status (servernum, newstatus, nicklist) {
	if (newstatus == [online]) {
		push online_list[$servernum] $nicklist;

		if (NOTIFY_USERHOST_AUTOMATIC == [ON]) {
			xeval -server $servernum {
				fe ($nicklist) n1 n2 n3 n4 n5 {
					userhost $n1 $n2 $n3 $n4 $n5 -cmd {
						shook notify_signon $0 $3@$4;
					};
				};
			};
		} else {
			fe ($nicklist) nickname {
				shook notify_signon $nickname <EMPTY>;
			};
		};
	} elsif (newstatus == [offline]) {
		@online_list[$servernum] = remws($nicklist / $online_list[$servernum]));

		fe ($nicklist) nickname {
			shook notify_signoff $nickname;
		};
	};
};

alias clear_notify_list {
	@notify_list = [];

	fe ($notify_networks $notify_groups) element {
		@notify_list[$encode($toupper($element))] = [];
	};

	@notify_networks = [];
	@notify_groups = [];

};

alias clear_notify_records {
	fe ($servernums()) servernum {
		@np_in_progress[$servernum] = [];
		@online_list[$servernum] = [];
	};
};

alias detect_signons {
	if (NOTIFY == [OFF]) {
		return;
	};

	fe ($servernums()) servernum {
		if (np_in_progress[$servernum] == 1) {
			continue;
		};

		@:groupname = toupper($serverctl(GET $servernum GROUP));
		@:network = toupper($serverctl(GET $servernum 005 NETWORK));
		@:ison_send_list = getnick[$servernum];

		push ison_send_list $notify_list;
		push ison_send_list $notify_list[$encode($toupper($groupname))];
		push ison_send_list $notify_list[$encode($toupper($network))];

		if (ison_send_list == []) {
			continue;
		};

		@np_in_progress[$servernum] = 1;

		ison -oncmd {
			@observe_online($uniq($*));
		} -offcmd {
			@observe_offline($uniq($*));
		} -end {
			@np_in_progress[$serverctl(from_server)] = 0;
		} -server $servernum $ison_send_list;
	};
};

alias establish_notify_hooks {
        ^on #^channel_nick 556 "*" {
                @observe_offline($1);
                @observe_online($2);
        };

        ^on #^channel_signoff 556 "*" {
                @observe_offline($1);
        };

        ^on #^join 556 "*" {
                @observe_online($0);
        };

        ^on #^msg 556 "*" {
                @observe_online($0);
        };

        ^on #^notice 556 "*" {
                @observe_online($0);
        };

	^on #^server_lost 556 "*" {
		@:servernum = [$0];

		@np_in_progress[$servernum] = 0;
		@online_list[$servernum] = [];
	};

	^on #^003 556 "*" {
		@:lserver = serverctl(from_server);

		@np_in_progress[$lserver] = 0;
		@online_list[$lserver] = [];
	};

        ^on #^311 556 "*" {
                @observe_online($1);
        };

        ^on #^401 556 "*" {
                @observe_offline($1);
        };

};

alias getnick (nickname) {
        if (nickname == []) {
                @:getnick = getnick[$serverctl(from_server)];

                if (getnick == []) {
                        xecho -b no getnick for $serverctl(get $serverctl(from_server) itsname) has been set;
                        xecho;
                } else {
                        xecho -b getnick for $serverctl(get $serverctl(from_server) itsname) is: $getnick;
                        xecho;
                };

                return;
        };

        if (nickname == [-]) {
                @getnick[$serverctl(from_server)] = [];

                xecho -b getnick for $serverctl(get $serverctl(from_server) itsname) has been unset;
                xecho;

		return;
        } else {
                @getnick[$serverctl(from_server)] = nickname;

                xecho -b getnick for $serverctl(get $serverctl(from_server) itsname) set to: $nickname;
                xecho;
        };

        @detect_signons();
};

alias notify (nicklist) {
	@:lserver = serverctl(from_server);
	@:lgroup = serverctl(GET $lserver GROUP);
	@:lnetwork = serverctl(GET $lserver 005 NETWORK);
	@:online_list = online_list[$lserver];
	@:offline_list = remws($online_list / $notify_list $notify_list[$encode($toupper($lgroup))] $notify_list[$encode($toupper($lnetwork))]);

	if (functioncall()) {
		@:notify_list = notify_list;

		if (#nicklist == 0) {
			fe ($notify_networks) network {
				fe ($notify_list[$encode($toupper($network))]) nickname {
					push notify_list $nickname:$network;
				};
			};

			fe ($notify_groups) groupname {
				fe ($notify_list[$encode($toupper($groupname))]) nickname {
					push notify_list $nickname::$groupname;
				};
			};

			return $notify_list;
		} elsif ((:word = findw(net $nicklist)) > -1) {
			@:online_list = [];
			@:network = word(${word + 1} $nicklist);
			@:list = word(${word - 1} $nicklist) == [] ? word(${word + 2} $nicklist) : word(${word - 1} $nicklist);

			fe ($notify_list[$encode($toupper($network))]) nickname {
				fe ($servernums()) servernum {
					if (serverctl(GET $servernum 005 NETWORK) == network && findw($nickname $online_list[$servernum]) > -1) {
						push online_list $nickname;
					};
				};
			};

			@:online_list = uniq($online_list);

			if (list == [off]) {
				return $remws($online_list / $notify_list[$encode($toupper($network))]);
			} else {
				return $online_list;
			};
		} elsif ((:word = findw(group $nicklist)) > -1) {
			@:online_list = [];
			@:groupname = word(${word + 1} $nicklist);
			@:list = word(${word - 1} $nicklist) == [] ? word(${word + 2} $nicklist) : word(${word - 1} $nicklist);

			fe ($notify_list[$encode($toupper($groupname))]) nickname {
				fe ($servernums()) servernum {
					if (serverctl(GET $servernum GROUP) == groupname && findw($nickname $online_list[$servernum]) > -1) {
						push online_list $nickname;
					};
				};
			};

			@:online_list = uniq($online_list);

			if (list == [off]) {
				return $remws($online_list / $notify_list[$encode($toupper($groupname))]);
			} else {
				return $online_list;
			};
		} else {
			if ((:word = findw(serv $nicklist)) > -1) {
				@:refnum = word(${word + 1} $nicklist);
				@:list = word(${word - 1} $nicklist) == [] ? word(${word + 2} $nicklist) : word(${word - 1} $nicklist);

			} elsif (#nicklist == 1) {
				@:lserver = serverctl(from_server);
				@:list = nicklist;
			} else {
				return -1;
			};

			if (list == [off]) {
				@:network = serverctl(GET $lserver 005 NETWORK);
				@:groupname = serverctl(GET $lserver GROUP);

				push notify_list $notify_list[$encode($toupper($network))];
				push notify_list $notify_list[$encode($toupper($groupname))];

				return $remws($online_list[$lserver] / $notify_list);
			} else {
				return $online_list[$lserver];
			};
		};
	};

	if (nicklist == []) {
		xecho -b -c -- Currently online: $online_list;
		xecho -b -c -- Currently offline: $offline_list;

		return;
	};

	if (nicklist == [-]) {
		@remove_notify_entry();

		xecho -b -c -- the global notify list has been cleared;

		return;
	};

	if (nicklist == [--]) {
		@clear_notify_list();
		@clear_notify_records();

		xecho -b -c -- the global and local notify lists have been cleared;
		return;
	};

	fe ($nicklist) nickname {
		@:separators = count(: $nickname);

		if (left(1 $nickname) == [-]) {
			@:nickname = rest($nickname);

			if (separators == 1) {
				@:network = after(: $nickname);
				@:nickname = before(: $nickname);

				if (nickname == []) {
					@remove_network_notify_entry($network);

					xecho -b -c -- the notify list for network $network has been cleared;

					continue;
				};

				if (remove_network_notify_entry($network $nickname) == 1) {
					xecho -b -c -- $nickname is not in the notify list for network ${network}!;
				} else {
					xecho -b -c -- $nickname has been removed from the notify list for network $network;
				};
			} elsif (separators >= 2) {
				@:groupname = after(-1 : $nickname);
				@:nickname = before(: $nickname);
				@:refnums = serverctl(GMATCH $groupname);

				if (refnums == []) {
					xecho -b -c -- server group $groupname does not exist;

					continue;
				};

				if (nickname == []) {
					@remove_group_notify_entry($groupname);

					xecho -b -c -- the notify list for server group $groupname has been cleared;

					continue;
				};

				if (remove_group_notify_entry($groupname $nickname) == 1) {
					xecho -b -c -- $nickname is not in the notify list for server group ${groupname}!;
				} else {
					xecho -b -c -- $nickname has been removed from the notify list for server group ${groupname};
				};
			} else {
				if (nickname == []) {
					continue;
				};

				if (remove_notify_entry($nickname) == 1) {
					xecho -b -c -- $nickname is not in the global notify list!;
				} else {
					xecho -b -c -- $nickname has been removed from the global notify list;
				};
			};
		} else {
			if (left(1 $nickname) == [+]) {
				@:nickname = rest($nickname);
			};

			if (separators == 1) {
				@:network = after(: $nickname);
				@:nethash = encode($toupper($network));
				@:nickname = before(: $nickname);

				if (nickname == []) {
					continue;
				};

				if (findw($nickname $notify_list[$nethash]) > -1) {
					xecho -b -c -- $nickname is already in the notify list for network ${network}!;

					continue;
				};

				if (findw($nickname $notify_list) > -1) {
					xecho -b -c -- $nickname is already in the global notify list!;

					continue;
				};

				if (findw($network $notify_networks) == -1) {
					push notify_networks $network;
				};

				push notify_list[$nethash] $nickname;

				xecho -b -c -- $nickname has been added to the notify list for network $network;
			} elsif (separators >= 2) {
				@:groupname = after(-1 : $nickname);
				@:grouphash = encode($toupper($groupname));
				@:nickname = before(: $nickname);

				if (nickname == []) {
					xecho -b -c -- no nickname provided for server group $groupname;
					continue;
				};


				if (serverctl(GMATCH $groupname) == []) {
					xecho -b -c -- server group $groupname does not exist;

					continue;
				};

				if (findw($nickname $notify_list[$grouphash]) > -1) {
					xecho -b -- $nickname is already in the notify list for server group ${groupname}!;
					continue;
				};


				if (findw($nickname $notify_list) > -1) {
					xecho -b -c -- $nickname is already in the global notify list!;

					continue;
				};

				if (findw($groupname $notify_groups) == -1) {
					push notify_groups $groupname;
				};

				push notify_list[$grouphash] $nickname;

				xecho -b -c -- $nickname has been added to the notify list for server group $groupname;
			} else {
				if (nickname == []) {
					continue;
				};

				if (findw($nickname $notify_list[$encode($toupper($lgroup))]) > -1) {
					xecho -b -c -- $nickname is already in the notify list for server group ${lgroup}!;

					continue;
				};

				if (findw($nickname $notify_list[$encode($toupper($lnetwork))]) > -1) {
					xecho -b -c -- $nickname is already in the notify list for the network ${lnetwork}!;
					continue;
				};

				if (findw($nickname $notify_list) > -1) {
					xecho -b -c -- $nickname is already in the global notify list!;

					continue;
				};

				push notify_list $nickname;

				xecho -b -c -- $nickname has been added to the global notify list;
			};
		};
	};

	if (do_notify_immediately == [ON]) {
		@detect_signons();
	};
};

alias observe_offline (nicklist) {
	@:signoffs = [];
        @:lserver = serverctl(from_server);

	fe ($nicklist) nickname {
	        if (getnick[$lserver] != [] && getnick[$lserver] != N && getnick[$lserver] == nickname) {
			//nick $getnick[$lserver];
		};

	        if (findw($nickname $online_list[$lserver]) > -1) {
			push signoffs $nickname;
		};
	};

	@change_notify_status($lserver offline $signoffs);
};

alias observe_online (nicklist) {
	@:signons = [];
        @:lserver = serverctl(from_server);
	@:groupname = serverctl(GET $lserver GROUP);
	@:network = serverctl(GET $lserver 005 NETWORK);
        @:offline_list = remws($online_list[$lserver] / $notify_list $notify_list[$encode($toupper($groupname))] $notify_list[$encode($toupper($network))]);

	fe ($nicklist) nickname {
		if (findw($nickname $offline_list) > -1) {
			push signons $nickname;
		};
	};

	@change_notify_status($lserver online $signons);
};

alias remove_notify_entry (nickname) {
	if (nickname == []) {
		fe ($notify_list) nickname {
			fe ($servernums()) servernum {
				@:groupname = serverctl(GET $servernum GROUP);
				@:network = serverctl(GET $servernum 005 NETWORK);

				if (findw($nickname $notify_list[$encode($toupper($groupname))]) == -1 && findw($nickname $notify_list[$encode($toupper($network))]) == -1) {
					@online_list[$servernum] = remw($nickname $online_list[$servernum]);
				};
			};
		};

		@notify_list = [];

		return 0;
	};

	if (findw($nickname $notify_list) == -1) {
		return 1;
	} else {
		@notify_list = remw($nickname $notify_list);

		fe ($servernums()) servernum {
			@:groupname = serverctl(GET $servernum GROUP);
			@:network = serverctl(GET $servernum 005 NETWORK);

			if (findw($nickname $notify_list[$encode($toupper($groupname))]) == -1 && findw($nickname $notify_list[$encode($toupper($network))]) == -1) {
				@online_list[$servernum] = remw($nickname $online_list[$servernum]);
			};
		};

		return 0;
	};

};

alias remove_group_notify_entry (groupname, nickname) {
	@:grouphash = encode($toupper($groupname));

	if (nickname == []) {
		fe ($notify_list[$grouphash]) nickname {
			fe ($servernums()) servernum {
				if (serverctl(GET $servernum GROUP) == groupname) {
					@:network = serverctl(GET $servernum 005 NETWORK);

					if (findw($nickname $notify_list[$encode($toupper($network))]) == -1) {
						@online_list[$servernum] = remw($nickname $online_list[$servernum]);
					};
				};
			};
		};

		@notify_list[$grouphash] = [];
		@notify_groups = remw($groupname $notify_groups);

		return 0;
	};

	if (findw($nickname $notify_list[$grouphash]) == -1) {
		return 1;
	} else {
		@notify_list[$grouphash] = remw($nickname $notify_list[$grouphash]);

		if (notify_list[$grouphash] == []) {
			@notify_groups = remw($groupname $notify_groups);
		};

		fe ($servernums()) servernum {
			if (serverctl(GET $servernum GROUP) == groupname) {
				@:network = serverctl(GET $servernum 005 NETWORK);

				if (findw($nickname $notify_list[$encode($toupper($network))]) == -1) {
					@online_list[$servernum] = remw($nickname $online_list[$servernum]);
				};
			};
		};

		return 0;
	};
};

alias remove_network_notify_entry (network, nickname) {
	@:nethash = encode($toupper($network));

	if (nickname == []) {
		fe ($notify_list[$nethash]) nickname {
			fe ($servernums()) servernum {
				if (serverctl(GET $servernum 005 NETWORK) == network) {
					@:groupname = serverctl(GET $servernum GROUP);

					if (findw($nickname $notify_list[$encode($toupper($groupname))]) == -1) {
						@online_list[$servernum] = remw($nickname $online_list[$servernum]);
					};
				};
			};
		};

		@notify_list[$nethash] = [];
		@notify_networks = remw($network $notify_networks);

		return 0;
	};

	if (findw($nickname $notify_list[$nethash]) == -1) {
		return 1;
	} else {
		@notify_list[$nethash] = remw($nickname $notify_list[$nethash]);

		if (notify_list[$nethash] == []) {
			@notify_networks = remw($network $notify_networks);
		};

		fe ($servernums()) servernum {
			if (serverctl(GET $servernum 005 NETWORK) == network) {
				@:groupname = serverctl(GET $servernum GROUP);

				if (findw($nickname $notify_list[$encode($toupper($groupname))]) == -1) {
					@online_list[$servernum] = remw($nickname $online_list[$servernum]);
				};
			};
		};

		return 0;
	};
};

alias remove_notify_hooks {
	^on #^channel_nick 556 -"*";
	^on #^channel_signoff 556 -"*";
	^on #^join 556 -"*";
	^on #^msg 556 -"*";
	^on #^notice 556 -"*";
	^on #^send_to_server 556 -"% % ISON *";
	^on #^server_lost 556 -"*";
	^on #^003 556 -"*";
	^on #^311 556 -"*";
	^on #^401 556 -"*";
};

alias servernums {
	@:refnums = [];

	fe ($serverctl(omatch *)) refnum {
		if (serverctl(get $refnum connected)) {
			push refnums $refnum;
		};
	};

	return $refnums;
};


on ^notify_signoff "*" {
	xecho -b -level CRAP -- Signoff by $0 detected;
};

on ^notify_signon "*" {
	if (NOTIFY_USERHOST_AUTOMATIC == [ON]) {
		if ([$1] == [<UNKNOWN>@<UNKNOWN>]) {
			xecho -b -level CRAP -- Signon by $0 \(\) detected;
		} else {
			 xecho -b -level CRAP Signon by $0 \($1\) detected;
		};
	} else {
		xecho -b -level CRAP -- Signon by $0 detected;
	};
};

on ^set "DO_NOTIFY_IMMEDIATELY" {
	if ([$1] == []) {
		xecho -b Current value of DO_NOTIFY_IMMEDIATELY is $do_notify_immediately;
	} elsif ([$1] == [off]) {
		@do_notify_immediately = [off];
		xecho -b Value of DO_NOTIFY_IMMEDIATELY set to OFF;
	} elsif ([$1] == [on]) {
		@do_notify_immediately = [on];
		xecho -b Value of DO_NOTIFY_IMMEDIATELY set to ON;
	} else {
		xecho -b Value of DO_NOTIFY_IMMEDIATELY must be ON or OFF;
	};
};

on ^set "NOTIFY *" {
	if ([$1] == []) {
		xecho -b Current value of NOTIFY is $notify;
	} elsif ([$1] == [off]) {
		@notify = [off];

		^timer -del notcheck;
		@remove_notify_hooks();

		xecho -b Value of NOTIFY set to OFF;
	} elsif ([$1] == [on]) {
		@notify = [on];

		@clear_notify_records();
		@establish_notify_hooks();
		^timer -refnum notcheck -rep -1 $notify_interval @detect_signons();

		xecho -b Value of NOTIFY set to ON;
	} else {
		xecho -b Value of NOTIFY must be ON or OFF;
	};
};

on ^set "NOTIFY_INTERVAL *" {
	if ([$1] == []) {
		xecho -b Current value of NOTIFY_INTERVAL is $notify_interval;
	} elsif (!isnumber($1)) {
		xecho -b Value of NOTIFY_INTERVAL must be numeric!;
	} elsif ([$1] < 1) {
		xecho -b Value of NOTIFY_INTERVAL must be greater than or equal to 1;
	} else {
		@notify_interval = [$1];

		^timer -del notcheck;
		^timer -refnum notcheck -rep -1 $notify_interval @detect_signons();

		xecho -b Value of NOTIFY_INTERVAL set to $notify_interval;
	};
};

on ^set "NOTIFY_USERHOST_AUTOMATIC *" {
	if ([$1] == []) {
		xecho -b Current value of NOTIFY_USERHOST_AUTOMATIC is $notify_userhost_automatic;
	} elsif ([$1] == [off]) {
		@notify_userhost_automatic = [OFF];
		xecho -b Value of NOTIFY_USERHOST_AUTOMATIC set to OFF;
	} elsif ([$1] == [on]) {
		@notify_userhost_automatic = [ON];
		xecho -b Value of NOTIFY_USERHOST_AUTOMATIC set to ON;
	} else {
		xecho -b Value of NOTIFY_USERHOST_AUTOMATIC must be ON or OFF;
	};
};

@clear_notify_list();
@clear_notify_records();
@establish_notify_hooks();
@detect_signons();
timer -refnum notcheck -rep -1 $notify_interval @detect_signons();
