Files
keinasystem/backend/apps/mail/models.py
Akira df16ab1ee0 変更内容まとめ
バックエンド
models.py — MailSender.rule に always_notify 追加、MailEmail.feedback にも追加、マイグレーション適用済み
views.py — FeedbackView.post が always_notify を受け取ったら MailSender ルールを作成(never_notify と同じ仕組み)
フロントエンド
feedback/[token]/page.tsx — 4択目「🔔 常に通知してほしい」を追加。スコープ選択(アドレス/ドメイン)もあり。色はteal系で区別
mail/rules/page.tsx — 追加フォームにルール種別セレクタを追加、一覧に「常に通知」バッジ(teal)を表示
Windmill側の使い方(メモ)
GET /api/mail/sender-rule/ のレスポンスに "rule": "always_notify" が返ってきたら、LLMをスキップして llm_verdict: "important" で直接 POST /api/mail/emails/ を呼べばOKです。
2026-02-22 09:49:28 +09:00

107 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import uuid
from django.db import models
SENDER_RULE_CHOICES = [
('always_notify', '常に通知'),
('never_notify', '通知しない'),
]
class MailSender(models.Model):
"""送信者ルール"""
email = models.EmailField(null=True, blank=True, verbose_name="メールアドレス")
domain = models.CharField(max_length=255, null=True, blank=True, verbose_name="ドメイン")
rule = models.CharField(
max_length=20,
choices=SENDER_RULE_CHOICES,
default='never_notify',
verbose_name="ルール"
)
note = models.TextField(blank=True, verbose_name="メモ")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "送信者ルール"
verbose_name_plural = "送信者ルール"
constraints = [
models.CheckConstraint(
check=(
models.Q(email__isnull=False, domain__isnull=True) |
models.Q(email__isnull=True, domain__isnull=False)
),
name='mail_sender_email_or_domain'
)
]
def __str__(self):
value = self.email or self.domain
kind = "アドレス" if self.email else "ドメイン"
return f"[{kind}] {value}"
ACCOUNT_CHOICES = [
('xserver', 'Xserver'),
('gmail', 'Gmail'),
('hotmail', 'Hotmail'),
]
FEEDBACK_CHOICES = [
('important', '重要だった'),
('not_important', '普通のメール'),
('never_notify', '今後通知しない'),
('always_notify', '常に通知してほしい'),
]
class MailEmail(models.Model):
"""受信メール記録LLMに渡したメール"""
account = models.CharField(max_length=20, choices=ACCOUNT_CHOICES, verbose_name="アカウント")
message_id = models.CharField(max_length=500, unique=True, verbose_name="Message-ID")
sender_email = models.EmailField(verbose_name="送信者アドレス")
sender_domain = models.CharField(max_length=255, verbose_name="送信者ドメイン")
subject = models.CharField(max_length=500, verbose_name="件名")
body_preview = models.TextField(verbose_name="本文冒頭")
received_at = models.DateTimeField(verbose_name="受信日時")
llm_verdict = models.CharField(
max_length=20,
choices=[('important', '重要'), ('not_important', '重要でない')],
verbose_name="LLM判定"
)
notified_at = models.DateTimeField(null=True, blank=True, verbose_name="LINE通知日時")
feedback = models.CharField(
max_length=20,
choices=FEEDBACK_CHOICES,
null=True, blank=True,
verbose_name="フィードバック"
)
feedback_at = models.DateTimeField(null=True, blank=True, verbose_name="フィードバック日時")
class Meta:
verbose_name = "受信メール"
verbose_name_plural = "受信メール"
ordering = ['-received_at']
def __str__(self):
return f"{self.subject} ({self.sender_email})"
class MailNotificationToken(models.Model):
"""LINEフィードバックURL用トークン有効期限なし"""
email = models.OneToOneField(
MailEmail,
on_delete=models.CASCADE,
related_name='notification_token',
verbose_name="メール"
)
token = models.UUIDField(default=uuid.uuid4, unique=True, verbose_name="トークン")
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = "通知トークン"
verbose_name_plural = "通知トークン"
def __str__(self):
return str(self.token)