Monday, March 30, 2009

Automating calls with Asterisk Administration Interface (AMI)

A few days ago a friend asked me for a way to automate calls from an asterisk server.
Up until now they were using the .call files functionality to make automatic calls at specific intervals.
But now, they need a bit more control over the generated call. They need to provide a source number, an specific action to perform over the channel once connected, etc.

On a few minutes I prepared an small sample script in perl (derived from taci.pl) demonstrating how to make calls from within asterisk thru it's administration interface (AMI).

As this is a thing a few people have asked me in the past.. I will let the sample script here so I can refer new questions to this post.


#!/usr/bin/perl -w

use Net::Telnet ();
use File::Basename;

#Pop in your asterisk server manager interface information here
$mgrUSERNAME='caller';
$mgrSECRET='pene';
$server_ip='127.0.0.1';

### End Basic Configuration Settings

$destination = $ARGV[0];
$callerid = $ARGV[1];
$duration = $ARGV[2];

if (!$destination || !$callerid || !$duration) {
print "Usage: caller.pl channel callerid duration";
exit 1;
}
#First we have the variable needed to make a call
print qq|
Attempting to call...
From $callerid to $destination (while $duration)
|;
#Make the file handle hot to print out the above HTML
$| = 1;

$tn = new Net::Telnet (Port => 5038,
Prompt => '/.*[\$%#>] $/',
Output_record_separator => '',
Errmode => 'return'
);
#Uncomment and make sure your apache user has access to write this file, good for debugging
#$tn->dump_log("./taci.log");
$tn->open("$server_ip");
$tn->waitfor('/0\n$/');
$tn->print("Action: Login\nUsername: $mgrUSERNAME\nSecret: $mgrSECRET\n\n");
$tn->waitfor('/Authentication accept*/')
or die "Asterisk Manager Interface login failed, verify username and password: ", $tn->lastline;

### Make a call now
# build the variable that holds the call out command, play with the Channel for IAX or Zap to Zap calls.
$tn->print("Action: originate\nChannel: $destination\nCallerid: $callerid\nApplication: Wait\nData: $duration\n\n");
$tn->waitfor('/Event: Newchannel.*/') or die "Unable to determine call status\n", $tn->lastline; # wait for asterisk to process
$tn->print("Action: Logoff\n\n");

#Timeout means it is broken, if we get here Asterisk took it. An invalid ext or account will make it not work though
print "Call information accepted by Asterisk.\n";
exit 0;

Sunday, March 29, 2009

ServiceLocator adaptor for Composite Web Application Block/Web Client Software Factory

In one my employer projects we are working with the CWAB/WCSF components from PnP team.
Also, as part of this project we are using IoC extensively, but some of the components are going to be shared across a few projects using different IoC container.

This seems to be the perfect fit for the PnP's CommonServiceLocator component.

Given the fact that CWAB has it's own IoC container, I made an small Adaptor so CSL can relay on CWAB IoC infrastructure (which is based on ObjectBuilder v1.0, by the way).

This is just the kind of code which can be handy to others.. so I will post it here:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.CompositeWeb;

namespace CompositeWeb.ServiceLocator
{
public class ServiceLocatorAdaptor : ServiceLocatorImplBase
{
private CompositionContainer container;

public ServiceLocatorAdaptor(CompositionContainer container)
{
this.container = container;
}

/// <summary>
/// When implemented by inheriting classes, this method will do the actual work of resolving
/// the requested service instance.
/// </summary>
/// <param name="serviceType">Type of instance requested.</param>
/// <param name="key">Name of registered service you want. May be null.</param>
/// <returns>
/// The requested service instance.
/// </returns>
protected override object DoGetInstance(Type serviceType, string key)
{
if (!String.IsNullOrEmpty(key))
throw new NotSupportedException("CompositeWeb IoC framework does not supports 'key' based services");

var ret = container.Services.Get(serviceType);

//if (ret == null)
// throw new ActivationException("No such service available");
return ret;
}

/// <summary>
/// When implemented by inheriting classes, this method will do the actual work of
/// resolving all the requested service instances.
/// </summary>
/// <param name="serviceType">Type of service requested.</param>
/// <returns>
/// Sequence of service instance objects.
/// </returns>
protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
{
return new List<object>() { container.Services.Get(serviceType) };
}
}
}



Greets..

Thursday, March 26, 2009

NCommon, a promising patterns framework

Since a few days ago I'm trying to apply some DDD patterns to one of my current projects.

In particular I'm specially interested on the Repository Pattern so popular this days. Which, for those of you which didnt know it, a repository pattern is an abstraction pattern which: "Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects." (From Martin Fowler web site).

I think this project will seriously benefit from using this patterns as well as a few related patterns, like the Specification Pattern, which it also helps implementing. This in fact will allow us to achieve a better separation of concerns, as well as adhering to persistance ignorance principles.. which beliveme, is "a really good thing".

But, as you might think, selecting a pattern is just a portion of the work, as we also needed to find a way to implement this patern and refactor our current project's code which, by the way, is based on .Net, Linq2Sql, and SqlServer, on what relates to data access layer.

As usual I started by searching related blog posts, sample implementations, etc. in order to have a better picture about how to implement this pattern. As this is a hot topic this days, you may guess that I found a few resources available. After a few hours reading, processing, and also discarding, many of this resources, I finally found a project on codeplex called NCommon which was nearly an 75% of "what I was looking for" (tm).

NCommon is, as it's author describes it is: "a library that contains implementations of commonly used design patterns when developing applications".

In fact, it covers things like:
  • UnitOfWork Pattern
  • Repository Pattern (with underlaying support for Linq2Sql, EF and NHibernate)
  • Specification Pattern
  • Business and Validation Rules (based on it's Specification Pattern implementation)
  • Per Application/Session/Request storage capabilities
After looking at the code, reading a few blog articles from it's author, analicing it's strenght and design, and also, considering added benefits relating to the fact that some time later we will switch our ORM technology to something "bigger" like NHibernate or, maybe, EF. This library seemed the best approach to help us implement our goals.

But, as usually, nothing is perfect, and while NCommon covers a high percent of what we needed in terms of framework, I faced a few bugs and a few shortcommings using this library.
As thus, in this and on a few following posts I will try to document my findings, as well as the additions, modifications, etc. I did to NCommon to make it a bit more feature rich.

Just in case you were wondering, this are some of the things I'm currently working on adding/fixing to NCommon:
  • Using interfaces instead of concrete objects at the repository level. (And at the same time, with linq).
  • Being able to use (¿inherit?) ambient transactions on UnitOfWork scopes.
  • Find a solution to the "Paging problem" on the repository level.
  • Fix a few NCommon shortcommings like: IRepository<>.Attach method not working on Linq2Sql repositories. Or, not being able to combine ISpecification<> objects.
If you are interested on any of this topics, keep watching, as I will be writing shortly about each of them..

Wednesday, March 25, 2009

Just a first time post..

What would be more appropiate for a first time post than just.. "humm"

;)