Exemple #1
0
        public PubSub(EndPoint proxy = null) : base(null, proxy)
        {
            ThreadDispatcher.EnsureCreated();

            base.OnPubSubServiceConnected += ((object sender, EventArgs e) => { ThreadDispatcher.Enqueue(() => OnPubSubServiceConnected?.Invoke(sender, e)); });
            base.OnPubSubServiceError     += ((object sender, OnPubSubServiceErrorArgs e) => { ThreadDispatcher.Enqueue(() => OnPubSubServiceError?.Invoke(sender, e)); });
            base.OnPubSubServiceClosed    += ((object sender, EventArgs e) => { ThreadDispatcher.Enqueue(() => OnPubSubServiceClosed?.Invoke(sender, e)); });
            base.OnListenResponse         += ((object sender, OnListenResponseArgs e) => { ThreadDispatcher.Enqueue(() => OnListenResponse?.Invoke(sender, e)); });
            base.OnTimeout             += ((object sender, OnTimeoutArgs e) => { ThreadDispatcher.Enqueue(() => OnTimeout?.Invoke(sender, e)); });
            base.OnBan                 += ((object sender, OnBanArgs e) => { ThreadDispatcher.Enqueue(() => OnBan?.Invoke(sender, e)); });
            base.OnUnban               += ((object sender, OnUnbanArgs e) => { ThreadDispatcher.Enqueue(() => OnUnban?.Invoke(sender, e)); });
            base.OnUntimeout           += ((object sender, OnUntimeoutArgs e) => { ThreadDispatcher.Enqueue(() => OnUntimeout?.Invoke(sender, e)); });
            base.OnHost                += ((object sender, OnHostArgs e) => { ThreadDispatcher.Enqueue(() => OnHost?.Invoke(sender, e)); });
            base.OnSubscribersOnly     += ((object sender, OnSubscribersOnlyArgs e) => { ThreadDispatcher.Enqueue(() => OnSubscribersOnly?.Invoke(sender, e)); });
            base.OnSubscribersOnlyOff  += ((object sender, OnSubscribersOnlyOffArgs e) => { ThreadDispatcher.Enqueue(() => OnSubscribersOnlyOff?.Invoke(sender, e)); });
            base.OnClear               += ((object sender, OnClearArgs e) => { ThreadDispatcher.Enqueue(() => OnClear?.Invoke(sender, e)); });
            base.OnEmoteOnly           += ((object sender, OnEmoteOnlyArgs e) => { ThreadDispatcher.Enqueue(() => OnEmoteOnly?.Invoke(sender, e)); });
            base.OnEmoteOnlyOff        += ((object sender, OnEmoteOnlyOffArgs e) => { ThreadDispatcher.Enqueue(() => OnEmoteOnlyOff?.Invoke(sender, e)); });
            base.OnR9kBeta             += ((object sender, OnR9kBetaArgs e) => { ThreadDispatcher.Enqueue(() => OnR9kBeta?.Invoke(sender, e)); });
            base.OnR9kBetaOff          += ((object sender, OnR9kBetaOffArgs e) => { ThreadDispatcher.Enqueue(() => OnR9kBetaOff?.Invoke(sender, e)); });
            base.OnBitsReceived        += ((object sender, OnBitsReceivedArgs e) => { ThreadDispatcher.Enqueue(() => OnBitsReceived?.Invoke(sender, e)); });
            base.OnStreamUp            += ((object sender, OnStreamUpArgs arg) => { ThreadDispatcher.Enqueue(() => OnStreamUp(sender, arg)); });
            base.OnStreamDown          += ((object sender, OnStreamDownArgs e) => { ThreadDispatcher.Enqueue(() => OnStreamDown?.Invoke(sender, e)); });
            base.OnViewCount           += ((object sender, OnViewCountArgs e) => { ThreadDispatcher.Enqueue(() => OnViewCount?.Invoke(sender, e)); });
            base.OnWhisper             += ((object sender, OnWhisperArgs e) => { ThreadDispatcher.Enqueue(() => OnWhisper?.Invoke(sender, e)); });
            base.OnChannelSubscription += ((object sender, OnChannelSubscriptionArgs e) => { ThreadDispatcher.Enqueue(() => OnChannelSubscription?.Invoke(sender, e)); });
        }
Exemple #2
0
        private void ParseMessage(string message)
        {
            var type = JObject.Parse(message).SelectToken("type")?.ToString();

            switch (type?.ToLower())
            {
            case "response":
                var resp = new Models.PubSub.Responses.Response(message);
                if (_previousRequests.Count != 0)
                {
                    foreach (var request in _previousRequests)
                    {
                        if (string.Equals(request.Nonce, resp.Nonce, StringComparison.CurrentCultureIgnoreCase))
                        {
                            OnListenResponse?.Invoke(this, new OnListenResponseArgs {
                                Response = resp, Topic = request.Topic, Successful = resp.Successful
                            });
                        }
                    }
                    return;
                }
                break;

            case "message":
                var msg = new Models.PubSub.Responses.Message(message);
                switch (msg.Topic.Split('.')[0])
                {
                case "channel-subscribe-events-v1":
                    var subscription = msg.messageData as ChannelSubscription;
                    OnChannelSubscription?.Invoke(this, new OnChannelSubscriptionArgs {
                        Subscription = subscription
                    });
                    return;

                case "whispers":
                    var whisper = (Whisper)msg.messageData;
                    OnWhisper?.Invoke(this, new OnWhisperArgs {
                        Whisper = whisper
                    });
                    return;

                case "chat_moderator_actions":
                    var cMA    = msg.messageData as ChatModeratorActions;
                    var reason = "";
                    switch (cMA?.ModerationAction.ToLower())
                    {
                    case "timeout":
                        if (cMA.Args.Count > 2)
                        {
                            reason = cMA.Args[2];
                        }
                        OnTimeout?.Invoke(this, new OnTimeoutArgs
                        {
                            TimedoutBy      = cMA.CreatedBy,
                            TimedoutById    = cMA.CreatedByUserId,
                            TimedoutUserId  = cMA.TargetUserId,
                            TimeoutDuration = TimeSpan.FromSeconds(int.Parse(cMA.Args[1])),
                            TimeoutReason   = reason,
                            TimedoutUser    = cMA.Args[0]
                        });
                        return;

                    case "ban":
                        if (cMA.Args.Count > 1)
                        {
                            reason = cMA.Args[1];
                        }
                        OnBan?.Invoke(this, new OnBanArgs {
                            BannedBy = cMA.CreatedBy, BannedByUserId = cMA.CreatedByUserId, BannedUserId = cMA.TargetUserId, BanReason = reason, BannedUser = cMA.Args[0]
                        });
                        return;

                    case "unban":
                        OnUnban?.Invoke(this, new OnUnbanArgs {
                            UnbannedBy = cMA.CreatedBy, UnbannedByUserId = cMA.CreatedByUserId, UnbannedUserId = cMA.TargetUserId
                        });
                        return;

                    case "untimeout":
                        OnUntimeout?.Invoke(this, new OnUntimeoutArgs {
                            UntimeoutedBy = cMA.CreatedBy, UntimeoutedByUserId = cMA.CreatedByUserId, UntimeoutedUserId = cMA.TargetUserId
                        });
                        return;

                    case "host":
                        OnHost?.Invoke(this, new OnHostArgs {
                            HostedChannel = cMA.Args[0], Moderator = cMA.CreatedBy
                        });
                        return;

                    case "subscribers":
                        OnSubscribersOnly?.Invoke(this, new OnSubscribersOnlyArgs {
                            Moderator = cMA.CreatedBy
                        });
                        return;

                    case "subscribersoff":
                        OnSubscribersOnlyOff?.Invoke(this, new OnSubscribersOnlyOffArgs {
                            Moderator = cMA.CreatedBy
                        });
                        return;

                    case "clear":
                        OnClear?.Invoke(this, new OnClearArgs {
                            Moderator = cMA.CreatedBy
                        });
                        return;

                    case "emoteonly":
                        OnEmoteOnly?.Invoke(this, new OnEmoteOnlyArgs {
                            Moderator = cMA.CreatedBy
                        });
                        return;

                    case "emoteonlyoff":
                        OnEmoteOnlyOff?.Invoke(this, new OnEmoteOnlyOffArgs {
                            Moderator = cMA.CreatedBy
                        });
                        return;

                    case "r9kbeta":
                        OnR9kBeta?.Invoke(this, new OnR9kBetaArgs {
                            Moderator = cMA.CreatedBy
                        });
                        return;

                    case "r9kbetaoff":
                        OnR9kBetaOff?.Invoke(this, new OnR9kBetaOffArgs {
                            Moderator = cMA.CreatedBy
                        });
                        return;
                    }
                    break;

                case "channel-bits-events-v1":
                    var cBE = msg.messageData as ChannelBitsEvents;
                    if (cBE != null)
                    {
                        OnBitsReceived?.Invoke(this, new OnBitsReceivedArgs
                        {
                            BitsUsed      = cBE.BitsUsed,
                            ChannelId     = cBE.ChannelId,
                            ChannelName   = cBE.ChannelName,
                            ChatMessage   = cBE.ChatMessage,
                            Context       = cBE.Context,
                            Time          = cBE.Time,
                            TotalBitsUsed = cBE.TotalBitsUsed,
                            UserId        = cBE.UserId,
                            Username      = cBE.Username
                        });
                    }
                    return;

                case "video-playback":
                    var vP = msg.messageData as VideoPlayback;
                    switch (vP?.Type)
                    {
                    case VideoPlaybackType.StreamDown:
                        OnStreamDown?.Invoke(this, new OnStreamDownArgs {
                            ServerTime = vP.ServerTime
                        });
                        return;

                    case VideoPlaybackType.StreamUp:
                        OnStreamUp?.Invoke(this, new OnStreamUpArgs {
                            PlayDelay = vP.PlayDelay, ServerTime = vP.ServerTime
                        });
                        return;

                    case VideoPlaybackType.ViewCount:
                        OnViewCount?.Invoke(this, new OnViewCountArgs {
                            ServerTime = vP.ServerTime, Viewers = vP.Viewers
                        });
                        return;
                    }
                    break;
                }
                break;
            }
            if (_logging)
            {
                UnaccountedFor(message);
            }
        }
Exemple #3
0
        /// <summary>
        /// Parses the message.
        /// </summary>
        /// <param name="message">The message.</param>
        private void ParseMessage(string message)
        {
            var type = JObject.Parse(message).SelectToken("type")?.ToString();

            switch (type?.ToLower())
            {
            case "response":
                var resp = new Models.Responses.Response(message);
                if (_previousRequests.Count != 0)
                {
                    bool handled = false;
                    _previousRequestsSemaphore.WaitOne();
                    try
                    {
                        for (int i = 0; i < _previousRequests.Count;)
                        {
                            var request = _previousRequests[i];
                            if (string.Equals(request.Nonce, resp.Nonce, StringComparison.CurrentCulture))
                            {
                                //Remove the request.
                                _previousRequests.RemoveAt(i);
                                _topicToChannelId.TryGetValue(request.Topic, out var requestChannelId);
                                OnListenResponse?.Invoke(this, new OnListenResponseArgs {
                                    Response = resp, Topic = request.Topic, Successful = resp.Successful, ChannelId = requestChannelId
                                });
                                handled = true;
                            }
                            else
                            {
                                i++;
                            }
                        }
                    }
                    finally
                    {
                        _previousRequestsSemaphore.Release();
                    }
                    if (handled)
                    {
                        return;
                    }
                }
                break;

            case "message":
                var msg = new Models.Responses.Message(message);
                _topicToChannelId.TryGetValue(msg.Topic, out var channelId);
                channelId = channelId ?? "";
                switch (msg.Topic.Split('.')[0])
                {
                case "user-moderation-notifications":
                    var userModerationNotifications = msg.MessageData as UserModerationNotifications;
                    switch (userModerationNotifications.Type)
                    {
                    case UserModerationNotificationsType.AutomodCaughtMessage:
                        var automodCaughtMessage = userModerationNotifications.Data as Models.Responses.Messages.UserModerationNotificationsTypes.AutomodCaughtMessage;
                        OnAutomodCaughtUserMessage?.Invoke(this, new OnAutomodCaughtUserMessage {
                            ChannelId = channelId, UserId = msg.Topic.Split('.')[2], AutomodCaughtMessage = automodCaughtMessage
                        });
                        break;

                    case UserModerationNotificationsType.Unknown:
                        break;
                    }
                    return;

                case "automod-queue":
                    var automodQueue = msg.MessageData as AutomodQueue;
                    switch (automodQueue.Type)
                    {
                    case AutomodQueueType.CaughtMessage:
                        var caughtMessage = automodQueue.Data as AutomodCaughtMessage;
                        OnAutomodCaughtMessage?.Invoke(this, new OnAutomodCaughtMessageArgs {
                            ChannelId = channelId, AutomodCaughtMessage = caughtMessage
                        });
                        break;

                    case AutomodQueueType.Unknown:
                        UnaccountedFor($"Unknown automod queue type. Msg: {automodQueue.RawData}");
                        break;
                    }
                    return;

                case "channel-subscribe-events-v1":
                    var subscription = msg.MessageData as ChannelSubscription;
                    OnChannelSubscription?.Invoke(this, new OnChannelSubscriptionArgs {
                        Subscription = subscription, ChannelId = channelId
                    });
                    return;

                case "whispers":
                    var whisper = (Whisper)msg.MessageData;
                    OnWhisper?.Invoke(this, new OnWhisperArgs {
                        Whisper = whisper, ChannelId = channelId
                    });
                    return;

                case "chat_moderator_actions":
                    var cma    = msg.MessageData as ChatModeratorActions;
                    var reason = "";
                    switch (cma?.ModerationAction.ToLower())
                    {
                    case "timeout":
                        if (cma.Args.Count > 2)
                        {
                            reason = cma.Args[2];
                        }
                        OnTimeout?.Invoke(this, new OnTimeoutArgs
                        {
                            TimedoutBy      = cma.CreatedBy,
                            TimedoutById    = cma.CreatedByUserId,
                            TimedoutUserId  = cma.TargetUserId,
                            TimeoutDuration = TimeSpan.FromSeconds(int.Parse(cma.Args[1])),
                            TimeoutReason   = reason,
                            TimedoutUser    = cma.Args[0],
                            ChannelId       = channelId
                        });
                        return;

                    case "ban":
                        if (cma.Args.Count > 1)
                        {
                            reason = cma.Args[1];
                        }
                        OnBan?.Invoke(this, new OnBanArgs {
                            BannedBy = cma.CreatedBy, BannedByUserId = cma.CreatedByUserId, BannedUserId = cma.TargetUserId, BanReason = reason, BannedUser = cma.Args[0], ChannelId = channelId
                        });
                        return;

                    case "delete":
                        OnMessageDeleted?.Invoke(this, new OnMessageDeletedArgs {
                            DeletedBy = cma.CreatedBy, DeletedByUserId = cma.CreatedByUserId, TargetUserId = cma.TargetUserId, TargetUser = cma.Args[0], Message = cma.Args[1], MessageId = cma.Args[2], ChannelId = channelId
                        });
                        return;

                    case "unban":
                        OnUnban?.Invoke(this, new OnUnbanArgs {
                            UnbannedBy = cma.CreatedBy, UnbannedByUserId = cma.CreatedByUserId, UnbannedUserId = cma.TargetUserId, UnbannedUser = cma.Args[0], ChannelId = channelId
                        });
                        return;

                    case "untimeout":
                        OnUntimeout?.Invoke(this, new OnUntimeoutArgs {
                            UntimeoutedBy = cma.CreatedBy, UntimeoutedByUserId = cma.CreatedByUserId, UntimeoutedUserId = cma.TargetUserId, UntimeoutedUser = cma.Args[0], ChannelId = channelId
                        });
                        return;

                    case "host":
                        OnHost?.Invoke(this, new OnHostArgs {
                            HostedChannel = cma.Args[0], Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "subscribers":
                        OnSubscribersOnly?.Invoke(this, new OnSubscribersOnlyArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "subscribersoff":
                        OnSubscribersOnlyOff?.Invoke(this, new OnSubscribersOnlyOffArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "clear":
                        OnClear?.Invoke(this, new OnClearArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "emoteonly":
                        OnEmoteOnly?.Invoke(this, new OnEmoteOnlyArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "emoteonlyoff":
                        OnEmoteOnlyOff?.Invoke(this, new OnEmoteOnlyOffArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "r9kbeta":
                        OnR9kBeta?.Invoke(this, new OnR9kBetaArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "r9kbetaoff":
                        OnR9kBetaOff?.Invoke(this, new OnR9kBetaOffArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;
                    }
                    break;

                case "channel-bits-events-v1":
                    if (msg.MessageData is ChannelBitsEvents cbe)
                    {
                        OnBitsReceived?.Invoke(this, new OnBitsReceivedArgs
                        {
                            BitsUsed      = cbe.BitsUsed,
                            ChannelId     = cbe.ChannelId,
                            ChannelName   = cbe.ChannelName,
                            ChatMessage   = cbe.ChatMessage,
                            Context       = cbe.Context,
                            Time          = cbe.Time,
                            TotalBitsUsed = cbe.TotalBitsUsed,
                            UserId        = cbe.UserId,
                            Username      = cbe.Username
                        });
                        return;
                    }
                    break;

                case "channel-bits-events-v2":
                    if (msg.MessageData is ChannelBitsEventsV2 cbev2)
                    {
                        OnBitsReceivedV2?.Invoke(this, new OnBitsReceivedV2Args
                        {
                            IsAnonymous   = cbev2.IsAnonymous,
                            BitsUsed      = cbev2.BitsUsed,
                            ChannelId     = cbev2.ChannelId,
                            ChannelName   = cbev2.ChannelName,
                            ChatMessage   = cbev2.ChatMessage,
                            Context       = cbev2.Context,
                            Time          = cbev2.Time,
                            TotalBitsUsed = cbev2.TotalBitsUsed,
                            UserId        = cbev2.UserId,
                            UserName      = cbev2.UserName
                        });
                        return;
                    }
                    break;

                case "channel-commerce-events-v1":
                    if (msg.MessageData is ChannelCommerceEvents cce)
                    {
                        OnChannelCommerceReceived?.Invoke(this, new OnChannelCommerceReceivedArgs
                        {
                            Username        = cce.Username,
                            DisplayName     = cce.DisplayName,
                            ChannelName     = cce.ChannelName,
                            UserId          = cce.UserId,
                            ChannelId       = cce.ChannelId,
                            Time            = cce.Time,
                            ItemImageURL    = cce.ItemImageURL,
                            ItemDescription = cce.ItemDescription,
                            SupportsChannel = cce.SupportsChannel,
                            PurchaseMessage = cce.PurchaseMessage
                        });
                        return;
                    }
                    break;

                case "channel-ext-v1":
                    var cEB = msg.MessageData as ChannelExtensionBroadcast;
                    OnChannelExtensionBroadcast?.Invoke(this, new OnChannelExtensionBroadcastArgs {
                        Messages = cEB.Messages, ChannelId = channelId
                    });
                    return;

                case "video-playback-by-id":
                    var vP = msg.MessageData as VideoPlayback;
                    switch (vP?.Type)
                    {
                    case VideoPlaybackType.StreamDown:
                        OnStreamDown?.Invoke(this, new OnStreamDownArgs {
                            ServerTime = vP.ServerTime, ChannelId = channelId
                        });
                        return;

                    case VideoPlaybackType.StreamUp:
                        OnStreamUp?.Invoke(this, new OnStreamUpArgs {
                            PlayDelay = vP.PlayDelay, ServerTime = vP.ServerTime, ChannelId = channelId
                        });
                        return;

                    case VideoPlaybackType.ViewCount:
                        OnViewCount?.Invoke(this, new OnViewCountArgs {
                            ServerTime = vP.ServerTime, Viewers = vP.Viewers, ChannelId = channelId
                        });
                        return;

                    case VideoPlaybackType.Commercial:
                        OnCommercial?.Invoke(this, new OnCommercialArgs {
                            ServerTime = vP.ServerTime, Length = vP.Length, ChannelId = channelId
                        });
                        return;
                    }
                    break;

                case "following":
                    var f = (Following)msg.MessageData;
                    f.FollowedChannelId = msg.Topic.Split('.')[1];
                    OnFollow?.Invoke(this, new OnFollowArgs {
                        FollowedChannelId = f.FollowedChannelId, DisplayName = f.DisplayName, UserId = f.UserId, Username = f.Username
                    });
                    return;

                case "community-points-channel-v1":
                    var cpc = msg.MessageData as CommunityPointsChannel;
                    switch (cpc?.Type)
                    {
                    case CommunityPointsChannelType.RewardRedeemed:
                        OnRewardRedeemed?.Invoke(this, new OnRewardRedeemedArgs {
                            TimeStamp = cpc.TimeStamp, ChannelId = cpc.ChannelId, Login = cpc.Login, DisplayName = cpc.DisplayName, Message = cpc.Message, RewardId = cpc.RewardId, RewardTitle = cpc.RewardTitle, RewardPrompt = cpc.RewardPrompt, RewardCost = cpc.RewardCost, Status = cpc.Status, RedemptionId = cpc.RedemptionId
                        });
                        return;

                    case CommunityPointsChannelType.CustomRewardUpdated:
                        OnCustomRewardUpdated?.Invoke(this, new OnCustomRewardUpdatedArgs {
                            TimeStamp = cpc.TimeStamp, ChannelId = cpc.ChannelId, RewardId = cpc.RewardId, RewardTitle = cpc.RewardTitle, RewardPrompt = cpc.RewardPrompt, RewardCost = cpc.RewardCost
                        });
                        return;

                    case CommunityPointsChannelType.CustomRewardCreated:
                        OnCustomRewardCreated?.Invoke(this, new OnCustomRewardCreatedArgs {
                            TimeStamp = cpc.TimeStamp, ChannelId = cpc.ChannelId, RewardId = cpc.RewardId, RewardTitle = cpc.RewardTitle, RewardPrompt = cpc.RewardPrompt, RewardCost = cpc.RewardCost
                        });
                        return;

                    case CommunityPointsChannelType.CustomRewardDeleted:
                        OnCustomRewardDeleted?.Invoke(this, new OnCustomRewardDeletedArgs {
                            TimeStamp = cpc.TimeStamp, ChannelId = cpc.ChannelId, RewardId = cpc.RewardId, RewardTitle = cpc.RewardTitle, RewardPrompt = cpc.RewardPrompt
                        });
                        return;
                    }
                    return;

                case "channel-points-channel-v1":
                    var channelPointsChannel = msg.MessageData as ChannelPointsChannel;
                    switch (channelPointsChannel.Type)
                    {
                    case ChannelPointsChannelType.RewardRedeemed:
                        var rewardRedeemed = channelPointsChannel.Data as RewardRedeemed;
                        OnChannelPointsRewardRedeemed?.Invoke(this, new OnChannelPointsRewardRedeemedArgs {
                            ChannelId = rewardRedeemed.Redemption.ChannelId, RewardRedeemed = rewardRedeemed
                        });
                        break;

                    case ChannelPointsChannelType.Unknown:
                        UnaccountedFor($"Unknown channel points type. Msg: {channelPointsChannel.RawData}");
                        break;
                    }
                    return;

                case "leaderboard-events-v1":
                    var lbe = msg.MessageData as LeaderboardEvents;
                    switch (lbe?.Type)
                    {
                    case LeaderBoardType.BitsUsageByChannel:
                        OnLeaderboardBits?.Invoke(this, new OnLeaderboardEventArgs {
                            ChannelId = lbe.ChannelId, TopList = lbe.Top
                        });
                        return;

                    case LeaderBoardType.SubGiftSent:
                        OnLeaderboardSubs?.Invoke(this, new OnLeaderboardEventArgs {
                            ChannelId = lbe.ChannelId, TopList = lbe.Top
                        });
                        return;
                    }
                    return;

                case "raid":
                    var r = msg.MessageData as RaidEvents;
                    switch (r?.Type)
                    {
                    case RaidType.RaidUpdate:
                        OnRaidUpdate?.Invoke(this, new OnRaidUpdateArgs {
                            Id = r.Id, ChannelId = r.ChannelId, TargetChannelId = r.TargetChannelId, AnnounceTime = r.AnnounceTime, RaidTime = r.RaidTime, RemainingDurationSeconds = r.RemainigDurationSeconds, ViewerCount = r.ViewerCount
                        });
                        return;

                    case RaidType.RaidUpdateV2:
                        OnRaidUpdateV2?.Invoke(this, new OnRaidUpdateV2Args {
                            Id = r.Id, ChannelId = r.ChannelId, TargetChannelId = r.TargetChannelId, TargetLogin = r.TargetLogin, TargetDisplayName = r.TargetDisplayName, TargetProfileImage = r.TargetProfileImage, ViewerCount = r.ViewerCount
                        });
                        return;

                    case RaidType.RaidGo:
                        OnRaidGo?.Invoke(this, new OnRaidGoArgs {
                            Id = r.Id, ChannelId = r.ChannelId, TargetChannelId = r.TargetChannelId, TargetLogin = r.TargetLogin, TargetDisplayName = r.TargetDisplayName, TargetProfileImage = r.TargetProfileImage, ViewerCount = r.ViewerCount
                        });
                        return;
                    }
                    return;

                case "predictions-channel-v1":
                    var pred = msg.MessageData as PredictionEvents;
                    switch (pred?.Type)
                    {
                    case PredictionType.EventCreated:
                        OnPrediction?.Invoke(this, new OnPredictionArgs {
                            CreatedAt = pred.CreatedAt, Title = pred.Title, ChannelId = pred.ChannelId, EndedAt = pred.EndedAt, Id = pred.Id, Outcomes = pred.Outcomes, LockedAt = pred.LockedAt, PredictionTime = pred.PredictionTime, Status = pred.Status, WinningOutcomeId = pred.WinningOutcomeId, Type = pred.Type
                        });
                        return;

                    case PredictionType.EventUpdated:
                        OnPrediction?.Invoke(this, new OnPredictionArgs {
                            CreatedAt = pred.CreatedAt, Title = pred.Title, ChannelId = pred.ChannelId, EndedAt = pred.EndedAt, Id = pred.Id, Outcomes = pred.Outcomes, LockedAt = pred.LockedAt, PredictionTime = pred.PredictionTime, Status = pred.Status, WinningOutcomeId = pred.WinningOutcomeId, Type = pred.Type
                        });
                        return;

                    case null:
                        UnaccountedFor("Prediction Type: null");
                        break;

                    default:
                        UnaccountedFor($"Prediction Type: {pred.Type}");
                        break;
                    }
                    return;
                }
                break;

            case "pong":
                _pongReceived = true;
                return;

            case "reconnect": _socket.Close(); break;
            }
            UnaccountedFor(message);
        }
Exemple #4
0
        public PubSub(EndPoint proxy = null) : base(null)
        {
            ThreadDispatcher.EnsureCreated();

            base.OnPubSubServiceConnected += ((object sender, EventArgs e) => { ThreadDispatcher.Enqueue(() => OnPubSubServiceConnected?.Invoke(sender, e)); });
            base.OnPubSubServiceError     += ((object sender, OnPubSubServiceErrorArgs e) => { ThreadDispatcher.Enqueue(() => OnPubSubServiceError?.Invoke(sender, e)); });
            base.OnPubSubServiceClosed    += ((object sender, EventArgs e) => { ThreadDispatcher.Enqueue(() => OnPubSubServiceClosed?.Invoke(sender, e)); });
            base.OnListenResponse         += ((object sender, OnListenResponseArgs e) => { ThreadDispatcher.Enqueue(() => OnListenResponse?.Invoke(sender, e)); });
            base.OnTimeout                   += ((object sender, OnTimeoutArgs e) => { ThreadDispatcher.Enqueue(() => OnTimeout?.Invoke(sender, e)); });
            base.OnBan                       += ((object sender, OnBanArgs e) => { ThreadDispatcher.Enqueue(() => OnBan?.Invoke(sender, e)); });
            base.OnUnban                     += ((object sender, OnUnbanArgs e) => { ThreadDispatcher.Enqueue(() => OnUnban?.Invoke(sender, e)); });
            base.OnUntimeout                 += ((object sender, OnUntimeoutArgs e) => { ThreadDispatcher.Enqueue(() => OnUntimeout?.Invoke(sender, e)); });
            base.OnHost                      += ((object sender, OnHostArgs e) => { ThreadDispatcher.Enqueue(() => OnHost?.Invoke(sender, e)); });
            base.OnSubscribersOnly           += ((object sender, OnSubscribersOnlyArgs e) => { ThreadDispatcher.Enqueue(() => OnSubscribersOnly?.Invoke(sender, e)); });
            base.OnSubscribersOnlyOff        += ((object sender, OnSubscribersOnlyOffArgs e) => { ThreadDispatcher.Enqueue(() => OnSubscribersOnlyOff?.Invoke(sender, e)); });
            base.OnClear                     += ((object sender, OnClearArgs e) => { ThreadDispatcher.Enqueue(() => OnClear?.Invoke(sender, e)); });
            base.OnEmoteOnly                 += ((object sender, OnEmoteOnlyArgs e) => { ThreadDispatcher.Enqueue(() => OnEmoteOnly?.Invoke(sender, e)); });
            base.OnEmoteOnlyOff              += ((object sender, OnEmoteOnlyOffArgs e) => { ThreadDispatcher.Enqueue(() => OnEmoteOnlyOff?.Invoke(sender, e)); });
            base.OnR9kBeta                   += ((object sender, OnR9kBetaArgs e) => { ThreadDispatcher.Enqueue(() => OnR9kBeta?.Invoke(sender, e)); });
            base.OnR9kBetaOff                += ((object sender, OnR9kBetaOffArgs e) => { ThreadDispatcher.Enqueue(() => OnR9kBetaOff?.Invoke(sender, e)); });
            base.OnBitsReceived              += ((object sender, OnBitsReceivedArgs e) => { ThreadDispatcher.Enqueue(() => OnBitsReceived?.Invoke(sender, e)); });
            base.OnStreamUp                  += ((object sender, OnStreamUpArgs arg) => { ThreadDispatcher.Enqueue(() => OnStreamUp(sender, arg)); });
            base.OnStreamDown                += ((object sender, OnStreamDownArgs e) => { ThreadDispatcher.Enqueue(() => OnStreamDown?.Invoke(sender, e)); });
            base.OnViewCount                 += ((object sender, OnViewCountArgs e) => { ThreadDispatcher.Enqueue(() => OnViewCount?.Invoke(sender, e)); });
            base.OnWhisper                   += ((object sender, OnWhisperArgs e) => { ThreadDispatcher.Enqueue(() => OnWhisper?.Invoke(sender, e)); });
            base.OnChannelSubscription       += ((object sender, OnChannelSubscriptionArgs e) => { ThreadDispatcher.Enqueue(() => OnChannelSubscription?.Invoke(sender, e)); });
            base.OnChannelCommerceReceived   += ((object sender, OnChannelCommerceReceivedArgs e) => { ThreadDispatcher.Enqueue(() => OnChannelCommerceReceived?.Invoke(sender, e)); });
            base.OnChannelExtensionBroadcast += ((object sender, OnChannelExtensionBroadcastArgs e) => { ThreadDispatcher.Enqueue(() => OnChannelExtensionBroadcast?.Invoke(sender, e)); });
            base.OnFollow                    += ((object sender, OnFollowArgs e) => { ThreadDispatcher.Enqueue(() => OnFollow?.Invoke(sender, e)); });
            base.OnCustomRewardCreated       += ((object sender, OnCustomRewardCreatedArgs e) => { ThreadDispatcher.Enqueue(() => OnCustomRewardCreated?.Invoke(sender, e)); });
            base.OnCustomRewardUpdated       += ((object sender, OnCustomRewardUpdatedArgs e) => { ThreadDispatcher.Enqueue(() => OnCustomRewardUpdated?.Invoke(sender, e)); });
            base.OnCustomRewardDeleted       += ((object sender, OnCustomRewardDeletedArgs e) => { ThreadDispatcher.Enqueue(() => OnCustomRewardDeleted?.Invoke(sender, e)); });
            base.OnRewardRedeemed            += ((object sender, OnRewardRedeemedArgs e) => { ThreadDispatcher.Enqueue(() => OnRewardRedeemed?.Invoke(sender, e)); });
            base.OnBitsReceivedV2            += ((object sender, OnBitsReceivedV2Args e) => { ThreadDispatcher.Enqueue(() => OnBitsReceivedV2?.Invoke(sender, e)); });
            base.OnLeaderboardSubs           += ((object sender, OnLeaderboardEventArgs e) => { ThreadDispatcher.Enqueue(() => OnLeaderboardSubs?.Invoke(sender, e)); });
            base.OnLeaderboardBits           += ((object sender, OnLeaderboardEventArgs e) => { ThreadDispatcher.Enqueue(() => OnLeaderboardBits?.Invoke(sender, e)); });
            base.OnRaidUpdate                += ((object sender, OnRaidUpdateArgs e) => { ThreadDispatcher.Enqueue(() => OnRaidUpdate?.Invoke(sender, e)); });
            base.OnRaidUpdateV2              += ((object sender, OnRaidUpdateV2Args e) => { ThreadDispatcher.Enqueue(() => OnRaidUpdateV2?.Invoke(sender, e)); });
            base.OnRaidGo                    += ((object sender, OnRaidGoArgs e) => { ThreadDispatcher.Enqueue(() => OnRaidGo?.Invoke(sender, e)); });
            base.OnMessageDeleted            += ((object sender, OnMessageDeletedArgs e) => { ThreadDispatcher.Enqueue(() => OnMessageDeleted?.Invoke(sender, e)); });
            base.OnLog                       += ((object sender, OnLogArgs e) => { ThreadDispatcher.Enqueue(() => OnLog?.Invoke(sender, e)); });
            base.OnCommercial                += ((object sender, OnCommercialArgs e) => { ThreadDispatcher.Enqueue(() => OnCommercial?.Invoke(sender, e)); });
            base.OnPrediction                += ((object sender, OnPredictionArgs e) => { ThreadDispatcher.Enqueue(() => OnPrediction?.Invoke(sender, e)); });
        }
Exemple #5
0
        /// <summary>
        /// Parses the message.
        /// </summary>
        /// <param name="message">The message.</param>
        private void ParseMessage(string message)
        {
            var type = JObject.Parse(message).SelectToken("type")?.ToString();

            switch (type?.ToLower())
            {
            case "response":
                var resp = new Models.Responses.Response(message);
                if (_previousRequests.Count != 0)
                {
                    bool handled = false;
                    foreach (var request in _previousRequests)
                    {
                        if (string.Equals(request.Nonce, resp.Nonce, StringComparison.CurrentCulture))
                        {
                            OnListenResponse?.Invoke(this, new OnListenResponseArgs {
                                Response = resp, Topic = request.Topic, Successful = resp.Successful
                            });
                            handled = true;
                        }
                    }
                    if (handled)
                    {
                        return;
                    }
                }
                break;

            case "message":
                var msg = new Models.Responses.Message(message);
                _topicToChannelId.TryGetValue(msg.Topic, out var channelId);
                channelId = channelId ?? "";
                switch (msg.Topic.Split('.')[0])
                {
                case "channel-subscribe-events-v1":
                    var subscription = msg.MessageData as ChannelSubscription;
                    OnChannelSubscription?.Invoke(this, new OnChannelSubscriptionArgs {
                        Subscription = subscription, ChannelId = channelId
                    });
                    return;

                case "whispers":
                    var whisper = (Whisper)msg.MessageData;
                    OnWhisper?.Invoke(this, new OnWhisperArgs {
                        Whisper = whisper, ChannelId = channelId
                    });
                    return;

                case "chat_moderator_actions":
                    var cma             = msg.MessageData as ChatModeratorActions;
                    var reason          = "";
                    var targetChannelId = msg.Topic.Split('.')[2];
                    switch (cma?.ModerationAction.ToLower())
                    {
                    case "timeout":
                        if (cma.Args.Count > 2)
                        {
                            reason = cma.Args[2];
                        }
                        OnTimeout?.Invoke(this, new OnTimeoutArgs
                        {
                            TimedoutBy      = cma.CreatedBy,
                            TimedoutById    = cma.CreatedByUserId,
                            TimedoutUserId  = cma.TargetUserId,
                            TimeoutDuration = TimeSpan.FromSeconds(int.Parse(cma.Args[1])),
                            TimeoutReason   = reason,
                            TimedoutUser    = cma.Args[0],
                            ChannelId       = channelId
                        });
                        return;

                    case "ban":
                        if (cma.Args.Count > 1)
                        {
                            reason = cma.Args[1];
                        }
                        OnBan?.Invoke(this, new OnBanArgs {
                            BannedBy = cma.CreatedBy, BannedByUserId = cma.CreatedByUserId, BannedUserId = cma.TargetUserId, BanReason = reason, BannedUser = cma.Args[0], ChannelId = channelId
                        });
                        return;

                    case "delete":
                        OnMessageDeleted?.Invoke(this, new OnMessageDeletedArgs {
                            DeletedBy = cma.CreatedBy, DeletedByUserId = cma.CreatedByUserId, TargetUserId = cma.TargetUserId, TargetUser = cma.Args[0], Message = cma.Args[1], MessageId = cma.Args[2], ChannelId = channelId
                        });
                        return;

                    case "unban":
                        OnUnban?.Invoke(this, new OnUnbanArgs {
                            UnbannedBy = cma.CreatedBy, UnbannedByUserId = cma.CreatedByUserId, UnbannedUserId = cma.TargetUserId, UnbannedUser = cma.Args[0], ChannelId = channelId
                        });
                        return;

                    case "untimeout":
                        OnUntimeout?.Invoke(this, new OnUntimeoutArgs {
                            UntimeoutedBy = cma.CreatedBy, UntimeoutedByUserId = cma.CreatedByUserId, UntimeoutedUserId = cma.TargetUserId, UntimeoutedUser = cma.Args[0], ChannelId = channelId
                        });
                        return;

                    case "host":
                        OnHost?.Invoke(this, new OnHostArgs {
                            HostedChannel = cma.Args[0], Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "subscribers":
                        OnSubscribersOnly?.Invoke(this, new OnSubscribersOnlyArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "subscribersoff":
                        OnSubscribersOnlyOff?.Invoke(this, new OnSubscribersOnlyOffArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "clear":
                        OnClear?.Invoke(this, new OnClearArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "emoteonly":
                        OnEmoteOnly?.Invoke(this, new OnEmoteOnlyArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "emoteonlyoff":
                        OnEmoteOnlyOff?.Invoke(this, new OnEmoteOnlyOffArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "r9kbeta":
                        OnR9kBeta?.Invoke(this, new OnR9kBetaArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "r9kbetaoff":
                        OnR9kBetaOff?.Invoke(this, new OnR9kBetaOffArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;
                    }
                    break;

                case "channel-bits-events-v1":
                    if (msg.MessageData is ChannelBitsEvents cbe)
                    {
                        OnBitsReceived?.Invoke(this, new OnBitsReceivedArgs
                        {
                            BitsUsed      = cbe.BitsUsed,
                            ChannelId     = cbe.ChannelId,
                            ChannelName   = cbe.ChannelName,
                            ChatMessage   = cbe.ChatMessage,
                            Context       = cbe.Context,
                            Time          = cbe.Time,
                            TotalBitsUsed = cbe.TotalBitsUsed,
                            UserId        = cbe.UserId,
                            Username      = cbe.Username
                        });
                        return;
                    }
                    break;

                case "channel-commerce-events-v1":
                    if (msg.MessageData is ChannelCommerceEvents cce)
                    {
                        OnChannelCommerceReceived?.Invoke(this, new OnChannelCommerceReceivedArgs
                        {
                            Username        = cce.Username,
                            DisplayName     = cce.DisplayName,
                            ChannelName     = cce.ChannelName,
                            UserId          = cce.UserId,
                            ChannelId       = cce.ChannelId,
                            Time            = cce.Time,
                            ItemImageURL    = cce.ItemImageURL,
                            ItemDescription = cce.ItemDescription,
                            SupportsChannel = cce.SupportsChannel,
                            PurchaseMessage = cce.PurchaseMessage
                        });
                        return;
                    }
                    break;

                case "channel-ext-v1":
                    var cEB = msg.MessageData as ChannelExtensionBroadcast;
                    OnChannelExtensionBroadcast?.Invoke(this, new OnChannelExtensionBroadcastArgs {
                        Messages = cEB.Messages, ChannelId = channelId
                    });
                    return;

                case "video-playback":
                case "video-playback-by-id":
                    var vP = msg.MessageData as VideoPlayback;
                    switch (vP?.Type)
                    {
                    case VideoPlaybackType.StreamDown:
                        OnStreamDown?.Invoke(this, new OnStreamDownArgs {
                            ChannelId = channelId, ServerTime = vP.ServerTime
                        });
                        return;

                    case VideoPlaybackType.StreamUp:
                        OnStreamUp?.Invoke(this, new OnStreamUpArgs {
                            ChannelId = channelId, PlayDelay = vP.PlayDelay, ServerTime = vP.ServerTime
                        });
                        return;

                    case VideoPlaybackType.ViewCount:
                        OnViewCount?.Invoke(this, new OnViewCountArgs {
                            ChannelId = channelId, ServerTime = vP.ServerTime, Viewers = vP.Viewers
                        });
                        return;

                    case VideoPlaybackType.Commercial:
                        OnCommercial?.Invoke(this, new OnCommercialArgs {
                            ChannelId = channelId, ServerTime = vP.ServerTime, Length = vP.Length
                        });
                        return;

                    case VideoPlaybackType.WatchPartyVod:
                        OnWatchPartyVod?.Invoke(this, new OnWatchPartyVodArgs {
                            ChannelId = channelId, WpId = vP.WpId, WpType = vP.WpType, IncrementUrl = vP.IncrementUrl, VodId = vP.VodId, Title = vP.Title, BroadcastType = vP.BroadcastType, Viewable = vP.Viewable
                        });
                        return;
                    }
                    break;

                case "broadcast-settings-update":
                    var bsu = msg.MessageData as BroadcastSettingsUpdate;
                    switch (bsu?.Type)
                    {
                    case BroadcastSettingsUpdateType.BroadcastSettingsUpdate:
                        OnBroadcastSettingsUpdate?.Invoke(this, new OnBroadcastSettingsUpdateArgs {
                            ChannelId = channelId, Channel = bsu.Channel, Game = bsu.Game, GameId = bsu.GameId, OldGame = bsu.OldGame, OldGameId = bsu.OldGameId, OldStatus = bsu.OldStatus, Status = bsu.Status
                        });
                        return;
                    }
                    break;

                case "following":
                    var f = (Following)msg.MessageData;
                    f.FollowedChannelId = msg.Topic.Split('.')[1];
                    OnFollow?.Invoke(this, new OnFollowArgs {
                        FollowedChannelId = f.FollowedChannelId, DisplayName = f.DisplayName, UserId = f.UserId, Username = f.Username
                    });
                    return;

                case "community-points-channel-v1":
                    var cpc = msg.MessageData as CommunityPointsChannel;
                    switch (cpc?.Type)
                    {
                    case CommunityPointsChannelType.RewardRedeemed:
                        OnRewardRedeemed?.Invoke(this, new OnRewardRedeemedArgs {
                            TimeStamp = cpc.TimeStamp, ChannelId = cpc.ChannelId, Login = cpc.Login, DisplayName = cpc.DisplayName, Message = cpc.Message, RewardId = cpc.RewardId, RewardTitle = cpc.RewardTitle, RewardPrompt = cpc.RewardPrompt, RewardCost = cpc.RewardCost, Status = cpc.Status
                        });
                        return;

                    case CommunityPointsChannelType.CustomRewardUpdated:
                        OnCustomRewardUpdated?.Invoke(this, new OnCustomRewardUpdatedArgs {
                            TimeStamp = cpc.TimeStamp, ChannelId = cpc.ChannelId, RewardId = cpc.RewardId, RewardTitle = cpc.RewardTitle, RewardPrompt = cpc.RewardPrompt, RewardCost = cpc.RewardCost
                        });
                        return;

                    case CommunityPointsChannelType.CustomRewardCreated:
                        OnCustomRewardCreated?.Invoke(this, new OnCustomRewardCreatedArgs {
                            TimeStamp = cpc.TimeStamp, ChannelId = cpc.ChannelId, RewardId = cpc.RewardId, RewardTitle = cpc.RewardTitle, RewardPrompt = cpc.RewardPrompt, RewardCost = cpc.RewardCost
                        });
                        return;

                    case CommunityPointsChannelType.CustomRewardDeleted:
                        OnCustomRewardDeleted?.Invoke(this, new OnCustomRewardDeletedArgs {
                            TimeStamp = cpc.TimeStamp, ChannelId = cpc.ChannelId, RewardId = cpc.RewardId, RewardTitle = cpc.RewardTitle, RewardPrompt = cpc.RewardPrompt
                        });
                        return;
                    }
                    return;

                case "leaderboard-events-v1":
                    var lbe = msg.MessageData as LeaderboardEvents;
                    switch (lbe?.Type)
                    {
                    case LeaderBoardType.BitsUsageByChannel:
                        OnLeaderboardBits?.Invoke(this, new OnLeaderboardEventArgs {
                            ChannelId = lbe.ChannelId, TopList = lbe.Top
                        });
                        return;

                    case LeaderBoardType.SubGiftSent:
                        OnLeaderboardSubs?.Invoke(this, new OnLeaderboardEventArgs {
                            ChannelId = lbe.ChannelId, TopList = lbe.Top
                        });
                        return;
                    }
                    return;

                case "raid":
                    var r = msg.MessageData as RaidEvents;
                    switch (r?.Type)
                    {
                    case RaidType.RaidUpdate:
                        OnRaidUpdate?.Invoke(this, new OnRaidUpdateArgs {
                            Id = r.Id, ChannelId = r.ChannelId, TargetChannelId = r.TargetChannelId, AnnounceTime = r.AnnounceTime, RaidTime = r.RaidTime, RemainingDurationSeconds = r.RemainigDurationSeconds, ViewerCount = r.ViewerCount
                        });
                        return;

                    case RaidType.RaidUpdateV2:
                        OnRaidUpdateV2?.Invoke(this, new OnRaidUpdateV2Args {
                            Id = r.Id, ChannelId = r.ChannelId, TargetChannelId = r.TargetChannelId, TargetLogin = r.TargetLogin, TargetDisplayName = r.TargetDisplayName, TargetProfileImage = r.TargetProfileImage, ViewerCount = r.ViewerCount
                        });
                        return;

                    case RaidType.RaidGo:
                        OnRaidGo?.Invoke(this, new OnRaidGoArgs {
                            Id = r.Id, ChannelId = r.ChannelId, TargetChannelId = r.TargetChannelId, TargetLogin = r.TargetLogin, TargetDisplayName = r.TargetDisplayName, TargetProfileImage = r.TargetProfileImage, ViewerCount = r.ViewerCount
                        });
                        return;
                    }
                    return;
                }
                break;
            }
            UnaccountedFor(message);
        }
 /// <summary>
 /// Passes <see cref="OnUntimeout"/> events down to subscribed plugins.
 /// </summary>
 /// <param name="sender">The source of the event.</param>
 /// <param name="e">An <see cref="OnUntimeoutArgs"/> object.</param>
 private void TwitchPubSub_OnUntimeout(object sender, OnUntimeoutArgs e) => OnUntimeout?.Invoke(this, e);
        /// <summary>
        /// Parses the message.
        /// </summary>
        /// <param name="message">The message.</param>
        private void ParseMessage(string message)
        {
            var type = JObject.Parse(message).SelectToken("type")?.ToString();

            switch (type?.ToLower())
            {
            case "response":
                var resp = new Models.Responses.Response(message);
                if (_previousRequests.Count != 0)
                {
                    bool handled = false;
                    foreach (var request in _previousRequests)
                    {
                        if (string.Equals(request.Nonce, resp.Nonce, StringComparison.CurrentCulture))
                        {
                            OnListenResponse?.Invoke(this, new OnListenResponseArgs {
                                Response = resp, Topic = request.Topic, Successful = resp.Successful
                            });
                            handled = true;
                        }
                    }
                    if (handled)
                    {
                        return;
                    }
                }
                break;

            case "message":
                var msg = new Models.Responses.Message(message);
                _topicToChannelId.TryGetValue(msg.Topic, out var channelId);
                channelId = channelId ?? "";
                switch (msg.Topic.Split('.')[0])
                {
                case "channel-subscribe-events-v1":
                    var subscription = msg.MessageData as ChannelSubscription;
                    OnChannelSubscription?.Invoke(this, new OnChannelSubscriptionArgs {
                        Subscription = subscription, ChannelId = channelId
                    });
                    return;

                case "whispers":
                    var whisper = (Whisper)msg.MessageData;
                    OnWhisper?.Invoke(this, new OnWhisperArgs {
                        Whisper = whisper, ChannelId = channelId
                    });
                    return;

                case "chat_moderator_actions":
                    var cma             = msg.MessageData as ChatModeratorActions;
                    var reason          = "";
                    var targetChannelId = msg.Topic.Split('.')[2];
                    switch (cma?.ModerationAction.ToLower())
                    {
                    case "timeout":
                        if (cma.Args.Count > 2)
                        {
                            reason = cma.Args[2];
                        }
                        OnTimeout?.Invoke(this, new OnTimeoutArgs
                        {
                            TimedoutBy      = cma.CreatedBy,
                            TimedoutById    = cma.CreatedByUserId,
                            TimedoutUserId  = cma.TargetUserId,
                            TimeoutDuration = TimeSpan.FromSeconds(int.Parse(cma.Args[1])),
                            TimeoutReason   = reason,
                            TimedoutUser    = cma.Args[0],
                            ChannelId       = channelId
                        });
                        return;

                    case "ban":
                        if (cma.Args.Count > 1)
                        {
                            reason = cma.Args[1];
                        }
                        OnBan?.Invoke(this, new OnBanArgs {
                            BannedBy = cma.CreatedBy, BannedByUserId = cma.CreatedByUserId, BannedUserId = cma.TargetUserId, BanReason = reason, BannedUser = cma.Args[0], ChannelId = channelId
                        });
                        return;

                    case "delete":
                        OnMessageDeleted?.Invoke(this, new OnMessageDeletedArgs {
                            DeletedBy = cma.CreatedBy, DeletedByUserId = cma.CreatedByUserId, TargetUserId = cma.TargetUserId, TargetUser = cma.Args[0], Message = cma.Args[1], MessageId = cma.Args[2], ChannelId = channelId
                        });
                        return;

                    case "unban":
                        OnUnban?.Invoke(this, new OnUnbanArgs {
                            UnbannedBy = cma.CreatedBy, UnbannedByUserId = cma.CreatedByUserId, UnbannedUserId = cma.TargetUserId, UnbannedUser = cma.Args[0], ChannelId = channelId
                        });
                        return;

                    case "untimeout":
                        OnUntimeout?.Invoke(this, new OnUntimeoutArgs {
                            UntimeoutedBy = cma.CreatedBy, UntimeoutedByUserId = cma.CreatedByUserId, UntimeoutedUserId = cma.TargetUserId, UntimeoutedUser = cma.Args[0], ChannelId = channelId
                        });
                        return;

                    case "host":
                        OnHost?.Invoke(this, new OnHostArgs {
                            HostedChannel = cma.Args[0], Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "subscribers":
                        OnSubscribersOnly?.Invoke(this, new OnSubscribersOnlyArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "subscribersoff":
                        OnSubscribersOnlyOff?.Invoke(this, new OnSubscribersOnlyOffArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "clear":
                        OnClear?.Invoke(this, new OnClearArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "emoteonly":
                        OnEmoteOnly?.Invoke(this, new OnEmoteOnlyArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "emoteonlyoff":
                        OnEmoteOnlyOff?.Invoke(this, new OnEmoteOnlyOffArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "r9kbeta":
                        OnR9kBeta?.Invoke(this, new OnR9kBetaArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;

                    case "r9kbetaoff":
                        OnR9kBetaOff?.Invoke(this, new OnR9kBetaOffArgs {
                            Moderator = cma.CreatedBy, ChannelId = channelId
                        });
                        return;
                    }
                    break;

                case "channel-bits-events-v1":
                    if (msg.MessageData is ChannelBitsEvents cbe)
                    {
                        OnBitsReceived?.Invoke(this, new OnBitsReceivedArgs
                        {
                            BitsUsed      = cbe.BitsUsed,
                            ChannelId     = cbe.ChannelId,
                            ChannelName   = cbe.ChannelName,
                            ChatMessage   = cbe.ChatMessage,
                            Context       = cbe.Context,
                            Time          = cbe.Time,
                            TotalBitsUsed = cbe.TotalBitsUsed,
                            UserId        = cbe.UserId,
                            Username      = cbe.Username
                        });
                        return;
                    }
                    break;

                case "channel-commerce-events-v1":
                    if (msg.MessageData is ChannelCommerceEvents cce)
                    {
                        OnChannelCommerceReceived?.Invoke(this, new OnChannelCommerceReceivedArgs
                        {
                            Username        = cce.Username,
                            DisplayName     = cce.DisplayName,
                            ChannelName     = cce.ChannelName,
                            UserId          = cce.UserId,
                            ChannelId       = cce.ChannelId,
                            Time            = cce.Time,
                            ItemImageURL    = cce.ItemImageURL,
                            ItemDescription = cce.ItemDescription,
                            SupportsChannel = cce.SupportsChannel,
                            PurchaseMessage = cce.PurchaseMessage
                        });
                        return;
                    }
                    break;

                case "channel-ext-v1":
                    var cEB = msg.MessageData as ChannelExtensionBroadcast;
                    OnChannelExtensionBroadcast?.Invoke(this, new OnChannelExtensionBroadcastArgs {
                        Messages = cEB.Messages, ChannelId = channelId
                    });
                    return;

                case "video-playback":
                    var vP = msg.MessageData as VideoPlayback;
                    switch (vP?.Type)
                    {
                    case VideoPlaybackType.StreamDown:
                        OnStreamDown?.Invoke(this, new OnStreamDownArgs {
                            ServerTime = vP.ServerTime
                        });
                        return;

                    case VideoPlaybackType.StreamUp:
                        OnStreamUp?.Invoke(this, new OnStreamUpArgs {
                            PlayDelay = vP.PlayDelay, ServerTime = vP.ServerTime
                        });
                        return;

                    case VideoPlaybackType.ViewCount:
                        OnViewCount?.Invoke(this, new OnViewCountArgs {
                            ServerTime = vP.ServerTime, Viewers = vP.Viewers
                        });
                        return;

                    case VideoPlaybackType.Commercial:
                        OnCommercial?.Invoke(this, new OnCommercialArgs {
                            ServerTime = vP.ServerTime, Length = vP.Length
                        });
                        return;
                    }
                    break;

                case "following":
                    var f = (Following)msg.MessageData;
                    f.FollowedChannelId = msg.Topic.Split('.')[1];
                    OnFollow?.Invoke(this, new OnFollowArgs {
                        FollowedChannelId = f.FollowedChannelId, DisplayName = f.DisplayName, UserId = f.UserId, Username = f.Username
                    });
                    return;
                }
                break;
            }
            UnaccountedFor(message);
        }
        public PubSub(EndPoint proxy = null) : base(null, proxy)
        {
            _threadDispatcher = new GameObject("TwitchPubSubUnityDispatcher");
            _threadDispatcher.AddComponent <ThreadDispatcher>();
            UnityEngine.Object.DontDestroyOnLoad(_threadDispatcher);

            base.OnPubSubServiceConnected += ((object sender, EventArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnPubSubServiceConnected?.Invoke(sender, e)); });
            base.OnPubSubServiceError     += ((object sender, OnPubSubServiceErrorArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnPubSubServiceError?.Invoke(sender, e)); });
            base.OnPubSubServiceClosed    += ((object sender, EventArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnPubSubServiceClosed?.Invoke(sender, e)); });
            base.OnListenResponse         += ((object sender, OnListenResponseArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnListenResponse?.Invoke(sender, e)); });
            base.OnTimeout             += ((object sender, OnTimeoutArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnTimeout?.Invoke(sender, e)); });
            base.OnBan                 += ((object sender, OnBanArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnBan?.Invoke(sender, e)); });
            base.OnUnban               += ((object sender, OnUnbanArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnUnban?.Invoke(sender, e)); });
            base.OnUntimeout           += ((object sender, OnUntimeoutArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnUntimeout?.Invoke(sender, e)); });
            base.OnHost                += ((object sender, OnHostArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnHost?.Invoke(sender, e)); });
            base.OnSubscribersOnly     += ((object sender, OnSubscribersOnlyArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnSubscribersOnly?.Invoke(sender, e)); });
            base.OnSubscribersOnlyOff  += ((object sender, OnSubscribersOnlyOffArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnSubscribersOnlyOff?.Invoke(sender, e)); });
            base.OnClear               += ((object sender, OnClearArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnClear?.Invoke(sender, e)); });
            base.OnEmoteOnly           += ((object sender, OnEmoteOnlyArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnEmoteOnly?.Invoke(sender, e)); });
            base.OnEmoteOnlyOff        += ((object sender, OnEmoteOnlyOffArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnEmoteOnlyOff?.Invoke(sender, e)); });
            base.OnR9kBeta             += ((object sender, OnR9kBetaArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnR9kBeta?.Invoke(sender, e)); });
            base.OnR9kBetaOff          += ((object sender, OnR9kBetaOffArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnR9kBetaOff?.Invoke(sender, e)); });
            base.OnBitsReceived        += ((object sender, OnBitsReceivedArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnBitsReceived?.Invoke(sender, e)); });
            base.OnStreamUp            += ((object sender, OnStreamUpArgs arg) => { ThreadDispatcher.Instance().Enqueue(() => OnStreamUp(sender, arg)); });
            base.OnStreamDown          += ((object sender, OnStreamDownArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnStreamDown?.Invoke(sender, e)); });
            base.OnViewCount           += ((object sender, OnViewCountArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnViewCount?.Invoke(sender, e)); });
            base.OnWhisper             += ((object sender, OnWhisperArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnWhisper?.Invoke(sender, e)); });
            base.OnChannelSubscription += ((object sender, OnChannelSubscriptionArgs e) => { ThreadDispatcher.Instance().Enqueue(() => OnChannelSubscription?.Invoke(sender, e)); });
        }