feat: Implement AI classification and Web UI, refactor to IMAP
This commit is contained in:
134
processing_logic.py
Normal file
134
processing_logic.py
Normal file
@@ -0,0 +1,134 @@
|
||||
import json
|
||||
from imap_client import ImapMailClient
|
||||
from ai_classifier import classify_email
|
||||
|
||||
def load_config(path='config.json'):
|
||||
"""설정 파일을 로드합니다."""
|
||||
print(f"'{path}' 파일에서 설정 로드 중...")
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
def load_rules(path='rules.json'):
|
||||
"""규칙 파일을 로드합니다."""
|
||||
print(f"'{path}' 파일에서 규칙 로드 중...")
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
def check_conditions(email_details, conditions):
|
||||
"""이메일이 주어진 조건을 만족하는지 확인합니다."""
|
||||
for key, value in conditions.items():
|
||||
if key == 'from_contains':
|
||||
if value.lower() not in email_details.get('from', '').lower():
|
||||
return False
|
||||
elif key == 'subject_contains':
|
||||
if value.lower() not in email_details.get('subject', '').lower():
|
||||
return False
|
||||
elif key == 'ai_classification_is':
|
||||
if value != email_details.get('ai_category'):
|
||||
return False
|
||||
# 여기에 'to_contains', 'body_contains' 등 다른 조건을 추가할 수 있습니다.
|
||||
return True
|
||||
|
||||
def process_emails(imap_client, rules_config, gemini_api_key):
|
||||
"""
|
||||
읽지 않은 이메일을 가져와서 규칙에 따라 처리합니다.
|
||||
"""
|
||||
unread_uids = imap_client.fetch_unread_emails()
|
||||
ai_categories = rules_config.get('ai_categories', [])
|
||||
|
||||
for uid in unread_uids:
|
||||
email_details = imap_client.fetch_email(uid)
|
||||
if not email_details:
|
||||
continue
|
||||
|
||||
print(f"\n[처리 시작] 메일 UID: {uid.decode()}, 제목: {email_details['subject']}")
|
||||
|
||||
# AI 분류 실행 (필요한 경우)
|
||||
# AI 규칙이 하나라도 있을 때만 API를 호출하도록 최적화할 수 있음
|
||||
email_details['ai_category'] = None
|
||||
if ai_categories:
|
||||
email_content_for_ai = f"Subject: {email_details['subject']}\n\nBody:\n{email_details['body']}"
|
||||
email_details['ai_category'] = classify_email(gemini_api_key, email_content_for_ai, ai_categories)
|
||||
|
||||
# 규칙 검사
|
||||
matched_rule = None
|
||||
for rule in rules_config.get('rules', []):
|
||||
if check_conditions(email_details, rule.get('conditions', {})):
|
||||
matched_rule = rule
|
||||
break
|
||||
|
||||
action_info = None
|
||||
if matched_rule:
|
||||
print(f"-> 규칙 '{matched_rule.get('rule_name', '이름 없음')}'에 해당합니다.")
|
||||
action_info = matched_rule.get('action')
|
||||
else:
|
||||
print("-> 일치하는 규칙이 없어 기본 행동을 실행합니다.")
|
||||
action_info = rules_config.get('default_action')
|
||||
|
||||
if action_info:
|
||||
execute_action(imap_client, email_details, action_info)
|
||||
else:
|
||||
print("-> 실행할 행동이 정의되지 않았습니다.")
|
||||
|
||||
|
||||
def execute_action(client, email, action):
|
||||
"""규칙에 따른 행동을 실행합니다."""
|
||||
action_type = action.get('type')
|
||||
params = action.get('parameters', {})
|
||||
uid = email['uid']
|
||||
|
||||
if action_type == 'DEVONTHINK':
|
||||
print("-> 행동: DEVONthink로 저장")
|
||||
inbox_path = params.get('devonthink_inbox_path')
|
||||
if inbox_path:
|
||||
client.download_email(email, inbox_path)
|
||||
# 다운로드 후 메일 삭제 또는 다른 곳으로 이동 등 추가 행동을 원하면 여기에 구현
|
||||
client.delete_email(uid) # 예: 다운로드 후 휴지통으로 이동
|
||||
else:
|
||||
print("! 경고: 'devonthink_inbox_path'가 rules.json에 지정되지 않았습니다.")
|
||||
|
||||
elif action_type == 'CATEGORIZE':
|
||||
target_mailbox = params.get('move_to_mailbox')
|
||||
# TODO: IMAP으로는 Synology의 '레이블'을 직접 제어하기 어려움.
|
||||
# 대신 메일함으로 이동하는 것으로 대체.
|
||||
print(f"-> 행동: '{target_mailbox}' 메일함으로 이동")
|
||||
if target_mailbox:
|
||||
client.move_email(uid, target_mailbox)
|
||||
else:
|
||||
print("! 경고: 'move_to_mailbox'가 rules.json에 지정되지 않았습니다.")
|
||||
|
||||
elif action_type == 'REVIEW':
|
||||
target_mailbox = params.get('move_to_mailbox')
|
||||
print(f"-> 행동: '{target_mailbox}' 메일함으로 이동 (검토 필요)")
|
||||
if target_mailbox:
|
||||
client.move_email(uid, target_mailbox)
|
||||
else:
|
||||
print("! 경고: 'move_to_mailbox'가 rules.json에 지정되지 않았습니다.")
|
||||
|
||||
elif action_type == 'TRASH':
|
||||
print("-> 행동: 휴지통으로 이동")
|
||||
client.delete_email(uid)
|
||||
|
||||
else:
|
||||
print(f"! 경고: 알 수 없는 행동 타입 '{action_type}' 입니다.")
|
||||
|
||||
|
||||
def run_email_processing():
|
||||
"""메인 실행 함수"""
|
||||
config = load_config()
|
||||
rules_config = load_rules()
|
||||
|
||||
imap_client = ImapMailClient(
|
||||
server=config['imap']['server'],
|
||||
port=config['imap']['port'],
|
||||
username=config['username'],
|
||||
password=config['password']
|
||||
)
|
||||
|
||||
gemini_api_key = config.get('gemini_api_key')
|
||||
|
||||
if imap_client.connect():
|
||||
process_emails(imap_client, rules_config, gemini_api_key)
|
||||
imap_client.close()
|
||||
else:
|
||||
print("프로세스를 시작할 수 없습니다. IMAP 연결 정보를 확인하세요.")
|
||||
Reference in New Issue
Block a user