Playing around with socket server in Air

Ok, socket servers are neat, everyone knows this, right?

I’m going to show you how you can put it to use by building an Air app that can controll your spotify app, or any other app really, by waiting for telnet connections and then run applescript calls based on commandos sent by the telnet client. Flexibel approach to controlling a huge set of applications on your mac. This could be achieved on a Windows or nix machine aswell, asumed that you can control whaterver you need to control by commando line.

Lets start with the simple as3 code to create the Socket Server to handle incoming commandos:

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.ProgressEvent;
	import flash.events.ServerSocketConnectEvent;
	import flash.net.ServerSocket;
	import flash.net.Socket;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	import flash.utils.ByteArray;

	import flash.desktop.NativeProcess;
	import flash.desktop.NativeProcessStartupInfo;
	import flash.filesystem.File;
	import UserIp;
	public class SpotifyRemote extends Sprite
	{
		private var serverSocket:ServerSocket = new ServerSocket();
		private var clientSocket:Socket;
		private var process:NativeProcess;
		private var localIP:TextField;
		private var localPort:TextField;
		private var logField:TextField;
		private var message:TextField;
		private var ipInfo:UserIp;

		public function SpotifyRemote()
		{
			setupUI();
			if( NativeProcess.isSupported ){
				are_we_native.text = "We dare to shell"
			}else{
				are_we_native.text = "no shell =("
			}
		}

		private function onConnect( event:ServerSocketConnectEvent ):void
		{
			clientSocket = event.socket;
			clientSocket.addEventListener( ProgressEvent.SOCKET_DATA, onClientSocketData );
			log( "Connection from " + clientSocket.remoteAddress + ":" + clientSocket.remotePort );

			if (clientSocket != null && clientSocket.connected)
				{
					clientSocket.writeUTFBytes( "+OK hello old friend" );
					clientSocket.flush();

				}
		}

		private function onClientSocketData( event:ProgressEvent ):void
		{
			var buffer:ByteArray = new ByteArray();
			clientSocket.readBytes( buffer, 0, clientSocket.bytesAvailable );
			log( "Received: " + buffer.toString() );

			do_shell( buffer.toString() )
		}

		private function bind( event:Event ):void
		{
			if (serverSocket.bound)
			{
				serverSocket.close();
				serverSocket = new ServerSocket();

			}
			serverSocket.bind( parseInt( localPort.text ), localIP.text );
			serverSocket.addEventListener( ServerSocketConnectEvent.CONNECT, onConnect );
			serverSocket.listen();
			log( "Bound to: " + serverSocket.localAddress + ":" + serverSocket.localPort );
		}

		private function send( event:Event ):void
		{
			try
			{
				if (clientSocket != null && clientSocket.connected)
				{
					clientSocket.writeUTFBytes( message.text );
					clientSocket.flush();
					log( "Sent message to " + clientSocket.remoteAddress + ":" + clientSocket.remotePort );
				}
				else
				{
					log("No socket connection.");
				}
			}
			catch (error:Error)
			{
				log( error.message );
			}
		}

		private function log( text:String ):void
		{
			logField.appendText( text + "\n" );
			logField.scrollV = logField.maxScrollV;
			trace( text );
		}

		private function setupUI():void
		{
			localIP = createTextField(10,10,"Local IP","192.168.0.15");
			localPort = createTextField(10,35,"Local port","8888");
			createTextButton( 170, 60, "Bind", bind );
			message = createTextField(10,85,"Message","Lucy can't drink milk.");
			createTextButton( 170, 110, "Send", send );
			logField = createTextField( 10, 135, "Log", "", false, 200 )
			            ;
			this.stage.nativeWindow.activate();
		}

		private function createTextField( x:int, y:int, label:String, defaultValue:String = '', editable:Boolean = true, height:int = 20 ):TextField
		{
			var labelField:TextField = new TextField();
			labelField.text = label;
			labelField.type = TextFieldType.DYNAMIC;
			labelField.width = 100;
			labelField.x = x;
			labelField.y = y;

			var input:TextField = new TextField();
			input.text = defaultValue;
			input.type = TextFieldType.INPUT;
			input.border = editable;
			input.selectable = editable;
			input.width = 280;
			input.height = height;
			input.x = x + labelField.width;
			input.y = y;

			this.addChild( labelField );
			this.addChild( input );

			return input;
		}

		private function createTextButton( x:int, y:int, label:String, clickHandler:Function ):TextField
		{
			var button:TextField = new TextField();
			button.htmlText = "" + label + "";
			button.type = TextFieldType.DYNAMIC;
			button.selectable = false;
			button.width = 180;
			button.x = x;
			button.y = y;
			button.addEventListener( MouseEvent.CLICK, clickHandler );

			this.addChild( button );
			return button;
		}

		private function trim( s:String ):String{
			return s.replace( /^([\s|\t|\n|\r]+)?(.*)([\s|\t|\n|\r]+)?$/gm, "$2" );
		}

		private function do_shell(args:String){

			args = trim(args);

			args = args.substr(0,4);

			var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
			var file:File = File.applicationDirectory.resolvePath("remote_calls.sh");
			nativeProcessStartupInfo.executable = file;
			var processArgs:Vector. = new Vector.();
			processArgs[0] = args;
			nativeProcessStartupInfo.arguments = processArgs;
			nativeProcessStartupInfo.workingDirectory = File.applicationDirectory;
			process = new NativeProcess();
			process.start(nativeProcessStartupInfo);
			are_we_native.text = '"'+args+'"';
		}
	}
}

You can go ahead and build this as an air app right away, on line 106 you might want to add your ip address, allthough you can change this once the app is running aswell.

The function we want to take a closer look to is the one called

do_shell()

, this is where the actual calling of functions is being handled. You might see that I’m calling on a shell script called

remote_calls.sh

, it’s just a simple shell script that executes some applescript files. Let’s take a look at it:

#!/bin/bash

#
#	Script to logg time spent of files
#	Author: Daniel @ Nute Digital Agency
#

echo $1

if [ $1 = "play" ]
	then
	arch -i386 osascript "play.scpt"
fi

if [ $1 = "stop" ]
	then
	arch -i386 osascript "play.scpt"
fi

if [ $1 = "next" ]
	then
	arch -i386 osascript "next.scpt"
fi

if [ $1 = "back" ]
	then
	arch -i386 osascript "back.scpt"
fi

if [ $1 = "upvo" ]
	then
	arch -i386 osascript "volume_up.scpt"
fi

if [ $1 = "down" ]
	then
	arch -i386 osascript "volume_down.scpt"
fi

And finally, the applescripts:

--play.scpt
tell application "Spotify"
	activate
end tell

tell application "System Events"
	keystroke space
end tell

--next.scpt
tell application "Spotify"
	activate
end tell

tell application "System Events"
	keystroke (ASCII character 29) using {command down} --next
end tell

--back.scpt
tell application "Spotify"
	activate
end tell

tell application "System Events"
	keystroke (ASCII character 28) using {command down}
end tell

--volume_up.scpt
tell application "Spotify"
	activate
end tell

tell application "System Events"
	keystroke (ASCII character 30) using {command down} --volumeup
end tell

--volume_down.scpt
tell application "Spotify"
	activate
end tell

tell application "System Events"
	keystroke (ASCII character 31) using {command down}
end tell

That it folks!

Categories