From 76ace6a13892403b57582a3b597bccdee7e453b6 Mon Sep 17 00:00:00 2001 From: Mike Rylander Date: Sun, 28 Sep 2014 16:37:33 -0400 Subject: [PATCH] LP#1375043: Support for in-A/T telephony configuration The AstCall reactor module creates a callfile for Asterisk, given a template describing the message and an environment defining necessary information for contacting the Asterisk server and scheduling a call with it. If you have only one SIP server, you can set it up like this in the opensrf.xml configuration file: 0 SIP Zap/1 Zap/2 IAX/user:secret@widgets.biz localhost 10080 evergreen evergreen To support more than one SIP server, say, per library, you can use Action/Trigger parameters like these, which model the same information as above: enabled = 0 driver = "SIP" channels = ["Zap/1", "Zap/2", "IAX/user:secret@widgets.biz"] host = "localhost" port = "10080" user = "evergreen" pw = "evergreen" callfile_lines = ["MaxRetries: 3", "RetryTime: 60", "WaitTime: 30", "Archive: 1", "Extension: 10"] Co-author credit goes to Steve Callender, who helped build this patch. Signed-off-by: Mike Rylander Signed-off-by: Galen Charlton --- .../OpenILS/Application/Trigger/Reactor/AstCall.pm | 144 ++++++++++++++++----- 1 file changed, 114 insertions(+), 30 deletions(-) diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor/AstCall.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor/AstCall.pm index dea456b8bd..7ea0198668 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor/AstCall.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor/AstCall.pm @@ -14,71 +14,147 @@ $Data::Dumper::Indent = 0; my $U = 'OpenILS::Application::AppUtils'; -# $last_channel_used is: +# %last_channel_used is, per event def with params or the config file: # ~ index (not literal value) of last channel used in a callfile -# ~ index is of position in @channels (zero-based) +# ~ index is of position in the array of channels (zero-based) # ~ cached at package level # ~ typically for Zap (PSTN), not VOIP -our @channels; -our $last_channel_used = 0; +our %last_channel_used = (); our $telephony; sub ABOUT { - return < + + 0 + SIP + + + Zap/1 + Zap/2 + IAX/user:secret@widgets.biz + + localhost + 10080 + evergreen + evergreen + + + + + To support more than one SIP server, say, per library, you can use + Action/Trigger parameters like these, which model the same information + as above: + + enabled = 0 + driver = "SIP" + channels = ["Zap/1", "Zap/2", "IAX/user:secret@widgets.biz"] + host = "localhost" + port = "10080" + user = "evergreen" + pw = "evergreen" + callfile_lines = ["MaxRetries: 3", "RetryTime: 60", "WaitTime: 30", "Archive: 1", "Extension: 10"] + ABOUT } sub get_conf { + my $part = shift; + my $env = shift; + + # get the part they want from the environment, if we have it + return $env->{params}{$part} if ( $part && $env && exists $env->{params}{$part}); # $logger->info(__PACKAGE__ . ": get_conf()"); - $telephony and return $telephony; - my $config = OpenSRF::Utils::SettingsClient->new; - # config object cached by package - $telephony = $config->config_value('notifications', 'telephony'); + + # failing all of that, just fetch the config file if we don't have it + if (!$telephony) { + my $config = OpenSRF::Utils::SettingsClient->new; + # config object cached by package + $telephony = $config->config_value('notifications', 'telephony'); + } + + # if they want a part, and we have the config file data, return that + return $$telephony{$part} if ( $part && $telephony && exists $$telephony{$part}); + + # but if they don't want a part, and we have the whole config file thing, return it return $telephony; } +sub channels_from { + my $env = shift; + + # report the event def id if we get the channels from params + return $env->{EventProcessor}{event}->event_def->id + if ( exists $env->{params}{channels}); + + # else just say '*' + return '*'; +} + sub get_channels { - @channels and return @channels; - my $config = get_conf(); # populated $telephony object - @channels = @{ $config->{channels} }; - return @channels; + my $env = shift; + @{ get_conf( channels => $env ) }; } sub next_channel { + my $env = shift; # Increments $last_channel_used, or resets it to zero, as necessary. # Returns appropriate value from channels array. - my @chans = get_channels(); + my $source = channels_from($env); + my @chans = get_channels($env); unless(@chans) { $logger->error(__PACKAGE__ . ": Cannot build call using " . (shift ||'driver') . ", no notifications.telephony.channels found in config!"); return; } - if (++$last_channel_used > $#chans) { - $last_channel_used = 0; + if (++$last_channel_used{$source} > $#chans) { + $last_channel_used{$source} = 0; } - return $chans[$last_channel_used]; # say, 'Zap/1' or 'Zap/12' + return $chans[$last_channel_used{$source}]; # say, 'Zap/1' or 'Zap/12' } sub channel { - my $tech = get_conf()->{driver} || 'SIP'; + my $env = shift; + my $tech = get_conf( driver => $env ) || 'SIP'; if ($tech !~ /^SIP/) { - return next_channel($tech); + return next_channel($env, $tech); } return $tech; # say, 'SIP' or 'SIP/ubab33' } sub get_extra_lines { - my $lines = get_conf()->{callfile_lines} or return ''; + my $env = shift; + my $lines = get_conf( callfile_lines => $env ) or return ''; + return '' if (ref($lines) && (ref($lines) !~ /ARRAY/)); + $lines = [ split "\n", $lines ] unless (ref($lines)); + my @fixed; - foreach (split "\n", $lines) { + foreach (@$lines) { s/^\s*//g; # strip leading spaces /\S/ or next; # skip empty lines push @fixed, $_; @@ -88,18 +164,26 @@ sub get_extra_lines { } sub host_string { - my $conf = get_conf(); - my $host = $conf->{host}; + my $env = shift; + my $host = get_conf( host => $env ); + my $port = get_conf( port => $env ); + unless ($host) { $logger->error(__PACKAGE__ . ": No telephony/host in config."); return; } + $logger->info(__PACKAGE__ . ": host [$host], port [$port]"); # prepend http:// if no protocol specified - $host =~ /^\S+:\/\// or $host = 'http://' . $host; + if ($host !~ /^\S+:\/\//) { + $host = 'http://' . $host; + } # append port number if specified - $conf->{port} and $host .= ":" . $conf->{port}; + if ($port) { + $host .= ":" . $port; + } + $logger->info(__PACKAGE__ . ": final host string [$host]"); return $host; } sub rpc_client { @@ -117,12 +201,12 @@ sub handler { $logger->info(__PACKAGE__ . ": entered handler"); # assignment, not comparison - unless ($env->{channel_prefix} = channel()) { + unless ($env->{channel_prefix} = channel($env)) { $logger->error(__PACKAGE__ . ": Cannot find tech/resource in config"); return 0; } - $env->{extra_lines} = get_extra_lines() || ''; + $env->{extra_lines} = get_extra_lines($env) || ''; my $tmpl_output = $self->run_TT($env); if (not $tmpl_output) { $logger->error(__PACKAGE__ . ": no template input"); @@ -160,7 +244,7 @@ sub handler { # TODO: add scheduling intelligence and use it here... or not if # relying only on crontab - my $client = rpc_client(); + my $client = rpc_client(host_string($env)); my $resp = $client->send_request( 'inject', $tmpl_output, $filename_fragment, 0 ); # FIXME: 0 could be seconds-from-epoch UTC if deferred call needed @@ -302,8 +386,8 @@ sub cleanup { } sub retrieve { - $logger->info("retrieve() not implemented. how'd we get here?"); # XXX - return; + $logger->info("retrieve() not implemented. how'd we get here?"); # XXX + return; } #sub retrieve { -- 2.11.0