notes and code for my payphone project
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 129 lines 3.0 kB view raw
1#!/usr/bin/perl 2 3use Asterisk::AGI; 4use Cwd "realpath"; 5use File::Basename; 6use Time::HiRes; 7use strict; 8 9use constant { 10 # where we live 11 SOUND_DIR => dirname(realpath($0)), 12 13 # outbound context we'll send calls to (since our current context should 14 # only allow dialing 0 from the ATA) 15 OUTBOUND_CONTEXT => "payphone-outbound", 16 17 # timeout in seconds to wait to get a number or a coin 18 TIMEOUT => 30, 19 20 # amount required to make a call 21 CALL_CENTS => 25, 22 23 # numbers allowed to be dialed without requiring coins 24 FREE_CALLS => qr/^(1?(8[02345678]{2})[0-9]{7}|911)/, 25 26 # valid numbers which will immediately dial instead of timing out 27 VALID_NUMBER => qr/^(1?[2-8][0-9]{9}|911)/, 28}; 29 30my $AGI = new Asterisk::AGI; 31 32my $last_activity = time(); 33my @last_coins; 34my $inserted = 0; 35my $dialed = ""; 36 37# generate dialtone while we wait for coins 38$AGI->exec("Playtones", "dial"); 39my $dialtone = 1; 40 41while (1) { 42 my $dig = $AGI->wait_for_digit(1000); 43 44 if ($dig >= 1) { 45 if ($dig == ord('$')) { 46 $AGI->verbose("got coin tone at " . Time::HiRes::time, 4); 47 48 # shift out old coins 49 my @new_coins; 50 for (my $x = 0; $x <= $#last_coins; $x++) { 51 if (Time::HiRes::time - $last_coins[$x] < 0.5) { 52 push(@new_coins, $last_coins[$x]); 53 } 54 } 55 @last_coins = @new_coins; 56 57 push(@last_coins, Time::HiRes::time); 58 59 # if we've heard 3 coin tones in close proximity, always count it 60 # as 5 since we might not hear the other 2 61 if ($#last_coins == 2) { 62 $inserted += 15; 63 $AGI->verbose("got 5-cent tone, counting as 15 cents (now " 64 . $inserted . " cents)", 4); 65 # provide some audible feedback that it's ok to make a call 66 $AGI->exec("Playtones", "stutter"); 67 } 68 elsif ($#last_coins > 2) { 69 $AGI->verbose("ignoring 4th or 5th 5-cent tone of 25-cents " 70 . "(still " . $inserted . " cents)", 4); 71 } 72 else { 73 $inserted += 5; 74 $AGI->verbose("got 5-cent tone (now " . $inserted . " cents)", 75 4); 76 } 77 } 78 else { 79 if ($dialtone) { 80 $AGI->exec("StopPlaytones", "dial"); 81 $dialtone = 0; 82 } 83 84 $dialed .= chr($dig); 85 $AGI->verbose("dialed " . chr($dig) . " (now " . $dialed . ")", 4); 86 } 87 88 $last_activity = time(); 89 } 90 91 my $elapsed = time() - $last_activity; 92 93 if ($dialed =~ FREE_CALLS) { 94 $AGI->verbose("connecting to " . $dialed . " (free call)", 1); 95 dial($dialed); 96 exit; 97 } 98 elsif ($dialed =~ VALID_NUMBER) { 99 if ($inserted < CALL_CENTS) { 100 $AGI->verbose("need " . CALL_CENTS . " cents to dial " . $dialed 101 . ", have " . $inserted, 4); 102 103 $AGI->stream_file(SOUND_DIR . "/not_deposited", "0"); 104 } 105 else { 106 $AGI->verbose("connecting to " . $dialed . " (inserted " 107 . $inserted . " cents)", 1); 108 dial($dialed); 109 } 110 exit; 111 } 112 elsif ($elapsed >= TIMEOUT) { 113 $AGI->verbose("timed out waiting for coins or digits", 1); 114 115 if ($dialed ne "") { 116 $AGI->stream_file(SOUND_DIR . "/invalid_number", "0"); 117 } 118 119 exit; 120 } 121} 122 123sub dial { 124 my ($number) = @_; 125 126 $AGI->set_context(OUTBOUND_CONTEXT); 127 $AGI->set_extension($dialed); 128 $AGI->exec("Goto", "1"); 129}