SMS рассылка через SMPP
Материал из BiTel WiKi
Приведу пример рассылки SMS через SMPP.
Создаем таблицу в базе:
CREATE TABLE `sms_informer` ( `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, `phone_number` varchar(12) NOT NULL, `sent` tinyint(1) NOT NULL DEFAULT '0', `last_operation` datetime DEFAULT NULL, `status` text, `message_id` text, `cmd` text, `data` text, `seq` text, `delivered` tinyint(1) NOT NULL DEFAULT '0', `message_text` text NOT NULL, `processed` tinyint(1) NOT NULL DEFAULT '0', `cid` int(10) UNSIGNED NOT NULL, `source_addr` varchar(7) NOT NULL, `create_dt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', PRIMARY KEY (`id`), KEY `cid` (`cid`) USING BTREE ) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=cp1251;
Добавляем в cron задание на запуск скрипта:
#!/usr/local/bin/perl -w use strict; use DBI; use Data::Dumper; use Time::Local; use Encode; use Net::SMTP; use Fcntl ":flock"; use Net::SMPP; use MIME::Base64; use cyrillic qw/866 uni2win win2koi koi2win win2uni convert locase upcase detect/; my $lockfilename="/var/run/sms.lock"; my $host = ''; my $port = '9932'; my $system_id = ''; my $password = ''; if (!open(LOCKFILE, ">$lockfilename")) { Debug("Can't lock file!\n"); exit(1); }; if (!flock(LOCKFILE, LOCK_EX | LOCK_NB)) { #процесс уже запущен Debug("Process already running!\n") ; exit(1); }; my $MySQLdb = DBI->connect('dbi:mysql:database=bgbilling;host=localhost;port=3306','sms','password'); if ($MySQLdb){ $MySQLdb->do('SET CHARACTER SET cp1251'); $MySQLdb->do('SET NAMES cp1251'); }; my $smpp = Net::SMPP->new_transmitter( $host, port => $port, system_id => $system_id, password => $password, system_type => 'SMPP', #? ); if (!$smpp) { Debug("ERROR (1) Couldn't connect to SMSC\n"); exit(4); } my $res = MySQLSelect( "Get sms tasks", ">=0", "select id, phone_number, message_text, source_addr from sms_informer where processed = false order by phone_number, id and create_dt>=ADDDATE(CURDATE(), -1)"); while (my ($id, $dest, $message_text, $source_addr) = $res->fetchrow){ # print "$id, $dest, $message_text, $source_addr\n"; send_sms($id, $dest, $message_text, $source_addr); }; #sleep 300; close(LOCKFILE); unlink($lockfilename); #exit; ######################################################################## sub send_sms { my $id = shift; my $dest = shift; my $text = shift; my $source_addr = shift; my @parts = split_msg($id, $text); my %part; my $resp_pdu; Debug(get_timestamp()." INFO sending to $dest ($id) SMS '$text'\n"); if ($text =~ m/[^a-zA-Z0-9 \,\.\?\!\;\:\'\"\[\]\{\}\(\)\-\+\=\*\&\^\%\$\#\@\~\`\\\/\<\>\|]/) { $text = convert("win", "uni", $text); } if (scalar(@parts) eq 1) { %part = %{$parts[0]}; $resp_pdu = $smpp->submit_sm( destination_addr => $dest, data_coding => $part{encoding}, short_message => $part{msg}, service_type => '', esm_class => 0x00, source_addr_ton => 0x00, source_addr_npi => 0x00, source_addr => $source_addr #, #registered_delivery => 0x01 ); if (!$resp_pdu) { Debug(get_timestamp()." ERROR (400) Couldn't submit short message.\n"); return 0; } Debug(get_timestamp()." DEBUG dump PDU:\n"); for my $key (keys %{$resp_pdu}) { if (!exists($resp_pdu->{$key}) || !defined($resp_pdu->{$key})){next;}; Debug("$key => ".$resp_pdu->{$key}."\n"); } Debug("-----------------------\n"); } else { for my $item (@parts) { %part = %{$item}; Debug(get_timestamp()." INFO sending part ".$part{seq}." of SMS to $dest\n"); $resp_pdu = $smpp->submit_sm( destination_addr => $dest, data_coding => $part{encoding}, short_message => $part{msg}, service_type => '', source_addr_ton => 0x00, source_addr_npi => 0x00, source_addr => $source_addr, registered_delivery => 0x01, sar_total_segments => scalar(@parts), sar_segment_seqnum => $part{seq}, sar_msg_ref_num => $part{id} ); if (!$resp_pdu) { Debug(get_timestamp()." ERROR (401) Couldn't submit short message.\n"); return 0; } Debug(get_timestamp()." DEBUG dump PDU:\n"); for my $key (keys %{$resp_pdu}) { if (!exists($resp_pdu->{$key}) || !defined($resp_pdu->{$key})){next;}; Debug("$key => ".$resp_pdu->{$key}."\n"); }; Debug("-----------------------\n"); } } my $message_id = (exists($resp_pdu->{message_id}) ? "'".$resp_pdu->{message_id}."'" : "NULL" ); my $seq = (exists($resp_pdu->{seq}) ? "'".$resp_pdu->{seq}."'" : "NULL" ); my $status = (exists($resp_pdu->{status}) ? "'".$resp_pdu->{status}."'" : "NULL" ); my $cmd = (exists($resp_pdu->{cmd}) ? "'".$resp_pdu->{cmd}."'" : "NULL" ); my $data = (exists($resp_pdu->{data}) ? $resp_pdu->{data} : "NULL" ); my $sent = (($status eq "'0'") ? 'true' : 'false'); my $delivered = ( $sent and exists($resp_pdu->{cmd}) and ( $resp_pdu->{cmd} eq 0x80000004 ) ? 'true' : 'false'); if ($data ne "NULL") { $data = encode_base64($data); $data =~ s/\s+//mg; $data = "'$data'"; } #UPDATE my $res2 = MySQLExec( "UPDATE task", "UPDATE sms_informer SET last_operation=NOW(), processed=true, sent=$sent, delivered=$delivered, message_id=$message_id, seq=$seq, status=$status, cmd=$cmd, data=$data WHERE id=$id" ); }; ######################################################################## sub split_msg { my $id = shift; my $text = shift; my $encoding = 0x00; if ($text =~ m/[^a-zA-Z0-9 \,\.\?\!\;\:\'\"\[\]\{\}\(\)\-\+\=\*\&\^\%\$\#\@\~\`\\\/\<\>\|]/) { $encoding = 0x08; } my @parts = (); push @parts, { id => $id, seq => 1, encoding => $encoding, msg => ($encoding eq 0x08 ? convert("win", "uni", $text) : $text) }; return @parts; }; ######################################################################## sub get_timestamp { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdist) = localtime time; return sprintf ("%04d-%02d-%02d %02d:%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min,$sec); }; ######################################################################## sub Debug { my $str = $_[0]; $str =~ s/\n//g; # printf "%-10s\n", $str; }; ####################################################################### sub MySQLSelect{ my ($header, $condition, $SQL_QUERY) = @_; if (!$header) {return -1}; if (!$condition) {return -1}; if (!$SQL_QUERY) {return -1}; my $res = $MySQLdb->prepare($SQL_QUERY); if (!$res->execute){ Debug("Error. Can't $SQL_QUERY"); Debug(DBI::errstr); }else{ my $ntuples = $res->rows; my $op = '$ntuples'."$condition"; my $condition_stat = eval $op; if (!$condition_stat){ Debug("Error result (ntuples: $ntuples, condition: $condition) for $SQL_QUERY"); }; }; return $res; } ######################################################################## sub MySQLExec{ my ($header, $SQL_QUERY) = @_; if (!$header) {return -1}; if (!$SQL_QUERY) {return -1}; if (!$MySQLdb->do($SQL_QUERY)){ Debug("Error. Can't $SQL_QUERY"); Debug(DBI::errstr); }; }; ########################################################################
У нас этот скрипт запускается раз в 10 минут.
Теперь для отправки SMS сообщение необходимо добавить запись в таблицу sms_informer через BGBS или сторонней программы. Например так:
INSERT INTO sms_informer (cid,phone_number,message_text,source_addr,create_dt) VALUES ('111111','79106667788','Privet abonent','0000111',NOW())