Закрыть
E-mail:
Пароль:
Забыли пароль?
В каталоге проекта: 11 868 веб-студий, 935 CMS, 233 298 сайтов.
РегистрацияCMS MagazineВход
CMS Magazine CMS Magazine

S.Builder 4.0: Система личных сообщений и почтовый ящик: работаем вместе.

Упоминаемые CMS

Недавно я столкнулся с необходимостью организации работы службы технической помощи пользователям. Наверняка Вы использовали подобные системы – помните, когда вы получали от Вашего провайдера примерно следующее письмо: «Ваше сообщение получено нашими сотрудниками. Ему присвоен номер 123. В ближайшее время мы Вам ответим». Сообщение стоит в очередь и как только освобождается менеджер, оно отправляется ему в работу. Как  сделать такую систему?

За основу, как и всегда, я беру систему управления сайтом S.Builder

Описание API интерфейса можно найти по адресу – http://api.sbuilder.ru/

Мануал по языку PHP – http://www.php.net/

Итак, поехали…

Прежде всего, хочу сказать, что для того , чтобы моя реализация задачи работала необходимо, чтобы PHP был скомпилирован с включенным модулем IMAP. Для того, чтобы проверить выводим phpinfo() и проверяем параметр Configure Command. Должны быть следующие строки: '--with-imap-ssl' '--with-imap=/usr/lib/dovecot/imap'. Скрипт будет работать в кроне.

Этот скрипт будет являться частью моей CRM – системы. Поэтому положим его в директорию cms/plugins/pl_crm/prog/. Назовем его post.php.

Начнем:

01 <?php

02 require_once '../../../kernel/prog/header.inc.php';

03 $from_user="7";

04 $to_user="1";

05 $server = "{127.0.0.1:143}INBOX";

06 $email = "support@***.ru";

07 $email_pass = "******";

Самая первая строка – подключение ядра системы S.Builder. Подключаем для того, чтобы использовать API. $from_user – переменная (в будущем вынесем ее в настройки), которая обозначает ID пользователя, от которого внутри системы будет приходить сообщение, $to_user – аналогично, только пользователь, принимающий сообщения.

$server, $email, $email_pass – тут я думаю все ясно. Первая переменная – сервер, к которому происходит подключение и папка с общениями, которую открываем. Вторые две – логин и пароль. Далее:

01 $mbox = imap_open($server, $email, $email_pass);

02 $auto_replay = false;

03 $inbox = imap_mailboxmsginfo($mbox);

Первая строка – соединение по IMAP с почтовым ящиком и выбор директории, указанной в настройках. Второе – системная настройка (надо ли отправлять auto-replay на сообщение). Третья строка – получение состояния почтового ящика на сервере (количества почтовых сообщений, дату и другие параметры). Далее создаем цикл:

01 for($i=1;$i<=$inbox->Nmsgs;$i++)

02 {

03            $header = imap_headerinfo($mbox, $i);

04

05            $subj = imap_utf8($header->Subject);

06            $sender = imap_utf8($header->fromaddress);

07            $date = $header->Date;

08            $charset = '';

09            $body = '';

10

11            $body=imap_fetchbody($mbox,$i,1,0);

12            $s = imap_fetchstructure($mbox, $i);

13            $q = 1;

14            if(count($s->parts) > 0)

15            foreach ($s->parts as $key => $val)

16            {

17                           $r = getMsg($mbox, $i, $val, $q);

18                           switch ($r['type'])

19                           {

20                                           case 'text':

21                                           $body .= $r['res']."<hr />";                                  

22                                           break;

23                                                          

24                                           case 'attach':

25                                           $body .= "К сообщению приложен файл: <a href='/uploads/pl_crm_mail/{$r['res']}' target='_blank'>{$r['res']}</a><hr />";

26                                           break;

27                           }

28                           $q++;

29            }

30            else

31            {

32                           $r = getMsg($mbox, $i, $s, 1);

33                           $body .= $r['res']."<hr />";                                  

34            }

35

36            if(preg_match("/^\[TID#([^<]*)\]/", $subj))

37            {

38                           // В заголовке есть TID, надо проверить к какому сообщению относится

39            }

40            else

41            {

42                           // В заголовке нет TID - письмо новое.

43                           $auto_replay = true;

44                           $auto_replay_to = $header->from[0]->personal." &lt;".$header->from[0]->mailbox."@".$header->from[0]->host."&gt;";

45                           $body .= "Replay to: ".$auto_replay_to.";<hr />";

46                           sql_param_query("INSERT INTO sb_users_messages (um_to_id, um_from_id, um_time, um_title, um_message, um_look, um_parent_id) VALUES ($to_user, $from_user, UNIX_TIMESTAMP(NOW()), ?, ?, 0, 0)", $subj, $body);

47                           $auto_replay_id = sql_insert_id();

48                           sql_param_query("UPDATE sb_users_messages SET um_title = ? WHERE um_id = ?d", "[TID#".$auto_replay_id."] ".$subj, $auto_replay_id);

49                           imap_delete($mbox, $i);

50            }

51 }

Что происходит в этом цикле? $inbox->Nmsgs обозначает количество сообщений в почтовом ящике, его мы и получаем. Далее по очереди обрабатываем каждое сообщение – в строках 03-07 мы получаем заголовки сообщения, выделяем из них тему письма, отправителя и дату отправления и кодируем все это в utf-8. Функцией $s = imap_fetchstructure($mbox, $i); мы получаем структуру сообщения (она понадобится далее – сообщение может быть с аттачментом и т.п.).

В строке 14 мы проверяем – состоит сообщение из одной части или из нескольких частей. В зависимости от этого, мы по-разному получаем его функцией getMsg(). Про нее я расскажу Вам позже. Забегая вперед , могу сказать, что функция возвращает массив, в котором содержится тип и собственно значение. Типов всего 2 – attach (наименование файла в attach`е сообщения) и text (текст письма).

Далее в строках 14-34 мы обрабатываем сообщение и его аттачмент. Если это простое сообщение – просто получаем его текст, если сложное – к тексту внизу дописываем ссылки на файлы в аттаче, которые функция getMsg() сохраняет в указанную нами директорию.

Далее у нас есть все составляющие для регистрации почтового запроса – заголовки, от кого пришло сообщение и его текст с аттачами. Остался один небольшой нюанс – выяснить, новое ли это сообщение или ответ на старое (которому уже присвоен номер запроса). Для этого мы используем регулярное выражение в строке 36.

Я не буду описывать в подробностях регистрацию отправления, опишу лишь один случай – письмо новое (необходимо присвоить ему номер, записать в базу данных, сохранить аттачменты и отправить пользователю автоматическое сообщение о том, что его запрос в работе). Для этого мы используем строки 43-49. Первым делом мы устанавливаем флаг auto_replay. Он означает, что в конце выполнения обработки сообщения необходимо отправить автоматический ответ пользователю. После этого мы получаем адрес отправившего запрос. К сожалению текущая версия модуля личных сообщений не позволяет записать его в отдельное поле – поэтому пойдем на уловку. Запишем его в конец сообщения. (строка 45). Далее идет запись в базу самого сообщения функцией sql_param_query, После того, как мы записали сообщение, у нас появляется его уникальный id. Надо добавить его в заголовок – что мы и делаем в строке 48. Сообщение обработано, строка 49 удаляет его из почтового ящика (зачем оно нам?).

Теперь разберемся с функцией getMsg() и автоматическим ответом пользователю.

01 function getMsg($stream, $msg, $s, $q)

02 {

03            switch ($s->type)

04            {

05                           case 0:

06                                           $body = imap_fetchbody($stream, $msg, $q, 0);

07

08                                           foreach ($s->parameters as $key => $value)

09                                           {

10                                                           if($value->attribute == 'charset')

11                                                           $charset = $value->value;

12                                           }

13                                           //

14

15                                           if($charset == '')

16                                           {

17                                                           $charset = mb_detect_encoding($body);

18                                           }

19

20                                           if($charset != 'utf-8')

21                                           $body=mb_convert_encoding($body, "UTF-8", $charset);

22

23                                           $res['type'] = 'text';

24                                           $res['res'] = $body;

25                                           break;

26

27                           default:

28                                           $body = imap_fetchbody($stream, $msg, $q, 0);

29                                           if($s->encoding == 3)

30                                           {

31                                                           $body = imap_base64($body);

32                                           }

33                                           elseif ($s->encoding == 4)

34                                           {

35                                                           $body = imap_qprint($body);

36                                           }

37

38                                           $id = uniqid();$name = imap_utf8($s->parameters[0]->value);

39                                           file_put_contents($GLOBALS['dir']."uploads/pl_crm_mail/".$id.”-“.$name, $body);

40

41                                           $res['type'] = 'attach';

42                                           $res['res'] = $id."-".$name;

43                                           break;

44            }

45            return $res;

46 }

Итак, эта функция определяет, что за сообщение пришло и возвращает нам его. Если тип сообщения 0 (сообщение без аттачмента) то функция декодирует его в кодировку utf-8. Определение кодировки сообщения происходит по параметрам сообщения. Если вдруг параметров нет – используется функция mb_detect_encoding() модуля mb_string. Возвращается тип «text» и собственно текст сообщения в utf-8.

В другом случае мы считаем, что сообщение пришло с аттачем. В этом случае в зависимости от кодировки мы декодируем аттачмент, создаем уникальный id (на случай если в один момент пришло два письма с одинаковым названием файла) и пишем атачмент в файл. Возвращается тип «attach» и название файла.

Остался последний момент – отправка автоответа пользователю. В цикле будет такой код:

01 $if($auto_replay)

02 {

03            $auto_replay_message = "Здравствуйте!\n\nВаше письмо принято службой технической поддержки.\nВашему запросу присвоен уникальный идентификатор - $auto_replay_id.\nВ дальнейшем Вы можете продолжить эту тему переписки, если будете отвечать на письма, используя функцию Reply to Sender Вашей программы чтения почты, сохраняя в поле subject строку вида [TID#$auto_replay_id] без изменений.\n";

04            $auto_replay_subj = "[TID#".$auto_replay_id."] ".$subj;

05

06            $mail->setText($auto_replay_message);

07            $mail->setSubject($auto_replay_subj);

08            $to = array($auto_replay_to);

09            $mail->send($to, true);

10 }

А до цикла надо прописать:

01 $mail = new sbMail();

02 $mail->setFrom("Support <$email>");

03 $mail->setReturnPath($email);

Во втором фрагменте мы инициализируем объект SBuilder`а, который отвечает за отправку писем, и устанавливаем параметры. В первом фрагменте задаем оставшиеся фрагменты письма. Второй флаг в методе $mail->send() означает что сообщение нужно собирать каждый раз при отправке (ведь у нас меняется часть его параметров.

То, что я здесь описал – это скелет, то с чего можно начать написание серьезной системы обработки сообщений. В качестве перспектив можно сделать настройки, по которым сообщения будут распределяться:

  • если компания зарегистрирована в CRM и у нее стоит контактный менеджер, то сообщение, присланное на support@***.ru, попадет в руки контактному менеджеру;

     

  • распределение по принципу обработанных сообщений (в системе можно оценить на какие сообщения менеджер ответил – если он свободен, то сообщение приходит ему, если нет – следующему менеджеру и т.п.);

     

  • распределение сообщений по группам пользователей (отделам компании);

     

Все настройки данного скрипта надо выносить в настройки системы и т.п. Если у Вас есть необходимость и желание – дорабатывайте! Я подскажу и помогу ;)

Если у кого-то есть вопросы по статье – пишите m@iqcompany.ru. Буду рад услышать отзывы и предложения по темам статей.

Максим Гасумянц (Компания «IQ - Разработка сайтов»)

Автор: Максим Гасумянц


CMS Magazine CMS Magazine
Реклама
RSS-подписка
CMS Magazine CMS Magazine
CMS Magazine