SMS уведомления своими руками с помощью smstools

Каждый системный администратор рано или поздно сталкивается с проблемой оповещений о каких-либо значимых событиях или проблемах с оборудованием или службами. Написанные скрипты или такие решения мониторинга как Nagios позволяют диагностировать проблему, но вот после этого системный администратор должен о возникшей проблеме как-то узнать! Хорошо, если под рукой есть почтовый клиент, но что делать когда его нет? Сразу же приходит на ум организация доставки уведомлений через SMS на мобильный телефон. В рунете существуют специальные службы, предоставляющие подобный сервис, но они недёшевы, да ещё и их надёжность и быстродействие остаются под вопросом. Статья рассказывает, как можно быстро и недорого организовать отправку SMS уведомлений, если под рукой есть машина с Linux и не слишком нужный старый сотовый телефон, с COM или USB подключением к компьютеру, предоставляющий COM-порт.

Автор: Виктор Вислобоков
Лицензия: Creative Commons BY-NC-ND


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

Что нужно, чтобы начать

Для работы нам потребуются:

  • Сотовый телефон с подключением к компьютеру. В моём случае это старый, дешёвый и простой Alcatel OT-V670 с USB-интерфейсом к компьютеру. В вашем случае это могут быть и старые телефоны типа Sony Ericsson и любые другие, даже с подключением по COM-порту.
  • Компьютер с Linux
  • SIM-карта к сотовому с тарифом, позволяющим отправлять дешёвые SMS

И вы сами решаете - нужно ли вам, чтобы компьютер был подключен к Интернету, или всё что нужно для отправки SMS вы можете получить в локальной сети или даже на самом компьютере - я имею в виду данные или события, на основании которых нужно отправлять оповещения.

Проверка пригодности сотового телефона

Проверяется очень просто. Подключаем телефон к компьютеру с Linux. Возможно на экране телефона появится просьба выбрать режим, в котором должен работать телефон. Тогда необходимо выбрать "COM порт" или "Модем" или что-то в этом роде - сообщение зависит от модели телефона.

После этого от root'а набираем команду dmesg и смотрим. У меня выдаёт что-то такое:

usb 6-2: new full speed USB device using uhci_hcd and address 3
usb 6-2: New USB device found, idVendor=0e8d, idProduct=0003
usb 6-2: New USB device strings: Mfr=3, Product=4, SerialNumber=5
usb 6-2: Product: OT-V670 
usb 6-2: Manufacturer: Alcatel 
usb 6-2: SerialNumber: 530315205084450
usb 6-2: configuration #1 chosen from 1 choice
cdc_acm 6-2:1.1: ttyACM0: USB ACM device

Интерес представляет как раз последняя строчка, которая говорит о том, что операционная система нашла новое устройство - COM порт через USB, а имя этого нового устройства ttyACM0, а мы тут же проверяем его фактическое наличие:

# ls -al /dev/ttyACM0 
crw-rw----. 1 root dialout 166, 0 Сен 22 14:18 /dev/ttyACM0

Устройство есть, можно идти дальше. Если Linux после подключения телефона не нашёл COM-порта, то вынужден вас огорчить - ищите другой телефон, благо моделей, которые поддерживает Linux предостаточно!

Ставим и настраиваем smstools

Для дальнейшей работы нам понадобится пакет smstools, который поставляется со многими дистрибутивами Linux. Если в вашем дистрибутиве его нет, то вы всегда можете скачать и собрать пакет с сайта: http://smstools3.kekekasvi.com/. О том как собирать пакет рассказывать не буду - не интересно, почитайте доки внутри пакета, если вдруг непонятно как.

Нам понадобится запустить демон smsd, который будет обслуживать отправку SMS, но перед этим его нужно настроить. Опять же настройки хорошо описаны в документации, внутри пакета, но также привожу и свои настройки, которые у меня работают на ура:

devices = GSM1
logfile = /var/log/smsd/smsd.log
loglevel = 7
user = smstools
# 3.1.5 introduced smart logging
# once your configuration is OK, set log level lower (5 is good in most cases)
smart_logging = yes
infofile = /var/run/smsd/smsd.working
pidfile = /var/run/smsd/smsd.pid
incoming = /var/spool/sms/incoming
outgoing = /var/spool/sms/outgoing
receive_before_send = no

[GSM1]
device = /dev/ttyACM0
incoming = no
#pin = 1111
mode = new
baudrate = 19200
rtscts = yes
cs_convert = yes
report = no
memory_start = 1

Особенно если вы не ставили smstool пакетом, а собирали сами, не рекомендуется тупо копировать эти настройки, лучше поймите что есть что и адаптируйте под себя.

Настало время запустить демон, что я с удовольствием и делаю командой:

# /etc/init.d/smsd start

Опять же подчеркну, что данная команда стработает, если у вас дистрибутив типа Fedora/Red Hat, Debian и т.д., т.е. если smstool ставился пакетом из дистрибутива или дополнительного репозитория. Остальным придётся выполнять запуск руками - читайте документацию.

Вот и всё

В принципе, всё готово к работе и вы можете отправить себе тестовую SMS командой:

$ smssend "номер" "текст сообщения"

Инструмент-шлюз E-mail->SMS

А теперь я предоставлю маленький самописный скрипт, который позволит вам забирать сообщения из почтового ящика где-нибудь на яндексе или гугле по протоколу POP3 и отправлять сообщения, пришедшие в этот ящик SMS'кой. Это очень полезно в случае, если диагностические сообщения с разных систем отправляются в какой-либо один ящик по E-mail. Будьте осторожны! Если сообщений будет много - вы можете попасть на бабки сотовому оператору, ведь отправка каждой SMS стоит каких-то, пусть и небольших денег в зависимости от тарифного плана. Вот сам скрипт, который написан на Perl'е и для работы которого вам понадобятся два модуля DB_File или Net::POP3, которые обычно есть в каждом дистрибутиве Linux:

#!/usr/bin/perl

# Copyright(C) Victor Vislobokov, 2011
# Under GNU GPL 2 License

use DB_File;
use Net::POP3;

# имя и пароль для доступа к почтовому ящику
$username = "username";
$password = "password";
# POP3 сервер
$server = "pop.yandex.ru";
# номер сотового куда отправлять SMS
$num = 'XXXXXXXXXX';
# База данных для хранения идентификаторов писем
$db_file = "/var/tmp/sms/id.db";
# Файл журнала для протоколирования действий
$logfile = "/var/tmp/sms/emailtosms.log";

$pop = Net::POP3->new($server, Timeout => 60);
$rc = $pop->login($username, $password);
if ($rc > 0) {
  # Открываем БД сохранённых сообщений
  $DB = tie(%hash_id, "DB_File", $db_file);

  # Получаем список сообщений
  $msgnums = $pop->list;
  foreach my $msgnum (keys %$msgnums) {
    
    # Получаем уникальный ID сообщения
    $uid = $pop->uidl($msgnum);
    
    # Отмечаем это сообщение в хеше текущих в данной сессии
    $msgs{$uid} = 1;
    
    # Проверяем не было ли уже обработано данное сообщение
    # Если обработано, идём дальше
    if (defined $hash_id{$uid}) {
      writeLog($logfile, "$uid already processed");
      next;
    }
    
    # Если нет, обрабатываем
    $hash_id{$uid} = 1;

    $msg = $pop->get($msgnum);
    foreach $line (@$msg) {
      if (index($line, "Subject: ") == 0) {
        $sms = substr($line, 9, 50);
        chomp($sms);
        $stamp = getCurrentDateTime();
        writeLog($logfile, "SMS '$sms' sended");
        system("/usr/bin/smssend \"$num\" \"$stamp $sms\"");
      }
    }
#    $pop->delete($msgnum);
  }
} elsif ($rc == 0) {
  writeLog($logfile, "No messages in mailbox");
} else {
  writeLog($logfile, "Login failed");
}

$pop->quit;

# Удаляем из базы старые ID
foreach $uid (keys %hash_id) {
  # Если ID из базы есть в текущей сессии, оставляем
  next if (defined $msgs{$uid});
  DB_File::DELETE($DB, $uid);
  writeLog($logfile, "$uid was been deleted");
}
untie(%hash_id);

# ----------------------------------------------------------------------------
# Получение текущей даты и времени
# ----------------------------------------------------------------------------
sub getCurrentDateTime {
  my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time());
  $mon++;
  $year=$year-100+2000;
  return(sprintf("%.4d-%.2d-%.2d %.2d:%.2d:%.2d", $year, $mon, $mday, $hour, $min, $sec));
}

# ----------------------------------------------------------------------------
# Запись в лог
# ----------------------------------------------------------------------------
sub writeLog {
  my ($logfile, $msg) = @_;

  if (!defined $logfile) {
    $logfile = "/dev/null";
  }
  
  my ($datetime, $fd, $rc);
  $datetime = getCurrentDateTime();
  
  $rc = open($fd, ">>$logfile");
  if (!defined $rc) {
    if ($main::config{'DEBUG'} > 0) {
      # При невозможности вывода в файл, выводим на стандартный ввод
      print "$msg\n";
    }
  } else {
    print($fd "$datetime ");
    print($fd "$msg");
    print($fd "\n");
    close($fd);
  }
}

Работа скрипта проста. Вы запускаете его по cron с нужной вам периодичностью. Скрипт лезет в почтовый ящик на сервере и забирает оттуда письма. Для каждого письма у POP3 сервера есть свой уникальный идентификатор, мы его получаем и проверяем нет ли у нас его в базе. Если есть, пропускаем письмо, так как считаем, что оно уже обработано, если идентификатора в базе нет, то мы сохраняем его туда. Затем получаем с сервера само сообщение, но используем только его тему (Subject:) для отправки по SMS. При этом не всю тему, а только 50 знаков, чтобы не превысить размер SMS. Затем отправяем SMS, предварительно сформировав текущее время, чтобы при получении было ясно, когда была отправка. После осуществления отправок всех SMS, чистим базу от старых идентификаторов сообщений, если сообщений с такими идентификаторами в ящике уже нет. Скрипт не удаляет само сообщение из ящика - этот вызов закомментирован.

Если вам по какой-то причине неудобна такая работа скрипта или вы хотите, что-либо изменить, пожалуйста - скрипт перед вами, правьте под свои нужды.

Заключение

Но ведь за отправку SMS нужно платить деньги, в чём же выйгрыш по сравнению с аналогичными платными службами в рунете, спросите вы? Да, безусловно, надо. Но если отправка одной SMS через службу вам обойдётся в 1.5-2 рубля, то вы без труда сможете найти у сотового оператора тарифный план или тарифную опцию, которые позволят существенно снизить стоимость отправки ваших SMS. Да и уверенность в том, что отправка пройдёт вовремя, без задержек на порядок выше.

Расширение возможностей скрипта

День добрый!

У меня возник вопрос по скрипту email2sms. Как можно расширить возможности скрипта, что-бы он отсылал не саму тему письма по смс, а например, емаил отправителя и часть текста самого письма (например 100 знаков). Я особо не разбираюсь в программировании на Перле, прошу помочь мне. Заранее благодарен.

Сергей