package users_service import ( "fmt" "html" "strings" ) // Шаблоны писем сервиса. Структура по базе знаний копирайтера (email.md): // приветствие → контекст («почему это письмо») → ценность (данные для входа) // → CTA → подпись → P.S. Письма транзакционные, но по форме — «человек // человеку»: Gmail и другие почтовики агрессивнее фильтруют короткие // «роботные» письма без структуры. // // Каждое письмо формируется в двух версиях: text/plain (для старых клиентов // и фильтров) и text/html (для отображения); sender собирает их в // multipart/alternative. const ( // welcomeSubject — тема письма о регистрации: конкретика без КАПСА и // кликбейта, до 50 символов (видно на мобильных целиком). welcomeSubject = "Твой доступ к «Вечернему детективу»" // resetSubject — тема письма о сбросе пароля. resetSubject = "Новый пароль для «Вечернего детектива»" ) // welcomePlain возвращает текстовую версию письма о регистрации. func welcomePlain(username, email, password, loginURL string) string { var b strings.Builder b.WriteString(greeting(username)) b.WriteString("\n\n") b.WriteString("Твоя регистрация в «Вечернем детективе» прошла. Данные для входа:\n\n") b.WriteString(" Логин: ") b.WriteString(email) b.WriteString("\n") b.WriteString(" Пароль: ") b.WriteString(password) b.WriteString("\n\n") if loginURL != "" { b.WriteString("Войти в игру: ") b.WriteString(loginURL) b.WriteString("\n\n") } b.WriteString("Если ты не регистрировался — просто проигнорируй это письмо.\n\n") b.WriteString("— Команда «Вечернего детектива»\n") b.WriteString("P.S. Сохрани это письмо: пароль понадобится для входа.") return sanitizeLine(b.String()) } // welcomeHTML возвращает HTML-версию письма о регистрации. func welcomeHTML(username, email, password, loginURL string) string { var content strings.Builder content.WriteString("
Привет, ") content.WriteString(html.EscapeString(username)) content.WriteString("!
\n") content.WriteString("Твоя регистрация в «Вечернем детективе» прошла. Данные для входа:
\n") content.WriteString(credentialsBlockHTML(email, password)) if loginURL != "" { content.WriteString(buttonHTML(loginURL, "Войти в игру")) } content.WriteString("Если ты не регистрировался — просто проигнорируй это письмо.
") return wrapHTML(content.String()) } // resetPlain возвращает текстовую версию письма о сбросе пароля. func resetPlain(username, email, password, loginURL string) string { var b strings.Builder b.WriteString(greeting(username)) b.WriteString("\n\n") b.WriteString("Ты запросил сброс пароля в «Вечернем детективе». Новый пароль для входа:\n\n") b.WriteString(" Логин: ") b.WriteString(email) b.WriteString("\n") b.WriteString(" Пароль: ") b.WriteString(password) b.WriteString("\n\n") if loginURL != "" { b.WriteString("Войти в игру: ") b.WriteString(loginURL) b.WriteString("\n\n") } b.WriteString("Никому не сообщай пароль и не пересылай это письмо.\n") b.WriteString("Если это был не ты — ответь на это письмо: мы поможем защитить аккаунт.\n\n") b.WriteString("— Команда «Вечернего детектива»\n") return sanitizeLine(b.String()) } // resetHTML возвращает HTML-версию письма о сбросе пароля. func resetHTML(username, email, password, loginURL string) string { var content strings.Builder content.WriteString("Привет, ") content.WriteString(html.EscapeString(username)) content.WriteString("!
\n") content.WriteString("Ты запросил сброс пароля в «Вечернем детективе». Новый пароль для входа:
\n") content.WriteString(credentialsBlockHTML(email, password)) if loginURL != "" { content.WriteString(buttonHTML(loginURL, "Войти в игру")) } content.WriteString("Никому не сообщай пароль и не пересылай это письмо.
\n") content.WriteString("Если это был не ты — ответь на это письмо: мы поможем защитить аккаунт.
") return wrapHTML(content.String()) } // greeting возвращает приветствие с именем пользователя; при пустом имени — // без обращения, чтобы не было «Привет, !». func greeting(username string) string { username = sanitizeLine(strings.TrimSpace(username)) if username == "" { return "Привет!" } return fmt.Sprintf("Привет, %s!", username) } // credentialsBlockHTML — блок «Логин / Пароль» в табличной вёрстке, // совместимой с Gmail. func credentialsBlockHTML(email, password string) string { return `| Логин | ` + html.EscapeString(email) + ` |
| Пароль | ` + html.EscapeString(password) + ` |
| ` + html.EscapeString(label) + ` |