@@ -34,6 +34,8 @@ log { target = "#services-notify"; bot = "OperServ"; other = "notify/..."; }
3434
3535#include " module.h"
3636
37+ #include < memory>
38+
3739
3840/* Dataset for each Notify mask (entry) */
3941struct NotifyEntry : Serializable
@@ -907,7 +909,15 @@ class OSNotify : public Module
907909 NotifyEntryType notifyentry_type;
908910 CommandOSNotify commandosnotify;
909911 BotInfo *OperServ;
910- std::vector<Anope::string> exclude_masks;
912+
913+ struct ExcludeMask final
914+ {
915+ Anope::string mask;
916+ bool is_regex = false ;
917+ std::unique_ptr<Regex> regex;
918+ };
919+
920+ std::vector<ExcludeMask> exclude_masks;
911921
912922 const Anope::string BuildNUHR (const User *u)
913923 {
@@ -922,11 +932,24 @@ class OSNotify : public Module
922932 if (!u || exclude_masks.empty ())
923933 return false ;
924934
925- for (const auto &mask : exclude_masks)
935+ for (const auto &ex : exclude_masks)
926936 {
937+ const auto &mask = ex.mask ;
927938 if (mask.empty ())
928939 continue ;
929940
941+ if (ex.is_regex )
942+ {
943+ if (!ex.regex )
944+ continue ;
945+
946+ const Anope::string uh = u->GetIdent () + ' @' + u->host ;
947+ const Anope::string nuhr = u->nick + ' !' + uh + ' #' + u->realname ;
948+ if (ex.regex ->Matches (uh) || ex.regex ->Matches (nuhr))
949+ return true ;
950+ continue ;
951+ }
952+
930953 /* If it's just a nick glob, match against the nick directly. */
931954 if (mask.find_first_of (" !@#" ) == Anope::string::npos && !(mask.length () >= 2 && mask[0 ] == ' /' && mask[mask.length () - 1 ] == ' /' ))
932955 {
@@ -1047,10 +1070,11 @@ class OSNotify : public Module
10471070 exclude_masks.clear ();
10481071 const auto &modconf = conf.GetModule (this );
10491072
1073+ std::vector<Anope::string> raw_excludes;
10501074 /* Space-separated list of masks to ignore (same matching rules as NOTIFY). */
10511075 {
10521076 spacesepstream sep (modconf.Get <const Anope::string>(" exclude" , " " ));
1053- sep.GetTokens (exclude_masks );
1077+ sep.GetTokens (raw_excludes );
10541078 }
10551079
10561080 /* Also support multiple exclude blocks: exclude { mask = "..." } */
@@ -1059,7 +1083,47 @@ class OSNotify : public Module
10591083 const auto &blk = modconf.GetBlock (" exclude" , i);
10601084 const auto &mask = blk.Get <const Anope::string>(" mask" , " " );
10611085 if (!mask.empty ())
1062- exclude_masks.push_back (mask);
1086+ raw_excludes.push_back (mask);
1087+ }
1088+
1089+ const Anope::string regexengine = Config->GetBlock (" options" ).Get <Anope::string>(" regexengine" );
1090+ ServiceReference<RegexProvider> provider (" Regex" , regexengine);
1091+
1092+ for (auto mask : raw_excludes)
1093+ {
1094+ mask.trim ();
1095+ if (mask.empty ())
1096+ continue ;
1097+
1098+ ExcludeMask ex;
1099+ ex.mask = mask;
1100+ ex.is_regex = (mask.length () >= 2 && mask[0 ] == ' /' && mask[mask.length () - 1 ] == ' /' );
1101+
1102+ if (ex.is_regex )
1103+ {
1104+ if (regexengine.empty ())
1105+ {
1106+ Log (LOG_NORMAL, " notify" ) << " NOTIFY: exclude mask " << mask << " ignored (regexengine is not configured)" ;
1107+ }
1108+ else if (!provider)
1109+ {
1110+ Log (LOG_NORMAL, " notify" ) << " NOTIFY: exclude mask " << mask << " ignored (unable to find regex engine " << regexengine << " )" ;
1111+ }
1112+ else
1113+ {
1114+ try
1115+ {
1116+ Anope::string stripped = mask.substr (1 , mask.length () - 2 );
1117+ ex.regex .reset (provider->Compile (stripped));
1118+ }
1119+ catch (const RegexException &exn)
1120+ {
1121+ Log (LOG_NORMAL, " notify" ) << " NOTIFY: exclude mask " << mask << " ignored (" << exn.GetReason () << " )" ;
1122+ }
1123+ }
1124+ }
1125+
1126+ exclude_masks.push_back (std::move (ex));
10631127 }
10641128 }
10651129
@@ -1299,4 +1363,4 @@ class OSNotify : public Module
12991363 }
13001364};
13011365
1302- MODULE_INIT (OSNotify)
1366+ MODULE_INIT (OSNotify)
0 commit comments