SMS рассылка через SMPP
Материал из BiTel WiKi
(Различия между версиями)
Lda (Обсуждение | вклад) (Новая страница: «Приведу пример рассылки SMS через SMPP. Создаем таблицу в базе: <source lang="sql"> CREATE TABLE `sms_informer` ( `i…») |
Lda (Обсуждение | вклад) |
||
Строка 23: | Строка 23: | ||
) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=cp1251; | ) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=cp1251; | ||
</source> | </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 минут. |
Версия 09:46, 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 минут.