File Coverage

blib/lib/POE/Component/Telephony/CTPort.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package POE::Component::Telephony::CTPort;
2              
3 1     1   21414 use strict;
  1         3  
  1         32  
4              
5 1     1   4 use vars qw($CTSERVER_RUN $VERSION $Default);
  1         3  
  1         119  
6              
7             $VERSION = '0.03';
8              
9             $CTSERVER_RUN = 0;
10              
11             $Default = {
12             DEBUG => 0,
13             alias => 'ctport',
14             ctserver => 'ctserver',
15             paths => [],
16             default_ext => '.au',
17             hostname => 'localhost',
18             port => 1,
19             reply_to => undef, # don't touch
20             ignore_dtmf => 0,
21             reconnect => 5,
22             manager_port => undef,
23             manager_d => -2,
24             };
25              
26 1     1   1024 use POE qw(Wheel::SocketFactory Wheel::ReadWrite Driver::SysRW Filter::Line Filter::Stream);
  1         55495  
  1         7  
27 1     1   119236 use Socket;
  1         2  
  1         607  
28 1     1   400 use Proc::ProcessTable;
  0            
  0            
29             use File::Basename qw(basename);
30             use IO::Pty; # for Wheel::Run as a pty
31              
32             =pod
33              
34             =head1 NAME
35              
36             POE::Component::Telephony::CTPort - Non-blocking telephony programming in Perl
37              
38             =head1 SYNOPSIS
39              
40             use POE qw(Compoent::Telephony::CTPort);
41            
42             POE::Session->create(
43             inline_states => {
44             _start => sub {
45             my $kernel = $_[KERNEL];
46            
47             POE::Component::Telephony::CTPort->spawn({
48             alias => 'ctport',
49             port => 1,
50             });
51            
52             $kernel->post(ctport => 'connect');
53             },
54             connected => sub {
55             my $kernel = $_[KERNEL];
56            
57             print "connected to ctserver on port 1\n";
58             },
59             input => sub {
60             my ($kernel, $in) = @_[KERNEL, ARG0];
61            
62             # all events are sent here, this is a good
63             # spot to use Data::Dumper
64             if ($in->{rly} eq 'ring') {
65             $kernel->yield(ring => $in);
66             }
67             },
68             ring => sub {
69             my $kernel = $_[KERNEL];
70            
71             # pick up phone
72             $kernel->post(ctport => 'off_hook');
73            
74             # play beep
75             $kernel->post(ctport => play => 'beep');
76            
77             # record
78             $kernel->post(ctport => record =>
79             # to this file
80             'prompt.wav',
81             # for 15 seconds
82             15,
83             # or until they hit #
84             '#',
85             # or 3 seconds of silence
86             3,
87             );
88            
89             # play it back to them
90             $kernel->post(ctport => play => 'prompt.wav');
91            
92             # play 3 beeps
93             $kernel->post(ctport => play => 'beep beep beep');
94            
95             # hangup
96             $kernel->post(ctport => 'on_hook');
97            
98             # shutdown
99             $kernel->post(ctport => 'disconnect');
100             $kernel->post(ctport => 'shutdown');
101             },
102             }
103             );
104              
105             =head1 DESCRIPTION
106              
107             This module implements a non blocking perl interface to CTserver, a
108             server that controls voictronix card operation.
109              
110             *****NOTE*****
111              
112             You need a voicetronix card, the voictronix driver, and ctserver installed
113             to use this module!
114              
115             *****NOTE*****
116              
117             =head1 CONSTRUCTOR
118              
119             POE::Component::Telephony::CTPort->spawn({
120             alias => 'ctport',
121             port => 1
122             });
123              
124             Don't start ctserver yourself, on the first spawn of CTPort, ctserver will be
125             launched in a fork(). To not run ctserver from this module, specify
126             no_ctserver_fork => 1 as a parameter.
127              
128             You can specify all or none of the parameters:
129              
130             =over 4
131              
132             =item *
133              
134             ctserver - the path to the ctserver binary, 'ctserver' is the default (in the path)
135              
136             =item *
137              
138             alias - name to address the component, 'ctport' is the default
139              
140             =item *
141              
142             paths - search paths for the play event, as an array ref:
143             [ '/mnt/cdrom0', '/mnt/cdrom1' ]
144              
145             =item *
146              
147             default_ext - default extension for sound files (default is '.au')
148              
149             =item *
150              
151             reply_to - allows you to specify a different session id to send events to
152              
153             =item *
154              
155             hostname - default is localhost
156              
157             =item *
158              
159             port - a port number from 1 to 4 (1 is default)
160              
161             =item *
162              
163             ignore_dtmf - 1 _or_ 0 (inital setting, used for playing sounds)
164              
165             =item *
166              
167             no_ctserver_fork - 1 _or_ 0 (0 is default)
168              
169             =back
170              
171             spawn() returns a reference to the internal session, but do not keep a copy
172             of it. Instead call the ID method and save that:
173              
174             $heap->{ctport} = POE::Component::Telephony::CTPort->spawn()->ID;
175              
176             If you spawn more than one CTPort session, change the alias! Like this:
177              
178             POE::Component::Telephony::CTPort->spawn({ alias => 'ct1' });
179             POE::Component::Telephony::CTPort->spawn({ alias => 'ct2' });
180             POE::Component::Telephony::CTPort->spawn({ alias => 'ct3' });
181             POE::Component::Telephony::CTPort->spawn({ alias => 'ct4' });
182              
183             This will spawn four sessions and you are ready to tell each one to connect to
184             a different ctserver port. See the 'connect' event.
185              
186             =cut
187              
188             sub spawn {
189             my ($class, $args) = @_;
190              
191             my $debug = $args->{DEBUG} || $Default->{DEBUG};
192            
193             print STDERR "spawn called\n" if ($debug);
194              
195             # skip ctserver spawn if already running
196             if ($CTSERVER_RUN > 0 || $args->{no_ctserver_fork}) {
197             print STDERR "skipping ctserver spawn, already running\n" if ($debug);
198             #return _spawn($args);
199             return $poe_kernel->post(ctserver => spawn => $args);
200             }
201            
202             print STDERR "spawning ctserver\n" if ($debug);
203            
204             # only allow 1 session
205             $CTSERVER_RUN = 1;
206            
207             POE::Session->create(
208             heap => {
209             args => $args,
210             DEBUG => $debug,
211             ctserver => ($args->{ctserver} || $Default->{ctserver}),
212             },
213             inline_states => {
214             _start => sub {
215             my ($kernel, $heap, $args) = @_[KERNEL, HEAP];
216            
217             # reply_to workaround
218             $heap->{reply_to} = $_[SENDER]->ID;
219            
220             $kernel->sig('INT', 'signals');
221             $kernel->sig('TERM', 'signals');
222            
223             $kernel->alias_set('ctserver');
224            
225             $heap->{name} = basename($heap->{ctserver});
226            
227             $heap->{retries} = 0;
228            
229             my $p = Proc::ProcessTable->new();
230             my $t = $p->table();
231             foreach my $i ( 0 .. $#{$t} ) {
232             next unless (exists($t->[$i]->{fname})
233             && $t->[$i]->{fname} eq $heap->{name});
234            
235             print STDERR "ctserver already running at pid ".$t->[$i]->{pid}."\n" if ($heap->{DEBUG});
236             $kernel->yield(_kill => TERM => $t->[$i]->{pid});
237             $heap->{skip_wheel} = 1; # skip wheel setup
238             }
239            
240             # don't kill me because this is hardcoded in ctserver :)
241             my $pid;
242             if (-e '/var/run/ctserver.pid') {
243             open(FH,'/var/run/ctserver.pid');
244             $pid = ()[0];
245             close(FH);
246             }
247            
248             if (defined $pid && $pid) {
249             if (kill(0,$pid)) {
250             print STDERR "ctserver already running\n" if ($heap->{DEBUG});
251             # still runnnig
252             return $kernel->yield(_kill => TERM => $pid);
253             #} else {
254             # XXX normal startup
255             #print STDERR "ctserver already running, but not responding\n" if ($heap->{DEBUG});
256             #return $kernel->yield(_kill => 9 => $pid);
257             }
258             }
259            
260             return if ($heap->{skip_wheel});
261            
262             $kernel->call($_[SESSION] => 'setup_wheel');
263             },
264             _kill => sub {
265             my ($kernel, $heap, $sig, $pid) = @_[KERNEL, HEAP, ARG0, ARG1];
266              
267             if ($heap->{"_kill_$pid"}) {
268              
269             my $p = Proc::ProcessTable->new();
270             my $t = $p->table();
271             foreach my $i ( 0 .. $#{$t} ) {
272             next unless (exists($t->[$i]->{fname})
273             && $t->[$i]->{fname} eq $heap->{name} && $pid == $t->[$i]->{pid});
274            
275             print STDERR "ctserver didn't respond to $sig at pid $pid\n" if ($heap->{DEBUG});
276            
277             if (kill(0,$pid)) {
278             # still there...
279             if ($sig eq '9') {
280             # give up?
281             die "cannot kill ctserver at pid $pid!";
282             } else {
283             $sig = '9';
284             kill($sig,$pid);
285             # recheck in 5 seconds
286             return $kernel->delay_set(_kill => 5 => '9' => $pid);
287             }
288             }
289             }
290            
291             delete $heap->{"_kill_$pid"};
292            
293             # ok, its gone, continue startup
294             return $kernel->call($_[SESSION] => 'setup_wheel');
295             } else {
296             $heap->{"_kill_$pid"} = time();
297            
298             # DIE DIE DIE!
299             kill($sig,$pid);
300            
301             print STDERR "sending $sig to pid $pid\n" if ($heap->{DEBUG});
302            
303             # recheck in 5 seconds
304             $kernel->delay_set(_kill => 5 => $sig => $pid);
305             }
306             },
307             _stop => sub {
308             print STDERR "ctserver session ended\n" if $_[HEAP]->{DEBUG};
309             },
310             setup_wheel => sub {
311             my ($kernel, $heap) = @_[KERNEL, HEAP];
312              
313             return if ($heap->{ctserver_wheel});
314              
315             if ($heap->{retries} >= 5) {
316             warn 'too many restarts of the ctserver wheel, ctserver running already?';
317             return;
318             }
319              
320             print STDERR "setting up ctserver wheel\n" if ($heap->{DEBUG});
321              
322             $heap->{ctserver_wheel} = POE::Wheel::Run->new(
323             # What we will run in the separate process
324             Program => $heap->{ctserver},
325             Conduit => 'pty',
326             #ProgramArgs => ["--log $ENV{PWD}/log"],
327             # Redirect errors to our error routine
328             ErrorEvent => 'child_error',
329             # Send child died to our child routine
330             CloseEvent => 'child_closed',
331             # Send input from child
332             StdoutEvent => 'child_STDOUT',
333            
334             # STDERR not usable for
335             # Send input from child STDERR
336             #StderrEvent => 'child_STDERR',
337             # Set our filters
338             #StdinFilter => POE::Filter::Line->new(),
339             StdoutFilter => POE::Filter::Line->new(),
340             #StderrFilter => POE::Filter::Line->new(),
341             );
342            
343             $heap->{retries}++;
344            
345             # Check for errors
346             if ( ! defined $heap->{ctserver_wheel} ) {
347             warn 'Unable to create ctserver wheel';
348             $kernel->yield('setup_wheel');
349             } else {
350             print STDERR "ctserver wheel is up\n" if ($heap->{DEBUG});
351             }
352             },
353             signals => sub {
354             my ($kernel, $heap, $signal) = @_[KERNEL,HEAP,ARG0];
355            
356             return undef unless ($signal eq 'INT' || $signal eq 'TERM');
357            
358             $heap->{int}++;
359             if ($heap->{int} > 1) {
360             print "ok, ok, bye!\n";
361             exit;
362             }
363             print "INT signal received, please wait, closing ctserver...\n";
364             $kernel->sig_handled();
365             $kernel->alarm_remove_all();
366             $kernel->call($_[SESSION] => 'shutdown');
367            
368             return undef;
369             },
370             shutdown => sub {
371             my ($kernel, $heap) = @_[KERNEL, HEAP];
372            
373             return if ($heap->{shutdown});
374            
375             $heap->{shutdown} = 1;
376              
377             $kernel->alarm_remove_all();
378            
379             if (ref($heap->{children}) eq 'HASH') {
380             foreach my $c (keys %{$heap->{children}}) {
381             print STDERR "telling session $c to shutdown\n" if ($heap->{DEBUG});
382             $kernel->call($c => '_shutdown');
383             }
384             }
385            
386             },
387             drop_wheel => sub {
388             my ($kernel, $heap) = @_[KERNEL, HEAP];
389            
390             if ($heap->{ctserver_wheel}) {
391             $heap->{ctserver_wheel}->kill('TERM');
392             }
393             delete $heap->{ctserver_wheel};
394             $kernel->call(ct_man_port => '_shutdown');
395             $kernel->alias_remove();
396             },
397             'child_error' => sub {
398             my ( $operation, $errnum, $errstr ) = @_[ ARG0 .. ARG2 ];
399             print STDERR "ctserver got an $operation error $errnum: $errstr\n" if ($_[HEAP]->{DEBUG});
400             },
401             'child_closed' => sub {
402             my ($kernel, $heap) = @_[KERNEL,HEAP];
403            
404             print STDERR "ctserver wheel closed\n" if ($heap->{DEBUG});
405            
406             return if ($heap->{shutdown});
407              
408             # Emit debugging information
409             warn 'ctserver\'s Wheel died! Restarting it...';
410            
411             # Create the wheel again
412             delete $heap->{ctserver_wheel};
413             $kernel->call($_[SESSION] => 'setup_wheel');
414             },
415             'child_STDOUT' => sub {
416             my ($kernel, $heap, $input) = @_[KERNEL,HEAP,ARG0];
417            
418             print STDERR "ctserver Got STDOUT ( $input )\n" if ($heap->{DEBUG});
419            
420             # when the server is ready to accept connections, then spawn the client to connect
421             if ($CTSERVER_RUN == 1 && $input =~ m/Started!/) {
422             $CTSERVER_RUN++;
423             # internal manager
424             $kernel->yield(spawn => {
425             DEBUG => $heap->{DEBUG},
426             alias => 'ct_man_port',
427             reconnect => 1,
428             manager_port => 1198,
429             manager_d => 1
430             });
431             # the requested port connect
432             $kernel->yield(spawn => $heap->{args});
433             } elsif ($input =~ m/Address already in use Giving up/) {
434             # TODO search for pid first...
435             system("killall -9 ctserver");
436             }
437             },
438             'child_STDERR' => sub {
439             my $input = $_[ARG0];
440            
441             # Skip empty lines
442             if ( $input eq '' ) { return }
443            
444             print STDERR "ctserver Got STDERR ( $input )\n" if ($_[HEAP]->{DEBUG});
445             },
446             spawn => sub {
447             # reply_to workaround
448             if (!$_[ARG0]->{reply_to}) {
449             $_[ARG0]->{reply_to} = $_[HEAP]->{reply_to};
450             }
451             _spawn($_[ARG0]);
452             },
453             _child => sub {
454             print STDERR "ctserver child $_[ARG0] session_id:".$_[ARG1]->ID."\n" if ($_[HEAP]->{DEBUG});
455             if ($_[ARG0] eq 'create') {
456             #my $s = $_[KERNEL]->alias_resolve('ct_man_port');
457             #if (ref($s) && $s->ID != $_[ARG1]->ID) {
458             $_[HEAP]->{children}{$_[ARG1]->ID} = 1;
459             #}
460             } elsif ($_[ARG0] eq 'lose') {
461             delete $_[HEAP]->{children}{$_[ARG1]->ID};
462             }
463             },
464             },
465             );
466             }
467              
468             sub _spawn {
469             my $args = shift;
470             POE::Session->create(
471             args => [ $args ],
472             package_states => [
473             'POE::Component::Telephony::CTPort' => [qw(
474             _start
475             _stop
476             _sock_up
477             _sock_failed
478             _sock_down
479             _shutdown
480              
481             put
482             connect
483             disconnect
484             reconnect
485            
486             input
487            
488             off_hook
489             on_hook
490             wait_for_ring
491             wait_for_dial_tone
492             play_tone
493             stop_tone
494             play_stop
495             play
496             _play
497             record
498             record_stop
499             sleep
500             clear
501             clear_events
502             collect
503             dial
504             wait_for_event
505             start_timer
506             stop_timer
507             join
508             bridge
509             unbridge
510             join_conference
511             leave_conference
512             start_ring
513             stop_ring
514             ring_once
515             grunt_on
516             grunt_off
517             default_ext
518             ignore_dtmf
519             set_script_name
520             send_cid
521             listen_for_cid_jp
522             listen_for_cid
523             read_cid
524            
525             port_reset
526             port_status
527             roll_log
528             ser_version
529             shutdown
530             )],
531             ],
532             );
533             }
534              
535              
536             sub _start {
537             my ($kernel, $heap, $sender, $args) = @_[KERNEL, HEAP, SENDER, ARG0];
538            
539             $heap->{$_} = $args->{$_} || $Default->{$_} foreach
540             qw(DEBUG alias paths default_ext reply_to ignore_dtmf hostname port reconnect manager_port manager_d);
541              
542             $heap->{DEBUG} && do {
543             print STDERR "params:\n";
544             foreach my $h (keys %$heap) {
545             print STDERR "$h = $heap->{$h}\n";
546             }
547             };
548            
549             $kernel->alias_set($heap->{alias});
550             # $kernel->refcount_increment($sender->ID, __PACKAGE__);
551             $heap->{reply} = $heap->{reply_to} || $sender->ID;
552             $kernel->yield('connect');
553             }
554              
555             sub _stop {
556             print STDERR "ctport session ended\n" if $_[HEAP]->{DEBUG};
557             }
558              
559             sub _sock_up {
560             my ($kernel, $heap, $socket) = @_[KERNEL, HEAP, ARG0];
561            
562             print STDERR "sock up for session ".$_[SESSION]->ID."\n" if ($heap->{DEBUG});
563            
564             $heap->{sock} = POE::Wheel::ReadWrite->new(
565             Handle => $socket,
566             Driver => POE::Driver::SysRW->new(),
567             Filter => POE::Filter::Line->new(Literal => "\n"),
568             ErrorEvent => '_sock_down',
569             InputEvent => 'input',
570             );
571            
572             $kernel->post($heap->{reply} => 'connected' => splice(@_,ARG0));
573             }
574              
575             sub _sock_failed {
576             my ($kernel, $heap) = @_[KERNEL, HEAP];
577            
578             print STDERR "sock failed for session ".$_[SESSION]->ID."\n" if ($heap->{DEBUG});
579            
580             delete $heap->{sock};
581             return if ($heap->{shutdown});
582            
583             $kernel->post($heap->{reply} => 'socket_error' => splice(@_,ARG0));
584            
585             if (defined $heap->{reconnect}) {
586             $kernel->delay_set('reconnect' => $heap->{reconnect});
587             }
588             }
589              
590             # sigh, repeat code..
591             sub _sock_down {
592             my ($kernel, $heap) = @_[KERNEL, HEAP];
593            
594             print STDERR "sock down for session ".$_[SESSION]->ID."\n" if ($heap->{DEBUG});
595            
596             delete $heap->{sock};
597             return if ($heap->{shutdown});
598            
599             $kernel->post($heap->{reply} => 'disconnected' => splice(@_,ARG0));
600              
601             if (defined $heap->{reconnect}) {
602             $kernel->delay_set('reconnect' => $heap->{reconnect});
603             }
604             }
605              
606             sub _shutdown {
607             my ($kernel, $heap) = @_[KERNEL, HEAP];
608              
609             $heap->{shutdown} = 1;
610            
611             print STDERR "_shutdown called on session ".$_[SESSION]->ID."\n" if ($heap->{DEBUG});
612              
613             $kernel->call(ct_man_port => send_event => $heap->{port} => 'SHUTTING_DOWN');
614             $kernel->call($_[SESSION] => 'on_hook');
615             #$kernel->call($_[SESSION] => 'shutdown');
616             }
617              
618             sub reconnect {
619             my ($kernel, $heap) = @_[KERNEL, HEAP];
620            
621             print STDERR "reconnect called\n" if ($heap->{DEBUG});
622            
623             return unless (defined $heap->{hostname} && defined $heap->{port});
624              
625             print STDERR "reconnecting to $heap->{hostname} $heap->{port}\n" if ($heap->{DEBUG});
626              
627             delete $heap->{sock};
628            
629             $kernel->yield('connect' => {
630             hostname => $heap->{hostname},
631             port => $heap->{port},
632             });
633             }
634              
635             sub put {
636             my ($kernel, $heap) = @_[KERNEL, HEAP];
637             return unless ($heap->{sock});
638             if (ref($heap->{sock}) eq 'POE::Wheel::SocketFactory') {
639             # not connected yet!
640             print STDERR "not connected yet, queueing command until connect\n" if ($heap->{DEBUG});
641             $_[KERNEL]->delay_set(put => 1 => splice(@_,ARG0));
642             return;
643             }
644             my $line = $_[ARG0];
645             if ($line !~ m/ $/) {
646             $line .= " ";
647             }
648             $heap->{sock}->put($line);
649             print STDERR "put: '$line'\n" if ($heap->{DEBUG});
650             }
651              
652             =pod
653              
654             =head1 NOTES
655              
656             Any 'blocking' mentioned in this document is only related to the port does not
657             send or receive commands, POE will NOT block for any of these events.
658              
659             It takes alot of experimenting with this module and ctserver to get a working
660             routine down. My advice is to start off with 1 command, and analize the results
661             and setup your script to watch for those results before sending the next command.
662             Firing off the commands without knowing what is happening doesn't work well. :)
663              
664             The alias 'ctserver' is used internally for spawning and handling ctserver. Do
665             not use this alias in your scripts. Its ok to fire its shutdown event to start
666             a safe shutdown.
667              
668             This will module will probably not work on win32. (windows)
669              
670             =head1 RECEIVING EVENTS
671              
672             Your session will receive an event 'ct_input'
673             ARG0 will be a parsed version
674             of ARG1. ARG1 is the raw text from the server.
675              
676             Heres a dump of ARG0, a response from a off_hook event:
677              
678             {
679             'src' => '3',
680             'rly' => 'ctanswer',
681             'args' => [
682             'OK'
683             ],
684             'dst' => '3',
685             'argc' => '1'
686             }
687              
688             This is ARG1 from the above dump.
689              
690             rly=ctanswer src=3 dst=3 arg1=OK argc=1
691              
692             You need to check the first arg of args to see if it is an
693             event like the one listed below.
694              
695             =head2 dtmf
696              
697             =head2 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, *, #
698              
699             =head2 hangup
700              
701             =head2 loopdrop
702              
703             =head2 ring
704              
705             =head2 pickup
706              
707             =head2 timer
708              
709             =head2 cid
710              
711             =head2 flash
712              
713             =head2 toneend
714              
715             =cut
716              
717             sub input {
718             my ($kernel, $heap, $input) = @_[KERNEL, HEAP, ARG0];
719              
720             $input =~ s/\0//g;
721            
722             my %in;
723             foreach (split(/ /,$input)) {
724             my ($k,$v) = split(/=/);
725             if ($k =~ m/^arg(\d+)/) {
726             $in{args}->[($1-1)] = $v;
727             } else {
728             $in{$k} = $v;
729             }
730             }
731              
732             $kernel->post($heap->{reply} => 'ct_input' => \%in => $input);
733             }
734              
735             =pod
736              
737             =head1 SENDING EVENTS
738              
739             =head2 connect
740              
741             Connects to the ctserver and port specified in the spawn constructor.
742             You can also pass a hash ref of hostname and port(1-4) to override.
743              
744             =cut
745              
746             sub connect {
747             my ($kernel, $heap, $args) = @_[KERNEL, HEAP, ARG0];
748             $heap->{$_} = $args->{$_} || $heap->{$_} foreach qw(hostname port);
749              
750             # return if $heap->{sock};
751            
752             my $conport = $heap->{port} + 1199;
753            
754             $heap->{handle} = $heap->{port} - 1;
755             $heap->{d_handle} = $heap->{port} - 1;
756            
757             if ($heap->{manager_port} && $heap->{manager_port} > 0) {
758             $conport = $heap->{manager_port};
759             $heap->{port} = -2;
760             $heap->{handle} = -2;
761             $heap->{d_handle} = $heap->{manager_d};
762             }
763            
764             $heap->{sock} = POE::Wheel::SocketFactory->new(
765             SocketDomain => AF_INET,
766             SocketType => SOCK_STREAM,
767             SocketProtocol => 'tcp',
768             RemoteAddress => $heap->{hostname},
769             RemotePort => $conport,
770             SuccessEvent => '_sock_up',
771             FailureEvent => '_sock_failed',
772             );
773             }
774              
775             =head2 disconnect
776              
777             Disconnects from the ctserver.
778              
779             Note: Disconnecting doesn't stop a currently running
780             record, play, ect.
781              
782             =cut
783              
784             sub disconnect {
785             my ($kernel, $heap) = @_[KERNEL, HEAP];
786              
787             delete $heap->{sock};
788             }
789              
790             # "ctanswer"
791             # SUMMARY: takes the port off hook [non-blocking]
792             # ARGS: none
793             # RETURN ARGS: arg1=[OK|ERROR]
794              
795             =pod
796              
797             =head2 off_hook
798              
799             Takes port off hook, like picking up the phone.
800              
801             =cut
802              
803             sub off_hook {
804             my ($kernel, $heap) = @_[KERNEL, HEAP];
805              
806             $kernel->yield(put => sprintf('cmd=ctanswer src=%d dst=%d argc=0',$heap->{handle},$heap->{d_handle}));
807             }
808              
809             # "cthangup"
810             # SUMMARY: places the port on hook [non-blocking]
811             # ARGS: none
812             # RETURN ARGS: arg1=[OK|ERROR]
813              
814             =pod
815              
816             =head2 on_hook
817              
818             Puts the port on hook, like hanging up the phone.
819              
820             =cut
821              
822             sub on_hook {
823             my ($kernel, $heap) = @_[KERNEL, HEAP];
824              
825             $kernel->yield(put => sprintf('cmd=cthangup src=%d dst=%d argc=0',
826             $heap->{handle},$heap->{d_handle}));
827             }
828              
829             # "ctwaitforring"
830             # SUMMARY: Waits for a ring event and returns the caller ID if
831             # available [blocking]
832             # ARGS: arg1=
833             # RETURN ARGS: arg1=[OK|ERROR|EVENT]
834             # arg2=
835             # arg3=
836              
837             =pod
838              
839             =head2 wait_for_ring
840              
841             Blocks until port detects a ring, then returns. The caller
842             ID (if present) will be returned.
843              
844             =cut
845              
846             sub wait_for_ring {
847             my ($kernel, $heap) = @_[KERNEL, HEAP];
848             my $rings = $_[ARG0] || 2;
849             my $ird = $_[ARG1] || 0;
850              
851             $kernel->yield(put =>
852             sprintf('cmd=ctwaitforring src=%d dst=%d arg1=%d arg2=%s argc=2',
853             $heap->{handle},$heap->{d_handle},$rings,$ird));
854             }
855              
856             # "ctwaitfordial"
857             # SUMMARY: Waits for a dialtone for a max of 10 seconds [blocking]
858             # ARGS: none
859             # RETURN ARGS: arg1=[OK|ERROR]
860              
861             =pod
862              
863             =head2 wait_for_dial_tone
864              
865             Blocks until dial tone detected on port, then returns.
866              
867             =cut
868              
869             sub wait_for_dial_tone {
870             my ($kernel, $heap) = @_[KERNEL, HEAP];
871              
872             $kernel->yield(put => sprintf('cmd=ctwaitfordial src=%d dst=%d argc=0',
873             $heap->{handle},$heap->{d_handle}));
874             }
875              
876             # "ctplaytoneasync"
877             # SUMMARY: plays a tone asyncronously [non-blocking]
878             # ARGS: arg1=
879             # RETURN ARGS: arg1=[OK|ERROR]
880              
881             =pod
882              
883             =head2 play_tone => $type
884              
885             Plays a tone. $type can be busy, dialx, dial, or ringback.
886             A warning is produced if you supply an invalid tone.
887              
888             =cut
889              
890             sub play_tone {
891             my ($kernel, $heap, $type) = @_[KERNEL, HEAP, ARG0];
892             $type = lc($type);
893            
894             my $found = 0;
895             foreach my $t (qw(busy dialx dial ringback)) {
896             $found = 1 if ($type eq $t);
897             }
898             unless ($found) {
899             warn "play_tone: ctserver does not support tone $type";
900             return;
901             }
902              
903             $kernel->yield(put => sprintf('cmd=ctplaytoneasync src=%d dst=%d arg1=%s argc=1',
904             $heap->{handle},$heap->{d_handle},$type));
905             }
906              
907             # "ctstoptone"
908             # SUMMARY: stops an asynronous tone playing [non-blocking]
909             # ARGS: none
910             # RETURN ARGS: arg1=[OK|ERROR]
911              
912             =pod
913              
914             =head2 stop_tone
915              
916             Stops a playing tone
917              
918             =cut
919              
920             sub stop_tone {
921             my ($kernel, $heap) = @_[KERNEL, HEAP];
922              
923             $kernel->yield(put => sprintf('cmd=ctstoptone src=%d dst=%d argc=0',
924             $heap->{handle},$heap->{d_handle}));
925             }
926              
927             # "ctplay_stop"
928             # SUMMARY: Stops an asyncronous play [non-blocking]
929             # ARGS: none
930             # RETURN ARGS: arg1=[OK|ERROR]
931              
932             =pod
933              
934             =head2 play_stop
935              
936             Stops current playback.
937              
938             =cut
939              
940             sub play_stop {
941             my ($kernel, $heap) = @_[KERNEL, HEAP];
942              
943             $kernel->yield(put => sprintf('cmd=ctplay_stop src=%d dst=%d argc=0',
944             $heap->{handle},$heap->{d_handle}));
945             }
946              
947             =pod
948              
949             =head2 play => $file _or_ play => \@files _or_ play => [ $file1, $file2, $file3 ]
950              
951             Plays audio files, playing stops immediately if a DTMF key is
952             pressed. Any digits pressed while playing will be added to the digit buffer.
953              
954             It accepts a space seperated list of files:
955             $kernel->post(ctport => play => "1 2 3");
956              
957             or an array of files:
958             $kernel->post(ctport => play => ['hello', 'world']);
959              
960             Filename extensions:
961              
962             =over 4
963              
964             =item *
965              
966             The default is .au, can be redefined by posting/calling the default_ext
967             event with the file extension as the first parameter. For example:
968             $kernel->post(ctport => default_ext => '.wav');
969              
970             =item *
971              
972             You can override the default by providing the extension:
973             $kernel->post(ctport => play => "hello.wav");
974              
975             =back
976              
977             Searches for file in:
978              
979             =over 4
980              
981             =item *
982              
983             The paths defined by set_path event or as an option to the spawn
984             constructor: { path => '/var/audio/files/' }
985              
986             =item *
987              
988             The current directory
989              
990             =item *
991              
992             The "prompts" sub dir (relative to the current directory)
993              
994             =item *
995              
996             full path supplied by caller
997              
998             =item *
999              
1000             /var/ctserver/UsMEng
1001              
1002             =back
1003              
1004             You can play multiple files
1005              
1006             $kernel->post(ctport => play => "Hello World");
1007             (assumes you have Hello.au and World.au files available)
1008             (depending on what the default extension is set to)
1009              
1010             You can "speak" a limited vocabulary:
1011             $kernel->post(ctport => play => "1 2 3");
1012              
1013             See the /var/ctserver/UsMEng directory for the list of included files that
1014             defines the vocabulary.
1015              
1016             =cut
1017              
1018              
1019             # see below for $async flag
1020             sub play {
1021             my ($kernel, $files, $async) = @_[KERNEL, ARG0, ARG1];
1022              
1023             return unless ($files);
1024            
1025             $files = join(' ',@{$files}) if (ref($files) eq 'ARRAY');
1026              
1027             foreach my $f (split(/ /,$files)) {
1028             $kernel->yield(_play => $f => $async);
1029             }
1030             }
1031              
1032             # "ctplay"
1033             # SUMMARY: Plays a file and deals with events [blocking]
1034             # ARGS: arg1=
1035             # RETURN ARGS: arg1=[OK|ERROR|EVENT]
1036             # arg2=
1037             # arg3=
1038             #
1039             # "ctplay_async"
1040             # SUMMARY: Plays a file asyncronously [non-blocking]
1041             # ARGS: arg1=
1042             # RETURN ARGS: arg1=[OK|ERROR]
1043              
1044             sub _play {
1045             my ($kernel, $heap, $file, $async) = @_[KERNEL, HEAP, ARG0, ARG1];
1046              
1047             # TODO verify this works
1048             unless ($file =~ m/\./) {
1049             $file .= $heap->{default_ext};
1050             }
1051             my @path;
1052              
1053             if ($file =~ /^\//) {
1054             push(@path,$file);
1055             }
1056              
1057             # user supplied paths
1058             if ($heap->{paths}) {
1059             my $pt = $heap->{paths};
1060             for my $i ( 0 .. $#{$pt} ) {
1061             # make sure there's a slash on the end
1062             $pt->[$i] .= "/" unless ($pt->[$i] =~ m/\/$/);
1063             $pt->[$i] .= $file;
1064             }
1065             push(@path,@{$pt});
1066             }
1067             # undocumented feature...search only supplied path
1068             # TODO doc this
1069             unless ($heap->{paths_only}) {
1070             # current directory (at program start)
1071             push(@path,"$ENV{PWD}/$file");
1072             # prompts subdir
1073             push(@path,"$ENV{PWD}/prompts/$file");
1074             # default ctserver english (use 'paths' above to avoid english)
1075             push(@path,"/var/ctserver/USEngM/$file");
1076             }
1077            
1078             foreach my $p (@path) {
1079             if (-e "$p") {
1080             my ($extra,$num) = ('',1);
1081             # check for the ignore dtmf option
1082             if ($heap->{ignore_dtmf}) {
1083             $extra = ' arg2=ignore_dtmf';
1084             $num = 2;
1085             }
1086             if (defined($async)) {
1087             $kernel->yield(put => sprintf('cmd=ctplayasync src=%d dst=%d arg1=%s%s argc=%d',
1088             $heap->{handle},$heap->{d_handle},$p,$extra,$num));
1089             } else {
1090             $kernel->yield(put => sprintf('cmd=ctplay src=%d dst=%d arg1=%s%s argc=%d',
1091             $heap->{handle},$heap->{d_handle},$p,$extra,$num));
1092             }
1093             return;
1094             }
1095             }
1096            
1097             warn "play: File(s) not found: ".join(';',@path);
1098             }
1099              
1100             # "ctrecord"
1101             # SUMMARY: Records audio to a file [blocking]
1102             # ARGS: arg1=
1103             # arg2=
1104             # arg3=
1105             # arg4=
1106             # RETURN ARGS: arg1=[OK|ERROR|EVENT]
1107             # arg2=
1108             # arg3=
1109              
1110             =pod
1111              
1112             =head2 record => $file_name => $seconds => $digits
1113              
1114             Records $file_name for $seconds seconds or until any of the digits in $digits
1115             are pressed. The path of $file_name is considered absolute if there is a
1116             leading /, otherwise it is relative to the current directory.
1117              
1118             =cut
1119              
1120             sub record {
1121             my ($kernel, $heap, $file) = @_[KERNEL, HEAP, ARG0];
1122            
1123             # TODO does duration 0 mean until a digit is pressed?
1124             my $timeout = @_[ARG1] || 0;
1125             my $digits = @_[ARG2] || '';
1126             my $silence = @_[ARG3] || 0;
1127              
1128             if ($file !~ m/^\//) {
1129             $file = "$ENV{PWD}/$file";
1130             }
1131              
1132             $kernel->yield(put => sprintf('cmd=ctrecord src=%d dst=%d arg1=%s arg2=%d arg3=%s arg4=%d argc=4',
1133             $heap->{handle},$heap->{d_handle},$file,$timeout,$digits,$silence));
1134             }
1135              
1136             =pod
1137              
1138             =head2 record_stop
1139              
1140             Stops recording on the current port.
1141              
1142             =cut
1143              
1144             sub record_stop {
1145             my ($kernel, $heap, $file) = @_[KERNEL, HEAP, ARG0];
1146            
1147             # since ctserver doesn't have a direct way to call vpb_record_terminate, we use this work around
1148             # a user message sent to the port while recording causes it to stop
1149              
1150             $kernel->post(ct_man_port => send_event => $heap->{port} => 'RECORD_STOP');
1151             }
1152              
1153             # "ctsleep"
1154             # SUMMARY: Sleep for N seconds [blocking]
1155             # ARGS: arg1=
1156             # RETURN ARGS: arg1=[OK|ERROR|EVENT]
1157             # arg2=
1158             # arg3=
1159              
1160             =pod
1161              
1162             =head2 sleep => $seconds
1163              
1164             Blocks for $seconds, unless a DTMF key is pressed in which
1165             case it returns immediately. If $ctport->event() is already defined it
1166             returns immediately without sleeping.
1167              
1168             =cut
1169              
1170             sub sleep {
1171             my ($kernel, $heap, $secs) = @_[KERNEL, HEAP, ARG0];
1172              
1173             unless ($secs =~ m/^\d+$/) {
1174             warn "sleep: Seconds must be a number, ie '2' not 'two' :)";
1175             return;
1176             }
1177            
1178             $kernel->yield(put => sprintf('cmd=ctsleep src=%d dst=%d arg1=%d argc=1',
1179             $heap->{handle},$heap->{d_handle},$secs));
1180             }
1181              
1182             # "ctclear"
1183             # SUMMARY: Clears the digit buffer [non-blocking]
1184             # ARGS: none
1185             # RETURN ARGS: arg1=[OK|ERROR]
1186              
1187             =pod
1188              
1189             =head2 clear
1190              
1191             Clears the DTMF digit buffer. (It may clear events too!)
1192              
1193             =cut
1194              
1195             sub clear {
1196             my ($kernel, $heap) = @_[KERNEL, HEAP];
1197            
1198             $kernel->yield(put => sprintf('cmd=ctclear src=%d dst=%d argc=0',
1199             $heap->{handle},$heap->{d_handle}));
1200             delete $heap->{buffer};
1201             }
1202              
1203             # "ctclearevents"
1204             # SUMMARY: Clears the event queue [non-blocking]
1205             # ARGS: none
1206             # RETURN ARGS: arg1=[OK|ERROR]
1207              
1208             =pod
1209              
1210             =head2 clear_events
1211              
1212             Clears the event queue.
1213              
1214             =cut
1215              
1216             sub clear_events {
1217             my ($kernel, $heap) = @_[KERNEL, HEAP];
1218            
1219             $kernel->yield(put => sprintf('cmd=ctclearevents src=%d dst=%d argc=0',
1220             $heap->{handle},$heap->{d_handle}));
1221             }
1222              
1223             # "ctcollect"
1224             # SUMMARY: Collects digits [blocking]
1225             # ARGS: arg1=
1226             # arg2=
1227             # arg3=
1228             # RETURN ARGS: arg1=[OK|ERROR|EVENT]
1229             # arg2=
1230             # arg3=
1231              
1232             =pod
1233              
1234             =head2 collect => $max_digits => $max_seconds
1235              
1236             Returns up to $max_digits by waiting up to $max_seconds. Will return as soon
1237             as either $max_digits have been collected or $max_seconds have elapsed. On
1238             return, the event() method will return undefined.
1239              
1240             DTMF digits pressed at any time are collected in the digit buffer. The digit
1241             buffer is cleared by the clear method. Thus it is possible for this function
1242             to return immediately if there are already $max_digits in the digit buffer.
1243              
1244             =cut
1245              
1246             # XXX hmm, how do we do this in an event model?
1247             sub collect {
1248             my ($kernel, $heap) = @_[KERNEL, HEAP];
1249             my $maxdigits = $_[ARG0] || 0;
1250             my $maxseconds = $_[ARG1] || 0;
1251             my $maxinter = $_[ARG2] || 0;
1252            
1253             $kernel->yield(put => sprintf('cmd=ctcollect src=%d dst=%d arg1=%d arg2=%d arg3=%d argc=3',
1254             $heap->{handle},$heap->{d_handle},$maxdigits,$maxseconds,$maxinter));
1255             }
1256              
1257             # "ctdial"
1258             # SUMMARY: Dials a string of digits [blocking]
1259             # ARGS: arg1=
1260             # RETURN ARGS: arg1=[OK|ERROR|EVENT]
1261             # arg2=
1262              
1263             =pod
1264              
1265             =head2 dial => $number
1266              
1267             Dials a DTMF string. Valid characters are 1234567890#*,&
1268              
1269             =over 4
1270              
1271             =item *
1272              
1273             , gives a 1 second pause, e.g. $ctport->dial(",,1234) will wait 2 seconds,
1274             then dial extension 1234.
1275              
1276             =item *
1277              
1278             & generates a hook flash (used for transfers on many PBXs):
1279              
1280             $kernel->post(ctport => dial => '&,1234'); will send a flash, wait one second,
1281             then dial 1234.
1282              
1283             =back
1284              
1285             =cut
1286              
1287             sub dial {
1288             my ($kernel, $heap, $dial) = @_[KERNEL, HEAP, ARG0];
1289              
1290             #$dial =~ s/\D//g;
1291            
1292             $kernel->yield(put => sprintf('cmd=ctdial src=%d dst=%d arg1=%s argc=1',
1293             $heap->{handle},$heap->{d_handle},$dial));
1294             }
1295              
1296             # "ctwaitforevent"
1297             # SUMMARY: waits for an event [blocking]
1298             # ARGS: none
1299             # RETURN ARGS: arg1=[OK|ERROR]
1300             # arg2=
1301              
1302             =pod
1303              
1304             =head2 wait_for_event
1305              
1306             Blocks, waits for an event to happen.
1307             (probably not useful in POE)
1308              
1309             =cut
1310              
1311             sub wait_for_event {
1312             my ($kernel, $heap) = @_[KERNEL, HEAP];
1313            
1314             $kernel->yield(put => sprintf('cmd=ctwaitforevent src=%d dst=%d argc=0',
1315             $heap->{handle},$heap->{d_handle}));
1316             }
1317              
1318             # "ctsendevent"
1319             # SUMMARY: sends an event/message to another port [non-blocking]
1320             # ARGS: arg1=
1321             # ...
1322             # arg9=
1323             # RETURN ARGS: arg1=[OK|ERROR]
1324              
1325             =pod
1326              
1327             =head2 send_event => $port => $event
1328              
1329             Sends an event or message to another port.
1330              
1331             =cut
1332              
1333             sub send_event {
1334             my ($kernel, $heap, $port, $msg) = @_[KERNEL, HEAP, ARG0, ARG1];
1335            
1336             $kernel->yield(put => sprintf('cmd=ctsendevent src=%d dst=%d arg1=%s argc=1',
1337             $heap->{handle},$port,$msg));
1338             }
1339              
1340             # "ctstarttimerasync"
1341             # SUMMARY: starts an asyncronous timer [non-blocking]
1342             # ARGS: arg1=
1343             # RETURN ARGS: arg1=[OK|ERROR]
1344              
1345             =pod
1346              
1347             =head2 start_timer => $seconds
1348              
1349             Starts a timer that will send an event in $seconds seconds.
1350              
1351             =cut
1352              
1353             sub start_timer {
1354             my ($kernel, $heap, $secs) = @_[KERNEL, HEAP, ARG0];
1355            
1356             $kernel->yield(put => sprintf('cmd=ctstarttimerasync src=%d dst=%d arg1=%d argc=1',
1357             $heap->{handle},$heap->{d_handle},$secs));
1358             }
1359              
1360             # "ctstoptimer"
1361             # SUMMARY: stops an asyncronous timer [non-blocking]
1362             # ARGS: none
1363             # RETURN ARGS: arg1=[OK|ERROR]
1364              
1365             =pod
1366              
1367             =head2 stop_timer
1368              
1369             Stops the current timer.
1370              
1371             =cut
1372              
1373             sub stop_timer {
1374             my ($kernel, $heap) = @_[KERNEL, HEAP];
1375            
1376             $kernel->yield(put => sprintf('cmd=ctstoptimer src=%d dst=%d argc=0',
1377             $heap->{handle},$heap->{d_handle}));
1378             }
1379              
1380             # "ctjoin"
1381             # SUMMARY: bridges two ports [non-blocking]
1382             # ARGS: arg1=
1383             # arg2=
1384             # RETURN ARGS: arg1=[OK|ERROR]
1385              
1386             =pod
1387              
1388             =head2 join => $port1 => $port2
1389              
1390             Bridges $port1 and $port2.
1391              
1392             =cut
1393              
1394             sub join {
1395             my ($kernel, $heap, $port1, $port2) = @_[KERNEL, HEAP, ARG0, ARG1];
1396              
1397             $kernel->yield(put => sprintf('cmd=ctjoin src=%d dst=%d arg1=%d arg2=%d argc=2',
1398             $heap->{handle},$heap->{d_handle},$port1,$port2));
1399             }
1400              
1401             # "ctbridge"
1402             # SUMMARY: hardware bridges this port with one supplied [non-blocking]
1403             # ARGS: arg1=
1404             # RETURN ARGS: arg1=[OK|ERROR]
1405              
1406             =pod
1407              
1408             =head2 bridge => $port
1409              
1410             Hardware bridges the connected port to $port.
1411              
1412             =cut
1413              
1414             sub bridge {
1415             my ($kernel, $heap, $port) = @_[KERNEL, HEAP, ARG0];
1416              
1417             $kernel->yield(put => sprintf('cmd=ctbridge src=%d dst=%d arg1=%d argc=1',
1418             $heap->{handle},$heap->{d_handle},$port));
1419             }
1420              
1421             # "ctunbridge"
1422             # SUMMARY: Unbridges this port with one supplied [non-blocking]
1423             # ARGS: arg1=
1424             # RETURN ARGS: arg1=[OK|ERROR]
1425              
1426             =pod
1427              
1428             =head2 unbridge => $port
1429              
1430             Unbridges the connected port and $port.
1431              
1432             =cut
1433              
1434             sub unbridge {
1435             my ($kernel, $heap, $port) = @_[KERNEL, HEAP, ARG0];
1436              
1437             $kernel->yield(put => sprintf('cmd=ctunbridge src=%d dst=%d arg1=%d argc=1',
1438             $heap->{handle},$heap->{d_handle},$port));
1439             }
1440              
1441             # "ctjoinconference"
1442             # SUMMARY: Joins a port to a conference.
1443             # ARGS: arg1=
1444             # RETURN ARGS: arg1=[OK|ERROR]
1445              
1446             =pod
1447              
1448             =head2 join_conference => $port
1449              
1450              
1451             Joins a port to a conference.
1452              
1453             =cut
1454              
1455             sub join_conference {
1456             my ($kernel, $heap, $port) = @_[KERNEL, HEAP, ARG0];
1457              
1458             $kernel->yield(put => sprintf('cmd=ctjoinconference src=%d dst=%d arg1=%d argc=1',
1459             $heap->{handle},$heap->{d_handle},$port));
1460             }
1461              
1462             # "ctleaveconference"
1463             # SUMMARY: Removes a port from a conference.
1464             # ARGS: arg1=
1465             # RETURN ARGS: arg1=[OK|ERROR]
1466              
1467             =pod
1468              
1469             =head2 leave_conference => $port
1470              
1471             Removes $port from a conference.
1472              
1473             =cut
1474              
1475             sub leave_conference {
1476             my ($kernel, $heap, $port) = @_[KERNEL, HEAP, ARG0];
1477              
1478             $kernel->yield(put => sprintf('cmd=ctleaveconference src=%d dst=%d arg1=%d argc=1',
1479             $heap->{handle},$heap->{d_handle},$port));
1480             }
1481              
1482             # "ctstartringasync"
1483             # SUMMARY: Starts this port ringing [non-blocking]
1484             # ARGS: none
1485             # RETURN ARGS: arg1=[OK|ERROR]
1486              
1487             =pod
1488              
1489             =head2 start_ring
1490              
1491             Starts ringing the connected port.
1492              
1493             =cut
1494              
1495             sub start_ring {
1496             my ($kernel, $heap) = @_[KERNEL, HEAP];
1497              
1498             $kernel->yield(put => sprintf('cmd=ctstartringasync src=%d dst=%d argc=0',
1499             $heap->{handle},$heap->{d_handle}));
1500             }
1501              
1502             # "ctstopring"
1503             # SUMMARY: Stops this port from ringing [non-blocking]
1504             # ARGS: none
1505             # RETURN ARGS: arg1=[OK|ERROR]
1506              
1507             =pod
1508              
1509             =head2 stop_ring
1510              
1511             Stops the connected port from ringing.
1512              
1513             =cut
1514              
1515             sub stop_ring {
1516             my ($kernel, $heap) = @_[KERNEL, HEAP];
1517              
1518             $kernel->yield(put => sprintf('cmd=ctstopring src=%d dst=%d argc=0',
1519             $heap->{handle},$heap->{d_handle}));
1520             }
1521              
1522             # "ctstartringonceasync"
1523             # SUMMARY: Ring this port once [non-blocking]
1524             # ARGS: none
1525             # RETURN ARGS: arg1=[OK|ERROR]
1526              
1527             =pod
1528              
1529             =head2 ring_once
1530              
1531             Rings the connected port once.
1532              
1533             =cut
1534              
1535             sub ring_once {
1536             my ($kernel, $heap) = @_[KERNEL, HEAP];
1537              
1538             $kernel->yield(put => sprintf('cmd=ctstartringonceasync src=%d dst=%d argc=0',
1539             $heap->{handle},$heap->{d_handle}));
1540             }
1541              
1542             =pod
1543              
1544             =head2 grunt_on
1545              
1546             Turns grunt (non-silence) detection on.
1547              
1548             =cut
1549              
1550             sub grunt_on {
1551             my ($kernel, $heap) = @_[KERNEL, HEAP];
1552              
1553             $kernel->yield(put => sprintf('cmd=ctgrunton src=%d dst=%d argc=0',
1554             $heap->{handle},$heap->{d_handle}));
1555             }
1556              
1557             =pod
1558              
1559             =head2 grunt_off
1560              
1561             Turns grunt (non-silence) detection on.
1562              
1563             =cut
1564              
1565             sub grunt_off {
1566             my ($kernel, $heap) = @_[KERNEL, HEAP];
1567              
1568             $kernel->yield(put => sprintf('cmd=ctgruntoff src=%d dst=%d argc=0',
1569             $heap->{handle},$heap->{d_handle}));
1570             }
1571              
1572             =pod
1573              
1574             =head2 default_ext => '.wav'
1575              
1576             Changes default extension for playing files.
1577              
1578             =cut
1579              
1580             sub default_ext {
1581             my ($kernel, $heap, $arg) = @_[KERNEL, HEAP, ARG0];
1582              
1583             unless (defined($arg)) {
1584             warn 'You must specify an extension to default_ext';
1585             return;
1586             }
1587              
1588             if ($arg !~ m/^\./) {
1589             $arg = ".$arg";
1590             }
1591            
1592             $heap->{default_ext} = $arg;
1593             }
1594              
1595             =pod
1596              
1597             =head2 ignore_dtmf => 1 _or_ 0
1598              
1599             Turns on/off the ability for the caller to stop playback with dtmf.
1600              
1601             =cut
1602              
1603             sub ignore_dtmf {
1604             my ($kernel, $heap, $arg) = @_[KERNEL, HEAP, ARG0];
1605              
1606             unless (defined($arg)) {
1607             warn 'You must specify \'on\' / \'off\' or 1 / 0 to ignore_dtmf';
1608             return;
1609             }
1610            
1611             $heap->{ignore_dtmf} = ($arg =~ m/on/i || $arg =~ m/^1$/) ? 1 : 0;
1612             }
1613              
1614             # WTF is this useful for?
1615             =pod
1616              
1617             =head2 set_script_name => $name
1618              
1619             This allows you to set a name on this port. Shown in a port_status event.
1620              
1621             =cut
1622              
1623              
1624             sub set_script_name {
1625             my ($kernel, $heap, $name) = @_[KERNEL, HEAP, ARG0];
1626              
1627             $kernel->yield(put => sprintf('cmd=ctsetscript src=%d dst=%d arg1=%s argc=1',
1628             $heap->{handle},$heap->{d_handle},$name));
1629             }
1630              
1631             =pod
1632              
1633             =head2 send_cid => $number => $name
1634              
1635             Sends caller id
1636              
1637             =cut
1638              
1639             sub send_cid {
1640             my ($kernel, $heap, $number, $name) = @_[KERNEL, HEAP, ARG0, ARG1];
1641              
1642             $number =~ tr/\D//;
1643              
1644             $kernel->yield(put => sprintf('cmd=ctsetscript src=%d dst=%d arg1=%d arg2=%s argc=2',
1645             $heap->{handle},$heap->{d_handle},$number,$name));
1646             }
1647              
1648             # XXX you shouldn't have to do the timing yourself
1649             # what is the dif between the jpcid and just cid cmds?
1650              
1651             =pod
1652              
1653             =head2 listen_for_cid_jp
1654              
1655             Call after teh first ring on trunk port to start listening for
1656             caller ID. I'm not sure what JP is, but its not the same as the
1657             command below.
1658              
1659             =cut
1660              
1661             sub listen_for_cid_jp {
1662             my ($kernel, $heap) = @_[KERNEL, HEAP];
1663              
1664             $kernel->yield(put => sprintf('cmd=ctlistenforjpcid src=%d dst=%d argc=0',
1665             $heap->{handle},$heap->{d_handle}));
1666             }
1667              
1668             =pod
1669              
1670             =head2 listen_for_cid
1671              
1672             Call after teh first ring on trunk port to start listening for
1673             caller ID. After the second ring, you should call read_cid to get
1674             the caller id if it's available.
1675              
1676             =cut
1677              
1678             sub listen_for_cid {
1679             my ($kernel, $heap) = @_[KERNEL, HEAP];
1680              
1681             $kernel->yield(put => sprintf('cmd=ctlistenforcid src=%d dst=%d argc=0',
1682             $heap->{handle},$heap->{d_handle}));
1683             }
1684              
1685             =pod
1686              
1687             =head2 read_cid
1688              
1689             Call this after the second ring on a trunk port to receive a caller id
1690             event.
1691              
1692             =cut
1693              
1694             sub read_cid {
1695             my ($kernel, $heap) = @_[KERNEL, HEAP];
1696              
1697             $kernel->yield(put => sprintf('cmd=ctreadcid src=%d dst=%d argc=0',
1698             $heap->{handle},$heap->{d_handle}));
1699             }
1700              
1701             # XXX management commands
1702              
1703             # "portreset"
1704             # SUMMARY: Reset a port
1705             # DST:
1706             # ARGS: none
1707             # RETURN ARGS: none
1708              
1709             sub port_reset {
1710             my ($kernel, $heap, $port) = @_[KERNEL, HEAP, ARG0];
1711              
1712             $kernel->yield(put => sprintf('cmd=portstatus src=%d dst=%d argc=0',
1713             $heap->{handle},$port));
1714             }
1715              
1716             # "portstatus"
1717             # SUMMARY: Querys server for status of each port
1718             # DST: -2 [mandatory]
1719             # ARGS: none
1720             # RETURN ARGS: none
1721              
1722             # XXX I was reading server.cpp... you CAN send portstatus to a port to
1723             # get the status for 1 port or send portstatus to the manager and get
1724             # the status for all ports
1725              
1726             =pod
1727              
1728             =head2 port_status => $port
1729              
1730             Requests port status on a port, if $port is undef or -2, all ports are
1731             polled for status
1732              
1733             =cut
1734              
1735             sub port_status {
1736             my ($kernel, $heap, $port) = @_[KERNEL, HEAP, ARG0];
1737              
1738             # -2 means all
1739             $port = (defined($port)) ? $port : -2;
1740            
1741             $kernel->yield(put => sprintf('cmd=portstatus src=%d dst=%d argc=0',
1742             $heap->{handle},$port));
1743             }
1744              
1745             # "rolllog"
1746             # SUMMARY: Close current ctserver log and open new log with current date
1747             # DST: -2 [mandatory]
1748             # ARGS: none
1749             # RETURN ARGS: none
1750              
1751             =pod
1752              
1753             =head2 roll_log
1754              
1755             Tells ctserver to close the current log, and open a new one with the current
1756             date
1757              
1758             =cut
1759              
1760             sub roll_log {
1761             my ($kernel, $heap) = @_[KERNEL, HEAP];
1762              
1763             $kernel->yield(put => sprintf('cmd=rolllog src=%d dst=-2 argc=0',
1764             $heap->{handle}));
1765             }
1766              
1767             # "serversion"
1768             # SUMMARY: Querys server for CVS identification strings
1769             # DST: -2 [mandatory]
1770             # ARGS: none
1771             # RETURN ARGS: none
1772              
1773             =pod
1774              
1775             =head2 ser_version
1776              
1777             Requests the server version from ctserver
1778              
1779             =cut
1780              
1781             sub ser_version {
1782             my ($kernel, $heap) = @_[KERNEL, HEAP];
1783              
1784             $kernel->yield(put => sprintf('cmd=serversion src=%d dst=-2 argc=0',
1785             $heap->{handle}));
1786             }
1787              
1788             # "shutdown"
1789             # SUMMARY: Shutdown server.
1790             # DST: -2 [mandatory]
1791             # ARGS: none
1792             # RETURN ARGS: none
1793              
1794             =pod
1795              
1796             =head2 shutdown
1797              
1798             Shutsdown the server and the connection.
1799              
1800             =cut
1801              
1802             sub shutdown {
1803             my ($kernel, $heap) = @_[KERNEL, HEAP];
1804              
1805             $kernel->yield(put => sprintf('cmd=shutdown src=%d dst=-2 argc=0',
1806             $heap->{handle}));
1807             $kernel->alarm_remove_all();
1808             $kernel->alias_remove();
1809             }
1810              
1811             1;
1812             __END__