--- m_block_masshighlight_orig.c	2017-11-19 14:54:34.561310560 +0100
+++ m_block_masshighlight.c	2017-11-19 15:08:28.054262904 +0100
@@ -4,6 +4,9 @@
 // Config block
 #define MYCONF "block_masshighlight"
 
+// my channel mode
+#define IsAcceptingHilights(chptr)    (chptr->mode.extmode & EXTCMODE_HILIGHT)
+
 // Dem macros yo
 #define IsMDErr(x, y, z) \
 	do { \
@@ -21,11 +24,13 @@
 int masshighlight_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
 void masshighlight_md_free(ModData *md);
 char *masshighlight_hook_prechanmsg(aClient *sptr, aChannel *chptr, char *text, int notice);
+int extcmode_requireowner(aClient *cptr, aChannel *chptr, char mode, char *para, int checkt, int what);
 
 // Muh globals
 extern MODVAR int spamf_ugly_vchanoverride; // For viruschan shit =]
 static ModuleInfo *massHLMI = NULL; // Store ModuleInfo so we can use it to check for errors in MOD_LOAD
 ModDataInfo *massHLMDI; // To store some shit with the channel ;]
+Cmode_t EXTCMODE_HILIGHT; // store my channel mode
 
 struct {
 	unsigned short int maxnicks; // Maxnicks, unsigned cuz can't be negative anyways lol
@@ -37,6 +42,9 @@
 	unsigned short int banident; // Whether to ban ident@host or simply *@host
 	unsigned short int multiline; // Check over multiple lines or just the one ;]
 	unsigned short int allow_authed; // Allow identified users to bypass this shit
+	unsigned int allow_chmode; // Specify lowest channel privilege to bypass the limit
+	unsigned short int percent; // How many characters in a message recognized as a nickname is enough for the message to be rejected
+	unsigned short int show_message_content; // Display the suspicious message to operators
 
 	// These are just for setting to 0 or 1 to see if we got em config directives ;]
 	unsigned short int got_maxnicks;
@@ -48,12 +56,15 @@
 	unsigned short int got_banident;
 	unsigned short int got_multiline;
 	unsigned short int got_allow_authed;
+	unsigned short int got_allow_chmode;
+	unsigned short int got_percent;
+	unsigned short int got_show_message_content;
 } muhcfg;
 
 // Dat dere module header
 ModuleHeader MOD_HEADER(m_block_masshighlight) = {
 	"m_block_masshighlight", // Module name
-	"$Id: v1.03 2017/09/13 Gottem$", // Version
+	"$Id: v1.04 2017/11/19 Gottem&k4be$", // Version
 	"Prevent mass highlights network-wide", // Description
 	"3.2-b8-1", // Modversion, not sure wat do
 	NULL
@@ -71,6 +82,14 @@
 // Initialisation routine (register hooks, commands and modes or create structs etc)
 MOD_INIT(m_block_masshighlight) {
 	massHLMI = modinfo;
+	CmodeInfo req;
+
+	/* Channel mode */
+	memset(&req, 0, sizeof(req));
+	req.paracount = 0;
+	req.flag = 'H';
+	req.is_ok = extcmode_requireowner;
+	CmodeAdd(modinfo->handle, req, &EXTCMODE_HILIGHT);
 
 	// Request moddata for storing the last counter etc
 	ModDataInfo mreq;
@@ -117,11 +136,7 @@
 
 	if(chptr) { // Sanity cheqq
 		if((lp = find_membership_link(sptr->user->channel, chptr))) {
-			#ifdef PREFIX_AQ
-				if(lp->flags & (CHFL_CHANOWNER|CHFL_CHANPROT))
-			#else
-				if(lp->flags & CHFL_CHANOP)
-			#endif
+			if(lp->flags & muhcfg.allow_chmode)
 				return 1;
 		}
 	}
@@ -129,6 +144,16 @@
 	return 0; // No valid channel/membership or doesn't have enough axx lol
 }
 
+// testing for owner status on channel
+
+int extcmode_requireowner(aClient *cptr, aChannel *chptr, char mode, char *para, int checkt, int what) {
+	if (IsPerson(cptr) && is_chanowner(cptr, chptr))
+		return EX_ALLOW;
+	if (checkt == EXCHK_ACCESS_ERR)
+		sendto_one(cptr, err_str(ERR_CHANOWNPRIVNEEDED), me.name, cptr->name, chptr->chname);
+	return EX_DENY;
+}
+
 // Not using place_host_ban() cuz I need more control over the mask to ban ;];;];]
 void doXLine(char flag, aClient *sptr) {
 	// Double check for sptr existing, cuz inb4segfault
@@ -198,6 +223,30 @@
 				config_error("%s:%i: %s::%s must be one of: drop, notice, gline, zline, shun, tempshun, kill, viruschan", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
 				errors++;
 			}
+			
+			if(!errors) muhcfg.action = cep->ce_vardata[0]; // we need this value to be set in posttest
+			continue;
+		}
+		
+		if(!strcmp(cep->ce_varname, "allow_chmode")) {
+			muhcfg.got_allow_chmode = 1;
+			if(!cep->ce_vardata || !strlen(cep->ce_vardata)) {
+				config_error("%s:%i: %s::%s must be either non-empty or not specified at all", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
+				errors++; // Increment err0r count fam
+			}
+
+			if(strcmp(cep->ce_vardata, "v") && strcmp(cep->ce_vardata, "h") && strcmp(cep->ce_vardata, "o")
+	#ifdef PREFIX_AQ
+			 	&& strcmp(cep->ce_vardata, "a") && strcmp(cep->ce_vardata, "q")
+	#endif
+			 ) {
+				config_error("%s:%i: %s::%s must be one of: v, h, o"
+	#ifdef PREFIX_AQ
+				", a, q"
+	#endif
+				, cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
+				errors++;
+			}
 			continue;
 		}
 
@@ -254,6 +303,31 @@
 			continue;
 		}
 
+		if(!strcmp(cep->ce_varname, "percent")) {
+			muhcfg.got_maxnicks = 1;
+			// Should be an integer yo
+			if(!cep->ce_vardata) {
+				config_error("%s:%i: %s::%s must be an integer between 1 and 100", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
+				errors++; // Increment err0r count fam
+				continue;
+			}
+			for(i = 0; cep->ce_vardata[i]; i++) {
+				if(!isdigit(cep->ce_vardata[i])) {
+					config_error("%s:%i: %s::%s must be an integer between 1 and 100", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
+					errors++; // Increment err0r count fam
+					break;
+				}
+			}
+			if(!errors) {
+				int tmp = atoi(cep->ce_vardata);
+				if(tmp <= 1 || tmp > 100) {
+					config_error("%s:%i: %s::%s must be an integer between 1 and 100", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
+					errors++; // Increment err0r count fam
+				}
+			}
+			continue;
+		}
+
 		if(!strcmp(cep->ce_varname, "snotice")) {
 			muhcfg.got_snotice = 1;
 			if(!cep->ce_vardata || (strcmp(cep->ce_vardata, "0") && strcmp(cep->ce_vardata, "1"))) {
@@ -281,6 +355,15 @@
 			continue;
 		}
 
+		if(!strcmp(cep->ce_varname, "show_message_content")) {
+			muhcfg.got_show_message_content = 1;
+			if(!cep->ce_vardata || (strcmp(cep->ce_vardata, "0") && strcmp(cep->ce_vardata, "1"))) {
+				config_error("%s:%i: %s::%s must be either 0 or 1 fam", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
+				errors++; // Increment err0r count fam
+			}
+			continue;
+		}
+
 		if(!strcmp(cep->ce_varname, "allow_authed")) {
 			muhcfg.got_allow_authed = 1;
 			if(!cep->ce_vardata || (strcmp(cep->ce_vardata, "0") && strcmp(cep->ce_vardata, "1"))) {
@@ -310,7 +393,11 @@
 		muhcfg.action = 'g'; // Gline em by default lol
 		config_warn("[block_masshighlight] Unable to find 'action' directive, defaulting to: gline", muhcfg.action);
 	}
-
+	
+	if(!muhcfg.got_allow_chmode) {
+		muhcfg.allow_chmode = 0;
+	}
+	
 	if(strchr("gzs", muhcfg.action) && !muhcfg.got_duration) { // Duration is only required for X:Lines =]
 		muhcfg.duration = 604800; // 7 days yo
 		config_warn("[block_masshighlight] Unable to find 'duration' directive, defaulting to: %li seconds", muhcfg.duration);
@@ -333,6 +420,12 @@
 
 	if(!muhcfg.got_allow_authed)
 		muhcfg.allow_authed = 0; // Default to n0 fam
+		
+	if(!muhcfg.got_percent)
+		muhcfg.percent = 1; // 1% - max sensitivity
+		
+	if(!muhcfg.got_show_message_content)
+		muhcfg.show_message_content = 1; // defaulting to showing the message
 
 	*errs = errors;
 	return errors ? -1 : 1;
@@ -359,9 +452,18 @@
 		// Do we even have a valid name l0l?
 		if(!cep->ce_varname)
 			continue; // Next iteration imo tbh
-
-		if(!strcmp(cep->ce_varname, "action")) {
-			muhcfg.action = cep->ce_vardata[0];
+		
+		if(!strcmp(cep->ce_varname, "allow_chmode")) {
+			muhcfg.allow_chmode = 0;
+			switch(cep->ce_vardata[0]){
+				case 'v': muhcfg.allow_chmode |= CHFL_VOICE;
+				case 'h': muhcfg.allow_chmode |= CHFL_HALFOP;
+				case 'o': muhcfg.allow_chmode |= CHFL_CHANOP;
+	#ifdef PREFIX_AQ
+				case 'a': muhcfg.allow_chmode |= CHFL_CHANPROT;
+				case 'q': muhcfg.allow_chmode |= CHFL_CHANOWNER;
+	#endif
+			}
 			continue;
 		}
 
@@ -389,6 +491,11 @@
 			continue;
 		}
 
+		if(!strcmp(cep->ce_varname, "percent")) {
+			muhcfg.percent = atoi(cep->ce_vardata);
+			continue;
+		}
+
 		if(!strcmp(cep->ce_varname, "snotice")) {
 			muhcfg.snotice = atoi(cep->ce_vardata);
 			continue;
@@ -408,6 +515,11 @@
 			muhcfg.allow_authed = atoi(cep->ce_vardata);
 			continue;
 		}
+
+		if(!strcmp(cep->ce_varname, "show_message_content")) {
+			muhcfg.show_message_content = atoi(cep->ce_vardata);
+			continue;
+		}
 	}
 
 	return 1; // We good
@@ -438,6 +550,12 @@
 	char joinbuf[CHANNELLEN + 4]; // Also for viruschan, need to make room for "0,#viruschan" etc ;];]
 	char *vcparv[3]; // Arguments for viruschan JOIN
 	int bypassem = (muhcfg.allow_authed && IsPerson(sptr) && IsLoggedIn(sptr)); // If config var is tr00 and user es logged in
+	char already_detected[1024] = ""; // just append there all the nicks
+	int tmphlcount = 0; // store hilight count for further processing
+	int tmpcharcount = 0, msglength = 0; // for calculating percents
+	char mesgbuffer[1024]; // show the message to ircops if set to do so
+	
+	if(IsAcceptingHilights(chptr)) return text; // if channelmode is set, allow without further checking
 
 	// Some sanity + privilege checks ;];] (+a, +q, U:Lines and opers are exempt from this shit)
 	if(text && IsPerson(sptr) && MyClient(sptr) && !bypassem && !is_chanowner_prot(sptr, chptr) && !IsULine(sptr) && !IsOper(sptr)) {
@@ -450,24 +568,33 @@
 		// In case someone tries some funny business =]
 		if(!(cleantext = (char *)StripColors(text)) || !(cleantext = (char *)StripControlCodes(cleantext)))
 			return text;
-
+			
 		for(werd = strtoken(&p, cleantext, muhcfg.delimiters); werd; werd = strtoken(&p, NULL, muhcfg.delimiters)) { // Split that shit
+			msglength += strlen(werd); // we do not count separators, otherwise the percents would get strangely low
 			if((acptr = find_person(werd, NULL)) && (find_membership_link(acptr->user->channel, chptr))) { // User mentioned another user in this channel
-				hl += 1; // So increment counter
-				clearem = 0; // And flip the clear flag to n0
-
-				if(hl > muhcfg.maxnicks) { // No need to tokenise more if we're already over threshold
-					blockem = 1; // Flip flag to blockin' dat shit
-					break;
+				if(!strstr(already_detected, werd)){ // do not count the same nickname appended multiple times to a single message
+					strncat(already_detected, werd, 1023);
+					tmphlcount++;
+					tmpcharcount += strlen(werd);
 				}
 			}
 		}
+		
+		if(100*tmpcharcount / msglength > muhcfg.percent){
+			hl += tmphlcount;
+			clearem = 0;
+			if(hl > muhcfg.maxnicks) { // No need to tokenise more if we're already over threshold
+				blockem = 1; // Flip flag to blockin' dat shit
+			}
+		}
 
 		if(!muhcfg.multiline && !clearem) // If single line mode and found a nick
 			clearem = 1; // Need to clear that shit always lol
 
 		moddata_membership(lp2, massHLMDI).i = (clearem ? 0 : hl); // Actually set the counter =]
 		if(blockem) { // Need to bl0ck?
+			if(muhcfg.show_message_content)
+				strncpy(mesgbuffer, text, 1023); // Save the message for further snomask_global
 			text = NULL; // NULL makes Unreal drop the message entirely ;3
 			switch(muhcfg.action) {
 				case 'd': // Drop silently
@@ -533,6 +660,8 @@
 				default:
 					break;
 			}
+			if(muhcfg.show_message_content && mesgbuffer[0])
+				sendto_snomask_global(SNO_EYES, "*** The message was: %s", mesgbuffer);
 		}
 	}
 
