66from typing import Any
77
88from .naming import normalize_category_path
9+ from .records import decode_address_fields
910
1011
1112def _load_rules (config : dict [str , Any ]) -> list [dict [str , Any ]]:
@@ -16,10 +17,7 @@ def _match_rule(rule: dict[str, Any], record: dict[str, Any]) -> bool:
1617 match = rule .get ("match" , {})
1718 sender = (record .get ("sender" ) or "" ).lower ()
1819 subject = (record .get ("subject" ) or "" ).lower ()
19- try :
20- recipients = json .loads (record .get ("recipients_json" ) or "[]" )
21- except Exception :
22- recipients = []
20+ recipients = record ["recipients" ]
2321 all_addrs = [sender ] + [r .lower () for r in recipients ]
2422
2523 for domain in match .get ("from_domain" , []):
@@ -112,7 +110,7 @@ def _build_thread_input(
112110) -> dict [str , Any ]:
113111 """Aggregate thread messages into a single record suitable for rule matching."""
114112 if not messages :
115- return {"thread_key" : thread_key , "subject" : "" , "sender" : "" , "recipients_json " : "[]" }
113+ return {"thread_key" : thread_key , "subject" : "" , "sender" : "" , "recipients " : [] }
116114
117115 subject = ""
118116 for m in messages :
@@ -128,17 +126,14 @@ def _build_thread_input(
128126 break
129127
130128 # Collect all unique participant addresses across every message in the thread.
131- # Putting all of them into recipients_json ensures _match_rule's all_addrs covers
132- # every sender domain, not just the first message's sender.
129+ # Putting all non-sender participants into recipients ensures _match_rule's
130+ # all_addrs covers every sender domain, not just the first message's sender.
133131 all_addrs : set [str ] = set ()
134132 for m in messages :
135133 if m .get ("sender" ):
136134 all_addrs .add (m ["sender" ].lower ())
137- try :
138- for r in json .loads (m .get ("recipients_json" ) or "[]" ):
139- all_addrs .add (r .lower ())
140- except Exception :
141- pass
135+ for r in m ["recipients" ]:
136+ all_addrs .add (r .lower ())
142137
143138 sender = messages [0 ].get ("sender" ) or ""
144139 other_addrs = sorted (all_addrs - {sender .lower ()})
@@ -151,7 +146,7 @@ def _build_thread_input(
151146 "thread_key" : thread_key ,
152147 "subject" : subject ,
153148 "sender" : sender ,
154- "recipients_json " : json . dumps ( other_addrs ) ,
149+ "recipients " : other_addrs ,
155150 "_participants" : sorted (all_addrs ),
156151 "_excerpts" : excerpts ,
157152 "_message_count" : len (messages ),
@@ -330,8 +325,8 @@ def _run_rule_classification_thread(
330325 if account_id is not None :
331326 msg_rows = conn .execute (
332327 """
333- SELECT id, subject, sender, recipients_json, date_utc, body_text ,
334- thread_key, account_id
328+ SELECT id, subject, sender, recipients_json, cc_json, bcc_json ,
329+ date_utc, body_text, thread_key, account_id
335330 FROM messages
336331 WHERE thread_key = ? AND account_id = ?
337332 ORDER BY date_utc NULLS LAST, id
@@ -341,8 +336,8 @@ def _run_rule_classification_thread(
341336 else :
342337 msg_rows = conn .execute (
343338 """
344- SELECT id, subject, sender, recipients_json, date_utc, body_text ,
345- thread_key, account_id
339+ SELECT id, subject, sender, recipients_json, cc_json, bcc_json ,
340+ date_utc, body_text, thread_key, account_id
346341 FROM messages
347342 WHERE thread_key = ?
348343 ORDER BY date_utc NULLS LAST, id
@@ -351,10 +346,10 @@ def _run_rule_classification_thread(
351346 ).fetchall ()
352347
353348 cols = [
354- "id" , "subject" , "sender" , "recipients_json" , "date_utc " ,
355- "body_text" , "thread_key" , "account_id" ,
349+ "id" , "subject" , "sender" , "recipients_json" , "cc_json" , "bcc_json " ,
350+ "date_utc" , " body_text" , "thread_key" , "account_id" ,
356351 ]
357- messages = [dict (zip (cols , row )) for row in msg_rows ]
352+ messages = [decode_address_fields ( dict (zip (cols , row ) )) for row in msg_rows ]
358353 if not messages :
359354 continue
360355
@@ -404,7 +399,7 @@ def run_rule_classification(
404399
405400 query = """
406401 SELECT m.id, m.account_id, m.source_id, m.mbox_key, m.message_id, m.thread_key,
407- m.subject, m.sender, m.recipients_json, m.date_utc
402+ m.subject, m.sender, m.recipients_json, m.cc_json, m.bcc_json, m. date_utc
408403 FROM messages m
409404 LEFT JOIN classifications c
410405 ON c.message_db_id = m.id AND c.classifier_type IN ('rule', 'rule_hint')
@@ -418,9 +413,9 @@ def run_rule_classification(
418413 rows = conn .execute (query , params ).fetchall ()
419414 cols = [
420415 "id" , "account_id" , "source_id" , "mbox_key" , "message_id" , "thread_key" ,
421- "subject" , "sender" , "recipients_json" , "date_utc" ,
416+ "subject" , "sender" , "recipients_json" , "cc_json" , "bcc_json" , " date_utc" ,
422417 ]
423- records = [dict (zip (cols , row )) for row in rows ]
418+ records = [decode_address_fields ( dict (zip (cols , row ) )) for row in rows ]
424419
425420 classified = 0
426421 skipped = 0
0 commit comments