A well fed wolf
There are quite many wolves in the Ljubljana Zoo and they seem to be fed well.

XIFF chat - Part 3: Chat room

The third step in the process of building a chat with the XIFF library in Actionscript 3, is to initiate the room activity. It is also know as the Multi User Chat.

The server which offer the ability to use XMPP, usually identifies the conference service as “Public Chatrooms” located in a subdomain, such as “chatrooms.example.com” or “conference.example.com”.

The XMPP specification defines JID in the following way:

jid  = [ node "@" ] domain [ "/" resource ]

In the case of a chat room and the user involved in it, the JID can be like “room@chatrooms.example.com/user”, where as for the user JID it is build as “user@example.com”.

The example of getting all possible service items and a list of chat rooms, as well as automatically join to a invited room, is shown below. It is not meant to be used as such, it is only to show simple usage example of the classes.

package
{
    import flash.display.*;
    import flash.events.*;
    import flash.text.*;
    import flash.utils.*;
    import flash.system.Security;
    import flash.ui.Keyboard;

    import org.igniterealtime.xiff.conference.*;
    import org.igniterealtime.xiff.core.*;
    import org.igniterealtime.xiff.data.*;
    import org.igniterealtime.xiff.data.disco.*;
    import org.igniterealtime.xiff.events.*;
    import org.igniterealtime.xiff.exception.*;
    import org.igniterealtime.xiff.data.muc.*;
    import org.igniterealtime.xiff.util.*;

    [SWF(backgroundColor = '0x5B5B5B', frameRate = '33', width = '600', height = '400')]

    /**
     * @see http://paazmaya.fi/xiff-chat-part-3-chat-room
     */
    public class XIFFstep3 extends Sprite
    {
        private const SERVER:String = "192.168.1.37";
        private const PORT:Number = 5222;
        private const USERNAME:String = "flasher";
        private const PASSWORD:String = "flasher";
        private const RESOURCE_NAME:String = "flashPlayer";
        private const CHECK_POLICY:Boolean = true;

        private var _connection:XMPPConnection;
        private var _browser:Browser;
        private var _room:Room;
        private var _keepAlive:Timer;

        private var _outputField:TextField;
        private var _inputField:TextField;
        private var _buttonContainer:Sprite;

        private var _conferenceServer:String = "";

        public function XIFFstep3()
        {
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;

            loaderInfo.addEventListener(Event.INIT, onInit);
        }

        private function onInit(event:Event):void
        {
            if (CHECK_POLICY)
            {
                Security.loadPolicyFile("xmlsocket://" + SERVER + ":5229");
            }
            createElements();

            initChat();
        }

        private function createElements():void
        {
            _outputField = new TextField();
            _outputField.background = true;
            _outputField.backgroundColor = 0xF4F4F4;
            _outputField.textColor = 0x042444;
            _outputField.mouseWheelEnabled = true;
            _outputField.multiline = true;
            _outputField.width = (stage.stageWidth - 4) / 2;
            _outputField.height = stage.stageHeight - 34;
            _outputField.x = 2 + _outputField.width;
            _outputField.y = 2;
            addChild(_outputField);

            _inputField = new TextField();
            _inputField.background = true;
            _inputField.backgroundColor = 0xF4F4F4;
            _inputField.textColor = 0x042444;
            _inputField.type = "input";
            _inputField.width = stage.stageWidth - 4;
            _inputField.height = 28;
            _inputField.x = 2;
            _inputField.y = stage.stageHeight - 30;
            _inputField.maxChars = 40;
            _inputField.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
            addChild(_inputField);

            _buttonContainer = new Sprite();
            addChild(_buttonContainer);
        }

        private function addGroupMessage(message:Message):void
        {
            var date:Date = new Date();
            if (message.time != null)
            {
                date = message.time;
            }
            _outputField.appendText("[" + date.getHours() + ":" + date.getMinutes() + " | " + message.from.resource + "] " + message.body + "\n");
        }

        private function onKeyUp(event:KeyboardEvent):void
        {
            switch(event.keyCode)
            {
                case Keyboard.ENTER :
                    messageSend();
                    break;
            }
        }

        private function messageSend():void
        {
            var txt:String = _inputField.text;
            if (_connection.isLoggedIn() && _room.isActive)
            {
                _room.sendMessage(txt);
                _inputField.text = "";
            }
        }

        private function initChat():void
        {
            _connection = new XMPPConnection();
            _connection.addEventListener(XIFFErrorEvent.XIFF_ERROR, onXiffError);
            _connection.addEventListener(LoginEvent.LOGIN, onLogin);

            _connection.useAnonymousLogin = false;
            _connection.username = USERNAME;
            _connection.password = PASSWORD;
            _connection.server = SERVER;
            _connection.port = PORT;
            _connection.resource = RESOURCE_NAME;
            _connection.connect("flash");

            MUC.enable();

            _keepAlive = new Timer(2 * 60 * 1000);
            _keepAlive.addEventListener(TimerEvent.TIMER, onKeepAliveLoop);

            _room = new Room(_connection);
            _room.addEventListener(RoomEvent.ADMIN_ERROR, onRoom);
            _room.addEventListener(RoomEvent.AFFILIATIONS, onRoom);
            _room.addEventListener(RoomEvent.BANNED_ERROR, onRoom);
            _room.addEventListener(RoomEvent.CONFIGURE_ROOM, onRoom);
            _room.addEventListener(RoomEvent.DECLINED, onRoom);
            _room.addEventListener(RoomEvent.GROUP_MESSAGE, onRoom);
            _room.addEventListener(RoomEvent.LOCKED_ERROR, onRoom);
            _room.addEventListener(RoomEvent.MAX_USERS_ERROR, onRoom);
            _room.addEventListener(RoomEvent.NICK_CONFLICT, onRoom);
            _room.addEventListener(RoomEvent.PASSWORD_ERROR, onRoom);
            _room.addEventListener(RoomEvent.PRIVATE_MESSAGE, onRoom);
            _room.addEventListener(RoomEvent.REGISTRATION_REQ_ERROR, onRoom);
            _room.addEventListener(RoomEvent.ROOM_JOIN, onRoom);
            _room.addEventListener(RoomEvent.ROOM_LEAVE, onRoom);
            _room.addEventListener(RoomEvent.SUBJECT_CHANGE, onRoom);
            _room.addEventListener(RoomEvent.USER_BANNED, onRoom);
            _room.addEventListener(RoomEvent.USER_DEPARTURE, onRoom);
            _room.addEventListener(RoomEvent.USER_JOIN, onRoom);
            _room.addEventListener(RoomEvent.USER_KICKED, onRoom);
        }

        private function onXiffError(event:XIFFErrorEvent):void
        {
            trace("onXiffError. " + event.toString());
            trace("onXiffError.errorMessage: " + event.errorMessage);
        }

        private function onLogin(event:LoginEvent):void
        {
            trace("onLogin. " + event.toString());

            _keepAlive.start();

            var presence:Presence = new Presence(null, _connection.jid.escaped, null, Presence.SHOW_CHAT, "Gimme room", 2);
            _connection.send(presence);

            var serverJID:EscapedJID = new EscapedJID(SERVER);
            _browser = new Browser(_connection);
            _browser.getServiceItems(serverJID, "serviceItemsCall", this);
            _browser.getServiceInfo(serverJID, "serviceInfoCall", this);
        }

        private function onRoom(event:RoomEvent):void
        {
            trace("onRoom. " + event.toString());
            switch (event.type)
            {
                case RoomEvent.ADMIN_ERROR :
                    break;
                case RoomEvent.AFFILIATIONS :
                    break;
                case RoomEvent.BANNED_ERROR :
                    break;
                case RoomEvent.CONFIGURE_ROOM :
                    break;
                case RoomEvent.DECLINED :
                    break;
                case RoomEvent.GROUP_MESSAGE :
                    // Could also use MessageEvent event of the XMPPConnection.
                    trace("event.data is Message: " + (event.data is Message) + ", data: " + event.data);
                    addGroupMessage(event.data as Message);
                    break;
                case RoomEvent.LOCKED_ERROR :
                    break;
                case RoomEvent.MAX_USERS_ERROR :
                    break;
                case RoomEvent.NICK_CONFLICT :
                    break;
                case RoomEvent.PASSWORD_ERROR :
                    break;
                case RoomEvent.PRIVATE_MESSAGE :
                    break;
                case RoomEvent.REGISTRATION_REQ_ERROR :
                    break;
                case RoomEvent.ROOM_JOIN :
                    break;
                case RoomEvent.ROOM_LEAVE :
                    break;
                case RoomEvent.SUBJECT_CHANGE :
                    break;
                case RoomEvent.USER_BANNED :
                    break;
                case RoomEvent.USER_DEPARTURE :
                    break;
                case RoomEvent.USER_JOIN :
                    break;
                case RoomEvent.USER_KICKED :
                    break;
            }
        }

        private function createButton(txt:String, jid:String, counter:uint, xPos:Number = 0):void
        {
            var sp:Sprite = new Sprite();
            sp.name = jid;
            sp.mouseChildren = false;
            sp.buttonMode = true;
            sp.alpha = 0.6;

            var tx:TextField = new TextField();
            tx.text = txt;
            tx.textColor = 0xF2F2F2;
            tx.autoSize = TextFieldAutoSize.LEFT;
            tx.y = 2;
            tx.x = 2;
            sp.addChild(tx);

            var gr:Graphics = sp.graphics;
            gr.beginFill(0x3B3B3B);
            gr.lineStyle(1, 0x1A1A1A);
            gr.drawRoundRect(0, 0, sp.width + 4, 22, 8, 8);
            gr.endFill();

            if (txt == "Public Chatrooms")
            {
                _conferenceServer = jid;
                sp.addEventListener(MouseEvent.CLICK, onConferenceMouse);
                sp.alpha = 1.0;
            }
            else if (_conferenceServer != "" && jid.split("@").pop() == _conferenceServer)
            {
                sp.addEventListener(MouseEvent.CLICK, onRoomMouse);
                sp.alpha = 1.0;
            }
            sp.x = xPos;
            sp.y = sp.height * counter;
            _buttonContainer.addChild(sp);
        }

        private function onConferenceMouse(event:MouseEvent):void
        {
            var jid:EscapedJID = new EscapedJID(_conferenceServer);
            _browser.getServiceItems(jid, "serviceItemsCall", this);
        }

        private function onRoomMouse(event:MouseEvent):void
        {
            var jid:EscapedJID = new EscapedJID(event.target.name);
            if (_room.isActive)
            {
                _room.leave();
            }
            _room.roomJID = jid.unescaped;
            _room.join();
        }

        public function browseItemCall(iq:IQ):void
        {
            var extensions:Array = iq.getAllExtensions();
            for (var s:int = 0; s < extensions.length; ++s)
            {
                var disco:ItemDiscoExtension = extensions[s];
                var items:Array = disco.items;
                trace("-- browseItemCall. round " + s + ". items.length: " + items.length);
                for (var i:uint = 0; i < items.length; ++i)
                {
                    var obj:Object = items[i] as Object;
                    trace("-- name: " + obj.name + ", jid: " + obj.jid + ", node: " + obj.node + ", action: " + obj.action);
                }
            }
        }

        public function serviceItemsCall(iq:IQ):void
        {
            var xPos:Number = _buttonContainer.width;
            var extensions:Array = iq.getAllExtensions();
            for (var s:int = 0; s < extensions.length; ++s)
            {
                var disco:ItemDiscoExtension = extensions[s];
                var items:Array = disco.items;
                trace(">> serviceItemsCall. round " + s + ". items.length: " + items.length);
                for (var i:uint = 0; i < items.length; ++i)
                {
                    var obj:Object = items[i] as Object;
                    createButton(String(obj.name), String(obj.jid), i, xPos);
                    trace(">> name: " + obj.name + ", jid: " + obj.jid + ", node: " + obj.node + ", action: " + obj.action);
                }
            }
        }

        public function serviceInfoCall(iq:IQ):void
        {
            var extensions:Array = iq.getAllExtensions();
            for (var s:int = 0; s < extensions.length; ++s)
            {
                var disco:InfoDiscoExtension = extensions[s];
                var features:Array = disco.features;
                for (var r:int = 0; r < features.length; ++r)
                {
                    trace("** serviceInfoCall. " + r + " [" + features[r] + "]");
                }
                var identities:Array = disco.identities;
                for (var i:int = 0; i < identities.length; ++i)
                {
                    var obj:Object = identities[i] as Object;
                    trace("** serviceInfoCall. name: " + obj.name + ", type: " + obj.type + ", category: " + obj.category);
                }
            }
        }

        private function onKeepAliveLoop(event:TimerEvent):void
        {
            if (_connection.isLoggedIn())
            {
                _connection.sendKeepAlive();
            }
        }
    }
}

Pay special attention to the from JID of the message which comes from the room