Jelajahi Sumber

feat: upate

jerry 3 bulan lalu
induk
melakukan
cd2ac066ca

+ 6 - 2
app/api/router.py

@@ -1300,8 +1300,12 @@ async def vas_task_return_to_queue(task_id:int, db: AsyncSession = Depends(get_d
     return success(data=obj)
 
 @admin_required_router.post("/vas/task/manual_confirm", summary="设置任务完成", tags=["Visafly签证系统"], response_model=ApiResponse[VasTaskOut])
-async def vas_task_manual_confirm(task_id:int, db: AsyncSession = Depends(get_db)):
-    obj = await VasTaskService.manual_confirm(db, task_id)
+async def vas_task_manual_confirm(
+    task_id:int,
+    db: AsyncSession = Depends(get_db),
+    redis_client: Redis = Depends(get_redis_client)
+):
+    obj = await VasTaskService.manual_confirm(db, task_id, redis_client)
     return success(data=obj)
 
 @admin_required_router.get("/vas/task/pop", summary="任务出队(pop)", tags=["Visafly签证系统"], response_model=ApiResponse[VasTaskOut])

+ 120 - 130
app/services/task_handlers.py

@@ -5,17 +5,18 @@ from datetime import datetime
 from typing import Callable, Awaitable, Dict, Any
 from sqlalchemy.ext.asyncio import AsyncSession
 from sqlalchemy import select
+from redis.asyncio import Redis
+
 from app.models.user import VasUser
 from app.models.vas_task import VasTask
 from app.models.order import VasOrder
-
-# 导入具体的业务服务 (用于发邮件等)
+from app.services.notification_service import NotificationService
 from app.services.email_authorizations_service import EmailAuthorizationService
 
 logger = logging.getLogger(__name__)
 
-# 定义处理器函数的类型签名
-HandlerFunc = Callable[[AsyncSession, VasTask, VasOrder], Awaitable[None]]
+# 1. 修改 HandlerFunc 类型签名,增加 redis_client: Redis
+HandlerFunc = Callable[[AsyncSession, Redis, VasTask, VasOrder], Awaitable[None]]
 
 class TaskHandlerRegistry:
     """
@@ -26,14 +27,6 @@ class TaskHandlerRegistry:
         self._handlers: Dict[str, HandlerFunc] = {}
 
     def register(self, *routing_keys: str):
-        """
-        装饰器:将一个或多个 routing_key 注册到同一个处理函数
-        
-        用法:
-        @task_processor.register("key1", "key2")
-        async def my_handler(...):
-            pass
-        """
         def decorator(func: HandlerFunc):
             for key in routing_keys:
                 if key in self._handlers:
@@ -42,47 +35,38 @@ class TaskHandlerRegistry:
             return func
         return decorator
 
-    async def execute(self, routing_key: str, db: AsyncSession, task: VasTask, order: VasOrder):
+    # 2. 修改 execute 方法,接收 redis_client
+    async def execute(self, routing_key: str, db: AsyncSession, redis_client: Redis, task: VasTask, order: VasOrder):
         """
         根据 routing_key 查找并执行对应的处理函数
         """
         handler = self._handlers.get(routing_key)
         
         if not handler:
-            # 如果没有注册处理器,通常直接跳过即可
             logger.info(f"ℹ️ [TaskHandler] No handler found for routing_key: {routing_key}, skipping.")
             return
 
         try:
             logger.info(f"▶️ [TaskHandler] Executing handler '{handler.__name__}' for task {task.id} (key: {routing_key})")
             
-            # 执行具体的业务逻辑
-            await handler(db, task, order)
+            # 传递 redis_client 给具体处理函数
+            await handler(db, redis_client, task, order)
             
             logger.info(f"✅ [TaskHandler] Handler '{handler.__name__}' completed successfully.")
             
         except Exception as e:
-            # 捕获异常,防止阻断主流程 (manual_confirm) 的事务提交
-            # 也可以选择在这里将错误写入 task.meta
             logger.error(f"❌ [TaskHandler] Error executing handler for task {task.id}: {str(e)}", exc_info=True)
-            
-            # 可选:记录错误到 Task Meta
             # await self._record_error(db, task, str(e))
 
     async def _record_error(self, db: AsyncSession, task: VasTask, error_msg: str):
-        """辅助方法:将错误信息回写到 Task (可选)"""
         try:
             meta = dict(task.meta) if task.meta else {}
             meta["post_process_error"] = error_msg
             task.meta = meta
-            # 注意:这里不需要 commit,因为外层 manual_confirm 会统一 commit
         except Exception:
             pass
 
 
-# =================================================================
-# 实例化注册表 (在其他地方 import 这个实例)
-# =================================================================
 task_processor = TaskHandlerRegistry()
 
 
@@ -90,197 +74,203 @@ task_processor = TaskHandlerRegistry()
 # 具体的处理函数 (Handlers)
 # =================================================================
 
+# 辅助函数:获取用户信息(用于发通知)
+async def _get_user_by_id(db: AsyncSession, user_id: int) -> VasUser:
+    stmt = select(VasUser).where(VasUser.id == user_id)
+    result = await db.execute(stmt)
+    return result.scalar_one_or_none()
+
 @task_processor.register("auto.slot.dub.fr.tourist")
-async def forward_troov_appointment_letter(db: AsyncSession, task: VasTask, order: VasOrder):
+async def forward_troov_appointment_letter(db: AsyncSession, redis_client: Redis, task: VasTask, order: VasOrder):
     """
-    处理邮件转发相关的任务
-    支持 routing_key: email.forward, email.auto_reply
+    Troov (法国) 预约信转发及通知
     """
     # 1. 解析参数
-    # 假设参数存储在 task.user_inputs 或 task.config 中
     inputs: Dict[str, Any] = task.user_inputs or {}
     grabbed_history = task.grabbed_history or {}
-    
     task_config = task.config or {}
 
-    
     email_account = "visafly666@gmail.com"
-    forward_to = inputs.get("email", "")
-    if not forward_to:
-        uid = order.user_id
-        stmt = select(VasUser).where(VasUser.id == uid)
-        result = await db.execute(stmt)
-        user = result.scalar_one_or_none()
-        forward_to = user.email
-        
-    recipient = task_config.get('alias_email', "")
     
-    first_name = inputs.get('first_name', '')
-    last_name = inputs.get('last_name', '')
+    # 获取用户以发送通知
+    user = await _get_user_by_id(db, order.user_id)
+    forward_to = inputs.get("email") or (user.email if user else "")
     
-    first_name = first_name.capitalize()
-    last_name = last_name.capitalize()
+    recipient = task_config.get('alias_email', "")
+    first_name = inputs.get('first_name', '').capitalize()
+    last_name = inputs.get('last_name', '').capitalize()
     
+    # 处理日期格式用于关键词匹配
     book_date_str = grabbed_history.get("book_date", "")
-    dt = datetime.strptime(book_date_str, "%Y-%m-%dT%H:%M:%S")
-    formatted_date_time = dt.strftime("%B %-d, %Y to %Hh%M")
-    
+    formatted_date_time = ""
+    if book_date_str:
+        try:
+            # 假设 input 是 ISO 格式 "2024-03-15T09:00:00"
+            dt = datetime.strptime(book_date_str, "%Y-%m-%dT%H:%M:%S")
+            formatted_date_time = dt.strftime("%B %-d, %Y to %Hh%M") # Troov 邮件中的特殊格式
+            
+            # 为邮件通知准备通用格式
+            notification_date_str = dt.strftime("%d %b %Y, %H:%M")
+        except ValueError:
+            formatted_date_time = book_date_str
+            notification_date_str = book_date_str
+
     # 过滤条件
-    sender = "ne-pas-repondre at consulat.gouv.fr"
-    
+    sender = "ne-pas-repondre at consulat.gouv.fr" # 注意实际匹配时可能需要处理 @ 符号
     subject_keywords = inputs.get("subjectKeywords", "Confirmed,appointment,Section,Ambassade,France,Irlande")
     body_keywords = inputs.get("bodyKeywords", f"Concerned,person,{first_name},{last_name},{formatted_date_time}")
 
     if not email_account or not forward_to:
-        logger.warning(f"Task {task.id} missing required inputs for email forwarding.")
+        logger.warning(f"Task {task.id} missing inputs for email forwarding.")
         return
 
     logger.info(f"Task {task.id}: Forwarding emails from {email_account} to {forward_to}")
 
-    # 2. 获取授权配置
+    # 2. 获取授权并转发邮件
     auth = await EmailAuthorizationService.get_by_email(db, email_account)
     if not auth:
-        raise ValueError(f"Email authorization not found for account: {email_account}")
+        raise ValueError(f"Email authorization not found: {email_account}")
 
-    # 3. 调用邮件服务执行转发
-    # 这里复用你之前写的 Service 方法
     result = await EmailAuthorizationService.forward_first_matching_email2(
-        db=db,
-        auth=auth,
-        forward_to=forward_to,
-        sender=sender,
-        recipient=recipient,
-        subject_keywords=subject_keywords,
-        body_keywords=body_keywords
+        db=db, auth=auth, forward_to=forward_to, sender=sender, recipient=recipient,
+        subject_keywords=subject_keywords, body_keywords=body_keywords
     )
 
-    # 4. (可选) 将结果回写到 Task Meta
+    # 3. 更新 Task Meta
     meta = dict(task.meta) if task.meta else {}
     meta["forward_result"] = result
     task.meta = meta
     
-    
+    # 4. 发送预约成功通知邮件给用户 (利用 NotificationService + Redis)
+    if user and user.email:
+        logger.info(f"📧 Sending appointment confirmation email to {user.email}")
+        await NotificationService.post_email(
+            redis_client=redis_client,
+            receiver=user.email,
+            template_id="appointment_confirmation", # 对应之前定义的模板ID
+            payload={
+                "username": user.nickname or first_name,
+                "order_id": str(order.id),
+                "country": "France",
+                "city": "Dublin", # 根据 routing_key 或 inputs 推断
+                "appointment_date": formatted_date_time,
+                "visa_type": "Short Stay Any Purpose",
+                "user_email": forward_to
+            }
+        )
+
+
 @task_processor.register("auto.slot.dub.nl.tourist")
-async def forward_vfs_appointment_letter(db: AsyncSession, task: VasTask, order: VasOrder):
+async def forward_vfs_appointment_letter(db: AsyncSession, redis_client: Redis, task: VasTask, order: VasOrder):
     """
-    处理邮件转发相关的任务
-    支持 routing_key: email.forward, email.auto_reply
+    VFS (荷兰) 预约信转发及通知
     """
-    # 1. 解析参数
-    # 假设参数存储在 task.user_inputs 或 task.config 中
     inputs: Dict[str, Any] = task.user_inputs or {}
     grabbed_history = task.grabbed_history or {}
-    
     task_config = task.config or {}
 
-    
     email_account = "visafly666@gmail.com"
-    forward_to = inputs.get("email", "")
-    if not forward_to:
-        uid = order.user_id
-        stmt = select(VasUser).where(VasUser.id == uid)
-        result = await db.execute(stmt)
-        user = result.scalar_one_or_none()
-        forward_to = user.email
-    
+    user = await _get_user_by_id(db, order.user_id)
+    forward_to = inputs.get("email") or (user.email if user else "")
 
-    
     urn = grabbed_history.get("urn", "")
     
-    # 过滤条件
     sender = "donotreply at vfsglobal.com"
     recipient = task_config.get('alias_email', "")
     subject_keywords = inputs.get("subjectKeywords", "Appointment,Confirm")
-    body_keywords = inputs.get("bodyKeywords", f"urn")
+    body_keywords = inputs.get("bodyKeywords", f"urn") # VFS 通常用 URN 匹配
 
     if not email_account or not forward_to:
-        logger.warning(f"Task {task.id} missing required inputs for email forwarding.")
+        logger.warning(f"Task {task.id} missing inputs.")
         return
 
-    logger.info(f"Task {task.id}: Forwarding emails from {email_account} to {forward_to}")
-
-    # 2. 获取授权配置
     auth = await EmailAuthorizationService.get_by_email(db, email_account)
     if not auth:
-        raise ValueError(f"Email authorization not found for account: {email_account}")
+        raise ValueError(f"Email auth not found: {email_account}")
 
-    # 3. 调用邮件服务执行转发
-    # 这里复用你之前写的 Service 方法
     result = await EmailAuthorizationService.forward_first_matching_email2(
-        db=db,
-        auth=auth,
-        forward_to=forward_to,
-        sender=sender,
-        recipient=recipient,
-        subject_keywords=subject_keywords,
-        body_keywords=body_keywords
+        db=db, auth=auth, forward_to=forward_to, sender=sender, recipient=recipient,
+        subject_keywords=subject_keywords, body_keywords=body_keywords
     )
 
-    # 4. (可选) 将结果回写到 Task Meta
     meta = dict(task.meta) if task.meta else {}
     meta["forward_result"] = result
     task.meta = meta
-    
+
+    # 发送通知
+    if user and user.email:
+        await NotificationService.post_email(
+            redis_client=redis_client,
+            receiver=user.email,
+            template_id="appointment_confirmation",
+            payload={
+                "username": user.nickname or 'consumer',
+                "order_id": str(order.id),
+                "country": "Netherlands",
+                "city": "Dublin",
+                "appointment_date": "Check attachment", # VFS 可能没有直接的日期在 history
+                "visa_type": "Tourist",
+                "user_email": forward_to
+            }
+        )
+
+
 @task_processor.register("auto.slot.dub.de.tourist")
-async def forward_visametric_appointment_letter(db: AsyncSession, task: VasTask, order: VasOrder):
+async def forward_visametric_appointment_letter(db: AsyncSession, redis_client: Redis, task: VasTask, order: VasOrder):
     """
-    处理邮件转发相关的任务
-    支持 routing_key: email.forward, email.auto_reply
+    Visametric (德国) 预约信转发及通知
     """
-    # 1. 解析参数
-    # 假设参数存储在 task.user_inputs 或 task.config 中
     inputs: Dict[str, Any] = task.user_inputs or {}
     grabbed_history = task.grabbed_history or {}
-    
     task_config = task.config or {}
 
-    
     email_account = "visafly666@gmail.com"
-    forward_to = inputs.get("email", "")
-    if not forward_to:
-        uid = order.user_id
-        stmt = select(VasUser).where(VasUser.id == uid)
-        result = await db.execute(stmt)
-        user = result.scalar_one_or_none()
-        forward_to = user.email
+    user = await _get_user_by_id(db, order.user_id)
+    forward_to = inputs.get("email") or (user.email if user else "")
     
     first_name = inputs.get('first_name', '')
     last_name = inputs.get('last_name', '')
-    
     pnr_number = grabbed_history.get('pnr_number', '')
-    slot_date = grabbed_history.get('slot_date', '')
+    slot_date = grabbed_history.get('slot_date', '') # YYYY-MM-DD
     slot_time = grabbed_history.get('slot_time', '')
     
-    # 过滤条件
     sender = "noreply at visametric.com"
     recipient = task_config.get("alias_email", "")
     subject_keywords = inputs.get("subjectKeywords", f"{pnr_number},Visametric,Appointment,Request")
     body_keywords = inputs.get("bodyKeywords", f"{pnr_number},{slot_date},{slot_time},{first_name},{last_name}")
 
     if not email_account or not forward_to:
-        logger.warning(f"Task {task.id} missing required inputs for email forwarding.")
+        logger.warning(f"Task {task.id} missing inputs.")
         return
 
-    logger.info(f"Task {task.id}: Forwarding emails from {email_account} to {forward_to}")
-
-    # 2. 获取授权配置
     auth = await EmailAuthorizationService.get_by_email(db, email_account)
     if not auth:
-        raise ValueError(f"Email authorization not found for account: {email_account}")
+        raise ValueError(f"Email auth not found: {email_account}")
 
-    # 3. 调用邮件服务执行转发
-    # 这里复用你之前写的 Service 方法
     result = await EmailAuthorizationService.forward_first_matching_email2(
-        db=db,
-        auth=auth,
-        forward_to=forward_to,
-        sender=sender,
-        recipient=recipient,
-        subject_keywords=subject_keywords,
-        body_keywords=body_keywords
+        db=db, auth=auth, forward_to=forward_to, sender=sender, recipient=recipient,
+        subject_keywords=subject_keywords, body_keywords=body_keywords
     )
 
-    # 4. (可选) 将结果回写到 Task Meta
     meta = dict(task.meta) if task.meta else {}
     meta["forward_result"] = result
-    task.meta = meta
+    task.meta = meta
+
+    # 发送通知
+    if user and user.email:
+        display_date = f"{slot_date} {slot_time}" if slot_date else "Confirmed"
+        
+        await NotificationService.post_email(
+            redis_client=redis_client,
+            receiver=user.email,
+            template_id="appointment_confirmation",
+            payload={
+                "username": user.nickname or first_name,
+                "order_id": str(order.id),
+                "country": "Germany",
+                "city": "Dublin",
+                "appointment_date": display_date,
+                "visa_type": "Tourist",
+                "user_email": forward_to
+            }
+        )

+ 3 - 2
app/services/vas_task_service.py

@@ -5,6 +5,7 @@ from typing import List, Optional
 
 from sqlalchemy.ext.asyncio import AsyncSession
 from sqlalchemy import select, or_, and_
+from redis.asyncio import Redis  # 引入 Redis 类型
 
 
 from app.utils.search import apply_keyword_search_stmt
@@ -232,7 +233,7 @@ class VasTaskService:
         return rec
 
     @staticmethod
-    async def manual_confirm(db: AsyncSession, id: int) -> VasTask:
+    async def manual_confirm(db: AsyncSession, id: int, redis_client: Redis) -> VasTask:
         stmt = select(VasTask).where(VasTask.id == id)
         result = await db.execute(stmt)
         task = result.scalar_one_or_none()
@@ -251,7 +252,7 @@ class VasTaskService:
 
         order.status = "completed"
         
-        await task_processor.execute(task.routing_key, db, task, order)
+        await task_processor.execute(task.routing_key, db, redis_client, task, order)
         await db.commit()
         await db.refresh(task)
         return task

+ 137 - 0
app/tasks/notification_task.py

@@ -51,6 +51,10 @@ async def notification_consumer(session_factory, redis_client: Redis):
                     sender = "donotreply@visafly.top"
                     subject = "Ticket Created"
                     content = template_ticket_open(payload)
+                if "appointment_confirmation" == template_id:
+                    sender = "donotreply@visafly.top"
+                    subject = "Appointment Confirmation"
+                    content = template_appointment_confirmation(payload)
                 if content:
                     async with session_factory() as db:  # type: AsyncSession
                         auth = await EmailAuthorizationService.get_by_email(db, sender)
@@ -408,4 +412,137 @@ def template_ticket_open(payload):
                            .replace('{{ticket_url}}', str(payload.get('ticket_url', '#'))) \
                            .replace('{{app_name}}', str(payload.get('app_name', 'Visafly')))
 
+    return html_content
+
+def template_appointment_confirmation(payload):
+    """
+    生成预约成功确认邮件 (VisaFly)
+    
+    payload 需包含: 
+    - username: 用户名
+    - order_id: 订单号 (新增)
+    - country: 国家
+    - city: 城市
+    - appointment_date: 预约时间 (字符串, 例如 "2026-03-15 09:00")
+    - visa_type: 签证类型
+    - user_email: 用户邮箱 (用于提示信件已发往此处)
+    """
+    
+    # --- 1. 基础配置 (VisaFly) ---
+    company_name = "VisaFly"
+    support_email = "support@visafly.top"
+    website_home = "https://visafly.top"
+    website_contact = "https://visafly.top/refund-policy"
+
+    # --- 2. HTML 模板 ---
+    template = '''
+    <!DOCTYPE html>
+    <html>
+    <head>
+        <meta charset="UTF-8">
+        <title>Appointment Confirmed</title>
+        <style>
+            body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background-color: #f4f6f8; margin: 0; padding: 0; color: #333; }
+            .container { max-width: 600px; margin: 30px auto; background-color: #ffffff; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 12px rgba(0,0,0,0.05); }
+            
+            /* 头部: 品牌蓝 */
+            .header { background-color: #0056b3; padding: 30px 20px; text-align: center; color: white; }
+            .header h1 { margin: 0; font-size: 24px; font-weight: bold; }
+            .header .subtitle { margin-top: 5px; opacity: 0.9; font-size: 14px; }
+            
+            /* 正文区域 */
+            .content { padding: 30px 25px; line-height: 1.6; }
+            
+            /* 核心警告框 (黄色高亮 - 强调查收邮件) */
+            .alert-box { background-color: #fff8e1; border-left: 5px solid #ffc107; padding: 15px 20px; margin: 25px 0; border-radius: 4px; }
+            .alert-title { font-weight: bold; color: #b00020; display: block; margin-bottom: 5px; font-size: 16px; }
+            
+            /* 订单详情卡片 */
+            .details-box { background-color: #f8fafc; border: 1px solid #e2e8f0; border-radius: 6px; padding: 15px; margin-bottom: 25px; }
+            .info-row { display: flex; justify-content: space-between; margin-bottom: 10px; border-bottom: 1px dashed #e2e8f0; padding-bottom: 8px; }
+            .info-row:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
+            .label { color: #64748b; font-size: 13px; font-weight: 500; }
+            .value { font-weight: bold; color: #0f172a; font-size: 14px; text-align: right; }
+            
+            /* 底部联系方式 */
+            .help-section { background-color: #f1f5f9; padding: 20px; text-align: center; font-size: 14px; color: #475569; border-top: 1px solid #e2e8f0; }
+            .contact-link { color: #0056b3; font-weight: bold; text-decoration: none; margin: 0 5px; }
+            
+            .footer { text-align: center; padding: 20px; font-size: 12px; color: #94a3b8; }
+            a { color: #0056b3; text-decoration: none; }
+        </style>
+    </head>
+    <body>
+        <div class="container">
+            <div class="header">
+                <h1>✅ Booking Successful!</h1>
+                <div class="subtitle">Appointment Secured by {{company_name}}</div>
+            </div>
+            
+            <div class="content">
+                <p>Hello <b>{{username}}</b>,</p>
+                <p>Great news! We have successfully secured your appointment slot.</p>
+                
+                <!-- 详情卡片 -->
+                <div class="details-box">
+                    <div class="info-row">
+                        <span class="label">Order ID:</span>
+                        <span class="value">#{{order_id}}</span>
+                    </div>
+                    <div class="info-row">
+                        <span class="label">Country / City:</span>
+                        <span class="value">{{country}} - {{city}}</span>
+                    </div>
+                    <div class="info-row">
+                        <span class="label">Appointment Date:</span>
+                        <span class="value">{{appointment_date}}</span>
+                    </div>
+                    <div class="info-row">
+                        <span class="label">Visa Type:</span>
+                        <span class="value">{{visa_type}}</span>
+                    </div>
+                </div>
+
+                <!-- 核心:检查邮件提示 -->
+                <div class="alert-box">
+                    <span class="alert-title">📩 Important: Check Your Email</span>
+                    We have sent the official confirmation letter to <b>{{user_email}}</b>.<br><br>
+                    <span style="font-size: 13px;">If you don't see it in your Inbox, please check your <b>Spam/Junk</b> folder immediately.</span>
+                </div>
+            </div>
+
+            <!-- 兜底联系方式 -->
+            <div class="help-section">
+                <p style="margin-top: 0; font-weight: bold;">Did not receive the email?</p>
+                <p style="margin-bottom: 15px;">Please check your Spam folder first. If still missing, contact us:</p>
+                
+                <!-- 方式1: 邮件 -->
+                <a href="mailto:{{support_email}}" class="contact-link">✉️ Email Support</a>
+                <span style="color: #cbd5e1;">|</span>
+                <!-- 方式2: 官网 -->
+                <a href="{{website_contact}}" class="contact-link">🌐 Contact Us</a>
+            </div>
+            
+            <div class="footer">
+                &copy; 2026 {{company_name}}. All rights reserved.<br>
+                <a href="{{website_home}}">{{website_home}}</a>
+            </div>
+        </div>
+    </body>
+    </html>
+    '''
+
+    # --- 3. 执行替换 ---
+    html_content = template.replace('{{username}}', str(payload.get('username', 'Customer'))) \
+                           .replace('{{order_id}}', str(payload.get('order_id', 'N/A'))) \
+                           .replace('{{country}}', str(payload.get('country', ''))) \
+                           .replace('{{city}}', str(payload.get('city', ''))) \
+                           .replace('{{appointment_date}}', str(payload.get('appointment_date', 'Confirmed'))) \
+                           .replace('{{user_email}}', str(payload.get('user_email', 'your email'))) \
+                           .replace('{{visa_type}}', str(payload.get('visa_type', 'Standard'))) \
+                           .replace('{{company_name}}', company_name) \
+                           .replace('{{support_email}}', support_email) \
+                           .replace('{{website_contact}}', website_contact) \
+                           .replace('{{website_home}}', website_home)
+
     return html_content