Пример #1
0
using PE.Plugins.PubnubChat.Models;

using PubnubApi;

using System;

namespace PE.Plugins.PubnubChat
{
    public enum ChatState
    {
        None,
        Waiting,
        Typing,
        InCall
    }

    public class ChatService : IChatService, IDisposable
    {
        #region Events

        public event InitializationChangedEventHandler InitializedChanged;
        public event EventHandler ConnectedChanged;
        public event EventHandler<MessageEventArgs> MessageReceived;
        public event EventHandler<PresenceEventArgs> ChannelJoined;
        public event EventHandler<PresenceEventArgs> ChannelLeft;
        public event EventHandler<PresenceEventArgs> ChannelTimeout;
        public event EventHandler<PresenceEventArgs> ChannelState;
        public event EventHandler<MessageEventArgs> PublishComplete;

        #endregion Events

        #region Fields

        private Pubnub _Pubnub;

        private readonly string _PublishKey = string.Empty;
        private readonly string _SubscribeKey = string.Empty;

        private SubscribeCallbackExt _ListenerSubscribeCallack;

        private string _UserId = string.Empty;
        private string _AuthKey = string.Empty;
        private bool _Disposed = false;

        private int _FailCount = 0;

        private string _PushData = string.Empty;

        #endregion Fields

        #region Constructors

        public ChatService(ChatConfiguration configuration)
        {
            _PublishKey = configuration.PublishKey;
            _SubscribeKey = configuration.SubscribeKey;
        }

        ~ChatService()
        {
            Dispose();
        }

        #endregion Constructors

        #region Properties

        public bool Connected { get; private set; }

        public bool Initialized { get; private set; } = false;

        #endregion Properties

        #region Init

        public void Initialize(string userId, string authKey = null, bool reset = true, string push = "")
        {
            System.Diagnostics.Debug.WriteLine($"*** {GetType().Name}.Uninitialize - Initializing Chat for user {_UserId}, Reset: {reset}");
            if (reset) _FailCount = 0;
            //  we can only initialize if the user is registered
            if (string.IsNullOrEmpty(userId)) return;
            _UserId = userId;
            _AuthKey = authKey;
            _PushData = push;

            PNConfiguration config = new PNConfiguration();
            config.PublishKey = _PublishKey;
            config.SubscribeKey = _SubscribeKey;
            config.Uuid = _UserId;
            config.Secure = true;
            config.HeartbeatNotificationOption = PNHeartbeatNotificationOption.All;
            if (!string.IsNullOrEmpty(authKey)) config.AuthKey = authKey;

            _Pubnub = new Pubnub(config);

            _ListenerSubscribeCallack = new SubscribeCallbackExt(
                (pubnub, message) => OnMessageReceived(pubnub, message),
                (pubnub, presence) => OnPresenceReceived(pubnub, presence),
                (pubnub, status) => OnStatusReceived(pubnub, status));

            _Pubnub.AddListener(_ListenerSubscribeCallack);

            //  create and subscribe to the lobby channel
            _Pubnub
                .Subscribe<string>()
                .Channels(new string[] { _UserId })
                .WithPresence()
                .Execute();

            //  add push if necessary
            if (!string.IsNullOrEmpty(_PushData))
            {
                var data = _PushData.Split(new char[] { '|' });
                if (data.Length != 2) throw new ArgumentException("Push initialization data.");

                var type = (PNPushType)Enum.Parse(typeof(PNPushType), data[0]);

                _Pubnub.AddPushNotificationsOnChannels()
                    .PushType(type)
                    .Channels(new string[] { _UserId })
                    //.DeviceId(data[1])
                    .DeviceId(data[1])
                    .Async(new AddPushChannelCallback());
                }
            Initialized = true;
            InitializedChanged?.Invoke(Initialized);
        }

        public void Uninitialize()
        {
            //  this can be called before Initialize
            if (_Pubnub == null) return;
            try
            {
                System.Diagnostics.Debug.WriteLine($"*** {GetType().Name}.Uninitialize - Uninitializing Chat for user {_UserId}");
                _Pubnub.RemoveListener(_ListenerSubscribeCallack);
                _Pubnub.Unsubscribe<string>().Channels(new string[] { _UserId }).Execute();
                //  TODO: consider removing push channel association here
                Initialized = false;
                InitializedChanged?.Invoke(Initialized);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"{ GetType().Name}.Uninitialize - Exception: { ex}");
            }
        }

        #endregion Init

        #region Callbacks

        private void OnMessageReceived(Pubnub pubnub, PNMessageResult<object> result)
        {
            try
            {
                MessageReceived?.Invoke(this, new MessageEventArgs(result.Message.ToString()));
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"*** {GetType().Name}.MessageReceived - Unable to deserialize message: { result.Message}\r\n\tException: { ex}");
            }
        }

        private void OnPresenceReceived(Pubnub pubnub, PNPresenceEventResult result)
        {
            System.Diagnostics.Debug.WriteLine($"*** { GetType().Name}.OnPresenceReceived\r\n -> Event: { result.Event}");
            {
                // handle incoming presence data 
                if (result.Event.Equals("join"))
                {
                    RaiseChannelJoined(result.Channel, result.Uuid);
                }
                else if (result.Event.Equals("leave"))
                {
                    RaiseChannelLeft(result.Channel, result.Uuid);
                }
                else if (result.Event.Equals("state-change"))
                {
                    //  listen for status events - eg: typing, etc
                    if ((result.State == null) || (result.State.Count == 0)) return;
                    foreach (var key in result.State.Keys)
                    {
                        var state = (ChatState)Enum.Parse(typeof(ChatState), result.State[key].ToString());
                        RaiseChannelState(result.Channel, result.Uuid, state);
                    }
                }
                else if (result.Event.Equals("timeout"))
                {
                }
                else if (result.Event.Equals("interval"))
                {
                    //  find the ids that have joined
                    if ((result.Join != null) && (result.Join.Length > 0))
                    {
                        foreach (var uuid in result.Join) RaiseChannelJoined(result.Channel, uuid);
                    }
                    if ((result.Leave != null) && (result.Leave.Length > 0))
                    {
                        foreach (var uuid in result.Leave) RaiseChannelJoined(result.Channel, uuid);
                    }
                }
                else if (result.HereNowRefresh)
                {
                    //  TODO: request state for channels
                    //GetState();
                }
            }
        }

        private void OnStatusReceived(Pubnub pubnub, PNStatus status)
        {
            {
                System.Diagnostics.Debug.WriteLine($"*** {GetType().Name}.OnStatusReceived\r\n -> Operation: { status.Operation}\r\n -> Category: { status.Category}\r\n -> Error: { status.Error}\r\n -> Data: { status.ErrorData}\r\n -> Status Code: { status.StatusCode}");

                if (status.Operation == PNOperationType.PNHeartbeatOperation)
                {
                    Connected = !status.Error;
                    ConnectedChanged?.Invoke(this, new EventArgs());
                }

                if ((status.Operation == PNOperationType.PNSubscribeOperation) && (status.Category == PNStatusCategory.PNUnknownCategory) && (status.StatusCode == 404))
                {
                    try
                    {
                        _FailCount++;
                        if (_FailCount > 3)
Пример #2
0
 }
            Initialized = true;
            InitializedChanged?.Invoke(Initialized);
        }

        public void Uninitialize()
        {
            //  this can be called before Initialize
            if (_Pubnub == null) return;
            try
            {
                System.Diagnostics.Debug.WriteLine($"*** {GetType().Name}.Uninitialize - Uninitializing Chat for user {_UserId}");
                _Pubnub.RemoveListener(_ListenerSubscribeCallack);
                _Pubnub.Unsubscribe<string>().Channels(new string[] { _UserId }).Execute();
                //  TODO: consider removing push channel association here
                Initialized = false;
                InitializedChanged?.Invoke(Initialized);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"{ GetType().Name}.Uninitialize - Exception: { ex}");
            }
        }

        #endregion Init

        #region Callbacks

        private void OnMessageReceived(Pubnub pubnub, PNMessageResult<object> result)
        {
            try
            {
                MessageReceived?.Invoke(this, new MessageEventArgs(result.Message.ToString()));
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"*** {GetType().Name}.MessageReceived - Unable to deserialize message: { result.Message}\r\n\tException: { ex}");
            }
        }

        private void OnPresenceReceived(Pubnub pubnub, PNPresenceEventResult result)
        {
            System.Diagnostics.Debug.WriteLine($"*** { GetType().Name}.OnPresenceReceived\r\n -> Event: { result.Event}");
            {
                // handle incoming presence data 
                if (result.Event.Equals("join"))
                {
                    RaiseChannelJoined(result.Channel, result.Uuid);
                }
                else if (result.Event.Equals("leave"))
                {
                    RaiseChannelLeft(result.Channel, result.Uuid);
                }
                else if (result.Event.Equals("state-change"))
                {
                    //  listen for status events - eg: typing, etc
                    if ((result.State == null) || (result.State.Count == 0)) return;
                    foreach (var key in result.State.Keys)
                    {
                        var state = (ChatState)Enum.Parse(typeof(ChatState), result.State[key].ToString());
                        RaiseChannelState(result.Channel, result.Uuid, state);
                    }
                }
                else if (result.Event.Equals("timeout"))
                {
                }
                else if (result.Event.Equals("interval"))
                {
                    //  find the ids that have joined
                    if ((result.Join != null) && (result.Join.Length > 0))
                    {
                        foreach (var uuid in result.Join) RaiseChannelJoined(result.Channel, uuid);
                    }
                    if ((result.Leave != null) && (result.Leave.Length > 0))
                    {
                        foreach (var uuid in result.Leave) RaiseChannelJoined(result.Channel, uuid);
                    }
                }
                else if (result.HereNowRefresh)
                {
                    //  TODO: request state for channels
                    //GetState();
                }
            }
        }

        private void OnStatusReceived(Pubnub pubnub, PNStatus status)
        {
            {
                System.Diagnostics.Debug.WriteLine($"*** {GetType().Name}.OnStatusReceived\r\n -> Operation: { status.Operation}\r\n -> Category: { status.Category}\r\n -> Error: { status.Error}\r\n -> Data: { status.ErrorData}\r\n -> Status Code: { status.StatusCode}");

                if (status.Operation == PNOperationType.PNHeartbeatOperation)
                {
                    Connected = !status.Error;
                    ConnectedChanged?.Invoke(this, new EventArgs());
                }

                if ((status.Operation == PNOperationType.PNSubscribeOperation) && (status.Category == PNStatusCategory.PNUnknownCategory) && (status.StatusCode == 404))
                {
                    try
                    {
                        _FailCount++;
                        if (_FailCount > 3)
Пример #3
0
        public void Initialize(string userId, long lastActivity = 0)
        {
            try
            {
                //  we can only initialize if the user is registered
                if (string.IsNullOrEmpty(userId))
                {
                    return;
                }
                _UserId = userId;

                PNConfiguration config = new PNConfiguration();
                config.PublishKey   = _PublishKey;
                config.SubscribeKey = _SubscribeKey;
                config.Uuid         = _UserId;
                config.Secure       = true;

                _Pubnub = new Pubnub(config);

                SubscribeCallbackExt listenerSubscribeCallack = new SubscribeCallbackExt((pubnubObj, message) =>
                {
                    try
                    {
                        //  get the message base to determine type
                        BaseMessage m = Serializer.Deserialize <BaseMessage>(message.Message.ToString());
                        //  deserialize to actual type
                        m = (BaseMessage)Serializer.Deserialize(GetType().Assembly.GetType(m.Type), message.Message.ToString());
                        //  let listeners know
                        MessageReceived?.Invoke(this, new MessageEventArgs <BaseMessage>(m));
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine(string.Format("*** ChatService.MessageReceived - Unable to deserialize message: {0}", message.Message.ToString()));
                    }
                }, (pubnubObj, presence) =>
                {
                    // handle incoming presence data
                    if (presence.Event.Equals("join"))
                    {
                        RaiseChannelJoined(presence.Channel, presence.Uuid);
                    }
                    else if (presence.Event.Equals("leave"))
                    {
                        RaiseChannelLeft(presence.Channel, presence.Uuid);
                    }
                    else if (presence.Event.Equals("state-change"))
                    {
                        //  listen for status events - eg: typing, etc
                        if ((presence.State == null) || (presence.State.Count == 0))
                        {
                            return;
                        }
                        foreach (var key in presence.State.Keys)
                        {
                            var state = (ChatState)Enum.Parse(typeof(ChatState), presence.State[key].ToString());
                            RaiseChannelState(presence.Channel, presence.Uuid, state);
                        }
                    }
                    else if (presence.Event.Equals("timeout"))
                    {
                    }
                    else if (presence.Event.Equals("interval"))
                    {
                        //  find the ids that have joined
                        if ((presence.Join != null) && (presence.Join.Length > 0))
                        {
                            foreach (var uuid in presence.Join)
                            {
                                RaiseChannelJoined(presence.Channel, uuid);
                            }
                        }
                        if ((presence.Leave != null) && (presence.Leave.Length > 0))
                        {
                            foreach (var uuid in presence.Leave)
                            {
                                RaiseChannelJoined(presence.Channel, uuid);
                            }
                        }
                    }
                    else if (presence.HereNowRefresh)
                    {
                        //  TODO: request state for channels
                        //GetState();
                    }
                }, (pubnubObj, status) =>
                {
                    if (status.Operation == PNOperationType.PNHeartbeatOperation)
                    {
                        Connected = !status.Error;
                        ConnectedChanged?.Invoke(this, new EventArgs());
                    }
                    else if ((status.Operation != PNOperationType.PNSubscribeOperation) && (status.Operation != PNOperationType.PNUnsubscribeOperation))
                    {
                        return;
                    }

                    if (status.Category == PNStatusCategory.PNConnectedCategory)
                    {
                        // this is expected for a subscribe, this means there is no error or issue whatsoever
                    }
                    else if (status.Category == PNStatusCategory.PNReconnectedCategory)
                    {
                        // this usually occurs if subscribe temporarily fails but reconnects. This means
                        // there was an error but there is no longer any issue
                    }
                    else if (status.Category == PNStatusCategory.PNDisconnectedCategory)
                    {
                        // this is the expected category for an unsubscribe. This means there
                        // was no error in unsubscribing from everything
                    }
                    else if (status.Category == PNStatusCategory.PNUnexpectedDisconnectCategory)
                    {
                        // this is usually an issue with the internet connection, this is an error, handle appropriately
                    }
                    else if (status.Category == PNStatusCategory.PNAccessDeniedCategory)
                    {
                        // this means that PAM does allow this client to subscribe to this
                        // channel and channel group configuration. This is another explicit error
                    }
                });

                _Pubnub.AddListener(listenerSubscribeCallack);

                //  create and subscribe to the lobby channel
                _Pubnub
                .Subscribe <string>()
                .Channels(new string[] { _UserId })
                .WithPresence()
                .Execute();

                Initialized = true;
                InitializedChanged?.Invoke(this, new EventArgs());
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(string.Format("*** ChatService.Initialize - Exception: {0}", ex));
            }
        }
Пример #4
0
 protected virtual void OnInitializedChanged(bool state)
 {
     InitializedChanged?.Invoke(this, state);
 }