mirror of
https://github.com/langgenius/dify.git
synced 2026-02-09 23:20:12 -05:00
feat: send email when user mentioned in comment
This commit is contained in:
@@ -37,6 +37,7 @@ class EmailType(StrEnum):
|
||||
ENTERPRISE_CUSTOM = auto()
|
||||
QUEUE_MONITOR_ALERT = auto()
|
||||
DOCUMENT_CLEAN_NOTIFY = auto()
|
||||
WORKFLOW_COMMENT_MENTION = auto()
|
||||
EMAIL_REGISTER = auto()
|
||||
EMAIL_REGISTER_WHEN_ACCOUNT_EXIST = auto()
|
||||
RESET_PASSWORD_WHEN_ACCOUNT_NOT_EXIST_NO_REGISTER = auto()
|
||||
@@ -453,6 +454,18 @@ def create_default_email_config() -> EmailI18nConfig:
|
||||
branded_template_path="clean_document_job_mail_template_zh-CN.html",
|
||||
),
|
||||
},
|
||||
EmailType.WORKFLOW_COMMENT_MENTION: {
|
||||
EmailLanguage.EN_US: EmailTemplate(
|
||||
subject="You were mentioned in a workflow comment",
|
||||
template_path="workflow_comment_mention_template_en-US.html",
|
||||
branded_template_path="without-brand/workflow_comment_mention_template_en-US.html",
|
||||
),
|
||||
EmailLanguage.ZH_HANS: EmailTemplate(
|
||||
subject="你在工作流评论中被提及",
|
||||
template_path="workflow_comment_mention_template_zh-CN.html",
|
||||
branded_template_path="without-brand/workflow_comment_mention_template_zh-CN.html",
|
||||
),
|
||||
},
|
||||
EmailType.TRIGGER_EVENTS_LIMIT_SANDBOX: {
|
||||
EmailLanguage.EN_US: EmailTemplate(
|
||||
subject="You’ve reached your Sandbox Trigger Events limit",
|
||||
|
||||
@@ -8,8 +8,9 @@ from werkzeug.exceptions import Forbidden, NotFound
|
||||
from extensions.ext_database import db
|
||||
from libs.datetime_utils import naive_utc_now
|
||||
from libs.helper import uuid_value
|
||||
from models import WorkflowComment, WorkflowCommentMention, WorkflowCommentReply
|
||||
from models import App, TenantAccountJoin, WorkflowComment, WorkflowCommentMention, WorkflowCommentReply
|
||||
from models.account import Account
|
||||
from tasks.mail_workflow_comment_task import send_workflow_comment_mention_email_task
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -25,6 +26,81 @@ class WorkflowCommentService:
|
||||
if len(content) > 1000:
|
||||
raise ValueError("Comment content cannot exceed 1000 characters")
|
||||
|
||||
@staticmethod
|
||||
def _filter_valid_mentioned_user_ids(mentioned_user_ids: Sequence[str]) -> list[str]:
|
||||
"""Return deduplicated UUID user IDs in the order provided."""
|
||||
unique_user_ids: list[str] = []
|
||||
seen: set[str] = set()
|
||||
for user_id in mentioned_user_ids:
|
||||
if not isinstance(user_id, str):
|
||||
continue
|
||||
if not uuid_value(user_id):
|
||||
continue
|
||||
if user_id in seen:
|
||||
continue
|
||||
seen.add(user_id)
|
||||
unique_user_ids.append(user_id)
|
||||
return unique_user_ids
|
||||
|
||||
@staticmethod
|
||||
def _format_comment_excerpt(content: str, max_length: int = 200) -> str:
|
||||
"""Trim comment content for email display."""
|
||||
trimmed = content.strip()
|
||||
if len(trimmed) <= max_length:
|
||||
return trimmed
|
||||
if max_length <= 3:
|
||||
return trimmed[:max_length]
|
||||
return f"{trimmed[: max_length - 3].rstrip()}..."
|
||||
|
||||
@staticmethod
|
||||
def _build_mention_email_payloads(
|
||||
session: Session,
|
||||
tenant_id: str,
|
||||
app_id: str,
|
||||
mentioner_id: str,
|
||||
mentioned_user_ids: Sequence[str],
|
||||
content: str,
|
||||
) -> list[dict[str, str]]:
|
||||
"""Prepare email payloads for mentioned users."""
|
||||
if not mentioned_user_ids:
|
||||
return []
|
||||
|
||||
candidate_user_ids = [user_id for user_id in mentioned_user_ids if user_id != mentioner_id]
|
||||
if not candidate_user_ids:
|
||||
return []
|
||||
|
||||
app_name = session.scalar(
|
||||
select(App.name).where(App.id == app_id, App.tenant_id == tenant_id)
|
||||
) or "Dify app"
|
||||
commenter_name = session.scalar(select(Account.name).where(Account.id == mentioner_id)) or "Dify user"
|
||||
comment_excerpt = WorkflowCommentService._format_comment_excerpt(content)
|
||||
|
||||
accounts = session.scalars(
|
||||
select(Account)
|
||||
.join(TenantAccountJoin, TenantAccountJoin.account_id == Account.id)
|
||||
.where(TenantAccountJoin.tenant_id == tenant_id, Account.id.in_(candidate_user_ids))
|
||||
).all()
|
||||
|
||||
payloads: list[dict[str, str]] = []
|
||||
for account in accounts:
|
||||
payloads.append(
|
||||
{
|
||||
"language": account.interface_language or "en-US",
|
||||
"to": account.email,
|
||||
"mentioned_name": account.name or account.email,
|
||||
"commenter_name": commenter_name,
|
||||
"app_name": app_name,
|
||||
"comment_content": comment_excerpt,
|
||||
}
|
||||
)
|
||||
return payloads
|
||||
|
||||
@staticmethod
|
||||
def _dispatch_mention_emails(payloads: Sequence[dict[str, str]]) -> None:
|
||||
"""Enqueue mention notification emails."""
|
||||
for payload in payloads:
|
||||
send_workflow_comment_mention_email_task.delay(**payload)
|
||||
|
||||
@staticmethod
|
||||
def get_comments(tenant_id: str, app_id: str) -> Sequence[WorkflowComment]:
|
||||
"""Get all comments for a workflow."""
|
||||
@@ -112,7 +188,7 @@ class WorkflowCommentService:
|
||||
position_y: float,
|
||||
mentioned_user_ids: list[str] | None = None,
|
||||
) -> dict:
|
||||
"""Create a new workflow comment."""
|
||||
"""Create a new workflow comment and send mention notification emails."""
|
||||
WorkflowCommentService._validate_content(content)
|
||||
|
||||
with Session(db.engine) as session:
|
||||
@@ -129,17 +205,26 @@ class WorkflowCommentService:
|
||||
session.flush() # Get the comment ID for mentions
|
||||
|
||||
# Create mentions if specified
|
||||
mentioned_user_ids = mentioned_user_ids or []
|
||||
mentioned_user_ids = WorkflowCommentService._filter_valid_mentioned_user_ids(mentioned_user_ids or [])
|
||||
for user_id in mentioned_user_ids:
|
||||
if isinstance(user_id, str) and uuid_value(user_id):
|
||||
mention = WorkflowCommentMention(
|
||||
comment_id=comment.id,
|
||||
reply_id=None, # This is a comment mention, not reply mention
|
||||
mentioned_user_id=user_id,
|
||||
)
|
||||
session.add(mention)
|
||||
mention = WorkflowCommentMention(
|
||||
comment_id=comment.id,
|
||||
reply_id=None, # This is a comment mention, not reply mention
|
||||
mentioned_user_id=user_id,
|
||||
)
|
||||
session.add(mention)
|
||||
|
||||
mention_email_payloads = WorkflowCommentService._build_mention_email_payloads(
|
||||
session=session,
|
||||
tenant_id=tenant_id,
|
||||
app_id=app_id,
|
||||
mentioner_id=created_by,
|
||||
mentioned_user_ids=mentioned_user_ids,
|
||||
content=content,
|
||||
)
|
||||
|
||||
session.commit()
|
||||
WorkflowCommentService._dispatch_mention_emails(mention_email_payloads)
|
||||
|
||||
# Return only what we need - id and created_at
|
||||
return {"id": comment.id, "created_at": comment.created_at}
|
||||
@@ -155,7 +240,7 @@ class WorkflowCommentService:
|
||||
position_y: float | None = None,
|
||||
mentioned_user_ids: list[str] | None = None,
|
||||
) -> dict:
|
||||
"""Update a workflow comment."""
|
||||
"""Update a workflow comment and notify newly mentioned users."""
|
||||
WorkflowCommentService._validate_content(content)
|
||||
|
||||
with Session(db.engine, expire_on_commit=False) as session:
|
||||
@@ -188,21 +273,34 @@ class WorkflowCommentService:
|
||||
WorkflowCommentMention.reply_id.is_(None), # Only comment mentions, not reply mentions
|
||||
)
|
||||
).all()
|
||||
existing_mentioned_user_ids = {mention.mentioned_user_id for mention in existing_mentions}
|
||||
for mention in existing_mentions:
|
||||
session.delete(mention)
|
||||
|
||||
# Add new mentions
|
||||
mentioned_user_ids = mentioned_user_ids or []
|
||||
mentioned_user_ids = WorkflowCommentService._filter_valid_mentioned_user_ids(mentioned_user_ids or [])
|
||||
new_mentioned_user_ids = [
|
||||
user_id for user_id in mentioned_user_ids if user_id not in existing_mentioned_user_ids
|
||||
]
|
||||
for user_id_str in mentioned_user_ids:
|
||||
if isinstance(user_id_str, str) and uuid_value(user_id_str):
|
||||
mention = WorkflowCommentMention(
|
||||
comment_id=comment.id,
|
||||
reply_id=None, # This is a comment mention
|
||||
mentioned_user_id=user_id_str,
|
||||
)
|
||||
session.add(mention)
|
||||
mention = WorkflowCommentMention(
|
||||
comment_id=comment.id,
|
||||
reply_id=None, # This is a comment mention
|
||||
mentioned_user_id=user_id_str,
|
||||
)
|
||||
session.add(mention)
|
||||
|
||||
mention_email_payloads = WorkflowCommentService._build_mention_email_payloads(
|
||||
session=session,
|
||||
tenant_id=tenant_id,
|
||||
app_id=app_id,
|
||||
mentioner_id=user_id,
|
||||
mentioned_user_ids=new_mentioned_user_ids,
|
||||
content=content,
|
||||
)
|
||||
|
||||
session.commit()
|
||||
WorkflowCommentService._dispatch_mention_emails(mention_email_payloads)
|
||||
|
||||
return {"id": comment.id, "updated_at": comment.updated_at}
|
||||
|
||||
@@ -252,7 +350,7 @@ class WorkflowCommentService:
|
||||
def create_reply(
|
||||
comment_id: str, content: str, created_by: str, mentioned_user_ids: list[str] | None = None
|
||||
) -> dict:
|
||||
"""Add a reply to a workflow comment."""
|
||||
"""Add a reply to a workflow comment and notify mentioned users."""
|
||||
WorkflowCommentService._validate_content(content)
|
||||
|
||||
with Session(db.engine, expire_on_commit=False) as session:
|
||||
@@ -267,22 +365,31 @@ class WorkflowCommentService:
|
||||
session.flush() # Get the reply ID for mentions
|
||||
|
||||
# Create mentions if specified
|
||||
mentioned_user_ids = mentioned_user_ids or []
|
||||
mentioned_user_ids = WorkflowCommentService._filter_valid_mentioned_user_ids(mentioned_user_ids or [])
|
||||
for user_id in mentioned_user_ids:
|
||||
if isinstance(user_id, str) and uuid_value(user_id):
|
||||
# Create mention linking to specific reply
|
||||
mention = WorkflowCommentMention(
|
||||
comment_id=comment_id, reply_id=reply.id, mentioned_user_id=user_id
|
||||
)
|
||||
session.add(mention)
|
||||
# Create mention linking to specific reply
|
||||
mention = WorkflowCommentMention(
|
||||
comment_id=comment_id, reply_id=reply.id, mentioned_user_id=user_id
|
||||
)
|
||||
session.add(mention)
|
||||
|
||||
mention_email_payloads = WorkflowCommentService._build_mention_email_payloads(
|
||||
session=session,
|
||||
tenant_id=comment.tenant_id,
|
||||
app_id=comment.app_id,
|
||||
mentioner_id=created_by,
|
||||
mentioned_user_ids=mentioned_user_ids,
|
||||
content=content,
|
||||
)
|
||||
|
||||
session.commit()
|
||||
WorkflowCommentService._dispatch_mention_emails(mention_email_payloads)
|
||||
|
||||
return {"id": reply.id, "created_at": reply.created_at}
|
||||
|
||||
@staticmethod
|
||||
def update_reply(reply_id: str, user_id: str, content: str, mentioned_user_ids: list[str] | None = None) -> dict:
|
||||
"""Update a comment reply."""
|
||||
"""Update a comment reply and notify newly mentioned users."""
|
||||
WorkflowCommentService._validate_content(content)
|
||||
|
||||
with Session(db.engine, expire_on_commit=False) as session:
|
||||
@@ -300,20 +407,36 @@ class WorkflowCommentService:
|
||||
existing_mentions = session.scalars(
|
||||
select(WorkflowCommentMention).where(WorkflowCommentMention.reply_id == reply.id)
|
||||
).all()
|
||||
existing_mentioned_user_ids = {mention.mentioned_user_id for mention in existing_mentions}
|
||||
for mention in existing_mentions:
|
||||
session.delete(mention)
|
||||
|
||||
# Add mentions
|
||||
mentioned_user_ids = mentioned_user_ids or []
|
||||
mentioned_user_ids = WorkflowCommentService._filter_valid_mentioned_user_ids(mentioned_user_ids or [])
|
||||
new_mentioned_user_ids = [
|
||||
user_id for user_id in mentioned_user_ids if user_id not in existing_mentioned_user_ids
|
||||
]
|
||||
for user_id_str in mentioned_user_ids:
|
||||
if isinstance(user_id_str, str) and uuid_value(user_id_str):
|
||||
mention = WorkflowCommentMention(
|
||||
comment_id=reply.comment_id, reply_id=reply.id, mentioned_user_id=user_id_str
|
||||
)
|
||||
session.add(mention)
|
||||
mention = WorkflowCommentMention(
|
||||
comment_id=reply.comment_id, reply_id=reply.id, mentioned_user_id=user_id_str
|
||||
)
|
||||
session.add(mention)
|
||||
|
||||
mention_email_payloads: list[dict[str, str]] = []
|
||||
comment = session.get(WorkflowComment, reply.comment_id)
|
||||
if comment:
|
||||
mention_email_payloads = WorkflowCommentService._build_mention_email_payloads(
|
||||
session=session,
|
||||
tenant_id=comment.tenant_id,
|
||||
app_id=comment.app_id,
|
||||
mentioner_id=user_id,
|
||||
mentioned_user_ids=new_mentioned_user_ids,
|
||||
content=content,
|
||||
)
|
||||
|
||||
session.commit()
|
||||
session.refresh(reply) # Refresh to get updated timestamp
|
||||
WorkflowCommentService._dispatch_mention_emails(mention_email_payloads)
|
||||
|
||||
return {"id": reply.id, "updated_at": reply.updated_at}
|
||||
|
||||
|
||||
62
api/tasks/mail_workflow_comment_task.py
Normal file
62
api/tasks/mail_workflow_comment_task.py
Normal file
@@ -0,0 +1,62 @@
|
||||
import logging
|
||||
import time
|
||||
|
||||
import click
|
||||
from celery import shared_task
|
||||
|
||||
from extensions.ext_mail import mail
|
||||
from libs.email_i18n import EmailType, get_email_i18n_service
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@shared_task(queue="mail")
|
||||
def send_workflow_comment_mention_email_task(
|
||||
language: str,
|
||||
to: str,
|
||||
mentioned_name: str,
|
||||
commenter_name: str,
|
||||
app_name: str,
|
||||
comment_content: str,
|
||||
):
|
||||
"""
|
||||
Send workflow comment mention email with internationalization support.
|
||||
|
||||
Args:
|
||||
language: Language code for email localization
|
||||
to: Recipient email address
|
||||
mentioned_name: Name of the mentioned user
|
||||
commenter_name: Name of the comment author
|
||||
app_name: Name of the app where the comment was made
|
||||
comment_content: Comment content excerpt
|
||||
"""
|
||||
if not mail.is_inited():
|
||||
return
|
||||
|
||||
logger.info(click.style(f"Start workflow comment mention mail to {to}", fg="green"))
|
||||
start_at = time.perf_counter()
|
||||
|
||||
try:
|
||||
email_service = get_email_i18n_service()
|
||||
email_service.send_email(
|
||||
email_type=EmailType.WORKFLOW_COMMENT_MENTION,
|
||||
language_code=language,
|
||||
to=to,
|
||||
template_context={
|
||||
"to": to,
|
||||
"mentioned_name": mentioned_name,
|
||||
"commenter_name": commenter_name,
|
||||
"app_name": app_name,
|
||||
"comment_content": comment_content,
|
||||
},
|
||||
)
|
||||
|
||||
end_at = time.perf_counter()
|
||||
logger.info(
|
||||
click.style(
|
||||
f"Send workflow comment mention mail to {to} succeeded: latency: {end_at - start_at}",
|
||||
fg="green",
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
logger.exception("workflow comment mention email to %s failed", to)
|
||||
@@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
line-height: 16pt;
|
||||
color: #101828;
|
||||
background-color: #e9ebf0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 504px;
|
||||
min-height: 374px;
|
||||
margin: 40px auto;
|
||||
padding: 0 48px;
|
||||
background-color: #fcfcfd;
|
||||
border-radius: 16px;
|
||||
border: 1px solid #ffffff;
|
||||
box-shadow: 0px 3px 10px -2px rgba(9, 9, 11, 0.08), 0px 2px 4px -2px rgba(9, 9, 11, 0.06);
|
||||
}
|
||||
|
||||
.header {
|
||||
padding-top: 36px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.header img {
|
||||
max-width: 63px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 16px;
|
||||
color: #101828;
|
||||
font-size: 24px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #354052;
|
||||
font-size: 14px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
.content1 {
|
||||
margin: 0;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.content2 {
|
||||
margin: 0;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.comment-box {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 16px;
|
||||
border-radius: 12px;
|
||||
background-color: #f2f4f7;
|
||||
}
|
||||
|
||||
.comment-text {
|
||||
margin: 0;
|
||||
color: #101828;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.tips {
|
||||
margin: 0;
|
||||
padding-bottom: 24px;
|
||||
color: #354052;
|
||||
font-size: 14px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo" />
|
||||
</div>
|
||||
<p class="title">You were mentioned in a workflow comment</p>
|
||||
<div class="description">
|
||||
<p class="content1">Hi {{ mentioned_name }},</p>
|
||||
<p class="content2">{{ commenter_name }} mentioned you in {{ app_name }}.</p>
|
||||
</div>
|
||||
<div class="comment-box">
|
||||
<p class="comment-text">{{ comment_content }}</p>
|
||||
</div>
|
||||
<p class="tips">Open {{ application_title }} to reply to the comment.</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
line-height: 16pt;
|
||||
color: #101828;
|
||||
background-color: #e9ebf0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 504px;
|
||||
min-height: 374px;
|
||||
margin: 40px auto;
|
||||
padding: 0 48px;
|
||||
background-color: #fcfcfd;
|
||||
border-radius: 16px;
|
||||
border: 1px solid #ffffff;
|
||||
box-shadow: 0px 3px 10px -2px rgba(9, 9, 11, 0.08), 0px 2px 4px -2px rgba(9, 9, 11, 0.06);
|
||||
}
|
||||
|
||||
.header {
|
||||
padding-top: 36px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.header img {
|
||||
max-width: 63px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 16px;
|
||||
color: #101828;
|
||||
font-size: 24px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #354052;
|
||||
font-size: 14px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
.content1 {
|
||||
margin: 0;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.content2 {
|
||||
margin: 0;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.comment-box {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 16px;
|
||||
border-radius: 12px;
|
||||
background-color: #f2f4f7;
|
||||
}
|
||||
|
||||
.comment-text {
|
||||
margin: 0;
|
||||
color: #101828;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.tips {
|
||||
margin: 0;
|
||||
padding-bottom: 24px;
|
||||
color: #354052;
|
||||
font-size: 14px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo" />
|
||||
</div>
|
||||
<p class="title">你在工作流评论中被提及</p>
|
||||
<div class="description">
|
||||
<p class="content1">你好,{{ mentioned_name }}:</p>
|
||||
<p class="content2">{{ commenter_name }} 在 {{ app_name }} 中提及了你。</p>
|
||||
</div>
|
||||
<div class="comment-box">
|
||||
<p class="comment-text">{{ comment_content }}</p>
|
||||
</div>
|
||||
<p class="tips">请在 {{ application_title }} 中查看并回复此评论。</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
119
api/templates/workflow_comment_mention_template_en-US.html
Normal file
119
api/templates/workflow_comment_mention_template_en-US.html
Normal file
@@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
line-height: 16pt;
|
||||
color: #101828;
|
||||
background-color: #e9ebf0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 504px;
|
||||
min-height: 374px;
|
||||
margin: 40px auto;
|
||||
padding: 0 48px;
|
||||
background-color: #fcfcfd;
|
||||
border-radius: 16px;
|
||||
border: 1px solid #ffffff;
|
||||
box-shadow: 0px 3px 10px -2px rgba(9, 9, 11, 0.08), 0px 2px 4px -2px rgba(9, 9, 11, 0.06);
|
||||
}
|
||||
|
||||
.header {
|
||||
padding-top: 36px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.header img {
|
||||
max-width: 63px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 16px;
|
||||
color: #101828;
|
||||
font-size: 24px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #354052;
|
||||
font-size: 14px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
.content1 {
|
||||
margin: 0;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.content2 {
|
||||
margin: 0;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.comment-box {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 16px;
|
||||
border-radius: 12px;
|
||||
background-color: #f2f4f7;
|
||||
}
|
||||
|
||||
.comment-text {
|
||||
margin: 0;
|
||||
color: #101828;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.tips {
|
||||
margin: 0;
|
||||
padding-bottom: 24px;
|
||||
color: #354052;
|
||||
font-size: 14px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo" />
|
||||
</div>
|
||||
<p class="title">You were mentioned in a workflow comment</p>
|
||||
<div class="description">
|
||||
<p class="content1">Hi {{ mentioned_name }},</p>
|
||||
<p class="content2">{{ commenter_name }} mentioned you in {{ app_name }}.</p>
|
||||
</div>
|
||||
<div class="comment-box">
|
||||
<p class="comment-text">{{ comment_content }}</p>
|
||||
</div>
|
||||
<p class="tips">Open {{ application_title }} to reply to the comment.</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
119
api/templates/workflow_comment_mention_template_zh-CN.html
Normal file
119
api/templates/workflow_comment_mention_template_zh-CN.html
Normal file
@@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
line-height: 16pt;
|
||||
color: #101828;
|
||||
background-color: #e9ebf0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 504px;
|
||||
min-height: 374px;
|
||||
margin: 40px auto;
|
||||
padding: 0 48px;
|
||||
background-color: #fcfcfd;
|
||||
border-radius: 16px;
|
||||
border: 1px solid #ffffff;
|
||||
box-shadow: 0px 3px 10px -2px rgba(9, 9, 11, 0.08), 0px 2px 4px -2px rgba(9, 9, 11, 0.06);
|
||||
}
|
||||
|
||||
.header {
|
||||
padding-top: 36px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.header img {
|
||||
max-width: 63px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 16px;
|
||||
color: #101828;
|
||||
font-size: 24px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #354052;
|
||||
font-size: 14px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
.content1 {
|
||||
margin: 0;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.content2 {
|
||||
margin: 0;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.comment-box {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 16px;
|
||||
border-radius: 12px;
|
||||
background-color: #f2f4f7;
|
||||
}
|
||||
|
||||
.comment-text {
|
||||
margin: 0;
|
||||
color: #101828;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.tips {
|
||||
margin: 0;
|
||||
padding-bottom: 24px;
|
||||
color: #354052;
|
||||
font-size: 14px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo" />
|
||||
</div>
|
||||
<p class="title">你在工作流评论中被提及</p>
|
||||
<div class="description">
|
||||
<p class="content1">你好,{{ mentioned_name }}:</p>
|
||||
<p class="content2">{{ commenter_name }} 在 {{ app_name }} 中提及了你。</p>
|
||||
</div>
|
||||
<div class="comment-box">
|
||||
<p class="comment-text">{{ comment_content }}</p>
|
||||
</div>
|
||||
<p class="tips">请在 {{ application_title }} 中查看并回复此评论。</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user