protected void RaiseMessageReceived(NicoMessageContext messageContext)
 {
     if (messageContext != null)
     {
         MessageReceived?.Invoke(this, messageContext);
     }
 }
        public override async Task ConnectAsync(string input, IBrowserProfile browserProfile)
        {
            BeforeConnect();
            var nicoInput = Tools.ParseInput(input);

            if (nicoInput is InvalidInput invalidInput)
            {
                SendSystemInfo("未対応の形式のURLが入力されました", InfoType.Error);
                AfterDisconnected();
                return;
            }
            _isFirstConnection = true;
reload:
            _isDisconnectedExpected = false;
            _disconnectCts          = new CancellationTokenSource();
            try
            {
                await ConnectInternalAsync(nicoInput, browserProfile);
            }
            catch (ApiGetCommunityLivesException ex)
            {
                _isDisconnectedExpected = true;
                SendSystemInfo("コミュニティの配信状況の取得に失敗しました", InfoType.Error);
                _logger.LogException(ex, "", $"input:{input}, browser:{browserProfile.Type}");
            }
            catch (SpecChangedException ex)
            {
                _isDisconnectedExpected = true;
                SendSystemInfo("サイトの仕様変更があったためコメント取得を継続できません", InfoType.Error);
                _logger.LogException(ex, "", $"input:{input}, browser:{browserProfile.Type}");
            }
            catch (Exception ex)
            {
                _logger.LogException(ex, "", $"input:{input}, browser:{browserProfile.Type}");
            }
            _dataProps = null;
            if (!_isDisconnectedExpected)
            {
                _isFirstConnection = false;
                goto reload;
            }
            var m = new NicoDisconnected("");
            var s = new DisconnectedMessageMetadata(m, _options, _siteOptions);
            var c = new NicoMessageContext(m, s, new NicoMessageMethods());

            RaiseMessageReceived(c);
            AfterDisconnected();
        }
        public async Task <NicoMessageContext> CreateMessageContextAsync(IChat chat, string roomName, bool isInitialComment)
        {
            NicoMessageContext   messageContext = null;
            INicoMessageMetadata metadata;

            var userId = chat.UserId;


            INicoMessage message;
            var          messageType = Tools.GetMessageType(chat, _mainRoomThreadId);

            switch (messageType)
            {
            case NicoMessageType.Comment:
            {
                var user    = GetUser(userId);
                var comment = await Tools.CreateNicoComment(chat, user, _siteOptions, roomName, async userid => await API.GetUserInfo(_dataSource, userid), _logger);

                bool isFirstComment;
                if (_userCommentCountDict.ContainsKey(userId))
                {
                    _userCommentCountDict[userId]++;
                    isFirstComment = false;
                }
                else
                {
                    _userCommentCountDict.AddOrUpdate(userId, 1, (s, n) => n);
                    isFirstComment = true;
                }
                message  = comment;
                metadata = new CommentMessageMetadata(comment, _options, _siteOptions, user, _cp, isFirstComment)
                {
                    IsInitialComment = isInitialComment,
                    SiteContextGuid  = SiteContextGuid,
                };
            }
            break;

            case NicoMessageType.Info:
            {
                var info = Tools.CreateNicoInfo(chat, roomName, _siteOptions);
                message  = info;
                metadata = new InfoMessageMetadata(info, _options, _siteOptions);
            }
            break;

            case NicoMessageType.Ad:
            {
                var ad = Tools.CreateNicoAd(chat, roomName, _siteOptions);
                message  = ad;
                metadata = new AdMessageMetadata(ad, _options, _siteOptions);
            }
            break;

            case NicoMessageType.Item:
            {
                var item = Tools.CreateNicoItem(chat, roomName, _siteOptions);
                message  = item;
                metadata = new ItemMessageMetadata(item, _options, _siteOptions);
            }
            break;

            default:
                message  = null;
                metadata = null;
                break;
            }
            if (message == null || metadata == null)
            {
                return(null);
            }
            else
            {
                var methods = new NicoMessageMethods();
                messageContext = new NicoMessageContext(message, metadata, methods);
                return(messageContext);
            }
        }
        private void ProcessChatMessage(Chat.IChatMessage message)
        {
            switch (message)
            {
            case Chat.ChatMessage chat:
            {
                if (_isFirstConnection == false && _isInitialCommentsReceiving == true)
                {
                    //再接続時は初期コメントを無視する
                    return;
                }
                var  userId = chat.UserId;
                var  user   = GetUser(userId);
                bool isFirstComment;
                if (_userCommentCountDict.ContainsKey(userId))
                {
                    _userCommentCountDict[userId]++;
                    isFirstComment = false;
                }
                else
                {
                    _userCommentCountDict.AddOrUpdate(userId, 1, (s, n) => n);
                    isFirstComment = true;
                }
                //var comment = await Tools.CreateNicoComment(chat, user, _siteOptions, roomName, async userid => await API.GetUserInfo(_dataSource, userid), _logger);
                INicoMessage         comment;
                INicoMessageMetadata metadata;
                if (IsAd(chat))
                {
                    ///nicoad {"totalAdPoint":215500,"message":"シュガーさんが1700ptニコニ広告しました","version":"1"}
                    var     adJson = chat.Content.Replace("/nicoad", "");
                    dynamic d      = JsonConvert.DeserializeObject(adJson);
                    if ((string)d.version != "1")
                    {
                        //未対応
                        return;
                    }
                    var content = (string)d.message;
                    var ad      = new NicoAd(chat.Raw)
                    {
                        PostedAt = Common.UnixTimeConverter.FromUnixTime(chat.Date),
                        UserId   = userId,
                        Text     = content,
                    };
                    comment  = ad;
                    metadata = new AdMessageMetadata(ad, _options, _siteOptions)
                    {
                        IsInitialComment = _isInitialCommentsReceiving,
                        SiteContextGuid  = SiteContextGuid,
                    };
                }
                else if (IsGift(chat))
                {
                    var match = Regex.Match(chat.Content, "/gift (\\S+) (\\d+) \"(\\S+)\" (\\d+) \"(\\S+)\" \"(\\S+)\" (\\d+)");
                    if (!match.Success)
                    {
                        return;
                    }
                    var giftId    = match.Groups[1].Value;
                    var userIdp   = match.Groups[2].Value;      //ギフトを投げた人。userId == "900000000"
                    var username  = match.Groups[3].Value;
                    var point     = match.Groups[4].Value;
                    var what      = match.Groups[5].Value;
                    var itemName  = match.Groups[6].Value;
                    var itemCount = match.Groups[7].Value;        //アイテムの個数?ギフト貢献n位?
                    var text      = $"{username}さんがギフト「{itemName}({point}pt)」を贈りました";
                    var gift      = new NicoGift(chat.Raw)
                    {
                        Text      = text,
                        PostedAt  = Common.UnixTimeConverter.FromUnixTime(chat.Date),
                        UserId    = userIdp,
                        NameItems = Common.MessagePartFactory.CreateMessageItems(username),
                    };
                    comment  = gift;
                    metadata = new ItemMessageMetadata(gift, _options, _siteOptions)
                    {
                        IsInitialComment = _isInitialCommentsReceiving,
                        SiteContextGuid  = SiteContextGuid,
                    };
                }
                else if (IsSpi(chat))
                {
                    var spi = new NicoSpi(chat.Raw)
                    {
                        Text     = chat.Content,
                        PostedAt = Common.UnixTimeConverter.FromUnixTime(chat.Date),
                        UserId   = chat.UserId,
                    };
                    comment  = spi;
                    metadata = new SpiMessageMetadata(spi, _options, _siteOptions)
                    {
                        IsInitialComment = _isInitialCommentsReceiving,
                        SiteContextGuid  = SiteContextGuid,
                    };
                }
                else if (IsEmotion(chat))
                {
                    var content = chat.Content.Substring("/emotion ".Length);
                    var abc     = new NicoEmotion("")
                    {
                        ChatNo    = chat.No,
                        Anonymity = chat.Anonymity,
                        PostedAt  = Common.UnixTimeConverter.FromUnixTime(chat.Date),
                        Content   = content,
                        UserId    = chat.UserId,
                    };
                    comment  = abc;
                    metadata = new EmotionMessageMetadata(abc, _options, _siteOptions, user, this)
                    {
                        IsInitialComment = _isInitialCommentsReceiving,
                        SiteContextGuid  = SiteContextGuid,
                    };
                }
                else if (IsInfo(chat))
                {
                    var match = Regex.Match(chat.Content, "^/info (?<no>\\d+) (?<content>.+)$", RegexOptions.Singleline);
                    if (!match.Success)
                    {
                        throw new ParseException(chat.Raw);
                    }
                    else
                    {
                        var no      = int.Parse(match.Groups["no"].Value);
                        var content = match.Groups["content"].Value;
                        var info    = new NicoInfo(chat.Raw)
                        {
                            Text     = content,
                            PostedAt = Common.UnixTimeConverter.FromUnixTime(chat.Date),
                            UserId   = chat.UserId,
                            No       = no,
                        };
                        comment  = info;
                        metadata = new InfoMessageMetadata(info, _options, _siteOptions)
                        {
                            IsInitialComment = _isInitialCommentsReceiving,
                            SiteContextGuid  = SiteContextGuid,
                        };
                    }
                }
                else
                {
                    if (IsDisconnect(chat))        //NicoCommentではなく専用のクラスを作っても良いかも。
                    {
                        _chatProvider?.Disconnect();
                    }
                    var abc = new NicoComment("")
                    {
                        ChatNo   = chat.No,
                        Id       = chat.No.ToString(),
                        Is184    = chat.Anonymity == 1,
                        PostedAt = Common.UnixTimeConverter.FromUnixTime(chat.Date),
                        Text     = chat.Content,
                        UserId   = chat.UserId,
                        UserName = "",
                    };
                    comment  = abc;
                    metadata = new CommentMessageMetadata(abc, _options, _siteOptions, user, this, isFirstComment)
                    {
                        IsInitialComment = _isInitialCommentsReceiving,
                        SiteContextGuid  = SiteContextGuid,
                    };
                }


                var context = new NicoMessageContext(comment, metadata, new NicoMessageMethods());
                RaiseMessageReceived(context);
            }
            break;

            case Chat.Ping ping:
                if (ping.Content == "rs:0")
                {
                    _isInitialCommentsReceiving = true;
                }
                else if (ping.Content == "rf:0")
                {
                    _isInitialCommentsReceiving = false;
                }
                break;

            case Chat.UnknownMessage unknown:
                _logger.LogException(new ParseException(unknown.Raw));
                break;

            default:
                break;
            }
        }