Files
keinasystem/backend/apps/mail/models.py
Akira 7a1aa81f9f 実装完了
作成・変更したファイル
バックエンド(新規):

apps/mail/models.py — MailSender, MailEmail, MailNotificationToken
apps/mail/serializers.py
apps/mail/views.py — Windmill用API、フィードバック、ルール管理
apps/mail/urls.py
apps/mail/admin.py
マイグレーション(自動生成・適用済み)
バックエンド(変更):

settings.py — apps.mail 追加、MAIL_API_KEY/FRONTEND_URL 環境変数
urls.py — /api/mail/ 追加
フロントエンド(新規):

mail/feedback/[token]/page.tsx — 認証不要、フィードバック3択+スコープ選択
mail/rules/page.tsx — ルール管理(一覧・追加・削除)
フロントエンド(変更):

Navbar.tsx — 「メールルール」メニュー追加
types/index.ts — MailSender, MailEmailFeedback 型追加
次のステップ(Windmill側)
Keinaシステム側の実装は完了しています。次はWindmillにIMAPポーリングスクリプトを書く必要があります。Windmillのスクリプトが必要になったタイミングでお声がけください。
2026-02-22 09:27:27 +09:00

100 lines
3.5 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
class MailSender(models.Model):
"""送信者ルールnever_notify: 通知しない)"""
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=[('never_notify', '通知しない')],
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', '今後通知しない'),
]
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)