Skip to content

21 Август 2011

3

Софт для мейл рассылки

Mail

Не 1 хотя бы чуть-чуть серьезный проект не обходится без e-mail рассылки. О них и пойдет речь. Это средство оповещения ваших посетителей/пользователей/клиентов/покупателей о жизни вашего проекта. Новости, обновления, изменения… Нет, не спам. Обычные рассылки, возвращающие добрый процент старых посетителей.

И не смотря на то, что вроде бы уже рассылки «устарели», они нужны, как базовый шаг в вашей маркетинговой компании.

Mail Sender‘ов в сети — полно. Однако все те что пробовал — не подошли мне. :-S  Либо это были Win32 приложения, либо они не работали, либо работали медленно, либо был скудный функционал. А функционал нужен был мне минимальный: поддержка html и возможность вставлять в письмо любые данные. Например, логин пользователя, или его имя. Привет, {login}. Как оказалось — для большинства «сендеров» обработать такой шаблон — проблема.

:?:  В то время я как раз работал с очень хорошей сокет-библиотекойhttp://www.alhem.net/Sockets/. Рекомендую всем, для небольших работ, где важен размер приложения и не хочется тащить за собой всякие тяжелые библиотеки типа ACE или Qt. При своей невероятной легкости (статическая линковка) — она имеет в себе отличный арсенал. Многопоточные серверные обработчики, семафоры, поддержка SSL, и еще много всего вкусного. Также эта либа кроссплатформенна, что было важно для меня, так как софт mailsender’а должен был работать на Linux платформе.

Итак, представляю вам получившийся сендер. :cool:  Код местами без cleanup’а, и бывает довольно замороченный. Проект тащит за собой только 1 либу сокетов, о которой я говорил выше.

Возможности сендера:

  • :!:  Гибкая настройка параметров отправки
  • :!:  Возможность использовать любой html шаблон
  • :!:  Возможность использовать авто-подставляемые значения в шаблон, без ограничения их количества

:?: Исходники открыты для всех желающих: https://github.com/Sharm/mailsender

Самое интересно место — это работа с самим SMTP сервером. Кстати, он понадобиться вам для рассылки. О его правильной настройке и установке будет другая статья. Так вот как выглядит непосредственная работа с SMTP через Socket Lib Alhem’а:

switch (num){
		case 220:
			{
				std::stringstream buf;
				buf << "HELO " << sConf.StrValue[STR_SMTP_HOST].c_str() << "\r\n";
				Send(buf.str());
				break;
			}
		case 250:
			{
				std::string cmd = Utility::ToUpper(pa.getword());

				if (!strcasecmp(cmd.c_str(),"OK") && m_isDataSend){ // 5
					Send("QUIT\r\n");
					SetCloseAndDelete();
					static_cast<ClientHandler&>(Handler()).Release();
					static_cast<ClientHandler&>(Handler()).OkEmail(m_email);
				}

				else
				if (!strcasecmp(cmd.c_str(),"Accepted")) // 3
					Send("DATA\r\n");

				else
				if (!strcasecmp(cmd.c_str(),"OK") && !m_isDataSend){ // 2
					std::stringstream buf;
					buf << "RCPT TO:<" << m_email.c_str() << ">\r\n";
					Send(buf.str());
				}

				else{ // 1 в cmd - каша
					std::stringstream buf;
					buf << "MAIL FROM:<" << sConf.StrValue[STR_REAL_FROM].c_str() << ">\r\n";
					Send(buf.str());
				}

				break;
			}
		case 354:  // 4
			{
				std::stringstream buf;
				buf << "Precedence: bulk\r\n";
				buf << "X-Mailer: SharmMailer\r\n";
				buf << "To: " << m_email.c_str() << "\r\n";
				buf << "From: " << sConf.StrValue[STR_FROM].c_str() << "\r\n";
				buf << "Reply-To: " << sConf.StrValue[STR_REPLY_TO].c_str() << "\r\n";
				buf << "Subject: " << m_subj.c_str() <<"\r\n";
				buf << "Content-Type: text/html;\r\n";
				buf << m_html.c_str() << "\r\n.\r\n";

				Send(buf.str());

				m_isDataSend = true;
				break;
			}
		case 550: // 3 if email incorrect
		case 451:
		case 501:
			{
				Send("QUIT\r\n");
				SetCloseAndDelete();
				static_cast<ClientHandler&>(Handler()).Release();
				static_cast<ClientHandler&>(Handler()).BadEmail(m_email);
				break;
			}
		default:
			{
				sLog.outError("Unsupported command!");
				Send("QUIT\r\n");
				SetCloseAndDelete();
				static_cast<ClientHandler&>(Handler()).Release();
				break;
			}
	}

Тут освещена небольшая часть протокола SMTP, на котором приходится общаться. Цифры в комментариях описывают правильную последовательность обработки команд.

Немного о структуре кода, для тех кто будет в этом разбираться:

sConf — Класс для работы с конфиг-файлом. Я написал его реализацию очень давно, поэтому просто взял из старых проектов. Конфиг нужен всегда, даже в самой маленькой программе. Возможно он написан не идеально, поэтому планирую посвятить ему отдельную статью и дописать пару вещей.

sLog — Тоже самое. Мой старый класс для работы с консолью и логирования.

Все остальное — работа с сокет-библиотекой. Разобраться в ней будет не сложно — на офф сайте есть куча примеров и статей по использованию. Возможно и тут когда-нибудь появится статья про нее.

Рассмотрим конфигурационный файл программы с комментариями:

LogFile = Log.log // Пусть к лог файлу
Const LogFile = 1 // Обновлять лог-файл при каждом перезапуске? Если 1 - то не обновлять. Лог постоянный.
LogDebug = 1 // Логировать вывод, сделанный через outDebug() ?

SMTP Host = localhost // Хост SMTP сервера
SMTP Port = 25 // Порт SMTP сервера

Mail Real From = support@domain.ru // Это реальная почта через которую будет идти сообщение. Ее может не существовать, но домен существовать должен, если вы не хотите попадать в спам.
Mail From = "Super Puper Site" <noreply@site.ru> // Поле "От:" которое увидят получатели.
Mail Reply-to = support@domain.ru // А эта почта будет подставляться в поле "Кому:" при ответе на вашу рассылку.

Итак, теперь мы уже можем настроить наш сендер для рассылки. Осталось как-то сказать ему кому и что мы будем рассылать. В структуре программы есть 2 папки: /list и /template. В list мы должны положить текстовый файл с перечнем email адресов и параметрами для вставки в шаблон. Например, файл может выглядеть так:

mail1@gmail.com Petya 20
mail2@mail.ru Vasya 12
test@testinga.net Seva 8

Текст разделяется пробелами. Первая часть — e-mail получателя. Дальше через пробел перечисляются все параметры, которые мы будем вставлять в html шаблон.

Шаблон верстаем и кладем в template. Выглядеть он может так:

<p>Вниманию пользователя <strong>{0}</strong>!</p>
<p>С Вашего личного счета на <strong><em>Super Puper Site</em></strong>было снято {1}$ в счет продления услуги...</p>

Параметры нумеруются от {0} до бесконечности.

После того как список получателей и шаблон готовы — остается только запустить программу. Запускается через командную строку со следующими параметрами:

./mailsender /home/mailsender/list/list.txt /home/mailsender/template/template.html Subject Of The Letter

После работы генерируются списки bad_emails и send_emails. Они помогут установить сколько писем уже отправлено в случае непредвиденного завершения работы программы. А в bad_emails попадут адреса с несуществующими доменами.

:!: Исходники открыты для всех желающих: https://github.com/Sharm/mailsender

Еще из рубрики C/C++
3 коммент.
  1. 12 Окт 2011

    Очень сильно не хватает поддержки изменения заголовков письма. Например, по умолчанию установлен Content-Type: text/html, а для рассылки требуется указывать кодировку письма (UTF-8, к примеру), иначе письмо будет некорректно отображаться в почтовх клиентах (проверено). также может понадобиться base64 кодирование, для которого требуется установить свой заголовок

  2. Sharm
    12 Окт 2012

    upd.
    + Исходники софта выложены на github: https://github.com/Sharm/mailsender

Обсуждение закрыто.