import msgpack from django.core.management.base import BaseCommand from django.conf import settings from redis import StrictRedis from core.db.storage import db from core.lib.rules import NotificationRuleData from core.models import NotificationRule from core.util import logs log = logs.get_logger("processing") def process_rules(data): all_rules = NotificationRule.objects.filter(enabled=True, interval=0) for index, index_messages in data.items(): for message in index_messages: for rule in all_rules: # Quicker helper to get the data without spinning # up a NotificationRuleData object parsed_rule = rule.parse() matched = {} # Rule is invalid, this shouldn't happen if "index" not in parsed_rule: continue if "source" not in parsed_rule: continue rule_index = parsed_rule["index"] rule_source = parsed_rule["source"] # if not type(rule_index) == list: # rule_index = [rule_index] # if not type(rule_source) == list: # rule_source = [rule_source] if index not in rule_index: # We don't care about this index, go to the next one continue if message["src"] not in rule_source: # We don't care about this source, go to the next one continue matched["index"] = index matched["source"] = message["src"] rule_field_length = len(parsed_rule.keys()) matched_field_number = 0 for field, value in parsed_rule.items(): # if not type(value) == list: # value = [value] if field == "src": # We already checked this continue if field == "tokens": # Check if tokens are in the rule # We only check if *at least one* token matches for token in value: if "tokens" in message: if token in message["tokens"]: matched_field_number += 1 matched[field] = token # Break out of the token matching loop break # Continue to next field continue if field == "msg": # Allow partial matches for msg for msg in value: if "msg" in message: if msg.lower() in message["msg"].lower(): matched_field_number += 1 matched[field] = msg # Break out of the msg matching loop break # Continue to next field continue if field in message and message[field] in value: # Do exact matches for all other fields matched_field_number += 1 matched[field] = message[field] # Subtract 2, 1 for source and 1 for index if matched_field_number == rule_field_length - 2: meta = {"matched": matched, "total_hits": 1} # Parse the rule, we saved some work above to avoid doing this, # but it makes delivering messages significantly easier as we can # use the same code as for scheduling. rule_data_object = NotificationRuleData(rule.user, rule, db=db) # rule_notify(rule, index, message, meta=meta) rule_data_object.rule_matched_sync( index, message, meta=meta, mode="ondemand" ) class Command(BaseCommand): def handle(self, *args, **options): r = StrictRedis( host=settings.REDIS_HOST, port=settings.REDIS_PORT, password=settings.REDIS_PASSWORD, db=settings.REDIS_DB ) p = r.pubsub() p.psubscribe("messages") for message in p.listen(): if message: if message["channel"] == b"messages": data = message["data"] try: unpacked = msgpack.unpackb(data, raw=False) except TypeError: continue process_rules(unpacked)