fsconsole.pl 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. sub POE::Kernel::ASSERT_DEFAULT () { 1 };
  5. sub Term::Visual::DEBUG () { 1 }
  6. sub Term::Visual::DEBUG_FILE () { 'test.log' }
  7. use IO::Socket;
  8. use POE qw/Filter::FSSocket Component::Client::TCP/;
  9. use Data::Dumper;
  10. use Term::Visual;
  11. local *D;
  12. if (Term::Visual::DEBUG) {
  13. *D = *Term::Visual::ERRS;
  14. }
  15. #local *ERROR = *STDERR;
  16. $SIG{__DIE__} = sub {
  17. if (Term::Visual::DEBUG) {
  18. print Term::Visual::ERRS "Died: @_\n";
  19. }
  20. };
  21. ###############################################################################
  22. ## BEGIN Globals ##############################################################
  23. ###############################################################################
  24. our $server_address = "127.0.0.1";
  25. our $server_port = "8021";
  26. our $server_secret = "ClueCon";
  27. #this is where you can customize the color scheme
  28. our %Pallet = (
  29. 'warn_bullet' => 'bold yellow',
  30. 'err_bullet' => 'bold red',
  31. 'out_bullet' => 'bold green',
  32. 'access' => 'bright red on blue',
  33. 'current' => 'bright yellow on blue',
  34. );
  35. our $terminal;
  36. my %sockets;
  37. my %windows;
  38. my %unread_count;
  39. my %commands = (
  40. 'window' => 1,
  41. 'w' => 1,
  42. 'win' => 1,
  43. );
  44. ###############################################################################
  45. ## END Globals ##############################################################
  46. ###############################################################################
  47. #setup our session
  48. POE::Session->create(
  49. 'inline_states' => {
  50. '_start' => \&handle_start, #session start
  51. '_stop' => \&handle_stop, #session stop
  52. 'curses_input' => \&handle_curses_input, #input from the keyboard
  53. 'update_time' => \&handle_update_time, #update the status line clock
  54. 'quit' => \&handle_quit, #handler to do any cleanup
  55. 'server_input' => \&handle_server_input,
  56. '_default' => \&handle_default,
  57. },
  58. 'heap' => {
  59. 'terminal' => undef,
  60. 'freeswitch' => undef,
  61. },
  62. );
  63. #start the kernel a chugging along
  64. $poe_kernel->run;
  65. ###############################################################################
  66. ## BEGIN Handlers #############################################################
  67. ###############################################################################
  68. #handles any startup functions for our session
  69. sub handle_default {
  70. }
  71. sub handle_start {
  72. my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
  73. #setup our terminal
  74. $heap->{'terminal'} = Term::Visual->new(
  75. 'Alias' => 'terminal', #poe alias for this
  76. 'History_Size' => 300, #number of things to keep in history
  77. 'Common_Input' => 1, #all windows share input and history
  78. 'Tab_Complete' => \&tab_complete,
  79. );
  80. $terminal = $heap->{'terminal'};
  81. #setup the color palette
  82. $terminal->set_palette(%Pallet);
  83. #create a base window
  84. my $window_id = $terminal->create_window(
  85. 'Window_Name' => 'console',
  86. 'Buffer_Size' => 3000,
  87. 'Title' => 'FreeSWITCH Console',
  88. 'Status' => {
  89. '0' => {
  90. 'format' => '%s',
  91. 'fields' => ['time'],
  92. },
  93. '1' => {
  94. 'format' => '%s',
  95. 'fields' => ['window_status'],
  96. },
  97. },
  98. );
  99. $windows{'console'} = $window_id;
  100. $window_id = $terminal->create_window(
  101. 'Window_Name' => 'log',
  102. 'Buffer_Size' => 3000,
  103. 'Title' => 'FreeSWITCH Logs',
  104. 'Status' => {
  105. '0' => {
  106. 'format' => '%s',
  107. 'fields' => ['time'],
  108. },
  109. '1' => {
  110. 'format' => '%s',
  111. 'fields' => ['window_status'],
  112. },
  113. },
  114. );
  115. $windows{'log'} = $window_id;
  116. $window_id = $terminal->create_window(
  117. 'Window_Name' => 'event',
  118. 'Buffer_Size' => 3000,
  119. 'Title' => 'FreeSWITCH Event',
  120. 'Status' => {
  121. '0' => {
  122. 'format' => '%s',
  123. 'fields' => ['time'],
  124. },
  125. '1' => {
  126. 'format' => '%s',
  127. 'fields' => ['window_status'],
  128. },
  129. },
  130. );
  131. $windows{'event'} = $window_id;
  132. #tell the terminal what to call when there is input from the keyboard
  133. $kernel->post('terminal' => 'send_me_input' => 'curses_input');
  134. $terminal->change_window(0);
  135. $kernel->delay_set('update_time' => 1);
  136. $terminal->set_status_field(0, 'time' => scalar(localtime));
  137. new_message('destination_window' => 0, 'message' => "
  138. Welcome to the FreeSWITCH POE Curses Console!
  139. The console is split into three windows:
  140. - 'console' for api response messages
  141. - 'log' for freeswitch log output (simply send the log level you want
  142. to start seeing events eg: 'log all')
  143. - 'event' for freeswitch event output (must subscribe in plain format
  144. eg: 'event plain all')
  145. To switch between windows type 'w <windowname' so 'w log' for example.
  146. Coming soon:
  147. - Tab completion
  148. - command history
  149. - window status in the bar (messages added since last view, etc...)
  150. Send any bug reports or comments to jackhammer\@gmail.com
  151. Thanks,
  152. Paul\n");
  153. $terminal->set_status_field($terminal->current_window, 'window_status' => format_window_status());
  154. #connect to freeswitch
  155. $heap->{'freeswitch'} = POE::Component::Client::TCP->new(
  156. 'RemoteAddress' => $server_address,
  157. 'RemotePort' => $server_port,
  158. 'ServerInput' => \&handle_server_input,
  159. 'Connected' => \&handle_fs_connected,
  160. 'ServerError' => \&handle_server_error,
  161. 'Disconnected' => \&handle_server_disconnect,
  162. 'Domain' => AF_INET,
  163. 'Filter' => POE::Filter::FSSocket->new(),
  164. );
  165. }
  166. #called when users enter commands in a window
  167. sub handle_curses_input {
  168. my ($kernel, $heap, $input, $context) = @_[KERNEL, HEAP, ARG0, ARG1];
  169. #get the id of the window that is responsible for the input
  170. my $window = $heap->{'terminal'}->current_window;
  171. open(ERROR, ">>error.log");
  172. if($input eq "quit") {
  173. $kernel->yield('quit');
  174. } elsif ($input =~ /^w\ (.*)$/) {
  175. #get the id of the requested window
  176. eval {
  177. my $window_id = $windows{$1};
  178. #see if it's real
  179. if(defined($window_id)) {
  180. $unread_count{$window_id} = 0;
  181. $terminal->change_window($window_id);
  182. $terminal->set_status_field($window_id, 'window_status' => &format_window_status());
  183. }
  184. };
  185. if($@) {
  186. print ERROR "put error: $@\n";
  187. }
  188. } else {
  189. #see if we got connected at some point
  190. if(defined($sockets{'localhost'})) {
  191. my $cmd;
  192. if ($input =~ /^log|^event/) {
  193. $cmd = $input;
  194. } else {
  195. $cmd = "api $input";
  196. }
  197. #send the command
  198. $sockets{'localhost'}->put($cmd);
  199. }
  200. }
  201. }
  202. sub handle_fs_connected {
  203. my ($kernel, $heap) = @_[KERNEL, HEAP];
  204. eval {
  205. $sockets{'localhost'} = $heap->{'server'};
  206. }
  207. }
  208. #this is responsible for doing any cleanup and returning the terminal to the previous
  209. #state before we mucked with it
  210. sub handle_quit {
  211. my ($kernel, $heap) = @_[KERNEL, HEAP];
  212. #tell curses to clean up it's crap
  213. $kernel->post('terminal' => 'shutdown');
  214. #there is probably a more elegant way, but this works for now
  215. exit;
  216. }
  217. #data from freeswitch
  218. sub handle_server_input {
  219. my ($kernel,$heap,$input) = @_[KERNEL,HEAP,ARG0];
  220. eval {
  221. #terminal HATES null
  222. if(defined($input->{'__DATA__'})) {
  223. $input->{'__DATA__'} =~ s/[\x00]//g;
  224. }
  225. #handle the login
  226. if($input->{'Content-Type'} eq "auth/request") {
  227. $heap->{'server'}->put("auth $server_secret");
  228. } elsif ($input->{'Content-Type'} eq "api/response") {
  229. new_message('destination_window' => 0, 'message' => 'API Response: ');
  230. new_message('destination_window' => 0, 'message' => $input->{'__DATA__'});
  231. } elsif ($input->{'Content-Type'} eq "log/data") {
  232. new_message('destination_window' => 1, 'message' => $input->{'__DATA__'});
  233. } elsif ($input->{'Content-Type'} eq "text/event-plain") {
  234. new_message('destination_window' => 2, 'message' => Dumper $input);
  235. } elsif ($input->{'Content-Type'} eq "command/reply") {
  236. new_message('destination_window' => 0, 'message' => 'Command Response: ' . $input->{'Reply-Text'});
  237. }
  238. };
  239. if($@) {
  240. open(ERROR, ">>error.log");
  241. print ERROR "died: $@\n";
  242. print ERROR Dumper $heap;
  243. close(ERROR);
  244. }
  245. }
  246. sub handle_server_error {
  247. }
  248. sub handle_server_disconnect {
  249. }
  250. sub tab_complete {
  251. my $left = shift;
  252. my @return;
  253. if(defined($commands{$left})) {
  254. return [$left . " "];
  255. #} elsif () {
  256. }
  257. }
  258. sub handle_update_time {
  259. my ($kernel, $heap) = @_[KERNEL, HEAP];
  260. $terminal->set_status_field($terminal->current_window, 'time' => scalar(localtime));
  261. $kernel->delay_set('update_time' => 1);
  262. }
  263. ###############################################################################
  264. ## END Handlers #############################################################
  265. ###############################################################################
  266. sub new_message {
  267. my %args = @_;
  268. my $message = $args{'message'};
  269. my $destination_window = $args{'destination_window'};
  270. my $status_field;
  271. #see if we are on the window being updated
  272. if($terminal->current_window != $destination_window) {
  273. #increment the unread count for the window
  274. #FIXME, should we count messages or lines?
  275. $unread_count{$destination_window}++;
  276. #update the status bar
  277. eval {
  278. $terminal->set_status_field($terminal->current_window, 'window_status' => &format_window_status());
  279. };
  280. if($@) {
  281. print $@;
  282. }
  283. }
  284. #deliver the message
  285. $terminal->print($destination_window, $message);
  286. }
  287. sub format_window_status {
  288. my $status_field;
  289. #put all the windows in the bar with their current unread count
  290. foreach my $window (sort {$windows{$a} <=> $windows{$b}} keys %windows) {
  291. #see if we are printing the current window
  292. if($terminal->current_window == $windows{$window}) {
  293. $status_field .= "[\0(current)$window\0(st_frames)";
  294. } else {
  295. $status_field .= "[$window";
  296. }
  297. if($unread_count{$windows{$window}}) {
  298. $status_field .= " (" . $unread_count{$windows{$window}} . ")";
  299. }
  300. $status_field .= "] ";
  301. }
  302. return $status_field;
  303. }