diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c
index 71ffb7de09db97d025b0e1e7ded3026f9b105fce..db5f026b1408e19911b7a6d997d68eb140b380cb 100644
--- a/src/sbbs3/mailsrvr.c
+++ b/src/sbbs3/mailsrvr.c
@@ -3230,7 +3230,7 @@ static void smtp_thread(void* arg)
 				stats.msgs_received++;
 
 				/* Twit-listing (sender's name and e-mail addresses) here */
-				SAFEPRINTF(path,"%stwitlist.cfg",scfg.ctrl_dir);
+				twitlist_fname(&scfg, path, sizeof path);
 				if(fexist(path) && (findstr(sender,path) || findstr(sender_addr,path))) {
 					lprintf(LOG_NOTICE,"%04d %s %s !FILTERING TWIT-LISTED SENDER: '%s' <%s> (%lu total)"
 						,socket, client.protocol, client_id, sender, sender_addr, ++stats.msgs_refused);
diff --git a/src/sbbs3/readmsgs.cpp b/src/sbbs3/readmsgs.cpp
index 9090b05fb3a03717e08e69d0411e52aa1ab65b97..1067f6f34c849e8cd932337fb768d61d45f68274 100644
--- a/src/sbbs3/readmsgs.cpp
+++ b/src/sbbs3/readmsgs.cpp
@@ -1189,7 +1189,7 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find)
 					if(!(useron.misc&EXPERT))
 						menu("sysmscan");
 					bputs(text[OperatorPrompt]);
-					strcpy(str,"?ADCEHMQUV");
+					strcpy(str,"?ADCEHMQTUV");
 					if(SYSOP)
 						strcat(str,"SP");
 					switch(getkeys(str,0)) {
@@ -1274,6 +1274,14 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find)
 							if(getstr(str,50,K_LINE))
 								msgtotxt(&smb, &msg, str, /* header: */true, /* mode: */GETMSGTXT_ALL);
 							break;
+						case 'T':	/* Twit-list the sender */
+							domsg = false;
+							if(is_twit(&cfg, msg.from)) {
+								bprintf("\r\n%s is already twit-listed!\r\n", msg.from);
+								break;
+							}
+							list_twit(&cfg, msg.from, timestr(time(NULL)));
+							break;
 						case 'U':   /* User edit */
 							useredit(cfg.sub[subnum]->misc&SUB_NAME
 								? finduserstr(0, USER_NAME, msg.from)
diff --git a/src/sbbs3/sbbsecho.c b/src/sbbs3/sbbsecho.c
index a666207fffb912a3eefb0cb4b0d97fac654a67bb..00a93290d218eaf75fafbf1b2f8f054fb991f4e8 100644
--- a/src/sbbs3/sbbsecho.c
+++ b/src/sbbs3/sbbsecho.c
@@ -6267,8 +6267,7 @@ int main(int argc, char **argv)
 		return -1;
 	}
 
-	SAFEPRINTF(str,"%stwitlist.cfg",scfg.ctrl_dir);
-	twit_list=findstr_list(str);
+	twit_list=list_of_twits(&scfg);
 
 	subject_can = trashcan_list(&scfg,"subject");
 
diff --git a/src/sbbs3/scfglib.h b/src/sbbs3/scfglib.h
index dd7877c732512de5d92c3269de42d89c9ed9596e..348681451e3277e0ceaad5942f68d1d85f9ddc14 100644
--- a/src/sbbs3/scfglib.h
+++ b/src/sbbs3/scfglib.h
@@ -75,6 +75,7 @@ DLLEXPORT BOOL	is_valid_xtrnsec(scfg_t*, int);
 DLLEXPORT BOOL		trashcan(scfg_t* cfg, const char *insearch, const char *name);
 DLLEXPORT char *	trashcan_fname(scfg_t* cfg, const char *name, char* fname, size_t);
 DLLEXPORT str_list_t trashcan_list(scfg_t* cfg, const char* name);
+DLLEXPORT char *	twitlist_fname(scfg_t* cfg, char* fname, size_t);
 
 DLLEXPORT char *	sub_newsgroup_name(scfg_t*, sub_t*, char*, size_t);
 DLLEXPORT char *	sub_area_tag(scfg_t*, sub_t*, char*, size_t);
diff --git a/src/sbbs3/scfglib1.c b/src/sbbs3/scfglib1.c
index 784ebea4b9317a33acd72a8fc664d152d41eee99..b347d0adba4e981c00ab586cb554ab38394f3e2d 100644
--- a/src/sbbs3/scfglib1.c
+++ b/src/sbbs3/scfglib1.c
@@ -903,6 +903,13 @@ str_list_t trashcan_list(scfg_t* cfg, const char* name)
 	return findstr_list(trashcan_fname(cfg, name, fname, sizeof(fname)));
 }
 
+/****************************************************************************/
+char* twitlist_fname(scfg_t* cfg, char* fname, size_t maxlen)
+{
+	safe_snprintf(fname, maxlen, "%stwitlist.cfg", cfg->ctrl_dir);
+	return fname;
+}
+
 char* sub_newsgroup_name(scfg_t* cfg, sub_t* sub, char* str, size_t size)
 {
 	memset(str, 0, size);
diff --git a/src/sbbs3/un_qwk.cpp b/src/sbbs3/un_qwk.cpp
index e051c08c421b1dd9a20a3ac2b6bd436fdcd777b2..a6722ec9da2a3cc836b091fc449f78fd32e83dc1 100644
--- a/src/sbbs3/un_qwk.cpp
+++ b/src/sbbs3/un_qwk.cpp
@@ -136,9 +136,7 @@ bool sbbs_t::unpack_qwk(char *packet,uint hubnum)
 	msg_filters.ip_can = trashcan_list(&cfg,"ip");
 	msg_filters.host_can = trashcan_list(&cfg,"host");
 	msg_filters.subject_can = trashcan_list(&cfg,"subject");
-
-	SAFEPRINTF(fname,"%stwitlist.cfg",cfg.ctrl_dir);
-	msg_filters.twit_list = findstr_list(fname);
+	msg_filters.twit_list = list_of_twits(&cfg);
 
 	for(l=QWK_BLOCK_LEN;l<size;l+=blocks*QWK_BLOCK_LEN) {
 		if(terminated) {
diff --git a/src/sbbs3/un_rep.cpp b/src/sbbs3/un_rep.cpp
index 1ddb6657e0c59981e779e19af4fba70715745a34..00cd5a8bdf4f5c767c7f69b7993ca1e1afcb6e0d 100644
--- a/src/sbbs3/un_rep.cpp
+++ b/src/sbbs3/un_rep.cpp
@@ -173,8 +173,7 @@ bool sbbs_t::unpack_rep(char* repfile)
 	msg_filters.host_can = trashcan_list(&cfg,"host");
 	msg_filters.subject_can = trashcan_list(&cfg,"subject");
 
-	SAFEPRINTF(fname,"%stwitlist.cfg",cfg.ctrl_dir);
-	msg_filters.twit_list = findstr_list(fname);
+	msg_filters.twit_list = list_of_twits(&cfg);
 
 	now=time(NULL);
 	for(l=QWK_BLOCK_LEN;l<size;l+=blocks*QWK_BLOCK_LEN) {
diff --git a/src/sbbs3/userdat.c b/src/sbbs3/userdat.c
index 95d96bee772043699f1af8b2a3fdcbf04b2f5592..ae7a53a04ca5084a0ef4e96bdf720ac812a2a1af 100644
--- a/src/sbbs3/userdat.c
+++ b/src/sbbs3/userdat.c
@@ -4333,3 +4333,29 @@ enum parsed_vpath parse_vpath(scfg_t* cfg, const char* vpath, user_t* user, clie
 
 	return *filename == NULL ? PARSED_VPATH_DIR : PARSED_VPATH_FULL;
 }
+
+BOOL is_twit(scfg_t* cfg, const char* name)
+{
+	char path[MAX_PATH + 1];
+	return findstr(name, twitlist_fname(cfg, path, sizeof path));
+}
+
+/* Add a name to the global twit list */
+BOOL list_twit(scfg_t* cfg, const char* name, const char* comment)
+{
+	char path[MAX_PATH + 1];
+	FILE* fp = fnopen(/* fd: */NULL, twitlist_fname(cfg, path, sizeof path), O_WRONLY | O_APPEND);
+	if(fp == NULL)
+		return FALSE;
+	if(comment != NULL)
+		fprintf(fp, "\n; %s", comment);
+	BOOL result = fprintf(fp, "\n%s\n", name) > 0;
+	fclose(fp);
+	return result;
+}
+
+str_list_t list_of_twits(scfg_t* cfg)
+{
+	char path[MAX_PATH + 1];
+	return findstr_list(twitlist_fname(cfg, path, sizeof path));
+}
diff --git a/src/sbbs3/userdat.h b/src/sbbs3/userdat.h
index 5ecc043c462069dc1f879a9ae4a341192676e1fa..fb1589f4088801ed0ab0785cd348c87bbe2414e2 100644
--- a/src/sbbs3/userdat.h
+++ b/src/sbbs3/userdat.h
@@ -144,6 +144,10 @@ DLLEXPORT BOOL	is_download_free(scfg_t*, int dirnum, user_t*, client_t* client);
 DLLEXPORT BOOL	is_host_exempt(scfg_t*, const char* ip_addr, const char* host_name);
 DLLEXPORT BOOL	filter_ip(scfg_t*, const char* prot, const char* reason, const char* host
 								  ,const char* ip_addr, const char* username, const char* fname);
+DLLEXPORT BOOL	is_twit(scfg_t*, const char* name);
+DLLEXPORT BOOL	list_twit(scfg_t*, const char* name, const char* comment);
+DLLEXPORT str_list_t list_of_twits(scfg_t*);
+
 enum parsed_vpath {
 	PARSED_VPATH_NONE,
 	PARSED_VPATH_ROOT,