SMS рассылка через SMPP
Материал из BiTel WiKi
(Различия между версиями)
												
			
		| Lda  (Обсуждение | вклад)  (Новая страница: «Приведу пример рассылки SMS через SMPP.  Создаем таблицу в базе: <source lang="sql">  CREATE TABLE `sms_informer` (   `i…») | Lda  (Обсуждение | вклад)  | ||
| (3 промежуточные версии не показаны) | |||
| Строка 22: | Строка 22: | ||
|    KEY `cid` (`cid`) USING BTREE |    KEY `cid` (`cid`) USING BTREE | ||
| ) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=cp1251; | ) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=cp1251; | ||
| + | </source> | ||
| + | |||
| + | Добавляем в cron задание на запуск скрипта: | ||
| + | <source lang="perl"> | ||
| + | #!/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); | ||
| + |   }; | ||
| + | }; | ||
| + | ########################################################################		                                                | ||
| + | </source> | ||
| + | У нас этот скрипт запускается раз в 10 минут. | ||
| + | |||
| + | Теперь для отправки SMS сообщение необходимо добавить запись в таблицу sms_informer через BGBS или сторонней программы. | ||
| + | Например так: | ||
| + | <source lang="sql">  | ||
| + | INSERT INTO sms_informer (cid,phone_number,message_text,source_addr,create_dt) VALUES ('111111','79106667788','Privet abonent','0000111',NOW()) | ||
| </source> | </source> | ||
Текущая версия на 12:55, 20 сентября 2010
Приведу пример рассылки 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())
