"""Phase W - moderation rule sets (the "rules" half of the from-scratch engine). Each rule is (compiled_regex, weight, category). `weight` is the severity score (0..1) a match contributes; the classifier keeps the max weight per category. Design notes: - We match *intent + topic* structures (e.g. "how to build a bomb") rather than cataloguing slurs or explicit terms. That keeps this file non-gratuitous and cuts false positives. - Co-occurrence rules handle cases where each term is benign alone but the combination is the signal (CSAM, group-directed hate). - `normalize()` folds case + common leetspeak so "h@te" / "k1ll" still match. Letter-spacing evasion ("b o m b") is a known gap, left for a later pass to avoid the "the rapist -> therapist" class of false positives that naive de-spacing introduces. """ import re _LEET = str.maketrans({ "4": "a", "@": "a", "3": "e", "1": "i", "0": "o", "$": "s", "5": "s", "7": "t", "!": "i", }) _NON = re.compile(r"[^a-z0-9\s]") _WS = re.compile(r"\s+") def normalize(text: str) -> str: """Lowercase, fold leetspeak, strip punctuation, collapse whitespace.""" t = (text or "").lower().translate(_LEET) t = _NON.sub(" ", t) return _WS.sub(" ", t).strip() def _rx(pattern: str) -> "re.Pattern": return re.compile(pattern) # (pattern, weight, category) - matched against normalized text. _RAW_RULES = [ # ---- self-harm (handled with care; downgraded from block to soft refuse) ---- (r"\b(kill myself|killing myself|end my life|ending my life|commit suicide|" r"take my own life|want to die|wanna die)\b", 0.90, "self_harm"), (r"\b(self harm|selfharm|cut myself|cutting myself|hurt myself)\b", 0.70, "self_harm"), (r"\bhow (to|do i|can i)\b.*\b(kill myself|commit suicide|overdose)\b", 0.95, "self_harm"), # ---- violence toward others ---- (r"\bhow (to|do i|can i)\b.*\b(kill|murder|poison|stab|strangle)\b.*" r"\b(someone|him|her|them|people|person)\b", 0.90, "violence"), (r"\b(get away with|how to commit)\b.*\b(murder|killing|a killing)\b", 0.90, "violence"), (r"\bi (want|am going|plan|intend) to (kill|murder|hurt|attack)\b", 0.80, "violence"), # ---- harassment / direct threats ---- (r"\bkill yourself\b", 0.90, "harassment"), (r"\bi (will|am going to|gonna) (find|hurt|kill|beat|destroy) you\b", 0.85, "harassment"), # ---- weapons / explosives / CBRN ---- (r"\b(build|make|making|construct|assemble|synthesi[sz]e)\b.*" r"\b(bomb|explosive|explosives|pipe bomb|ied|grenade|nerve agent|bioweapon|" r"chemical weapon|dirty bomb)\b", 0.95, "weapons"), (r"\b(sarin|anthrax|ricin|vx nerve|mustard gas|nerve gas)\b", 0.80, "weapons"), (r"\bhow (to|do i)\b.*\b(detonat|enrich uranium|make a bomb|build a bomb)\b", 0.90, "weapons"), # ---- illicit: drug synthesis, malware, fraud ---- (r"\b(make|making|synthesi[sz]e|cook|manufacture)\b.*" r"\b(meth|methamphetamine|cocaine|heroin|fentanyl|mdma|lsd)\b", 0.90, "illicit"), (r"\b(write|create|build|code|make)\b.*" r"\b(malware|ransomware|a virus|spyware|a keylogger|a trojan|a botnet)\b", 0.85, "illicit"), (r"\b(launder money|money laundering|steal credit card|carding|sell drugs)\b", 0.65, "illicit"), # ---- sexual (adult, explicit) ---- (r"\b(porn|pornographic|explicit sex|sexually explicit|nsfw)\b", 0.50, "sexual"), (r"\bwrite (me )?(a |an )?(erotic|explicit sexual)\b", 0.55, "sexual"), ] RULES = [(_rx(p), w, c) for (p, w, c) in _RAW_RULES] # ----- CO-OCCURRENCE RULES ----- # Each side is benign alone; the *combination* of an A-term and a B-term trips it. _MINOR = (r"\b(child|children|kid|kids|minor|minors|underage|preteen|pre teen|" r"toddler|infant|teen|teenage|teenager|" r"(8|9|10|11|12|13|14|15) year old)\b") _SEXUAL = (r"\b(sex|sexual|sexually|naked|nude|nudes|porn|explicit|aroused|" r"molest|rape|fondle|genital|genitals)\b") _GROUP = (r"\b(muslims?|jews?|jewish|christians?|hindus?|buddhists?|arabs?|asians?|" r"latinos?|africans?|immigrants?|gays?|lesbians?|queer|trans|disabled)\b") _HATE_VERB = (r"\b(kill|exterminate|gas|eliminate|genocide|wipe out|should die|" r"deserve to die|are subhuman|are vermin|get rid of)\b") # (category, term_a_regex, term_b_regex, weight) _RAW_COOC = [ ("sexual_minors", _MINOR, _SEXUAL, 1.00), ("hate", _GROUP, _HATE_VERB, 0.85), ] COOCCURRENCE = [(c, _rx(a), _rx(b), w) for (c, a, b, w) in _RAW_COOC]