Asterisk (mit Lua) an Vodafone mit analog Telefonen, MySQL, Werbeblocker

phonator

Neuer User
Mitglied seit
21 Dez 2010
Beiträge
3
Punkte für Reaktionen
0
Punkte
0
Hallo zusammen,


als Referenz poste ich hier mal die relevanten Teile meines momentanen Asterisk Setups.
Insbesondere habe ich kaum relevante Informationen zur Verwendung von Lua als Extension-Sprache gefunden,
daher hoffe ich, dass dies jemand weiterhelfen kann.

Es ist nicht ganz fertig, da ich auf Hardware-technische Schwierigkeiten (CLIP Bug des Telefons) und
Einschränkungen von Asterisk (remote bridging von ge-NAT-teten Telefonen bei dynamischer IP) gestoßen bin.

Der Aufbau momentan:

  • Vodafone DSL
  • Easybox 602

  • analoge Telefone angeschlossen, funktionieren unabhängig von Asterisk
  • zusätzlich meldet sich Asterisk ebenfalls am Vodafone VOIP an, es klingelt bei Asterisk und analogen Telefonen gleichzeitig
  • 2 SIP Telefone (fest und WLAN) am Asterisk
  • SIP/RTP UDP-Ports fest auf interne Adressen gemappt
  • MySQL Datenbank zum Speichern von Anrufen und den Zuordnungen von Nummern

Asterisk überprüft die anrufende Nummer:
  • Lookup in der Datenbank
  • Lookup im Internet: momentan dasortliche.de und "whocallsme.com", ab 4 Reports

  • Reaktion je nach detektiertem Anrufer:
    • "ads": Werbung, Asterisk hebt ab, sagt ein wenig was, spielt eine Weile (lizenzfreie) Musik, und geht dann auf Anrufbeantworter-Modus (für die ganz hartnäckigen)
    • "dontanswer": Asterisk geht ran und simuliert einen Klingelton
    • "hidden", "unknown": klingeln lassen, detektierten Anrufer anzeigen, beim Abheben wird aufgezeichnet
    • "family", "job", etc.: klingeln lassen, detektierten Anrufer anzeigen, später evtl. besonderer Klingelton
  • später evtl. Klingeln nur bei gefragter Person

Bei ungewünschten Anrufern in der Datenbank klingelt somit in der Regel nichts, muss erst im Internet nachgeschlagen werden,
so kann es sein, dass die analogen Telefone 1-2x klingeln.



Der Code:

MySQL-Tabellen
Code:
CREATE TABLE `numbers` (
  `number` varchar(100) NOT NULL,
  `type` varchar(50) NOT NULL default 'ads',
  `name` varchar(100) NOT NULL default 'Unbekannt',
  `for` varchar(100) default 'me',
  PRIMARY KEY  (`number`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

CREATE TABLE `calls` (
  `number` varchar(100) default NULL,
  `direction` varchar(10) default 'in',
  `date` datetime NOT NULL default '0000-00-00 00:00:00',
  `name` varchar(100) NOT NULL,
  `type` varchar(100) NOT NULL,
  PRIMARY KEY  (`date`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

sip.conf
Code:
[general]
context=default                 ; Default context for incoming calls

realm=192.168.0.3
udpbindaddr=0.0.0.0:5060        ; IP address to bind UDP listen socket to (0.0.0.0 binds to all)
tcpenable=no                    ; Enable server for incoming TCP connections (default is no)
tcpbindaddr=0.0.0.0             ; IP address for TCP server to bind to (0.0.0.0 binds to all interfaces)
srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls

disallow=all

useragent=DSL-EasyBox/DSL-EasyBox-20.02.022

rtptimeout=120
rtpholdtimeout=160

language=de

dtmfmode = rfc2833
relaxdtmf=yes
rfc2833compensate=yes

localnet=192.168.0.0/255.255.255.0

externhost=meine.dyndns.org:5069
externrefresh=180

nat=yes
directrtpsetup=no
directmedia=yes
prematuremedia=no
progressinband=no


register => [email protected]:[email protected]/0765432101234~300


[vodafone]
type=peer
host=07654.sip.arcor.de 
outboundproxy=07654.sip.arcor.de
fromuser=0765432101234
username=0765432101234
authuser=0765432101234
fromdomain=arcor.de
secret=passwort
remotesecret=passwort
canreinvite=no
insecure=very,port,invite
disallow=all
allow=alaw
allow=g729
allow=g726
qualify=1800
nat=yes
context=fromvodafone
call-limit=1
dtmfmode=rfc2833
mailbox=1


[intern0]
type=friend 			
username=intern0
secret=123123
host=dynamic
defaultip=192.168.0.1
qualify=yes
canreinvite=no
call-limit=1
dtmfmode=rfc2833
disallow=all
allow=alaw
context=internal
nat=no
mailbox=1

[intern1]
type=friend 			
username=intern1
secret=123123
host=dynamic
defaultip=192.168.0.70
qualify=yes
canreinvite=yes
call-limit=1
nat=no
dtmfmode=rfc2833
context=internal
disallow=all
allow=alaw
mailbox=1

[intern2]
type=friend 			
username=intern2
secret=225533
host=dynamic
defaultip=192.168.0.73
qualify=yes
canreinvite=yes
call-limit=1
nat=no
dtmfmode=rfc2833
disallow=all
allow=alaw
context=internal
mailbox=1

rtp.conf
Code:
[general]
rtpstart=20000
rtpend=20049
rtpchecksums=no

extensions.lua
Code:
CONSOLE = "Console/dsp" -- Console interface for demo
IAXINFO = "guest"       -- IAXtel username/password
TRUNK = "DAHDI/G2"
TRUNKMSD = 1

require "string"
require "io"

function os.capture(cmd, raw)
	local f = assert(io.popen(cmd, 'r'))
	local s = assert(f:read('*a'))
	f:close()
	if raw then return s end
	s = string.gsub(s, '^%s+', '')
	s = string.gsub(s, '%s+$', '')
	s = string.gsub(s, '[\n\r]+', ' ')
	return s
end


-- status: book, ads
function lookupNumberInWeb(nr)

	local res = os.capture("/opt/asterisk/misc/lookup-number "..nr)
	app.noop("lookup result: "..res)

	if res == "unknown" then return nil,nil end

	local typ = string.sub(res, 1, 4)
	local name = string.sub(res, 5)

	typ = string.gsub(typ, '^%s+', '')
	typ = string.gsub(typ, '%s+$', '')

	return typ, name, nil

end

function lookupNumberInDB(nr)

	local typ,name,forPerson = querysingle([[SELECT type, name, `for`
						FROM numbers
						WHERE ']]..nr..[[' LIKE CONCAT(number, '%')
						ORDER BY LENGTH(number) DESC]])

	if typ == nil then return nil,nil,nil end

	return typ,name,forPerson

end

function lookupNumber(nr)

	local typ,name,forPerson

	if nr == nil then return nil,nil,nil end

	app.noop("looking up number "..nr)

	typ,name,forPerson = lookupNumberInDB(nr)
	if typ ~= nil then return typ,name,forPerson end
	
	typ,name,forPerson = lookupNumberInWeb(nr)
	if typ ~= nil then return typ,name,forPerson end

	return nil,nil,nil

end

function identifyCaller(name, nr)

	app.noop(channel.CALLERID("name"):get())
	app.noop(channel.CALLERID("num"):get())

	channel.CallerType = "hidden"
	channel.CallerName = "unknown"
	channel.CallerNr = "unknown"
	channel.CallerFor = ""

	if nr == nil then return end
	if nr == "anonymous" then return end

	channel.CallerType = "nr"
	channel.CallerNr = nr

	local typ,name,forPerson = lookupNumber(nr)
	
	if typ ~= nil then
		channel.CallerType = typ
		if name ~= nil then channel.CallerName = name end
		if forPerson ~= nil then channel.CallerFor = forPerson end
	end

end


function connect_database()
	if channel.connid.value ~= nil then return end

	app.noop("connecting to database")
	app.mysql("CONNECT connid localhost asterisk asteriskDBPW asterisk")
end

function query(resname, querystring)
	connect_database()
	app.mysql("QUERY "..resname.." "..channel.connid.value.." "..querystring)
end
function execute(querystring)
	resultName = "executequery"
	query(resultName, querystring)
end
function querysingle(querystring)
	resultName = "singlequery"
	query(resultName, querystring)
	v1,v2,v3,v4,v5,v6,v7,v8,v9,v10 = fetch(resultName)
	if v1 == nil then v1str = "nil" else v1str = v1 end
	app.noop("Result Single Query: "..v1str)
	endquery(resultName)

	return v1,v2,v3,v4,v5,v6,v7,v8,v9,v10
end

function fetch(resname)
	app.mysql("FETCH fetchid "..channel[resname].value.." var1 var2 var3 var4 var5 var6 var7 var8 var9 var10")

	vars = {}

	for i=1, 10 do
		if channel["var"..i] ~= nil then vars[i] = channel["var"..i].value end
	end
	return unpack(vars)
end

function endquery(resname)
	app.mysql("CLEAR "..channel[resname].value)
end

function close_database()
	if channel.connid.value == nil then return end

	app.noop("closing database connection")
	app.mysql("DISCONNECT "..channel.connid.value.."")
end

function say(text)
	app.festival(text)
end

function handleinvalid()
	nr = channel.INVALID_EXTEN.value;

	app.answer()
	app.playtones("busy")
	app.wait(10)
	app.hangup()
end;

function handletimeout()
	app.answer()
	app.playtones("busy")
	app.wait(10)
	app.hangup()
end;

function initialize()

end;

function cleanup()
	close_database()
	app.hangup()
end;

function ringThePhones(timeout, monitor)
	if timeout == nil then timeout = 20 end
	if monitor == nil then monitor = false end

	if monitor then
		app.mixmonitor("/data/calls/current.wav", "ab", "mv /data/calls/current.wav \"/data/calls/`date +%Y-%m-%d-%H:%M:%S`.wav\"")
	end

	-- set the identified caller name
	if channel.CallerName ~= nil then
		channel.CALLERID("name"):set(channel.CallerName.value)
	end
	
	app.dial("SIP/intern0&SIP/intern1&SIP/intern2", timeout, "g")

	if channel.DIALSTATUS:get() ~= "ANSWER" then
		app.answer()
		app.wait(1)
		app.VoiceMail(1)
	end
end

function recordCall(nr, typ, name, direction)

	if nr == nil then nr = "unknown" end
	if typ == nil then typ = "unknown" end
	if name == nil then name = "unknown" end

	execute("INSERT INTO calls (number,direction,date,name,type) VALUES"..
							"('"..nr.."', '"..direction.."', NOW(), '"..name.."', '"..typ.."')")
end

function handleExternalCall(name, num)
	identifyCaller(name, num)

	recordCall(channel.CallerNr.value, channel.CallerType.value, channel.CallerName.value, "in")

	local action = {
		["hidden"] = function(number, name, forPerson)
				ringThePhones(40, true)
			end,
		["unknown"] = function(number, name, forPerson)
				ringThePhones(40, true)
			end,
		["nr"] = function(number, name, forPerson)
				ringThePhones(40, true)
			end,
		["known"] = function(number, name, forPerson)
				ringThePhones(40)
			end,
		["friend"] = function(number, name, forPerson)
				ringThePhones(40)
			end,
		["family"] = function(number, name, forPerson)
				ringThePhones(60)
			end,
		["job"] = function(number, name, forPerson)
				ringThePhones(40)
			end,
		["ads"] = function(number, name, forPerson)
				app.answer()
				app.wait(1)
				app.background("willkommen")
				app.wait(1)
				app.background("wenn-sie-warten-werden-sie-automatisch-mit-der-telefonzentrale-verbunden")
				app.wait(1)
				app.background("vielen-dank")
				app.MusicOnHold("default", 20)
				app.VoiceMail(1)
				app.hangup()
			end,
		["dontanswer"] = function(number, name, forPerson)
				app.answer()
				app.playtones("ring")
				app.wait(60)
				app.playtones("busy")
				app.wait(50)
				app.hangup()
			end,
	}
	
	app.noop(channel.CallerType.value)
	action[channel.CallerType.value](channel.CallerNr.value, channel.CallerName.value, channel.CallerFor.value)

	app.hangup()
end;

function handleAfterDial()
		app.noop(channel.DIALSTATUS:get())
		if channel.DIALSTATUS:get() == "BUSY" then
			app.playtones("busy")
			app.wait(10)
		end
end;


extensions = {
	fromvodafone = {
		s = function()
			initialize()

			handleExternalCall(channel.CALLERID("name"):get(), channel.CALLERID("num"):get())
		end;
		i = handleinvalid;
		t = handletimeout;
		h = cleanup;

		["<vorwahl><nummer>"] = function()
			app.goto("default", "s", 1)
		end;
	};
	
	internal = {
		h = cleanup;

		["_X."] = function(c,e)
			initialize()
			app.answer()
			channel.CALLERID("name"):set("<vorwahl><nummer>")
			app.dial("SIP/"..e.."@vodafone", 180, "gm")
			recordCall(e, "-", "-", "out")
			handleAfterDial()
			app.hangup()
		end;

		["*1"] = function ()
			initialize()
			app.answer()
			app.dial("SIP/intern0", 60, "g")
			handleAfterDial()
			app.hangup()
		end;
		["*2"] = function ()
			initialize()
			app.answer()
			app.dial("SIP/intern1", 150, "gm")
			handleAfterDial()
			app.hangup()
		end;
		["*3"] = function ()
			initialize()
			app.answer()
			app.dial("SIP/intern2", 150, "gm")
			handleAfterDial()
			app.hangup()
		end;
		["#"] = function ()
			app.VoiceMailMain(1, "s")
		end;

		["_*9."] = function (context, exten)
			local num = string.sub(exten, 3)
			handleExternalCall(num, num)
		end;
	};

	default = {
		include = {"fromvodafone"};
	};
}

voicemail.conf
Code:
[general]
format=wav49|gsm|wav
serveremail=asterisk
attach=yes
maxmsg=999
maxsecs=300
minsecs=3
skipms=3000
maxsilence=10
silencethreshold=128
maxlogins=3
moveheard=yes
emaildateformat=%A, %B %d, %Y at %r
pagerdateformat=%A, %B %d, %Y at %r
sendvoicemail=yes

[zonemessages]
eastern=America/New_York|'vm-received' Q 'digits/at' IMp
central=America/Chicago|'vm-received' Q 'digits/at' IMp
central24=America/Chicago|'vm-received' q 'digits/at' H N 'hours'
military=Zulu|'vm-received' q 'digits/at' H N 'hours' 'phonetic/z_p'
european=Europe/Copenhagen|'vm-received' a d b 'digits/at' HM

[default]
1 => 0,Name,root@localhost

musiconhold.conf
Code:
[general]

[default]
mode=quietmp3
directory=/opt/asterisk/var/lib/asterisk/mohmp3free
digit=#
sort=random

lookup-number Perl-Skript
Code:
#!/usr/bin/perl

use warnings;
no warnings qw(redefine);

use strict;

use POSIX;
use URI;
use List::Util qw[min max];
use LWP 5.64;
use Switch;
use Time::Local;
use Module::Load;

sub getBrowser
{
	my $browser;
	$browser = LWP::UserAgent->new;
	$browser->agent('Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)');
	return $browser;
}

sub lookupTelephonebook
{
	my $nr = shift;

	my $browser = getBrowser();

	my $url = "http://dasoertliche.de/Controller?form_name=search_inv&ph=".$nr;

	my $resp = $browser->get($url);
	return undef unless $resp->is_success;

	# get the result count
	########################################
	my $fullhtml = $resp->content;

	print LOGFILE $fullhtml;

	if(not ($fullhtml =~ m/vorname=(.*?)&nachname=(.*?)&strasse/))
	{
		return undef;
	}

	my $name = $1.$2;

	$name =~ s/\+/ /g;

	return $name;
}

sub lookupWhoCallsMe
{
	my $nr = shift;

	my $browser = getBrowser();

	my $url = "http://whocallsme.com/Phone-Number.aspx/".$nr;

	my $resp = $browser->get($url);
	return undef unless $resp->is_success;

	# get the result count
	########################################
	my $fullhtml = $resp->content;
	my $cnt = () = ($fullhtml =~ m/class="oos_p2/g);

	if($cnt > 3)
	{
		return 1;
	}

	return 0;
}


open(LOGFILE, '>>/var/log/ncid.log');

# READ INPUT

my $CIDNMBR = $ARGV[0];

print LOGFILE "Working on $CIDNMBR\n";

my $announcedName = "unknown";

my $name = lookupTelephonebook($CIDNMBR);
if($name)
{
	print LOGFILE "Found name $name in telephonebook.\n";
	$announcedName = "book ".$name;
}

my $idiot = lookupWhoCallsMe($CIDNMBR);
if($idiot)
{
	print LOGFILE "Found in whocallsme.\n";
	$announcedName = "ads  whocallsme";
}

print LOGFILE "Resolved $CIDNMBR to $announcedName\n";

print $announcedName;

close(LOGFILE);

exit 0
 

Zurzeit aktive Besucher

Statistik des Forums

Themen
246,284
Beiträge
2,249,439
Mitglieder
373,877
Neuestes Mitglied
Bbj
Holen Sie sich 3CX - völlig kostenlos!
Verbinden Sie Ihr Team und Ihre Kunden Telefonie Livechat Videokonferenzen

Gehostet oder selbst-verwaltet. Für bis zu 10 Nutzer dauerhaft kostenlos. Keine Kreditkartendetails erforderlich. Ohne Risiko testen.

3CX
Für diese E-Mail-Adresse besteht bereits ein 3CX-Konto. Sie werden zum Kundenportal weitergeleitet, wo Sie sich anmelden oder Ihr Passwort zurücksetzen können, falls Sie dieses vergessen haben.