SuperDeluxe Conference Manager

From JoatWiki
Jump to: navigation, search

SuperDeluxe Conference Manager

(23 Nov 2008) - Please note that this is a work in progress. Some details may move around between visits.

I've put a bit more effort into developing the conference manager. Rather than change the original page for the Homebrew Asterisk Conference Manager, I'm adding a new page here.

Note: Both this and the previous version are based on a vanilla Asterisk 1.4.x setup. It does run on Asterisk 1.6.x with minimal tweaking.

Note: This is still not for the rank beginner as it may require a bit of code tweaking to get it to work properly (espcially in getting the metadata-refresh stuff to work properly). You really don't have to use this program as I've written it. Feel free to chop it up (it's written mostly in Perl) and adapt it to your own use. Just be sure to give credit and not claim someone else's work as your own.

Disclaimer: Feel free to use my code. However, please realize that it's a work-in-progress and probably shouldn't show up in production environments. It's better suited for internal LANs and the like.

Contents

Features

The web interface currently has the following:

  • Volume controls for talking and/or listening for individual callsers
  • Volume controls for talking and/or listening for the entire conference
  • Hangup a specific caller
  • Kick (hangup with message) a specific caller
  • Mute/unmute a specific caller
  • Mute/unmute all callers in conference
  • Lock a conference (block anyone else from joining the call)
  • Unlock a conference
  • Record/stop recording the call
  • Make a call from the conference
  • Create/edit/delete conference rooms on the fly
  • Speaker detection

Feature Wish List

  • Transfer a specific caller to another number
  • Auto-expiring conference rooms
  • Improve the GUI (colors, alignment, etc.)
  • GUI for the conference room options (note: can be inserted in conf # now but it's really kludgy)

Realtime Conference Rooms

To create/edit/delete conference rooms on the fly, you'll need to configure RealTime conference rooms. The back end for this is quite simple. Add the following line to /etc/asterisk/extconfig.conf:

 meetme => mysql,asterisk,rooms

Basically, the above line tells Asterisk that, for any meetme conference rooms, the configuration can be found in the MySQL database called "asterisk", in a table called "rooms". A snapshot of some test rooms that I have running look like:

mysql> select * from rooms;
+--------+------+----------+---------+
| confno | pin  | adminpin | members |
+--------+------+----------+---------+
| 200    |      |          |       1 |
| 201    |      |          |       0 |
| 202    |      |          |       0 |
+--------+------+----------+---------+
3 rows in set (0.00 sec)

Note: By default, Asterisk only recognizes these four columns (I've looked at the source code). However, you can add other columns to suit your needs (such as conference-start and conference-expires (a future wish list item)).

Note the "1" in the members column for the first record? The content of that column is maintained by Asterisk (do not edit the "members" column). In other words, there is one caller dialed into conference room 200.

Also, for your work, the "starts" and "expires" fields are not required. Those are from my working on auto-expiring conference rooms (a possible future feature). You can generate this table by logging into MySQL, typing "use asterisk;", and then typing the following:

create table rooms (
confno varchar(80) not null primary key,
pin varchar(20),
adminpin varchar(20),
members int(11) not null);

Note: Don't forget to restart Asterisk once you do this.

Note: For you 64-bit Fedora 10 users, if you want to do the above with MySQL, you'll probably have to add res_mysql (via asterisk-addons) manually as the packages for Asterisk 1.6 did not exist. However, please note that the when you compile asterisk-addons, you'll either have to declare the new library install path or copy the resulting library (via the following) once it's done (I used the latter method):

cp /usr/lib/asterisk/modules/res_config_mysql.so /usr/lib64/asterisk/modules/

Feature configuration

The program takes advantage of a number of features made available via the MeetMeAdmin command and also takes advantage of Asterisk's pattern matching capabilities. These are triggered from the web interface via the Asterisk Manager Interface (AMI) (i.e., having AMI generate calls to trigger MeetMeAdmin commands). How AMI does this is explained in part in the older page for the Homebrew Asterisk Conference Manager. To do this, you'll need to add the following to /etc/asterisk/extensions.conf (hint: put the following in a separate file and use the "include" directive):

;
; conference manager commands
;
; kick all users
exten => 111222,1,Answer()
exten => 111222,2,MeetMeAdmin(${EXTEN:6},K)
exten => 111222,3,Hangup()
;
; kick a specific user
exten => _111223.,1,Answer()
exten => _111223.,2,MeetMeAdmin(${EXTEN:6:3},k,${EXTEN:9})
exten => _111223.,3,Hangup()
;
; lock a conference
exten => 111224,1,Answer()
exten => 111224,2,MeetMeAdmin(${EXTEN:6},L)
exten => 111224,3,Hangup()
;
; unlock a conference
exten => 111225,1,Answer()
exten => 111225,2,MeetMeAdmin(${EXTEN:6},l)
exten => 111225,3,Hangup()
;
; mute a user
exten => _111226.,1,Answer()
exten => _111226.,2,MeetMeAdmin(${EXTEN:6:3},M,${EXTEN:9})
exten => _111226.,3,Hangup()
;
; unmute a user
exten => _111227.,1,Answer()
exten => _111227.,2,MeetMeAdmin(${EXTEN:6:3},m,${EXTEN:9})
exten => _111227.,3,Hangup()
;
; increase conference listen volume
exten => 111228,1,Answer()
exten => 111228,2,MeetMeAdmin(${EXTEN:6},V)
exten => 111228,3,Hangup()
;
; decrease conference listen volume
exten => 111229,1,Answer()
exten => 111229,2,MeetMeAdmin(${EXTEN:6},v)
exten => 111229,3,Hangup()
;
; increase conference speaking volume
exten => 111230,1,Answer()
exten => 111230,2,MeetMeAdmin(${EXTEN:6},S)
exten => 111230,3,Hangup()
;
; decrease conference speaking volume
exten => 111231,1,Answer()
exten => 111231,2,MeetMeAdmin(${EXTEN:6},s)
exten => 111231,3,Hangup()
;
; reset????
;exten => 111232,1,Answer()
;exten => 111232,2,MeetMeAdmin(${EXTEN:6},R)
;exten => 111232,3,Hangup()
;
; mute all callers in conference
exten => 111233,1,Answer()
exten => 111233,2,MeetMeAdmin(${EXTEN:6},N)
exten => 111233,3,Hangup()
;
; unmute all callers in conference
exten => 111234,1,Answer()
exten => 111234,2,MeetMeAdmin(${EXTEN:6},n)
exten => 111234,3,Hangup()
;
; increase a specific caller's talk volume
exten => _111235.,1,Answer()
exten => _111235.,2,MeetMeAdmin(${EXTEN:6:3},T,${EXTEN:9})
exten => _111235.,3,Hangup()
;
; decrease a specific caller's talk volume
exten => _111236.,1,Answer()
exten => _111236.,2,MeetMeAdmin(${EXTEN:6:3},t,${EXTEN:9})
exten => _111236.,3,Hangup()
;
; reset ???
;exten => _111237.,1,Answer()
;exten => _111237.,2,MeetMeAdmin(${EXTEN:6:3},r,${EXTEN:9})
;exten => _111237.,3,Hangup()
;
; increase a specific caller's listen volume
exten => _111238.,1,Answer()
exten => _111238.,2,MeetMeAdmin(${EXTEN:6:3},U,${EXTEN:9})
exten => _111238.,3,Hangup()
;
; decrease a specific caller's listen volume
exten => _111239.,1,Answer()
exten => _111239.,2,MeetMeAdmin(${EXTEN:6:3},u,${EXTEN:9})
exten => _111239.,3,Hangup()
;
; record a call into the conference room
exten => _111240.,1,Answer()
exten => _111240.,2,Record(/var/spool/asterisk/monitor/${EXTEN:6}-%d:wav)


You don't have to use these numbers. I've only used the "111" pattern because it doesn't normally show up in anyone's dial plans. For those that don't know, if an extension starts with an underscore "_", it's a flag to indicate that the extension is actually a pattern to match against. You might also notice the period at the end of the extension. This is actually a flag to indicate that any number of digits follows the pattern.

Values are passed to MeetMeAdmin based on the full number dials by chopping up the EXTEN variable. Examples:

  • By using ${EXTEN:6}, you're stating "use the value in EXTEN but ignore the first 6 digits".
  • By using ${EXTEN:6:3}, you're stating "use the value in EXTEN but ignore the first 6 digits and use only the next three digits after that".

Note: In the above configuration, the volume reset functions do not appear to work. Because of this, I've commented out those lines.

AMI Commands

The interface between the above extensions and the web front-end is actually the Asterisk Management Interface (AMI). The core of this interface is a small subroutine

sub asterisk_command () {
	my $command=$_[0];
	my $ami=IO::Socket::INET->new(
		PeerAddr=>'127.0.0.1',
		PeerPort=>5038,
		Proto=>'tcp') or die "failed to connect to AMI!";

	print $ami "Action: Login\r\nUsername: myuser\r\nSecret: mysecret\r\n\r\n$command\r\nAction: Logoff\r\n\r\n";
}

Note: a patch to the AMI's Originate function has broken the above. The problem in the above is that the script disconnects before the AMI has a chance to execute the offered command, which causes the AMI to "forget" the request. The quick repair for this is below. It amounts to adding a delay to the script which "covers" the normal amount of time needed to execute any command handed to the AMI. I will work on a more elegant solution which actually waits for the AMI to respond. For now the following works.

sub asterisk_command () {
	my $command=$_[0];
	my $ami=IO::Socket::INET->new(
		PeerAddr=>'127.0.0.1',
		PeerPort=>5038,
		Proto=>'tcp') or die "failed to connect to AMI!";

	print $ami "Action: Login\r\nUsername: myuser\r\nSecret: mysecret\r\n\r\n$command\r\n";
	sleep 3;
	print $ami "Action: Logoff\r\n\r\n";
}

This allows the CGI script to log into the AMI and pass a command to Asterisk. For the purposes of this script, the "Originate" command is heavily relied upon. In other words, we get the system to dial itself at specific extensions. (Get the idea?)

The code connects to the AMI on port 5038 and passes commands to it and/or pulls statuses from it. You can do this manually by Telnet'ing to port 5038 on the Asterisk box and logging into it. Following is an example of doing this:

joat@localhost:~$ telnet localhost 5038
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Asterisk Call Manager/1.0

action: login
username: myuser
secret: mysecret

Response: Success
Message: Authentication accepted

The commands that you type in are case insensitive, except when you try to pass a channel name (so be careful). Please note that command sequences sent to AMI must have a double carriage return to signal AMI that the command should be processed. To get a list of all of the available AMI commands, connect to asterisk (via "asterisk -r") and type "manager show commands". When you pass commmands via AMI, don't forget that you have to login first, then pass the command, then logout. This is actually three separate AMI actions.

Oh, and, of course, you must be sure that you've configured AMI to allow you to connect. To do so, you'll want an entry in /etc/asterisk/manager.conf that looks something like the following:

[myuser]
secret=mypass
deny=0.0.0.0/0.0.0.0
permit=127.0.0.1/255.255.255.255
read = system,call,log,verbose,command,agent,user
write = system,call,log,verbose,command,agent,user

The above allows the "myuser" user to login with a password of "mypass" from localhost while denying access from all other IPs. It should be noted that, if you use this feature over the network, all content is clear-text and therefore is a security issue. Don't use this feature remotely in networks where someone might sniff this traffic.

The Web Interface

The bad news is that this is a horrible kluge. The good news is that it works quite nicely. Like previously stated, the core of the manager is the little bit of Perl code that allows the web server to talk to the Asterisk Management Interface (AMI). Just about everything else is window dressing, including the CGI code, which is also written in Perl. More good news is that the heavy lifting (code-wise) is above. Writing your own web interface should be easy (mine is quite kludgy but it works).

Authentication

The web interface uses session cookies for authorization. The code for this is adapted from a routine posted on BigNoseBird. This type of authentication should suffice in close-network situations (e.g., internal LANs). However, if you're going to use this in a production environment, I suggest that you employ a more secure method.

sdcm1.jpg

The Conference Room Manager

The conference room manager allows you to add, edit, and delete conference rooms on the fly. These are standard Asterisk MeetMe conference rooms so there's no extra software to install.

sdcm2.jpg

Note that the interface can tell you how many users are currently in a conference room.

The Call Manager

Clicking on the conference room number (in the screen above) drops you into the manager for the individual conference room.

sdcm3.jpg

Note that speaker detection turns the caller info bold blue. The information displayed (such as the call length at the end of each line) is easily customizable by commenting out/uncommenting lines in the code.

There's a lot of controls here. You can:

  • adjust the volume of individual callers (speak and listen volumes are separate)
  • adjust the volume of the conference call as a whole (speak and listen volumes are separate)
  • record the call
  • lock/unlock the conference call (prevents more callers from joining)
  • mute/unmute individual callers
  • mute/unmute everyone in the call
  • hang up on or boot individual callers from the call
  • boot everyone from the call

At the bottom of the screen, you can even call extensions in your dial plan. With simple pattern matching in your dial plan, you can dial out into the real world.

Note: the above is rendered entirely without Cascading Style Sheets (CSS). You can make the interfaces look much more polished by adding your own CSS to the code. Early on, I did do this but I realized that I was spending more time on tweaking the look of the interface than I was on the interface itself. For my own use, the vanilla interface works nicely.

The Code

The code and config files can be grabbed from the link below.

Other Thoughts

  1. Hint: It's a good idea to add CallerID info to your dial plan. Otherwise, this conference manager will tack in whatever data is handy and may or may not be intelligible (e.g., music on hold may just look like a number from your (or someone else's) dialplan).
  2. I think the most overlooked, yet most valuable, feature in this conference manager is the volume controls. They are quite handy when a caller can't figure out how to adjust his own volume. Basically, it allows you to tweak individual settings without having to talk about it during a recording.
  3. I've called it the SuperDeluxe Conference Manager because "SuperDeluxe" was the most obnoxious thing I could think of. The idea is to get you to change the name when you install it.
  4. Known issue, with the upgrade to Asterisk 1.6, it appears that Asterisk has modified how channels are named for Local calls (such as those that connect hold music to a conference room). The problem is that symbols such as '@' and ';' are employed and I've yet to figure out how to pass that across multiple functions successfully. (Perl arrays start with the "@" symbol.) The result: the "Hang up" function for Local calls does not work. The "Kill" call button still works though and it appears this issue only affects calls where both ends are in the same PBX. Consider this a wish list fix for 1.6. The code works on 1.4.
  5. Known issue: the code for the meetme speaker detection is often borked in new versions (as it is for 1.6.1.0). In other words, the conference manager won't show who's talking because the app_meetme needs repair. Just be patient, the coders usually fix this issue pretty quickly (i.e., usually in the next update).


Updates

Missing users table

(20 Sep 2009) - Adam asked that I provide the table structure for "users", which follows. My apologies for leaving it off.
mysql> show columns from users;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment | 
| uname | varchar(255) | NO   |     | NULL    |                | 
| upass | varchar(255) | NO   |     | NULL    |                | 
+-------+--------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

The above can be created by running the following at the MySQL command line prompt:

create table users (
id int not null auto_increment primary key,
uname varchar(255) not null,
upass varchar(255) not null);





Comments:

  • Michele Says:

    Could you send me the code ? Is it open source ? Do you provide support services ?

  • joat Says:

    I'll post the code this weekend. I'm traveling to southern New Jersey on Saturday but should be able to post the code some time on Sunday. The code is written in Perl and depends on a number of features (e.g., dial plan entries) in Asterisk, but it's not that difficult. Depends on what you man by "support services". (heh)

  • asifaleem Says:

    hi,

    i hoped you will be fine. i need the source code of super deluxe conference manger if yon can give me i will b so thankful to you.


    thanks

  • joat Says:

    The link to the code is posted above. It still needs a lot of cleanup. However, the Record function works and the Hang function has been repaired.

  • alok Says:

    Can you post the code.Its look great

  • joat Says:

    Huh? The link to the code is there, under "The Code" and above "Other thoughts". I should have time this weekend to add a bit more code and possibly re-insert the CSS.

  • Andy Says:

    Do you in vision this to go to the open source world? I have a brain storm of how to incorporate several conference calls at the same time and then also have a capability to stream them. It seems as if your app could be a good start for this. Have you looked at posting it out on googlecode?

  • joat Says:

    Andy, yes, it's open source code, built on top of open source code. Feel free to use part or all of the code in your own project (please attribute though). The function that interfaces with AMI was inspired by John at Astrikast (I converted his code from PHP to Perl). The rest just "grew" as I was reading the source code for MeetMe and the docs for Asterisk. I hadn't thought of posting it on Google Code. I will look into it.

  • joat Says:

    Just in case anyone's still reading this, I took Andy up on his suggestion and have opened a page on Google Code to host/develop this code. See it at http://code.google.com/p/crtk/

  • Adam Says:

    Hi, can you post the "users" table structure? I'm sure it's fairly trivial for people who deal with this every day, but there's no mention of it in these instructions.

  • Adam Says:

    -- Executing [111240800@default:1] Answer("Local/111240800@default-cedd,2", "") in new stack

          > Channel Local/111240800@default-cedd,1 was answered.
       -- Executing [111240800@default:2] Record("Local/111240800@default-cedd,2", "/var/spool/asterisk/monitor/800-%d:wav") in new stack
       -- Executing [800@default:1] NoOp("Local/111240800@default-cedd,1", "") in new stack
     == Auto fallthrough, channel 'Local/111240800@default-cedd,1' status is 'UNKNOWN'
    

    [Sep 18 19:05:11] WARNING[18396]: file.c:747 ast_readaudio_callback: Failed to write frame

       --  Playing 'beep' (language 'en')
    

    [Sep 18 19:05:11] WARNING[18396]: app_record.c:243 record_exec: ast_streamfile failed on Local/111240800@default-cedd,2

    == Spawn extension (default, 111240800, 2) exited non-zero on 'Local/111240800@default-cedd,2'

  • joat Says:

    Adam: I've added the table structure for users above. I'm not sure what's causing that error. What version of Asterisk are you running? - Tim (joat)

  • joat Says:

    Adam: I believe the "fallthrough" warning is caused by me leaving off the hangup statement after the record statement. Add the following to your dialplan (after the record statement) and the warning should go away: exten => _111240.,3,Hangup()

  • Adam Says:

    Hi Tim,

    I'm running 1.4.26.2.

    Thanks for posting the DB structure - I built something similar that's working, but I might adjust it to be exact. I also had to figure out how to get a password in to the DB that worked with the perl crypt function... I created this note (this info could be handy to share with others):

    ----

    To add conference manager users, add them to the asterisk db, users table like so:

    insert into users (uname, upass) values ('username', ENCRYPT('password','username'));

    replace "password" and "username" appropriately. Yes, "username" gets used twice.

    ----

    I'm still not getting any recordings happening (nor call bridging with the call-out function, they seem to die similar deaths), even with the Hangup() added in to the dialplan where you suggested. Here's what I get when I hit the record button on a conference:

       -- Executing [111240800@default:1] Answer("Local/111240800@default-7e34,2", "") in new stack
          > Channel Local/111240800@default-7e34,1 was answered.
       -- Executing [111240800@default:2] Record("Local/111240800@default-7e34,2", "/var/spool/asterisk/monitor/800-%d:wav") in new stack
       --  Playing 'beep' (language 'en')
       -- Executing [800@default:1] NoOp("Local/111240800@default-7e34,1", "") in new stack
     == Auto fallthrough, channel 'Local/111240800@default-7e34,1' status is 'UNKNOWN'
    
    == Spawn extension (default, 111240800, 2) exited non-zero on 'Local/111240800@default-7e34,2'

  • Adam Says:

    The call-out feature does this for me - first, it is using another Asterisk server over IAX to place the outbound call - and the outbound call is happening, it's just that when I answer the call that's been called-out to, things fail and the call is terminated.

    ----

       -- Executing [8250XXXXXXX@default:1] Dial("Local/8250XXXXXXX@default-1199,2", "IAX2/Toronto/8250XXXXXXX8@Internal") in new stack
       -- Called Toronto/8250XXXXXXX@Internal
       -- Call accepted by 66.11.XXX.X (format ulaw)
       -- Format for call is ulaw
       -- IAX2/Toronto-5551 is making progress passing it to Local/8250XXXXXXX@default-1199,2
       -- IAX2/Toronto-5551 answered Local/8250XXXXXXX@default-1199,2
          > Channel Local/8250XXXXXXX@default-1199,1 was answered.
       -- Executing [800@default:1] NoOp("Local/8250XXXXXXX@default-1199,1", "") in new stack
     == Auto fallthrough, channel 'Local/8250XXXXXXX@default-1199,1' status is 'UNKNOWN'
       -- Hungup 'IAX2/Toronto-5551'
    

    ----

    Oh, and the NoOp statement is being triggered in my default context by this dialplan line:

    exten => _X.,1,NoOp()

    From the example files you provided, I have extensions 191, 192, 193 and 199 copied in to the default context, as well as including in the big whack of extra extensions to handle the _1112XX extensions.

    Thanks in advance for any advice or suggestions on what the heck I'm missing.

  • joat Says:

    The problem may be caused by a recent patch to Originate. After upgrading to the newest version, I'm finding out that the dialout function no longer works properly. Looks like I have some reading/repairing to do.

  • joat Says:

    Adam,

    My problem doesn't appear to be yours (though it could be). Mine was caused by an update to the Originate function which now requires a bit of timing. In its current form, the Perl script disconnects before the AMI executes any command, causing the AMI to "forget" the requested command. Note the update in red (much above). I'll post the fix for this shortly. It may fix your issue also. Are you seeing a "broken pipe" or fwrite error?

    For your issue, if it's caused by something different, everything up to "Channel Local/8250XXXXXXX@default-1199,1 was answered" looks good. It's the "Executing [800@default:1]" that has me scratching my head (what triggered that?). What comes after "exten => _X.,1,NoOp() " in your dialplan? What does the dialplan entry for "Local/8250XXXXXXX" in the target dialplan look like?

    - Tim (joat)

  • Adam Says:

    Yes, I have seen the broken pipe error, and after a little googling around I found out almost the same thing you did, that the script was finishing before asterisk had a chance to process anything. I "corrected" this by adding some "sleep(1);"'s to the 3 CGI scripts. However, I don't know if this has really corrected anything more than just getting rid of the "broken pipe" and fwrite error messages.

    My entire [default] section of the dialplan, minus the 191, 192, 193 and 199 extensions, looks like this:

    exten => s,1,Goto(incoming,${EXTEN},1) exten => _8N.,1,Dial(IAX2/Toronto/${EXTEN}@Internal) exten => _8N.,n,Hangup() exten => _X.,1,NoOp() exten => i,1,Goto(incoming,${EXTEN},1)

    I actually only have two defined extensions in [incoming], numbered 790 and 791 that pass the caller through to MeetMe, one with the admin flag, one without, so things like the invalid extension in default has a very low chance of actually doing anything useful. But I added it in because in my first several tests of things from the cgi's I was getting complaints from asterisk about things dropping as invalid but there was no invalid extension defined.

  • Adam Says:

    By the way - if a partial comment got submitted several times yesterday, I apologise for the mess. Had problems with a mouse setting and a side button going wonky. Really, it's that sad.

    At any rate, one thing that I don't think I've mentioned in any of these comments is that this software seems nearly perfect for managing conferences (save for the couple of minor bugs). It's fairly simple, not prettified (that's a really good thing), and does a comprehensive job. Big kudos for making this available!

    That said, there's 3 things I've found that would make it perfect for my uses, and I might be able to help hack some of this in. I know if I were great a perl this should be fairly trivial, but for me it's more complex and I'll have to seek help.

    The 3 things are:

    1) A comment field for the conference rooms.. needs to be no more than probably 60 chars. 2) An admin flag (boolean) for the users. No room editing/deleting without this flag. (But all other features remain intact) 3) A room access regex field for users. Only rooms matching the regex would show up in the room list for the user, nor would the user be able to force going in to a room to manage it (via passing the vars on the URI).

    I'm going to try hacking in the room comment field immediately, will let you know how it goes.

  • Adam Says:

    I believe I've successfully implemented the comments field for the conference rooms. My rooms table was updated with "alter table rooms add column comment varchar(60);", and my changes to cm.cgi are downloadable from http://kinetix.kiwicanuck.ca/webfm in the SDConference folder. (The patch also has my sleep(1); update in it, so that may or may not be desirable).

  • joat Says:

    Adam,

    Actually, I used sleep(3). It does avoid the fwrite / broken pipe issue by ignoring the AMI response long enough for the commands to be repeated. By the time that you read this (probably), I'll have my cheesy solution posted above. I'll work on building the version of the function that listens for the proper response from the AMI.

    I did like the older, cheesier version of the function. It was ugly but it was short / simple. I'm not that great of a Perl coder either.

    Concerning your "not prettified" comment: you should have seen the earlier versions. I put so much effort into adding CSS to the web end that I nearly broke the whole thing. The style sheet was actually much bigger than the code. I finally threw the whole CSS thing out. You may notice Apache log entries about missing .css files. That's easily fixed.

    The one thing that seems to differ across versions (or even updates) is speaker detection. In some versions, speaker detection is enabled by default and the text (in the web page) turns blue without having to edit anything. In other versions, you have to explicitly declare speaker detection in your Asterisk .conf files before it works in the web page.

    I'm glad you like the code. I did nothing more than wrap Perl around the existing functions, turn on realtime Meetme, and glue MySQL to the back of it. Andy (further back in the comments) encouraged me to hang the scripts on Google Code. I'm currently working on doing such (search for CRTK), adding lengthy documentation and calling the whole thing a toolkit. If you're agreeable, once you have your version tweaked, I'd like to post it (or a link to it) as a "working example".

    Somewhere around here, I have a version that mimics a radio call-in board (callers reach the receptionist, who posts a comment about what the caller wants to talk about and pushes the call to the host's conference room, where he/she unmutes the caller when it's time to talk to them). It's another non-prettified script but it fits the bill. I'll throw that in as a "recipe". I'm hoping that others pick up this stuff and polish it further.

    - Tim (joat)

  • Adam Says:

    Hi again Tim, thanks for the comments. Have you found any hints as to what's causing the Call out function and the Record functions to not work, or what changes need to be made to get this working again?

  • joat Says:

    Adam,

    Adding the sleep function seems to have fixed the Call Out function (try "3" if "1" isn't working well). It's likely to also be the source of the Record issue as I'm no longer experiencing either issue. Still working on the "more polite" version of the function that waits for an appropriate response from AMI. Also noticed, while looking at this, that a "stop recording" button is needed. (heh)

    - Tim (joat)

  • Adam Says:

    Hi Tim,

    I've adjusted the sleep values to 3 for the fun of it, but I still get this when attempting to use Record:

       -- Executing [111240305@default:1] Answer("Local/111240305@default-18ee,2", "") in new stack
          > Channel Local/111240305@default-18ee,1 was answered.
       -- Executing [111240305@default:2] Record("Local/111240305@default-18ee,2", "/var/spool/asterisk/monitor/305-%d:wav") in new stack
       -- Executing [305@default:1] NoOp("Local/111240305@default-18ee,1", "") in new stack
     == Auto fallthrough, channel 'Local/111240305@default-18ee,1' status is 'UNKNOWN'
    

    [Sep 28 18:09:05] WARNING[6672]: file.c:747 ast_readaudio_callback: Failed to write frame

       --  Playing 'beep' (language 'en')
    

    [Sep 28 18:09:05] WARNING[6672]: app_record.c:243 record_exec: ast_streamfile failed on Local/111240305@default-18ee,2

     == Spawn extension (default, 111240305, 2) exited non-zero on 'Local/111240305@default-18ee,2'
    

    And this time I'm testing in conference room 305. As with previous attempts, I wind up with a 44-byte wav file.

    When I try the call-out again, I get basically the same results as last time:

       -- Executing [8250XXXXXXX@default:1] Dial("Local/8250XXXXXXX@default-9d70,2", "IAX2/Toronto/8250XXXXXXX@Internal") in new stack
       -- Called Toronto/8250XXXXXXX@Internal
       -- Call accepted by 66.11.XXX.X (format ulaw)
       -- Format for call is ulaw
       -- IAX2/Toronto-214 is making progress passing it to Local/8250XXXXXXX@default-9d70,2
    

    ... wait wait wait ...

       -- IAX2/Toronto-214 answered Local/8250XXXXXXX@default-9d70,2
          > Channel Local/8250XXXXXXX@default-9d70,1 was answered.
       -- Executing [305@default:1] NoOp("Local/8250XXXXXXX@default-9d70,1", "") in new stack
     == Auto fallthrough, channel 'Local/8250XXXXXXX@default-9d70,1' status is 'UNKNOWN'
    
    -- Hungup 'IAX2/Toronto-214'

  • Adam Says:

    I should have mentioned too, that one thing that seems really really weird to me, is that I added in the Hangup() at the end of the Record extension in the dialplan, yet Asterisk is dropping through to the NoOp.

    exten => _111240.,1,Answer() exten => _111240.,2,Record(/var/spool/asterisk/monitor/${EXTEN:6}-%d:wav) exten => _111240.,3,Hangup()

    These changes are live... a "show dialplan" reveals:

     '_111240.' =>     1. Answer()                                   [pbx_config]
                       2. Record(/var/spool/asterisk/monitor/${EXTEN:6}-%d:wav) [pbx_config]
                       3. Hangup()                                   [pbx_config]
    
    One question I have meant to ask but forgot amongst other details - Do I need asterisk 1.6 for this and/or is it a problem if I don't have the 'hint's in the dialplan?

  • joat Says:

    Adam,

    No, 1.6 is not required. The only difference between 1.4 and 1.6 is in the configuration of the conference rooms (i.e., there isn't that much difference in their basic dialplan commands). The only point that I've noticed is speaker detection where, in one version you don't have to declare it, in the other you do. The hints are not needed. They're there for other reasons.

    Questions about the record function: Are you dialing it directly or connecting it to the conference room? Does it continue to record even though it continues through the dialplan? (This should be how it works.)

    Your use of the "_X." pattern match still confuses me. I will attempt to write an improved record function this weekend.

    - Tim (joat)

  • joat Says:

    Adam,

    Could the issue be sourced on the other Asterisk box?

    - Tim (joat)

  • joat Says:

    We may have to come at this in small steps. The controls can be installed on a static conference room. The conference room manager is (mostly) independent of the in-call functions. - Tim (joat)

  • Adam Says:

    To answer your question about the record function - No, I wouldn't consider that I'm dialling it directly, it's being called via the CGI - I'm just looking at a conference room that has callers in it and hitting the Record button. Oh, and no, it doesn't continue to record. I just get 44-byte files. Same thing with the call button - I'm just putting in the appropriate # for a call-out through the other PBX and the call disconnects at the point where the PBXs try to bridge the call.

  • joat Says:

    Adam,

    Please try out the dialer.pl script at http://code.google.com/p/crtk/wiki/The_AMI_Interface

    Given that the majority of the functions are controlled via the asterisk_command function, this may prove valuable. Be sure to open a console and crank the verbosity up (I use "7"). The console will be a bit noisy but you'll see it. Try setting up a conference call first, and then run "./dialer.pl 201 111240". That is assuming that your conference extension is "201" and "111240" is the extension in the dial plan to which the Record function is connected.

    - Tim (joat)

  • Adam Says:

    Well, I think I know what's going on... the dialer.pl script got me thinking.

    With the record and call-out functions, they're triggering their special extension in the dialplan as well as an extension equalling the conference number... ie, if you're in conference 305, there's two thing executed in the dialplan - a call to 111240305, and then it in turn calls the conference room, 305... at least that's what this suggests to me:

       -- Executing [111240305@default:1] Answer("Local/111240305@default-7449,2", "") in new stack
       -- Executing [111240305@default:2] Record("Local/111240305@default-7449,2", "/var/spool/asterisk/monitor/305-%d:wav") in new stack
          > Channel Local/111240305@default-7449,1 was answered.
       -- Executing [305@default:1] MeetMe("Local/111240305@default-7449,1", "305|q|6000") in new stack
    

    However, there's two problems with the way this is executed: First, one has to setup an extension for each (or all) meetme rooms that allows entry without prompting for a name recording (hence my addition of the 'q' option to Meetme, above), and second, a specific dialplan entry has to be created so that the Record function isn't sitting there being prompted for a password (6000 in the example above). I'm not sure if you're using passwordless, quiet Meetme rooms, but it seems that this is how these functions bridge together.

    In my dialplan, I fixed the Record and Call functions for Meetme room 305 by putting in the [default] context:

    exten => 305,1,MeetMe(${EXTEN}|q|6000)

    But that doesn't appear to be a very good solution if people can manage the conference rooms via the web interface. Any change in password (or new conference rooms added in, etc) would break the record and call functions for that room.

    What seems rather odd to me is that there doesn't appear to be a (documented) Meetme function that will allow someone to join a conference without requiring the name recording or PIN, for scenarios like this where you want another function to join the conference, but don't want anything impeding it's path to the conference.

    Is my only option then here for a generic Meetme function in the dialplan to not set an admin password and have the other functions hit something like this:

    exten => _X.,1,MeetMe(${EXTEN}|a)

    ?

  • joat Says:

    Adam,

    Yes. Most of what you've assumed is correct. The only one that I think you've incorrect is that, for the way I've written it, only one (the admin) or two (the speaker and the screener) should have access to the web interface. All other callers shouldn't have access, though I'll put that idea on the list of "to write". You're thinking Talkshoe clone? The set up above is more of a managed conference. I imagine that a user interface would involve a separate set of scripts.

    In answer to your question concerning access to conference rooms, keep in mind that the conference room is just an end point. You can define multiple entries (i.e., extensions) into a single conference room: one without a PIN, one with a PIN, one for the admin, one that allows for listen-only, etc. Most of those functions are defined in extensions.conf vice meetme.conf. PINs can be defined in either meetme.conf or extensions.conf. The primary difficulty in any of this is in defining context (i.e., controlling who gets access to which extensions). You're correct in assuming my use of PIN-less rooms.

    As far as I can tell, the "undocumented" Meetme function is the default configuration. I've had no issue with it. Admittedly, I've cheated and do use open access conference rooms. My "control" is that I tend to delete conference rooms when they're not in use and only one other person has access to the web interface.

    Following are my dial plan entries for connecting to the conference rooms (rooms can be 201 through 209, as defined via the web interface):

    exten => _20Z,1,Wait(1)
    exten => _20Z,2,MeetMe(${EXTEN},T)
    

    As you can tell, it's quite basic. The first line allows for the channel to "settle" before the next line dumps the caller into conference. The "T" turns on talker detection. I have absolutely nothing defined in meetme.conf, everything is defined in real time records in the MySQL database (and there isn't much there). Admittedly, for production or externally available systems, this probably is a bit too "open". When I get around to it, I'll probably add a PIN function to the dial plan, rather than having to rewrite the conference rooms' configuration(s).

    If you're using real time Meetme, another point to keep in mind is that the fields normally used by real time are the minimum required. Asterisk tolerates additional fields in those tables (i.e., you can add extra data to each record). I'll attempt to add this to the documentation for CRTK (I have a lot of writing ahead of me!).

    - Tim (joat)

  • Adam Says:

    Heya Tim,

    No, not really envisioning the system allowing all callers access to the web interface. Just managerial/conference screener/etc. access to manage their own conferences. For my particular scenario, various people have their own conference rooms assigned to them, there are some general ones, etc. I have, btw, implemented a regex parameter on to each user account which will limit the conference list in cm.cgi. I haven't implemented the _real_ appropriate security in the other scripts, as this will of course be used in a controlled environment, so limiting the conference view is enough. I'll try and get the changes posted up on my site pretty soon.

    While I understand and agree that there can be multiple ways of entering conferences via different MeetMe calls in either different extensions or contexts in the dialplan, I have yet to see a way to enter a conference that has a password defined on it without using the password in the MeetMe call. If one omits the password in the MeetMe call, then MeetMe is going to ask the connected channel to enter the password... this is undesirable in all circumstances (I believe) when trying to bridge the Record function in to a conference.

    Try this: Setup a new conference with a password on it. Now try and get the Record or Call features working without hard-coding a MeetMe call in to extensions.conf with the password in the call.

    If there's a solution (which I figure there _must_ be, because I'm that kind of optimist, heh), then I hope we can find it. As I previously mentioned, the only way that I currently know of working around this problem is having the Record and Call extensions in the dialplan hit a MeetMe function with the admin flag turned on, and have no admin password on the conference. This would actually work fine in our case, as we don't have people hit MeetMe with the admin flag ever anyway.

Leave a Comment

Personal tools