Работа с MailChimp API. Часть вторая

Ильдар Сарибжанов | 12.04.2017

Вступление

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

Постановка задачи

Нужно чтобы рассылка уходила по предпочтениям пользователя и его географическому положению, предпочтения включают специализацию, а география — это город или возможность удаленной работы. iOS-разработчики из Бобруйска должны получать ios вакансии Бобруйска, а любители удаленной работы должны получать вакансии, где указана возможность удаленного сотрудничества.

Решение

Пока изучал варианты решения первой части поставленной задачи, наткнулся на пост на хабре, где автор рассказывал про сегменты и рассылку по сегментам, реализованную через MailChilm API. Был только один нюанс, он писал свой код в 2012 году, и версия API была 1.3, а сейчас уже 3.0. Там значительно всё изменилось, но некоторая обратная совместимость сохранилась, по крайней мере идеологическая =)

Основная мысль вот в чем: помимо классических листов рассылки в MailChimp можно создавать дополнительные фильтры (сегменты) по листу, при этом сегмент может быть создан по различным признакам, например, все пользователи, которые добавились в рассылку до определенной даты или после, или пользователи email которых находится на gmail.com, или пользователи с именем Александр. Вот за эту идею я и решил ухватиться.

Несмотря на то, что документация по API структурно написана достаточно интуитивно, но вот с примерами там есть некоторая беда, в примерах дается только минимально необходимый набор параметров. Т.е. если для создания компании необходимо передать только html-текст, plain-текст и id листа рассылки, то в примере будут обозначены только эти параметры, а то, что метод может принимать еще вагон дополнительных необязательных параметров, конечно, сказано, но как это сделать не всегда бывает очевидно. Поэтому многое решалось экспериментами.

Несмотря на то, что сегменты могут быть построены по огромному количеству параметров, я не нашел способа добавлять свои атрибуты, т.е. никакое дополнительное кастомное поле создать для пользователя нельзя, что меня несколько огорчило, это было бы самое крутое решение. Тогда я пошел по пути наименьшего сопротивления. При добавлении подписчика дополнительно передаются 2 поля FNAME и LNAME, куда, как логично предположить, должны быть вписаны имя и фамилия, но мы для AppJobs используем только поле FNAME, поэтому LNAME я решил задействовать для собственных нужд.

Мне нужны фильтры! Следовательно в поле LNAME буду писать некоторую служебную информацию. На сайте AppJobs введены специализации: ios-разработчик, andriod-разработчик, дизайнер и т.д — а так же регион в формате страна+город и удаленная работа. Соответственно, первой реализацией фильтров было писать в поле фамилии желаемую специализацию подписчика и регион отдельными словами (забегая вперед: сегменты поддерживают выборку по включению определенного слова), например, ios Россия-Саратов удаленно. Осталось научиться создавать сегменты.

В интерфейсе MailChimp это делается в разделе листов рассылки. Нужно перейти к одному из листов и там в верхней панели выбрать создание сегмента

Раз уж заговорили про ios-разработчика из Саратова, то попробуем создать сегмент для него

Тут все очевидно, нужно чтобы фильтр соответствовал обоим параметрам, а именно, содержал в поле фамилии слово ios и Россия-Саратов.  Contacts match может принимать 2 состояния all и any, т.е. совпадение со всеми правилами или с любым из перечисленных. Нам, конечно нужно жесткое соответствие обоим правилам. После кнопки Preview Segment, в следующем окне будут отображены подписчики, которые удовлетворяют заданным условиям, не забываем сохранить сегмент, если это нужно. Технически, можно все сегменты создать руками, если их не очень много, а потом на стороне сайта обрабатывать их id, но в моем случае сервис ориентирован на все страны СНГ, а это уже сильно больше «нескольких сегментов» — тысячи городов в каждой стране. Вариант создавать сегменты руками — не вариант. Поэтому нужны методы API.

Для создания сегментов существует post метод, который в качестве параметров принимает имя и правила сегментации. Я написал в своем классе обертку для этого метода

/**
 * Создание сегмента
 *
 * @param string $name
 * @param string $list_id
 * @param array  $cond
 *
 * @return mixed
 */
public function createSegment($name = '', $list_id = '', $cond = array())
{
	$data = array('name' => $name, 'options' => $cond);
	
	$res = $this->request('lists/' . $list_id . '/segments', 'post', $data);
	
	return $res;
}

Для нашего случая, правило $cond должно содержать массив

$cond = array(
	'match'      => 'all',
	'conditions' => array(
		array(
			'condition_type' => 'TextMerge',
			'field'          => 'LNAME',
			'op'             => 'contains',
			'value'          => 'ios'
		),
		array(
			'condition_type' => 'TextMerge',
			'field'          => 'LNAME',
			'op'             => 'contains',
			'value'          => 'Россия-Саратов'
		)
	)
);

А если нужны те, кто готов работать удаленно, то правило примет такой вид

$cond = array(
    'match'      => 'all',
    'conditions' => array(
        array(
            'condition_type' => 'TextMerge',
            'field'          => 'LNAME',
            'op'             => 'contains',
            'value'          => 'ios'
        ),
        array(
            'condition_type' => 'TextMerge',
            'field'          => 'LNAME',
            'op'             => 'contains',
            'value'          => 'удаленно'
        )
    )
);

Правила создавать научился. Потестировал создание и отправку рассылок по сегментам несколько раз и наткнулся на багулю. Первый раз рассылка нормально отправляется по сегментам, а второй и последующие чего-то никак. Посмотрел в логи и обнаружил, что при попытке создать сегмент с уже существующим именем, MailChimp возвращает ошибку, т.к. не может пересоздать. Имена я, конечно, создавал типовые, чтобы не плодить сегменты с одинаковыми правилами, но разными заголовками. Вообще, мне было странно такое поведение, я ожидал, что при попытке создать уже существующий сегмент, API мне вернет старый id.

Ну да ладно, раз не хочешь создавать, то давай мне верни по имени сегмента его id. Только вот незадача, он так не умеет, от слова вообще. Можно получить информацию о сегменте по его id, а по имени нельзя получить инфу. Жаль, придется изобрести что-то другое.

Посмотрев немногочисленные методы для работы с сегментами придумалось следующее, забираем из mailchimpa список всех доступных сегментов, пихаем это всё в одномерный массив, где ключ равен id сегмента, а значение равно имени, а потом перед созданием нового сегмента с нашей стороны, проверяем наличие его имени в уже созданных. В общем в класс добавился еще один метод работы с АПИ

/**
 * Получить все сегменты в листе
 *
 * @param string $list_id
 *
 * @return array
 */
public function getListSegments($list_id = '')
{
	$segments = $this->request('lists/' . $list_id . '/segments', 'get');
	
	$res = array();
	
	foreach ($segments->segments as $segment_itm) {
		$res[$segment_itm->id] = $segment_itm->name;
	}
	
	return $res;
}

Вот теперь в классе AppjobsMailchimp есть все необходимые методы, чтобы наша рассылка заработала.

И так: получаю новую вакансию, собираю для этой вакансии сегменты и… Эй! А как прицепить несколько сегментов к одной компании?! Что? Нельзя чтоль? Что за бред?!

Пятиминутка театральной пантомимы окончена.

Я в очередной раз немного лажанул. Оказалось, что к компании нельзя прикрепить несколько сегментов. Я думал, что можно будет просто указать id’шники сегментов, которые мне нужны и оправить рассылку, но в глубине души я подозревал, что такой исход возможен, но надеялся на благополучный исход. Не срослось.

Было принято решение изменить логику набора ключевых слов следующим образом: теперь одним куском будет идти специализация и регион. То есть если раньше были отдельные слова, например, наш мобильный разработчик, который пишет на ios и хочет работать удаленно или в офисе в Саратове, а еще он стал писать под android, поле фамилии будет содержать такую строку: ios android удаленно Россия-Саратов. А теперь буду писать так: ios-удаленно android-удаленно ios-Россия-Саратов android-Россия-Саратов. Немного громоздко, зато одной выборкой можно собрать сегмент для ios разработчиков которые могут работать в офисе в Саратове или удаленно.

$cond = array(
    'match'      => 'any',
    'conditions' => array(
        array(
            'condition_type' => 'TextMerge',
            'field'          => 'LNAME',
            'op'             => 'contains',
            'value'          => 'ios-удаленно'
        ),
        array(
            'condition_type' => 'TextMerge',
            'field'          => 'LNAME',
            'op'             => 'contains',
            'value'          => 'ios-Россия-Саратов'
        )
    )
);

Соответственно, то что раньше требовало 2 различных сегмента, теперь выбирается одним, но с правилом match=any.

И в общем случае скрипт рассылки будет содержать такой код:

// Получим все уже созданные сегменты для листа $list_id
$access_segments = $chimp->getListSegments($list_id);

// Попробуем найти новый сегмент среди созданных, если нет, создадим его
$segment_id = array_search($new_segment_name, $access_segments);
if ($segment_id === false) {
    $new_segment = $chimp->createSegment($new_segment_name, $list_id, $new_segment_cond);
    $segment_id  = $new_segment->id;
}

// Создем компанию
$res = $chimp->createCamping($list_id, $email_subject, $from_name, $reply_email, $segment_id);

$camp_id = $res->id;

// Добавлем к ней контент
$res = $chimp->createCampingContent($plain, $html, $camp_id);

// Отправка рассылки
$res = $chimp->sendCamping($camp_id);

Итог

Наконец-то задача, которую подразумевал заказчик выполнена, теперь точно =) Созданный класс все так же лежит на гитхабе. Надеюсь он пригодится не только мне)

Всем рок!