SMS рассылка через SMPP

Материал из BiTel WiKi

(Различия между версиями)
Перейти к: навигация, поиск
(Новая страница: «Приведу пример рассылки SMS через SMPP. Создаем таблицу в базе: <source lang="sql"> CREATE TABLE `sms_informer` ( `i…»)
 
(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())
Личные инструменты