Add /ignore message filtering patterns
This commit is contained in:
		
							parent
							
								
									fcb6e2909f
								
							
						
					
					
						commit
						25f419465f
					
				
							
								
								
									
										1
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
									
									
									
									
								
							| @ -12,6 +12,7 @@ OBJS += complete.o | ||||
| OBJS += config.o | ||||
| OBJS += edit.o | ||||
| OBJS += handle.o | ||||
| OBJS += ignore.o | ||||
| OBJS += irc.o | ||||
| OBJS += log.o | ||||
| OBJS += ui.o | ||||
|  | ||||
							
								
								
									
										4
									
								
								README.7
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								README.7
									
									
									
									
									
								
							| @ -1,4 +1,4 @@ | ||||
| .Dd March 25, 2020 | ||||
| .Dd March 31, 2020 | ||||
| .Dt README 7 | ||||
| .Os "Causal Agency" | ||||
| . | ||||
| @ -132,6 +132,8 @@ line editing | ||||
| tab complete | ||||
| .It Pa url.c | ||||
| URL detection | ||||
| .It Pa ignore.c | ||||
| message filtering | ||||
| .It Pa log.c | ||||
| chat logging | ||||
| .It Pa config.c | ||||
|  | ||||
							
								
								
									
										26
									
								
								catgirl.1
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								catgirl.1
									
									
									
									
									
								
							| @ -1,4 +1,4 @@ | ||||
| .Dd March 30, 2020 | ||||
| .Dd March 31, 2020 | ||||
| .Dt CATGIRL 1 | ||||
| .Os | ||||
| . | ||||
| @ -17,6 +17,7 @@ | ||||
| .Op Fl a Ar auth | ||||
| .Op Fl c Ar cert | ||||
| .Op Fl h Ar host | ||||
| .Op Fl i Ar patt | ||||
| .Op Fl j Ar join | ||||
| .Op Fl k Ar priv | ||||
| .Op Fl n Ar nick | ||||
| @ -147,6 +148,22 @@ and write it to | ||||
| Connect to | ||||
| .Ar host . | ||||
| . | ||||
| .It Fl i Ar pattern, Cm ignore = Ar pattern | ||||
| Add a case-insensitive message filtering pattern, | ||||
| which may contain | ||||
| .Ql * , | ||||
| .Ql \&? | ||||
| and | ||||
| .Ql [] | ||||
| wildcards as in | ||||
| .Xr sh 1 . | ||||
| The format of the pattern is as follows: | ||||
| .Bd -ragged -offset indent | ||||
| .Ar nick Ns Op Ar !user@host | ||||
| .Op Ar command | ||||
| .Op Ar channel | ||||
| .Ed | ||||
| . | ||||
| .It Fl j Ar join , Cm join = Ar join | ||||
| Join the comma-separated list of channels | ||||
| .Ar join . | ||||
| @ -309,6 +326,11 @@ Type | ||||
| .Ic q | ||||
| to return to | ||||
| .Nm . | ||||
| .It Ic /ignore Op Ar pattern | ||||
| List message filtering patterns | ||||
| or temporarily add a pattern. | ||||
| To permanently add a pattern, use | ||||
| .Fl i . | ||||
| .It Ic /move Oo Ar name Oc Ar num | ||||
| Move named window to number. | ||||
| .It Ic /open Op Ar count | ||||
| @ -320,6 +342,8 @@ Open the most recent URL from | ||||
| .Ar nick | ||||
| or matching | ||||
| .Ar substring . | ||||
| .It Ic /unignore Ar pattern | ||||
| Temporarily remove a message filtering pattern. | ||||
| .It Ic /window Ar name | ||||
| Switch to window by name. | ||||
| .It Ic /window Ar num , Ic / Ns Ar num | ||||
|  | ||||
							
								
								
									
										4
									
								
								chat.c
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								chat.c
									
									
									
									
									
								
							| @ -127,7 +127,7 @@ int main(int argc, char *argv[]) { | ||||
| 	const char *user = NULL; | ||||
| 	const char *real = NULL; | ||||
| 
 | ||||
| 	const char *Opts = "!C:H:N:O:RS:a:c:eg:h:j:k:ln:p:r:s:u:vw:"; | ||||
| 	const char *Opts = "!C:H:N:O:RS:a:c:eg:h:i:j:k:ln:p:r:s:u:vw:"; | ||||
| 	const struct option LongOpts[] = { | ||||
| 		{ "insecure", no_argument, NULL, '!' }, | ||||
| 		{ "copy", required_argument, NULL, 'C' }, | ||||
| @ -140,6 +140,7 @@ int main(int argc, char *argv[]) { | ||||
| 		{ "cert", required_argument, NULL, 'c' }, | ||||
| 		{ "sasl-external", no_argument, NULL, 'e' }, | ||||
| 		{ "host", required_argument, NULL, 'h' }, | ||||
| 		{ "ignore", required_argument, NULL, 'i' }, | ||||
| 		{ "join", required_argument, NULL, 'j' }, | ||||
| 		{ "priv", required_argument, NULL, 'k' }, | ||||
| 		{ "log", no_argument, NULL, 'l' }, | ||||
| @ -168,6 +169,7 @@ int main(int argc, char *argv[]) { | ||||
| 			break; case 'e': sasl = true; | ||||
| 			break; case 'g': genCert(optarg); | ||||
| 			break; case 'h': host = optarg; | ||||
| 			break; case 'i': ignoreAdd(optarg); | ||||
| 			break; case 'j': self.join = optarg; | ||||
| 			break; case 'k': priv = optarg; | ||||
| 			break; case 'l': logEnable = true; | ||||
|  | ||||
							
								
								
									
										11
									
								
								chat.h
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								chat.h
									
									
									
									
									
								
							| @ -200,7 +200,7 @@ const char *commandIsNotice(uint id, const char *input); | ||||
| const char *commandIsAction(uint id, const char *input); | ||||
| void commandCompleteAdd(void); | ||||
| 
 | ||||
| enum Heat { Cold, Warm, Hot }; | ||||
| enum Heat { Ice, Cold, Warm, Hot }; | ||||
| extern struct Util uiNotifyUtil; | ||||
| void uiInit(void); | ||||
| void uiShow(void); | ||||
| @ -261,6 +261,15 @@ void urlOpenCount(uint id, uint count); | ||||
| void urlOpenMatch(uint id, const char *str); | ||||
| void urlCopyMatch(uint id, const char *str); | ||||
| 
 | ||||
| enum { IgnoreCap = 256 }; | ||||
| extern struct Ignore { | ||||
| 	size_t len; | ||||
| 	char *patterns[IgnoreCap]; | ||||
| } ignore; | ||||
| const char *ignoreAdd(const char *pattern); | ||||
| bool ignoreRemove(const char *pattern); | ||||
| enum Heat ignoreCheck(enum Heat heat, const struct Message *msg); | ||||
| 
 | ||||
| extern bool logEnable; | ||||
| void logFormat(uint id, const time_t *time, const char *format, ...) | ||||
| 	__attribute__((format(printf, 3, 4))); | ||||
|  | ||||
							
								
								
									
										31
									
								
								command.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								command.c
									
									
									
									
									
								
							| @ -348,6 +348,35 @@ static void commandCopy(uint id, char *params) { | ||||
| 	urlCopyMatch(id, params); | ||||
| } | ||||
| 
 | ||||
| static void commandIgnore(uint id, char *params) { | ||||
| 	if (params) { | ||||
| 		const char *pattern = ignoreAdd(params); | ||||
| 		uiFormat( | ||||
| 			id, Cold, NULL, "Ignoring \3%02d%s\3", | ||||
| 			Brown, pattern | ||||
| 		); | ||||
| 	} else { | ||||
| 		for (size_t i = 0; i < ignore.len; ++i) { | ||||
| 			uiFormat( | ||||
| 				Network, Warm, NULL, "Ignoring \3%02d%s\3", | ||||
| 				Brown, ignore.patterns[i] | ||||
| 			); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void commandUnignore(uint id, char *params) { | ||||
| 	if (!params) return; | ||||
| 	if (ignoreRemove(params)) { | ||||
| 		uiFormat( | ||||
| 			id, Cold, NULL, "No longer ignoring \3%02d%s\3", | ||||
| 			Brown, params | ||||
| 		); | ||||
| 	} else { | ||||
| 		uiFormat(id, Cold, NULL, "Not ignoring \3%02d%s\3", Brown, params); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void commandExec(uint id, char *params) { | ||||
| 	execID = id; | ||||
| 
 | ||||
| @ -404,6 +433,7 @@ static const struct Handler { | ||||
| 	{ "/except", commandExcept, 0 }, | ||||
| 	{ "/exec", commandExec, Multiline | Restricted }, | ||||
| 	{ "/help", commandHelp, 0 }, | ||||
| 	{ "/ignore", commandIgnore, 0 }, | ||||
| 	{ "/invex", commandInvex, 0 }, | ||||
| 	{ "/invite", commandInvite, 0 }, | ||||
| 	{ "/join", commandJoin, Restricted }, | ||||
| @ -428,6 +458,7 @@ static const struct Handler { | ||||
| 	{ "/topic", commandTopic, 0 }, | ||||
| 	{ "/unban", commandUnban, 0 }, | ||||
| 	{ "/unexcept", commandUnexcept, 0 }, | ||||
| 	{ "/unignore", commandUnignore, 0 }, | ||||
| 	{ "/uninvex", commandUninvex, 0 }, | ||||
| 	{ "/voice", commandVoice, 0 }, | ||||
| 	{ "/whois", commandWhois, 0 }, | ||||
|  | ||||
							
								
								
									
										16
									
								
								handle.c
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								handle.c
									
									
									
									
									
								
							| @ -305,7 +305,7 @@ static void handleJoin(struct Message *msg) { | ||||
| 		msg->params[2] = NULL; | ||||
| 	} | ||||
| 	uiFormat( | ||||
| 		id, Cold, tagTime(msg), | ||||
| 		id, ignoreCheck(Cold, msg), tagTime(msg), | ||||
| 		"\3%02d%s\3\t%s%s%sarrives in \3%02d%s\3", | ||||
| 		hash(msg->user), msg->nick, | ||||
| 		(msg->params[2] ? "(" : ""), | ||||
| @ -337,7 +337,7 @@ static void handlePart(struct Message *msg) { | ||||
| 	completeRemove(id, msg->nick); | ||||
| 	urlScan(id, msg->nick, msg->params[1]); | ||||
| 	uiFormat( | ||||
| 		id, Cold, tagTime(msg), | ||||
| 		id, ignoreCheck(Cold, msg), tagTime(msg), | ||||
| 		"\3%02d%s\3\tleaves \3%02d%s\3%s%s", | ||||
| 		hash(msg->user), msg->nick, hash(msg->params[0]), msg->params[0], | ||||
| 		(msg->params[1] ? ": " : ""), | ||||
| @ -388,7 +388,7 @@ static void handleNick(struct Message *msg) { | ||||
| 			set(&idNames[id], msg->params[0]); | ||||
| 		} | ||||
| 		uiFormat( | ||||
| 			id, Cold, tagTime(msg), | ||||
| 			id, ignoreCheck(Cold, msg), tagTime(msg), | ||||
| 			"\3%02d%s\3\tis now known as \3%02d%s\3", | ||||
| 			hash(msg->user), msg->nick, hash(msg->user), msg->params[0] | ||||
| 		); | ||||
| @ -406,7 +406,7 @@ static void handleQuit(struct Message *msg) { | ||||
| 	for (uint id; (id = completeID(msg->nick));) { | ||||
| 		urlScan(id, msg->nick, msg->params[0]); | ||||
| 		uiFormat( | ||||
| 			id, Cold, tagTime(msg), | ||||
| 			id, ignoreCheck(Cold, msg), tagTime(msg), | ||||
| 			"\3%02d%s\3\tleaves%s%s", | ||||
| 			hash(msg->user), msg->nick, | ||||
| 			(msg->params[0] ? ": " : ""), | ||||
| @ -427,7 +427,7 @@ static void handleInvite(struct Message *msg) { | ||||
| 	require(msg, true, 2); | ||||
| 	if (!strcmp(msg->params[0], self.nick)) { | ||||
| 		uiFormat( | ||||
| 			Network, Hot, tagTime(msg), | ||||
| 			Network, ignoreCheck(Hot, msg), tagTime(msg), | ||||
| 			"\3%02d%s\3\tinvites you to \3%02d%s\3", | ||||
| 			hash(msg->user), msg->nick, hash(msg->params[1]), msg->params[1] | ||||
| 		); | ||||
| @ -1103,7 +1103,7 @@ static void handlePrivmsg(struct Message *msg) { | ||||
| 			logFormat(id, tagTime(msg), "-%s- %s", msg->nick, msg->params[1]); | ||||
| 		} | ||||
| 		uiFormat( | ||||
| 			id, Warm, tagTime(msg), | ||||
| 			id, ignoreCheck(Warm, msg), tagTime(msg), | ||||
| 			"\3%d-%s-\3%d\t%s", | ||||
| 			hash(msg->user), msg->nick, LightGray, msg->params[1] | ||||
| 		); | ||||
| @ -1111,7 +1111,7 @@ static void handlePrivmsg(struct Message *msg) { | ||||
| 		logFormat(id, tagTime(msg), "* %s %s", msg->nick, msg->params[1]); | ||||
| 		const char *mentions = colorMentions(id, msg); | ||||
| 		uiFormat( | ||||
| 			id, (mention || query ? Hot : Warm), tagTime(msg), | ||||
| 			id, ignoreCheck((mention || query ? Hot : Warm), msg), tagTime(msg), | ||||
| 			"%s\35\3%d* %s\17\35\t%s%s", | ||||
| 			(mention ? "\26" : ""), hash(msg->user), msg->nick, | ||||
| 			mentions, msg->params[1] | ||||
| @ -1120,7 +1120,7 @@ static void handlePrivmsg(struct Message *msg) { | ||||
| 		logFormat(id, tagTime(msg), "<%s> %s", msg->nick, msg->params[1]); | ||||
| 		const char *mentions = colorMentions(id, msg); | ||||
| 		uiFormat( | ||||
| 			id, (mention || query ? Hot : Warm), tagTime(msg), | ||||
| 			id, ignoreCheck((mention || query ? Hot : Warm), msg), tagTime(msg), | ||||
| 			"%s\3%d<%s>\17\t%s%s", | ||||
| 			(mention ? "\26" : ""), hash(msg->user), msg->nick, | ||||
| 			mentions, msg->params[1] | ||||
|  | ||||
							
								
								
									
										73
									
								
								ignore.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								ignore.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| /* Copyright (C) 2020  C. McEnroe <june@causal.agency>
 | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <err.h> | ||||
| #include <fnmatch.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sysexits.h> | ||||
| 
 | ||||
| #include "chat.h" | ||||
| 
 | ||||
| struct Ignore ignore; | ||||
| 
 | ||||
| const char *ignoreAdd(const char *pattern) { | ||||
| 	if (ignore.len == IgnoreCap) errx(EX_CONFIG, "ignore limit exceeded"); | ||||
| 	uint ex = 0, sp = 0; | ||||
| 	for (const char *ch = pattern; *ch; ++ch) { | ||||
| 		if (*ch == '!') ex++; | ||||
| 		if (*ch == ' ') sp++; | ||||
| 	} | ||||
| 	char **dest = &ignore.patterns[ignore.len++]; | ||||
| 	if (!ex && !sp) { | ||||
| 		asprintf(dest, "%s!*@* * *", pattern); | ||||
| 	} else if (sp < 1) { | ||||
| 		asprintf(dest, "%s * *", pattern); | ||||
| 	} else if (sp < 2) { | ||||
| 		asprintf(dest, "%s *", pattern); | ||||
| 	} else { | ||||
| 		*dest = strdup(pattern); | ||||
| 	} | ||||
| 	if (!*dest) err(EX_OSERR, "strdup"); | ||||
| 	return *dest; | ||||
| } | ||||
| 
 | ||||
| bool ignoreRemove(const char *pattern) { | ||||
| 	bool found = false; | ||||
| 	for (size_t i = 0; i < ignore.len; ++i) { | ||||
| 		if (strcasecmp(ignore.patterns[i], pattern)) continue; | ||||
| 		free(ignore.patterns[i]); | ||||
| 		ignore.patterns[i] = ignore.patterns[--ignore.len]; | ||||
| 		found = true; | ||||
| 	} | ||||
| 	return found; | ||||
| } | ||||
| 
 | ||||
| enum Heat ignoreCheck(enum Heat heat, const struct Message *msg) { | ||||
| 	char match[512]; | ||||
| 	snprintf( | ||||
| 		match, sizeof(match), "%s!%s@%s %s %s", | ||||
| 		msg->nick, msg->user, msg->host, msg->cmd, | ||||
| 		(msg->params[0] ? msg->params[0] : "") | ||||
| 	); | ||||
| 	for (size_t i = 0; i < ignore.len; ++i) { | ||||
| 		if (fnmatch(ignore.patterns[i], match, FNM_CASEFOLD)) continue; | ||||
| 		return Ice; | ||||
| 	} | ||||
| 	return heat; | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user