OSD für Windows mit Rückrufmöglichkeit

Prymaxx

Neuer User
Mitglied seit
17 Jun 2004
Beiträge
15
Punkte für Reaktionen
0
Punkte
1
Hallo zusammen.

Ich war schon seit längerem auf der Suche nach einer Möglichkeit, die CallerID eines Anrufer per OSD unter Windows anzuzeigen. Da ich nichts gefunden habe was meinen Anforderungen gerecht wurde, habe ich mich mal rangesetzt und eine kleine Lösung mittels ein paar Perl-Scripts und dem Automatisierungs-Tool Girder gebastelt.

An dieser Stelle vielen Dank an Ber5erker, dessen Client-/Server-Scripts ich dabei verwurstet habe :)

---

Um dem Anrufer auch einen Namen zugeben, habe ich ein kleines Script geschrieben welches anhand einer Text-Datei einen Abgleich vornimmt.

/var/lib/asterisk/agi-bin/cid-match.agi
Code:
#!/usr/bin/perl
use Asterisk::AGI;

$AGI = new Asterisk::AGI;
my %input = $AGI->ReadParse();
my ($cid)=@ARGV;

my $basedir = "/etc/asterisk/";
$filename = "cid-match.settings";

&open_file("FILE1","","$basedir$filename");
my $counter = 0;
my $cidname = "unbekannt";

while (($line = &read_file("FILE1")) && ($counter < 100))
	{
    @tabledata = split(/\s*\|\s*/,$line ,$fields);
    if ($tabledata[0] =~ $cid)
    	{
		$cidname = $tabledata[1];
		print STDOUT "SET VARIABLE CALLER_NAME \"$cidname\"\n";
    	}

    else
    	{
		print STDOUT "SET VARIABLE CALLER_NAME \"$cidname\"\n";
		$counter++;
    	}
	}

close(FILE1);
if ($counter == 0)
	{
    print STDERR "Datei hat keinen Inhalt.\n";
	}

sub open_file
	{
    local ($filevar, $filemode, $filename) = @_;
    open ($filevar,$filemode . $filename) ||
    die ("Unable to open $filename");
	}

sub read_file
	{
    local ($filevar) = @_;
    <$filevar>;
	}

Die zugehörige Text-Datei ist recht simpel gestrickt und hat folgenden Aufbau:

/etc/asterisk/cid-match.settings
Code:
1234567|Hans Testermann
3456789|Julia Musterfrau

/etc/asterisk/extensions.conf
Code:
exten => s,n,AGI(cid-match.agi|${CALLERIDNUM}) ;Auswertung der CID und Rückgabe des Anrufer-Namens in ${CALLER_NAME}
exten => s,n,System(perl /usr/girder/ievent.pl osd "${CALLER_NAME}" ${CALLERIDNUM} "${DATETIME}")

Der zweite Eintrag in der extensions.conf ist die Übermittlung von Event-Trigger, Name, Rufnummer und Zeit an Girder auf dem Windows-PC. Hier habe ich bei dem Script auf eine bereits existierende Lösung von jjk aus dem Promixis-Forum zurückgegriffen.
IP und Port des Zielrechners sind fest im Script hinterlegt. Werde ich bei Zeiten noch anpassen um flexibler zu sein.

/usr/girder/ievent.pl
Code:
#!/usr/bin/perl
# Set path according to your system

#
# Internet event client for Perl
# Modified from IE2Client.pl
#


$host = "10.10.x.x"; # Hardwire the host, port, and password
$port = 1244; # to match the settings of the server
$passwd = "xxxx"; # plugin.

use IO::Socket;
use Text::ParseWords;
use Digest::MD5;

my ($socket); # Socket will be global

##
## Mainline
##
{


my ($rsp, $ctx, $i, $DEBUG);
$DEBUG=0;
# Stop if no event
if ($#ARGV == -1) {die "Usage: ievent.pl <event payload(s)>\n"; }

# Create/connect the socket
$socket = IO::Socket::INET->new("$host:$port") || die $@;

$rsp = grdr_resp("quintessence"); # Validate/start the connection
$ctx = Digest::MD5->new->add("$rsp:$passwd");
$rsp = grdr_resp($ctx->hexdigest);
if ($rsp !~ /^accept$/) { die "Invalid password" }

if ($DEBUG) { printf("ARGV = %d\n", $#ARGV); }

if ($#ARGV > 0) {
for ($i = 1; $i <= $#ARGV; $i++) {
grdr_send("payload @ARGV[$i]");
if ($DEBUG) {printf("payload @ARGV[$i]\n");}
}
}

grdr_send(@ARGV[0]);
if ($DEBUG) {printf("$ARGV[0]\n");}

grdr_send("close"); # Shut everything down
close ($socket);
}

##
## Subroutines
##


sub grdr_send ## Send a girder command line
{
print $socket $_[0],"\n"; # Girder wants terminating "\n"
}

sub grdr_recv ## Get a girder response
{
my $line = <$socket>; # Get response
chomp $line; # Strip trailing "\n"
return $line;
}

sub grdr_resp ## Do a command-response sequence
{
grdr_send($_[0]);
return grdr_recv();
}

Auf dem Windows-PC nimmt der Girder Internet Event Server die Daten entgegen. Girder öffnet das OSD und zeigt für ein paar Sekunden Anrufer-Name und -Nummer an (Anhang: Asterisk-OSD1.jpg).
Da mir wichtig war, mehr als nur den letzten Anrufer zu sehen, werden die Daten der letzten fünf Anrufer zwischengespeichert.
Über Ctrl+Shift+C kann ich bei Bedarf meinen "Anrufmonitor" (Anhang: Asterisk-OSD2.jpg) öffnen. Im Prinzip nichts weiter als ein völlig frei konfigurierbares HTML OSD. Dort werden mir, sofern vorhanden, die Daten der letzten Anrufer angezeigt.

Durch anklicken der Namen kann ein direkter Rückruf ausgeführt werden. Erst klingelt das interne Telefon, und nach dem Abnehmen wird der Ruf nach Aussen signalisiert.
Dafür läuft folgendes Script auf dem Asterisk und wartet auf Daten:

/usr/girder/empfaenger.pl
Code:
#!/usr/bin/perl
# empfaenger.pl -- Ein Single-threaded-TCP-Server

use strict;
use IO::Socket;
use Asterisk::AGI;

use constant MYPORT => 52401;

my $sock = '';
my $client = 0;
my $counter = 0;
my @payload = '';
my $spoolpath = "\/var/spool\/asterisk\/outgoing";

$sock = new IO::Socket::INET(LocalPort => MYPORT,
                             Reuse     => 1,
                             Listen    => 5)
    or die "can't create local socket: $@\n";
print "Accepting connections on Port ", MYPORT, "...\n";

while ($client = $sock->accept())
  {

  for (<$client>)
    {
    chomp;
    @payload = split(/\s*\-\s*/,$_);

	# Debug
	#    while (($payload[$counter] ne "") && ($counter < 30)) {
	#    print "Payload$counter: $payload[$counter]\n";
	#    $counter++; }

    my $callfile = (int(rand(99999))+ 1) . ".call";
    open(OUT, ">$spoolpath\/$callfile") or
		die "Cannot create file $callfile: $!";
    print OUT "Channel: $payload[0]\n";
    print OUT "MaxRetries: 1\n";
    print OUT "RetryTime: 60\n";
    print OUT "WaitTime: 30\n";
    print OUT "Context: osd-callback\n";
    print OUT "Extension: $payload[1]\n";
    print OUT "Priority: 1\n";
    close(OUT);
    print STDOUT "SET VARIABLE OSD-EXTERN \"$payload[1]\"\n";
    }

  $client->close() if defined $client;
  }

Das Gegenstück auf dem Windows-PC sieht so aus:

sender.pl
Code:
#!/usr/bin/perl
# sender.pl -- Ein einfacher TCP-Client.

use strict;
use IO::Socket;

use constant TIMEOUT => 5;
my $ipaddr = '10.10.x.x';
my $portnr = '52401';
my $sock = '';
my $reply = '';

$sock = new IO::Socket::INET(PeerAddr => $ipaddr,
                             PeerPort => $portnr,
                             Proto    => 'tcp', Timeout => TIMEOUT)
    or die "can't connect to $ARGV[0]:$ARGV[1]: $@\n";

print $sock $ARGV[0],$ARGV[1];

$sock->close();

Da ich auf dem Windows-PC kein Perl laufen habe, hab ich die sender.pl mit perl2exe direkt ausführbar gemacht.
Bei einem Rückruf übergibt Girder Channel und Zielnummer als Parameter an die sender.exe. Diese überträgt die Daten an das Script empfaenger.pl auf dem Asterisk welches das Callfile erstellt und in den Context osd-callback springt.

/etc/asterisk/extensions.conf
Code:
[osd-callback]
exten => _.,1,NoOp(${EXTEN}) ;Debug
exten => _.,2,Dial(SIP/${EXTEN}@qsc_out||rm
exten => _.,3,Hangup
exten => h,1,Hangup

Alles in allem vielleicht keine ideale Lösung und schlampig programmiert, aber man ist doch recht flexibel in der Darstellung und Verarbeitung der Daten.

Bei Interesse stelle ich das passende Girder GML-File gerne bereit.

Verbessungsvorschläge sowie positive und negative Kritik werden gerne genommen.
 

Anhänge

  • Asterisk-OSD1.jpg
    Asterisk-OSD1.jpg
    4.6 KB · Aufrufe: 308
  • Asterisk-OSD2.jpg
    Asterisk-OSD2.jpg
    27 KB · Aufrufe: 300
Windoze Rückruf

Hallo,

soetwas habe ich schon länger gesucht, bisher aber auch noch nichts simples gefunden. Deine Toolsammlung macht schon einen sehr guten Eindruck.

Folgende Vorschläge habe ich:
Zur Namensauflösung das agi reverse.agi http://www.voip-info.org/wiki-Reverse+Lookup+in+Germany
verwenden. Das habe ich schon einige Zeit in Benutzung, es hat auch einen lokalen Cache, da kann man dann auch nicht vorhandene Nummern eintragen. Vielleicht kann man das mit Deinen Skripten verquicken, ich habe dort bisher nur einen Windows-Popup eingebaut (smbclient -M ....).

Girder ist etwas gewaltig für die Aufgabe, ausserdem kostet es Geld.
Vielleicht kann man da auch was kleines in VisualBasic zusammenhacken.
Wobei klein und VB sich ja fast ausschliesst.
 
Hi,

das reverse.agi hatte ich ausgeschlossen weil eigentlich niemand in meinem Bekanntenkreis über Reverse-Lookup zu finden ist. Aber wenn man die Möglichkeit hat Nummern nachzupflegen, ist es natürlich die bessere Wahl. Hätte ich mir vielleicht doch genauer ansehen sollen :)

Auf Girder bin ich eigentlich nur gekommen, weil ich es damals für meinen CarPC angeschafft hatte und es sowieso hier rumlag. AFAIK war Girder bis Version 3.2.9 sogar völlig kostenlos. Ich hab aber nicht getestet ob die Version im Zusammenspiel mit den Scripten funktioniert.
Da sich meine Programmierkenntnisse auf wenig Perl und noch weniger Shell-Programmierung beschränken, war VB keine wirkliche Alternative. Derzeit versuche ich ein Perl Tcl/Tk Frontend als Ersatz für Girder zu schreiben, aber wenn Du was in VB zusammenstricken kannst, warte ich gerne ;)
 
Als Bestandteil meines VoIPBoX Projektes gibt es da auch etwas.

  • Ermittlung der Anruferdaten, sofern vorhanden aus lokaler Tabelle
  • Ermittlung des Ortsnamens anhand der Vorwahl aus lokaler SQLite Datenbank
  • Optionales Lookup über das "Örtliche"
  • Eintrag in lokale Tabelle zur Vermeidung unnötiger Abfragen
  • Popup Mechanismus für Windows Clients mit Rückruffunktion

Falls Interesse daran besteht, kann ich das gern mal separat zu Verfügung stellen.
 
rbaer schrieb:
Falls Interesse daran besteht, kann ich das gern mal separat zu Verfügung stellen.

Das wäre super!

Danke,
Tin
 
rbaer schrieb:
Falls Interesse daran besteht, kann ich das gern mal separat zu Verfügung stellen.
Da sag ich nicht nein, warum das Rad neu erfinden.
:habenwol:

rbaer schrieb:
Als Bestandteil meines VoIPBoX Projektes gibt es da auch etwas.
Ich hab die aktuelle Version hier testweise untern VMWare laufen, aber die Funktion hab ich noch nicht gesehen. Ist das optional?
 
wie weit seid ihr :)

Hallo
ich erlaube mir kurz anzufragen, ob das VBScript schon gediehen ist? Zudem: Wo bekommt man girder und was kostet es?
Die hier diskutierten Lösungen interessieren mich nämlich sehr und ich danke euch schon jetzt für eine kurze Antwort....

Herzliche Grüsse
Stephan
 
stephanw schrieb:
Wo bekommt man girder und was kostet es?

Hallo Stephan,

die aktuelle Version gibt´s für $49,99 untern http://www.promixis.com
Für die älteren, kostenlosen Versionen müsstest Du mal die Suchmaschine Deines Vertrauens bemühen.

Gruss
Benni
 
Hallo zusammen, gibt es auch eine ods Server für KDE? Oder wie läst sich soetwas unter linux umsetzten. Es ist vermutlich sogar einfacher, aber ich weiß noch nicht so viel über Linux.

Danke Markus
 
Hallo,
hatte ich schon gesehen (osd_cat), aber gibt es auch etwas was das so schön wie bei amaroK machen kann? Oder kann ich das mit XOSD auch machen? Ich kenne nur osd_cat.

Danke schonmal,
goebiman
 
Hallo,

ich habe mich auch mal an dem Thema versucht. Dabei ist folgendes rausgekommen.

Ein kleines Windows-Programm in c# geschrieben. Ich habe es "Asterwatch" genannt. Das horcht also auf einem Port auf eingehende Nachrichten und zeigt diese dann auch an. Es ist also von der Funktionsweise relativ einfach bisher.

asterwatch

Der asterisk server verbindet sich über ein perl-script. Also Vorlage habe ich das von Prymaxx genommen. Der Unterschied ist, dass man bei meinem mehrere Empfänger angeben kann und das es multithreaded die Nachrichten verschickt.
Es werden einfach alle Parameter des Skripts an die Empfänger als Nachrichten verschickt.

Code:
#!/usr/bin/perl
# Set path according to your system

#
# Internet event client for Perl
# Modified from IE2Client.pl
#

use threads;
use Digest::MD5;
use IO::Socket::INET;

@hosts = ( "192.168.2.12|5678|mypassword",
           "192.168.2.15|5678|mypassword" ); # hosts array
$DEBUG = 0;

if ( $#ARGV == -1 ) { die "Usage: ievent.pl <event payload(s)>\n"; }

foreach (@hosts)
{
  my @items = split(/\|/);

  if ($DEBUG) { printf("Starting thread for \"$_\"\n"); }
  my $thread = threads->create("thread_func", @items);
}

foreach (threads->list)
{
  $_->join;
}

sub thread_func
{
  @host =  @_;

  # Create/connect the socket
  $socket = IO::Socket::INET->new(PeerAddr => @host[0], PeerPort => @host[1], Proto => 'tcp', Timeout => 1) || die $@;

  $rsp = grdr_resp($socket, "quintessence"); # Validate/start the connection
  $ctx = Digest::MD5->new->add("$rsp:@host[2]");
  $rsp = grdr_resp($socket, $ctx->hexdigest);

  if ($rsp !~ /^accept$/)
  {
    print "Invalid password\n"
  } else {
    grdr_send($socket, join(" ", @ARGV) );
  }

  grdr_send($socket, "close"); # Shut everything down
  close ($socket);
}

sub grdr_send ## Send a girder command line
{
  @args = @_;
  $msg = @args[1];
  $socket = @args[0];

  print $socket $msg."\n"; # Girder wants terminating "\n"
}

sub grdr_recv ## Get a girder response
{
  $socket = @_[0];
  my $line = <$socket>; # Get response
  chomp $line; # Strip trailing "\n"
  return $line;
}

sub grdr_resp ## Do a command-response sequence
{
  grdr_send($_[0], $_[1]);
  return grdr_recv($_[0]);
}

Der Aufruf unter Asterisk sieht bei mir so aus:
Code:
exten => _216531N,2,System(/usr/local/bin/asterisk-ievent.pl ${CALLERIDNUM} ${LONGNAME} )

Anbei noch ein Screenshot des Programms
 

Anhänge

  • asterwatch1.JPG
    asterwatch1.JPG
    10.2 KB · Aufrufe: 49
  • asterwatch2.JPG
    asterwatch2.JPG
    27.9 KB · Aufrufe: 100
Wenns Interessiert:

Ich habe da mal so ein kleines Projekt gemacht, was mit asterisk noch nicht so ganz Stable ist.
Kurze Erläuterung:
Es ist eine Server-Client Architektur. Der Daemon der idealerweise auf dem Rechner auf dem Asterisk läuft ausgeführt werden sollte, dient zum Anrufe Loggen und um zu wählen etc. Dazu verbindet sich der Unictrl Daemon zu dem Asterisk-Server per Callmanager.
Es soll also am Ende so eine Art CTI für asterisk sein, das aber mehr kann als nur dass.

Der Client läuft auf gängigen Linux-Desktops sowie unter Windows (es sollte auch unter MacOS gehen, das habe ich aber aufgrund von zeit(geld)mangel nicht testen können)

Das ganze ist Pluginbasiert, es gibt implementationen für ein Telefonbuch. Etherwake, Shellscriptausführung per Knopfdruck, Systeminformationen ...
Jedes Plugin hat einen "Admin" mit dem man es einfach konfigurieren kann.

Momentan wird eine Interne Datenbank (die XBase als Grundlage verwendet) zur Speicherung benutzt. Ich habe aber Schon anbindung an andere Datenbaknen im Blick.
Eine Rückwärtssuche über div. webseiten baue ich gerade.

Nun der Bezug zu deinem Thread:
Bei einem Anruf kann man einen Balloontip (unter windows und Linux) Anzeigen lassen, sowie die Wählhilfe zum Rückruf verwenden. Man hat eine Anrufliste der letzten Calls usw.

Weitere Informationen auf wunsch von mir. Wer interesse hat, hier einfach posten.
Ein paar sachen stehen auch schon im WIki.

Wie gesagt: das Ganze ist noch nicht wirklich stable, wer lust hat an soetwas mitzuarbeiten ist jederzeit herzlich willkommen.
(wir suchen Programmierer, Grafiker, Übersetzer etc)

Projektseite:

http://unictrl.de
 
Zuletzt bearbeitet von einem Moderator:
Hallo soeren,

das was du da schreibst kommt mir ziemlich bekannt vor. Ich hab vor langer Zeit auch mal sowas ähnliches programmiert. Ebenfalls Client/Server Architektur mit Plugin-Schnittstelle, Userverwaltung und Rechteverwaltung.

Mein Projekt findet man unter: Semi|Con

Viele Grüße
Sebastian
 
Irgentwie ist dein Link kaputt, ich kann damit nichts machen ;-(

Ich arbeite jetzt schon ca 1 Jahr an dem Unictrl Code (mehr oder weniger regelmäßig). Ich habe da auch eine Komplette Userverwaltung mit Rechtesystem & Co drinne.

Aber was du sagst, hört sich aber interessant an. Anschauen werde ich das mal.
Hast du lust an sowas mitzuarbeiten?
 
Hallo,

bei dem Link ist wohl was schiefgelaufen, ich habs geändert. Was ich nicht habe und interessant wäre ist eine Plugin für Asterisk. Ich könnte dir sicherlich bei der ein oder anderen Sache unter die Arme greifen. Ich habe leider durch die Arbeit bedingt nicht so viel Zeit.

Grüße
Sebastian
 
Fehler beim Starten

Hallo Sebastian,

ich finde das Projekt Super, allerdings bekomme ich auf 3 von 4 Rechnern das Programm nicht ans Fliegen.
Bei dem einen startet das Programm und wird mit einem unbekannten Ausnahmefehler beendet. Bei den anderen dreien erhalte ich eine Fehlermeldung:

"Anwendung konnte nicht richtig initalisiert werden (0xc0000135). Klicken Sie auf "OK" um die Anwendung zu beenden.

.NET 2.0 habe ich die aktuelle Version heruntergeladen und installiert.

Hast du eine Idee für mich ?

Gruß,

MasterD
 
Hi nochmal,

Fehler gefunden, es muss .NET 3.0 SP1 installiert sein.

Gruß

MasterD
 
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.