private void ParseLiveCommentServerResponse(string recievedString) { if (string.IsNullOrWhiteSpace(recievedString)) { Debug.Write($"IGNORE"); return; } if (!recievedString.StartsWith("<") || !recievedString.EndsWith(">")) { // Note: 寄り厳密にXMLフォーマットチェックをやるなら // <>の数が同数であることをチェックする Debug.Write($"illigal format, required XML"); Debug.Write($" -> "); Debug.Write(recievedString); return; } var xmlDoc = XDocument.Parse(recievedString); var xmlRoot = xmlDoc.Root; var elementName = xmlRoot.Name.LocalName; Debug.Write(elementName); Debug.Write(" -> "); if (elementName == "thread") { Debug.Write("connect success"); IsCommentServerConnected = true; // <thread ticket="{チケット}" server_time="{サーバー時刻}" last_res="{送信される過去のコメント数?(NECOで使用してないので不明)}"> var serverTimeText = xmlRoot.Attribute(XName.Get("server_time")).Value; long serverTime; if (long.TryParse(serverTimeText, out serverTime)) { _ServerTime = serverTime; } var ticketText = xmlRoot.Attribute(XName.Get("ticket")).Value; _Ticket = ticketText; CommentServerConnected?.Invoke(); } else if (elementName == "chat_result") { // <chat_result status="{コメント投稿要求の返答}" /> var result = xmlRoot.Attribute(XName.Get("status")).Value; /* * 0 = 投稿に成功した * 1 = 投稿に失敗した(短時間に同じ内容のコメントを投稿しようとした、パラメータが間違っている、他) * 4 = 投稿に失敗した(ごく短時間にコメントを連投しようとした、パラメータが間違っている、他) */ Debug.Write(result); Debug.Write(result == "0" ? " (success)" : " (failed)"); CommentPosted?.Invoke(result == "0", _LastPostChat); _LastPostChat = null; } else if (elementName == "chat") { // _LiveComments.Add(chat); // <chat anonymity="{184か}" no="{コメントの番号}" date="{コメントが投稿されたリアル時間?}" mail="{コマンド}" premium="{プレミアムID}" thread="{スレッドID}" user_id="{ユーザーID}" vpos="{コメントが投稿された生放送の時間}" score="{NGスコア}">{コメント}</chat>\0 try { var chatSerializer = new XmlSerializer(typeof(Chat)); using (var readerStream = new StringReader(recievedString)) { var chat = chatSerializer.Deserialize(readerStream) as Chat; if (chat != null) { Debug.Write(chat.Text); OperationCommnad officialCommand = null; string[] officialCommandArguments = null; if (ChcekOfficialOperationComment(chat, out officialCommand, out officialCommandArguments)) { var args = new NicoLiveOperationCommandEventArgs() { CommandType = officialCommand.CommandType, Arguments = officialCommandArguments, Chat = chat }; OperationCommandRecieved?.Invoke(this, args); } else { CommentRecieved?.Invoke(chat); } } } } catch { } } else { Debug.WriteLine($"not supproted"); Debug.Write(" -> "); Debug.Write(recievedString); } }
private void ParseChatXml(string xml) { var xmlDoc = XDocument.Parse(xml); var xmlRoot = xmlDoc.Root; var elementName = xmlRoot.Name.LocalName; Debug.Write(elementName); Debug.Write(" -> "); if (elementName == "thread") { Debug.Write("connect success"); IsConnected = true; // <thread ticket="{チケット}" server_time="{サーバー時刻}" last_res="{送信される過去のコメント数?(NECOで使用してないので不明)}"> var serverTimeText = xmlRoot.Attribute(XName.Get("server_time")).Value; long serverTime; if (long.TryParse(serverTimeText, out serverTime)) { _ServerTime = serverTime; } var ticketText = xmlRoot.Attribute(XName.Get("ticket")).Value; _Ticket = ticketText; Connected?.Invoke(this, new CommentServerConnectedEventArgs() { Ticket = _Ticket, ServerTime = (int)_ServerTime, Thread = this.ThreadId }); } else if (elementName == "chat_result") { // <chat_result status="{コメント投稿要求の返答}" /> var result = xmlRoot.Attribute(XName.Get("status")).Value; /* * 0 = 投稿に成功した * 1 = 投稿に失敗した(短時間に同じ内容のコメントを投稿しようとした、パラメータが間違っている、他) * 4 = 投稿に失敗した(ごく短時間にコメントを連投しようとした、パラメータが間違っている、他) */ Debug.Write(result); Debug.Write(result == "0" ? " (success)" : " (failed)"); CommentPosted?.Invoke(this, new CommentPostedEventArgs() { ChatResult = (ChatResult)int.Parse(result), }); _LastPostChat = null; } else if (elementName == "chat") { // _LiveComments.Add(chat); // <chat anonymity="{184か}" no="{コメントの番号}" date="{コメントが投稿されたリアル時間?}" mail="{コマンド}" premium="{プレミアムID}" thread="{スレッドID}" user_id="{ユーザーID}" vpos="{コメントが投稿された生放送の時間}" score="{NGスコア}">{コメント}</chat>\0 try { var chatSerializer = new XmlSerializer(typeof(Chat)); using (var readerStream = new StringReader(xml)) { var chat = chatSerializer.Deserialize(readerStream) as Chat; if (chat != null) { Debug.Write(chat.Text); OperationCommnad officialCommand = null; string[] officialCommandArguments = null; if (ChcekOfficialOperationComment(chat, out officialCommand, out officialCommandArguments)) { var args = new NicoLiveOperationCommandEventArgs() { CommandType = officialCommand.CommandType, Arguments = officialCommandArguments, Chat = chat }; // OperationCommandRecieved?.Invoke(this, args); } else { var liveChatData = new LiveChatData() { No = (int)chat.GetCommentNo(), Thread = chat.Thread, IsAnonymity = chat.GetAnonymity(), Content = chat.Text, Mail = chat.Mail, UserId = chat.User_id, Vpos = (int)chat.GetVpos(), Date = int.Parse(chat.Date), }; CommentRecieved?.Invoke(this, new CommentRecievedEventArgs() { Chat = liveChatData }); } } } } catch { } } else { Debug.WriteLine($"not supproted"); Debug.Write(" -> "); Debug.Write(xml); } }
private async void _NicoLiveCommentClient_OperationCommandRecieved(NicoLiveCommentClient sender, NicoLiveOperationCommandEventArgs args) { await HohoemaApp.UIDispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async() => { switch (args.CommandType) { case NicoLiveOperationCommandType.Play: break; case NicoLiveOperationCommandType.PlaySound: break; case NicoLiveOperationCommandType.PermanentDisplay: if (args.Arguments.Length > 0) { PermanentDisplayText = args.Arguments[0]; } break; case NicoLiveOperationCommandType.ClearPermanentDisplay: PermanentDisplayText = null; break; case NicoLiveOperationCommandType.Vote: break; case NicoLiveOperationCommandType.CommentMode: break; case NicoLiveOperationCommandType.Call: break; case NicoLiveOperationCommandType.Free: break; case NicoLiveOperationCommandType.Reset: // 動画接続のリセット await RetryRtmpConnection(); break; case NicoLiveOperationCommandType.Info: // 1:市場登録 2:コミュニティ参加 3:延長 4,5:未確認 6,7:地震速報 8:現在の放送ランキングの順位 // /info 数字 "表示内容" if (args.Arguments.Length >= 2) { int infoType; if (int.TryParse(args.Arguments[0], out infoType)) { var nicoLiveInfoType = (NicoLiveInfoType)infoType; args.Chat.Text = args.Arguments[1]; args.Chat.Mail = "shita"; _LiveComments.Add(args.Chat); } } break; case NicoLiveOperationCommandType.Press: // http://dic.nicovideo.jp/a/%E3%83%90%E3%83%83%E3%82%AF%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%83%91%E3%82%B9 // TODO: BSPユーザーによるコメントに対応 // BSPコメへの風当たりはやや強いのでオプションでON/OFF切り替え対応必要かも if (args.Arguments.Length >= 4) { args.Chat.Mail = args.Arguments[1]; args.Chat.Text = args.Arguments[2]; //var name = args.Arguments[3]; _LiveComments.Add(args.Chat); } break; case NicoLiveOperationCommandType.Disconnect: // 放送者側からの切断要請 // Note: RTMPによる動画受信の停止はDisconnect後の // RtmpClient.Closedイベントによって処理されます。 // また、RtmpClientがクローズ中にここでRtmpClient.Close()を行うと // スレッドセーフではないためか、例外が発生します。 await CloseRtmpConnection(); await Task.Delay(500); // 次枠の自動巡回を開始 // await StartNextLiveSubscribe(DefaultNextLiveSubscribeDuration); break; case NicoLiveOperationCommandType.Koukoku: break; case NicoLiveOperationCommandType.Telop: /* * on ニコ生クルーズ(リンク付き)/ニコニコ実況コメント * show クルーズが到着/実況に接続 * show0 実際に流れているコメント * perm ニコ生クルーズが去って行きました<改行>(降りた人の名前、人数) * off (プレイヤー下部のテロップを消去) */ if (args.Arguments.Length >= 2) { // TODO: } break; case NicoLiveOperationCommandType.Hidden: break; case NicoLiveOperationCommandType.CommentLock: break; default: break; } }); }