"""
Seed default notification templates for SMS and Email channels.
Templates are company-level (not per-merchant).
Usage:
python -m scripts.seed_templates
python -m scripts.seed_templates --mongodb-uri mongodb://prod-host:27017
Templates seeded:
- cust_otp (customer OTP)
- staff_otp_login (staff OTP)
- otp_verification (generic / service partner OTP)
- welcome_credentials (employee & merchant welcome password)
- password_reset (password reset)
- password_reset_link (password reset with link)
"""
import asyncio
import argparse
import uuid
import sys
import os
from datetime import datetime
# Allow running from project root
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from motor.motor_asyncio import AsyncIOMotorClient
COLLECTION = "notification_templates"
# ── Template definitions ─────────────────────────────────────────────────────
TEMPLATES = [
# ── Customer OTP ─────────────────────────────────────────────────────
{
"channel": "sms",
"name": "cust_otp",
"subject": None,
"body": "{{otp}} is your verification code. Valid for {{expiry_minutes}} minutes. Do not share this code with anyone.",
"html_body": None,
"dlt_template_id": "",
"variables": ["otp", "expiry_minutes"],
},
{
"channel": "email",
"name": "cust_otp",
"subject": "Your verification code - {{otp}}",
"body": "Your verification code is {{otp}}. It is valid for {{expiry_minutes}} minutes. Do not share this code with anyone.",
"html_body": (
"
"
"
Verification Code
"
"
Your verification code is:
"
"
{{otp}}
"
"
Valid for {{expiry_minutes}} minutes. Do not share this code.
"
"
"
),
"dlt_template_id": None,
"variables": ["otp", "expiry_minutes"],
},
# ── Staff OTP ────────────────────────────────────────────────────────
{
"channel": "sms",
"name": "staff_otp_login",
"subject": None,
"body": "{{otp}} is your staff login OTP. Valid for {{expiry_minutes}} minutes. Do not share.",
"html_body": None,
"dlt_template_id": "",
"variables": ["otp", "expiry_minutes"],
},
{
"channel": "email",
"name": "staff_otp_login",
"subject": "Staff Login OTP - {{otp}}",
"body": "Your staff login OTP is {{otp}}. It is valid for {{expiry_minutes}} minutes.",
"html_body": (
""
"
Staff Login OTP
"
"
Your one-time password for staff login:
"
"
{{otp}}
"
"
Valid for {{expiry_minutes}} minutes.
"
"
"
),
"dlt_template_id": None,
"variables": ["otp", "expiry_minutes"],
},
# ── Generic / Service Partner OTP ────────────────────────────────────
{
"channel": "sms",
"name": "otp",
"subject": None,
"body": "{{otp}} is your OTP. Valid for {{expiry_minutes}} minutes. Do not share this code.",
"html_body": None,
"dlt_template_id": "",
"variables": ["otp", "expiry_minutes"],
},
{
"channel": "email",
"name": "otp",
"subject": "Your OTP - {{otp}}",
"body": "Your OTP is {{otp}}. It is valid for {{expiry_minutes}} minutes. Do not share this code with anyone.",
"html_body": (
""
"
OTP Verification
"
"
Your one-time password:
"
"
{{otp}}
"
"
Valid for {{expiry_minutes}} minutes.
"
"
"
),
"dlt_template_id": None,
"variables": ["otp", "expiry_minutes"],
},
# ── Welcome Credentials (Employee / Merchant) ────────────────────────
{
"channel": "sms",
"name": "welcome_credentials",
"subject": None,
"body": "Welcome {{name}}! Your login: Username: {{username}}, Password: {{password}}. Please change your password after first login.",
"html_body": None,
"dlt_template_id": "",
"variables": ["name", "username", "password", "merchant_name"],
},
{
"channel": "email",
"name": "welcome_credentials",
"subject": "Welcome {{name}} - Your Login Credentials",
"body": "Hi {{name}},\n\nYour account has been created.\n\nUsername: {{username}}\nPassword: {{password}}\n\nPlease change your password after your first login.\n\nRegards,\n{{merchant_name}}",
"html_body": (
""
"
Welcome, {{name}}!
"
"
Your account has been created. Here are your login credentials:
"
"
"
"| Username | {{username}} |
"
"| Password | {{password}} |
"
"
"
"
Please change your password after your first login.
"
"
{{merchant_name}}
"
"
"
),
"dlt_template_id": None,
"variables": ["name", "username", "password", "merchant_name"],
},
# ── Password Reset ───────────────────────────────────────────────────
{
"channel": "sms",
"name": "password_reset",
"subject": None,
"body": "Hi {{name}}, your password has been reset. New password: {{new_password}}. Please change it after login.",
"html_body": None,
"dlt_template_id": "",
"variables": ["name", "new_password", "merchant_name"],
},
{
"channel": "email",
"name": "password_reset",
"subject": "Password Reset - Action Required",
"body": "Hi {{name}},\n\nYour password has been reset.\n\nNew password: {{new_password}}\n\nPlease change it immediately after login.\n\nRegards,\n{{merchant_name}}",
"html_body": (
""
"
Password Reset
"
"
Hi {{name}}, your password has been reset.
"
"
"
"| New Password | {{new_password}} |
"
"
"
"
Please change your password immediately after login.
"
"
{{merchant_name}}
"
"
"
),
"dlt_template_id": None,
"variables": ["name", "new_password", "merchant_name"],
},
# ── Password Reset Link ──────────────────────────────────────────────
{
"channel": "sms",
"name": "password_reset_link",
"subject": None,
"body": "Hi {{name}}, reset your password here: {{reset_link}}. Link expires in 60 minutes.",
"html_body": None,
"dlt_template_id": "",
"variables": ["name", "reset_link", "merchant_name"],
},
{
"channel": "email",
"name": "password_reset_link",
"subject": "Reset Your Password",
"body": "Hi {{name}},\n\nClick the link below to reset your password:\n{{reset_link}}\n\nThis link expires in 60 minutes.\n\nIf you did not request this, please ignore this email.\n\nRegards,\n{{merchant_name}}",
"html_body": (
""
"
Reset Your Password
"
"
Hi {{name}},
"
"
Click the button below to reset your password:
"
"
"
"Reset Password"
"
"
"
This link expires in 60 minutes. If you did not request this, ignore this email.
"
"
{{merchant_name}}
"
"
"
),
"dlt_template_id": None,
"variables": ["name", "reset_link", "merchant_name"],
},
]
# ── Main ─────────────────────────────────────────────────────────────────────
async def main(mongodb_uri: str, db_name: str):
"""Connect to MongoDB and upsert company-level seed templates."""
client = AsyncIOMotorClient(mongodb_uri)
db = client[db_name]
col = db[COLLECTION]
# Ensure unique index on (channel, name) — company-level
await col.create_index(
[("channel", 1), ("name", 1)],
unique=True,
name="ux_channel_name",
)
created = 0
skipped = 0
now = datetime.utcnow()
for tpl in TEMPLATES:
filt = {"channel": tpl["channel"], "name": tpl["name"]}
existing = await col.find_one(filt, {"_id": 1})
if existing:
print(f" SKIP {tpl['channel']:5s} {tpl['name']} (already exists)")
skipped += 1
continue
doc = {
"template_id": str(uuid.uuid4()),
"channel": tpl["channel"],
"name": tpl["name"],
"subject": tpl.get("subject"),
"body": tpl["body"],
"html_body": tpl.get("html_body"),
"dlt_template_id": tpl.get("dlt_template_id"),
"variables": tpl.get("variables", []),
"status": "active",
"created_by": "seed_script",
"created_at": now,
"updated_by": None,
"updated_at": None,
}
await col.insert_one(doc)
print(f" NEW {tpl['channel']:5s} {tpl['name']} → {doc['template_id']}")
created += 1
print(f"\nDone — created: {created}, skipped: {skipped}, total templates: {len(TEMPLATES)}")
client.close()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Seed default notification templates (company-level)")
parser.add_argument(
"--mongodb-uri",
default=os.getenv("MONGODB_URI", "mongodb://localhost:27017"),
help="MongoDB connection URI (default: $MONGODB_URI or localhost)",
)
parser.add_argument(
"--db-name",
default=os.getenv("MONGODB_DB_NAME", "cuatrolabs"),
help="MongoDB database name (default: $MONGODB_DB_NAME or cuatrolabs)",
)
args = parser.parse_args()
print(f"Seeding company-level notification templates")
print(f"MongoDB: {args.mongodb_uri} / {args.db_name}\n")
asyncio.run(main(args.mongodb_uri, args.db_name))