diff --git a/BLiveIF/BLiveIF.csproj b/BLiveIF/BLiveIF.csproj new file mode 100644 index 00000000..c91b685b --- /dev/null +++ b/BLiveIF/BLiveIF.csproj @@ -0,0 +1,41 @@ + + + net462 + 8.0 + Release;Beta;Alpha;Debug + + + bin\Release\ + TRACE + 4 + pdbonly + true + prompt + 4 + + + bin\Beta\ + TRACE;BETA + 4 + true + pdbonly + AnyCPU + prompt + + + TRACE;DEBUG;ALPHA + 4 + full + true + false + + + DEBUG;TRACE + 4 + full + true + + + + + \ No newline at end of file diff --git a/BLiveIF/Message.cs b/BLiveIF/Message.cs new file mode 100644 index 00000000..8892d4a6 --- /dev/null +++ b/BLiveIF/Message.cs @@ -0,0 +1,70 @@ +using SitePlugin; +using System; +using System.Collections.Generic; + +namespace BLiveSitePlugin +{ + public enum BLiveMessageType + { + Unknown, + Comment, + //Item, + Stamp, + Yell, + Connected, + Disconnected, + } + + + public interface IBLiveMessage : ISiteMessage + { + BLiveMessageType BLiveMessageType { get; } + } + public interface IBLiveConnected : IBLiveMessage + { + string Text { get; } + } + public interface IBLiveDisconnected : IBLiveMessage + { + string Text { get; } + } + public interface IBLiveComment : IBLiveMessage + { + IEnumerable NameItems { get; } + IEnumerable MessageItems { get; } + string Id { get; } + DateTime PostTime { get; } + string UserId { get; } + } + public interface IBLiveStamp : IBLiveMessage + { + IMessageImage Stamp { get; } + string Message { get; } + IEnumerable NameItems { get; set; } + IMessageImage UserIcon { get; } + DateTime PostTime { get; } + string Id { get; } + } + public interface IBLiveYell : IBLiveMessage + { + string YellPoints { get; } + string Message { get; } + IEnumerable NameItems { get; } + IMessageImage UserIcon { get; } + DateTime PostTime { get; } + string Id { get; } + } + //public interface IBLiveItem : IBLiveMessage + //{ + // string ItemName { get; } + // int ItemCount { get; } + // //string Comment { get; } + // long Id { get; } + // //string UserName { get; } + // string UserPath { get; } + // long UserId { get; } + // string AccountName { get; } + // long PostedAt { get; } + // string UserIconUrl { get; } + //} +} \ No newline at end of file diff --git a/BLiveIF/Properties/AssemblyInfo.cs b/BLiveIF/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/BLiveIF/Properties/AssemblyInfo.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/BLiveSitePlugin/API.cs b/BLiveSitePlugin/API.cs new file mode 100644 index 00000000..4fbd7cc8 --- /dev/null +++ b/BLiveSitePlugin/API.cs @@ -0,0 +1,135 @@ +using Codeplex.Data; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace BLiveSitePlugin.Low.BanList +{ + public class Item + { + public string banned_user_id { get; set; } + } + + public class Data + { + public List items { get; set; } + } + + public class RootObject + { + public int status { get; set; } + public Data data { get; set; } + } +} +namespace BLiveSitePlugin +{ + class Me + { + public string DisplayName { get; set; } + public string UserId { get; set; } + } + static class API + { + public static async Task GetMeAsync(IDataSource server, CookieContainer cc) + { + var me = new Me(); + var url = "https://live.carol-i.com"; + var res = await server.GetAsync(url, cc); + var match0 = Regex.Match(res, "
([^<]*)
"); + if (match0.Success) + { + var displayName = match0.Groups[1].Value; + me.DisplayName = displayName; + } + var match1 = Regex.Match(res, "
([^<]*)
"); + if (match1.Success) + { + me.UserId = match1.Groups[1].Value; + } + return me; + } + public static async Task GetMovieInfo(IDataSource dataSource, string liveId, CookieContainer cc) + { + //https://public.blive.tv/external/api/v5/movies/pC8n3HQX5gh + var url = "https://public.blive.tv/external/api/v5/movies/" + liveId; + var ret = await dataSource.GetAsync(url, cc); + var obj = Tools.Deserialize(ret); + return new MovieInfo(obj); + } + public static async Task GetChannelMovies(IDataSource dataSource, string channelId) + { + //https://public.blive.tv/external/api/v5/movies?channel_id=rainbow6jp + var url = "https://public.blive.tv/external/api/v5/movies?channel_id=" + channelId; + var ret = await dataSource.GetAsync(url); + var obj = Tools.Deserialize(ret); + return obj; + } + public static async Task GetMovies(IDataSource dataSource, string channelId) + { + var url = $"https://public.blive.tv/external/api/v5/movies?channel_id={channelId}&sort=onair_status"; + var res = await dataSource.GetAsync(url); + var obj = Tools.Deserialize(res); + return obj; + } + public static async Task<(Low.Chats.RootObject[], string raw)> GetChats(IDataSource dataSource, string liveId, DateTime toCreatedAt, CookieContainer cc) + { + //https://public.blive.tv/external/api/v5/movies/9PgmVnlqtMz/chats?to_created_at=2018-07-24T19:32:50.395Z + var url = "https://public.blive.tv/external/api/v5/movies/" + liveId + "/chats?to_created_at=" + toCreatedAt.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); + var res = await dataSource.GetAsync(url, cc); + var obj = Tools.Deserialize(res); + return (obj, res); + } + } +} +namespace BLiveSitePlugin.Low +{ + public class WebsocketContext2 + { + public string sid { get; set; } + public List upgrades { get; set; } + public int pingInterval { get; set; } + public int pingTimeout { get; set; } + } + public class Item + { + public string user_id { get; set; } + public string user_name { get; set; } + public string user_type { get; set; } + public string user_key { get; set; } + public int user_rank { get; set; } + public string user_icon { get; set; } + public string room_id { get; set; } + public string chat_id { get; set; } + public string message { get; set; } + public string item { get; set; } + public int supporter_rank { get; set; } + public int is_creaters { get; set; } + public string golds { get; set; } + public string cre_dt { get; set; } + public int is_fresh { get; set; } + public int is_warned { get; set; } + public int has_banned_word { get; set; } + public int is_moderator { get; set; } + public int is_premium { get; set; } + public int is_premium_hidden { get; set; } + public string user_color { get; set; } + public string display_dt { get; set; } + public string del_flg { get; set; } + public string quality_type { get; set; } + } + public class Data + { + public List items { get; set; } + } + + public class ChatList + { + public int status { get; set; } + public Data data { get; set; } + } +} \ No newline at end of file diff --git a/BLiveSitePlugin/BLiveCommentData.cs b/BLiveSitePlugin/BLiveCommentData.cs new file mode 100644 index 00000000..50a663ca --- /dev/null +++ b/BLiveSitePlugin/BLiveCommentData.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using SitePlugin; + +namespace BLiveSitePlugin +{ + class BLiveCommentData : IBLiveCommentData + { + public bool IsYell => !string.IsNullOrEmpty(YellPoints); + public string YellPoints { get; set; } + public string Message { get; set; } + public string Id { get; set; } + public string UserId { get; set; } + public DateTime PostTime { get; set; } + public string UserKey { get; set; } + public string UserType { get; set; } + public IMessageImage Stamp { get; set; } + public string Name { get; set; } + public List NameIcons { get; set; } + public TimeSpan Elapsed { get; set; } + public string UserIconUrl { get; set; } + } +} diff --git a/BLiveSitePlugin/BLiveCommentViewModel.cs b/BLiveSitePlugin/BLiveCommentViewModel.cs new file mode 100644 index 00000000..ec1cf8ec --- /dev/null +++ b/BLiveSitePlugin/BLiveCommentViewModel.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Common; +using SitePlugin; + +namespace BLiveSitePlugin +{ + public interface IBLiveCommentViewModel : ICommentViewModel + { + string PostDate { get; } + string Elapsed { get; } + bool IsStamp { get; } + bool IsYell { get; } + } + class BLiveCommentViewModel : CommentViewModelBase, IBLiveCommentViewModel + { + public override MessageType MessageType { get; protected set; } + private ICommentOptions _options; + private readonly IBLiveSiteOptions _siteOptions; + + public string PostDate { get; } + public string Elapsed { get; } + public override string UserId { get; } + public bool IsStamp { get; } + public bool IsYell { get; } + public BLiveCommentViewModel(IBLiveCommentData commentData, ICommentOptions options, IBLiveSiteOptions siteOptions, ICommentProvider commentProvider, bool isFirstComment, IUser user) + : base(options, user, commentProvider, isFirstComment) + { + MessageType = MessageType.Comment; + _options = options; + _siteOptions = siteOptions; + UserId = commentData.UserId; + Id = commentData.Id; + PostDate = commentData.PostTime.ToString("HH:mm:ss"); + var elapsed = commentData.Elapsed; + Elapsed = Tools.ElapsedToString(elapsed); + IsStamp = commentData.Stamp != null; + IsYell = commentData.IsYell; + if (!string.IsNullOrEmpty(commentData.UserIconUrl)) + { + Thumbnail = new MessageImage { Url = commentData.UserIconUrl }; + } + if (siteOptions.IsAutoSetNickname) + { + var nick = ExtractNickname(commentData.Message); + if (!string.IsNullOrEmpty(nick)) + { + user.Nickname = nick; + } + } + //Name + { + var nameItems = new List(); + nameItems.Add(MessagePartFactory.CreateMessageText(commentData.Name)); + nameItems.AddRange(commentData.NameIcons); + NameItemsInternal = nameItems; + } + //Message + { + var messageItems = new List(); + if (commentData.IsYell) + { + MessageType = MessageType.BroadcastInfo; + messageItems.Add(MessagePartFactory.CreateMessageText("エールポイント:" + commentData.YellPoints + Environment.NewLine)); + } + messageItems.Add(MessagePartFactory.CreateMessageText(commentData.Message)); + if (commentData.Stamp != null) + { + MessageType = MessageType.BroadcastInfo; + messageItems.Add(commentData.Stamp); + } + MessageItems = messageItems; + } + Init(); + } + protected virtual void PlaySound(string filePath) + { + var player = new System.Media.SoundPlayer(filePath); + player.Play(); + } + public override async Task AfterCommentAdded() + { + await Task.Yield(); + try + { + if (IsStamp) + { + if (_siteOptions.IsPlayStampMusic && !string.IsNullOrEmpty(_siteOptions.StampMusicFilePath)) + { + PlaySound(_siteOptions.StampMusicFilePath); + } + } + if (IsYell) + { + if (_siteOptions.IsPlayYellMusic && !string.IsNullOrEmpty(_siteOptions.YellMusicFilePath)) + { + PlaySound(_siteOptions.YellMusicFilePath); + } + } + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + } + } + } +} diff --git a/BLiveSitePlugin/BLiveOptionsPanel.xaml b/BLiveSitePlugin/BLiveOptionsPanel.xaml new file mode 100644 index 00000000..1a5ac23f --- /dev/null +++ b/BLiveSitePlugin/BLiveOptionsPanel.xaml @@ -0,0 +1,12 @@ + + + + + diff --git a/BLiveSitePlugin/BLiveOptionsPanel.xaml.cs b/BLiveSitePlugin/BLiveOptionsPanel.xaml.cs new file mode 100644 index 00000000..c094de82 --- /dev/null +++ b/BLiveSitePlugin/BLiveOptionsPanel.xaml.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace BLiveSitePlugin +{ + /// + /// Interaction logic for BLiveOptionsPanel.xaml + /// + public partial class BLiveOptionsPanel : UserControl + { + public BLiveOptionsPanel() + { + InitializeComponent(); + } + public void SetViewModel(BLiveOptionsViewModel vm) + { + this.DataContext = vm; + } + public BLiveOptionsViewModel GetViewModel() + { + return (BLiveOptionsViewModel)this.DataContext; + } + } +} diff --git a/BLiveSitePlugin/BLiveOptionsTabPage.cs b/BLiveSitePlugin/BLiveOptionsTabPage.cs new file mode 100644 index 00000000..1fbd04d1 --- /dev/null +++ b/BLiveSitePlugin/BLiveOptionsTabPage.cs @@ -0,0 +1,28 @@ +using SitePlugin; +using System.Windows.Controls; + +namespace BLiveSitePlugin +{ + public class BLiveOptionsTabPage : IOptionsTabPage + { + public string HeaderText { get; } + + public UserControl TabPagePanel => _panel; + + public void Apply() + { + var optionsVm = _panel.GetViewModel(); + optionsVm.OriginOptions.Set(optionsVm.ChangedOptions); + } + + public void Cancel() + { + } + private readonly BLiveOptionsPanel _panel; + public BLiveOptionsTabPage(string displayName, BLiveOptionsPanel panel) + { + HeaderText = displayName; + _panel = panel; + } + } +} diff --git a/BLiveSitePlugin/BLiveOptionsViewModel.cs b/BLiveSitePlugin/BLiveOptionsViewModel.cs new file mode 100644 index 00000000..a3e8f816 --- /dev/null +++ b/BLiveSitePlugin/BLiveOptionsViewModel.cs @@ -0,0 +1,121 @@ +using GalaSoft.MvvmLight.CommandWpf; +using System; +using System.ComponentModel; +using System.Windows.Input; + +namespace BLiveSitePlugin +{ + public class BLiveOptionsViewModel : INotifyPropertyChanged + { + public ICommand ShowOpenStampMusicSelectorCommand { get; } + public ICommand ShowOpenYellMusicSelectorCommand { get; } + private void ShowOpenStampMusicSelector() + { + var filename = OpenFileDialog("", "音声ファイルを指定して下さい", "waveファイル|*.wav"); + if (!string.IsNullOrEmpty(filename)) + { + StampMusicFilePath = filename; + } + } + private void ShowOpenYellMusicSelector() + { + var filename = OpenFileDialog("", "音声ファイルを指定して下さい", "waveファイル|*.wav"); + if (!string.IsNullOrEmpty(filename)) + { + YellMusicFilePath = filename; + } + } + protected virtual string OpenFileDialog(string defaultPath, string title, string filter) + { + string ret = null; + var fileDialog = new Microsoft.Win32.OpenFileDialog(); + fileDialog.Title = title; + fileDialog.Filter = filter; + var result = fileDialog.ShowDialog(); + if (result == true) + { + ret = fileDialog.FileName; + } + return ret; + } + public int StampSize + { + get { return _changed.StampSize; } + set { _changed.StampSize = value; } + } + public bool IsPlayStampMusic + { + get { return _changed.IsPlayStampMusic; } + set + { + _changed.IsPlayStampMusic = value; + RaisePropertyChanged(); + } + } + public string StampMusicFilePath + { + get { return _changed.StampMusicFilePath; } + set + { + _changed.StampMusicFilePath = value; + RaisePropertyChanged(); + } + } + public bool IsPlayYellMusic + { + get { return _changed.IsPlayYellMusic; } + set + { + _changed.IsPlayYellMusic = value; + RaisePropertyChanged(); + } + } + public string YellMusicFilePath + { + get { return _changed.YellMusicFilePath; } + set + { + _changed.YellMusicFilePath = value; + RaisePropertyChanged(); + } + } + public bool IsAutoSetNickname + { + get { return ChangedOptions.IsAutoSetNickname; } + set { ChangedOptions.IsAutoSetNickname = value; } + } + private readonly BLiveSiteOptions _origin; + private readonly BLiveSiteOptions _changed; + internal BLiveSiteOptions OriginOptions { get { return _origin; } } + internal BLiveSiteOptions ChangedOptions { get { return _changed; } } + + internal BLiveOptionsViewModel(BLiveSiteOptions siteOptions) + { + _origin = siteOptions; + _changed = siteOptions.Clone(); + ShowOpenStampMusicSelectorCommand = new RelayCommand(ShowOpenStampMusicSelector); + ShowOpenYellMusicSelectorCommand = new RelayCommand(ShowOpenYellMusicSelector); + } + + #region INotifyPropertyChanged + [NonSerialized] + private System.ComponentModel.PropertyChangedEventHandler _propertyChanged; + /// + /// + /// + public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged + { + add { _propertyChanged += value; } + remove { _propertyChanged -= value; } + } + /// + /// + /// + /// + protected void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "") + { + _propertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); + } + #endregion + } +} diff --git a/BLiveSitePlugin/BLiveSiteContext.cs b/BLiveSitePlugin/BLiveSiteContext.cs new file mode 100644 index 00000000..bf8ebb6e --- /dev/null +++ b/BLiveSitePlugin/BLiveSiteContext.cs @@ -0,0 +1,97 @@ +using System; +using System.Linq; +using System.Text; +using SitePlugin; +using Common; +using System.Windows.Controls; +using System.Diagnostics; +using SitePluginCommon; + +namespace BLiveSitePlugin +{ + public class BLiveSiteContext : SiteContextBase + { + public override Guid Guid => new Guid("F4434012-3E68-4DD9-B2A8-F2BD7D601725"); + + public override string DisplayName => "B-LIVE"; + protected override SiteType SiteType => SiteType.BLive; + public override IOptionsTabPage TabPanel + { + get + { + var panel = new BLiveOptionsPanel(); + panel.SetViewModel(new BLiveOptionsViewModel(_siteOptions)); + return new BLiveOptionsTabPage(DisplayName, panel); + } + } + + public override ICommentProvider CreateCommentProvider() + { + return new CommentProvider(_options, _siteOptions, _logger, _userStoreManager) + { + SiteContextGuid = Guid, + }; + } + + public override UserControl GetCommentPostPanel(ICommentProvider commentProvider) + { + var nicoCommentProvider = commentProvider as CommentProvider; + Debug.Assert(nicoCommentProvider != null); + if (nicoCommentProvider == null) + return null; + + var vm = new CommentPostPanelViewModel(nicoCommentProvider, _logger); + var panel = new CommentPostPanel + { + //IsEnabled = false, + DataContext = vm + }; + return panel; + } + + public override bool IsValidInput(string input) + { + return Tools.IsValidUrl(input); + } + + public override void LoadOptions(string path, IIo io) + { + _siteOptions = new BLiveSiteOptions(); + try + { + var s = io.ReadFile(path); + + _siteOptions.Deserialize(s); + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + _logger.LogException(ex, "", $"path={path}"); + } + } + + public override void SaveOptions(string path, IIo io) + { + try + { + var s = _siteOptions.Serialize(); + io.WriteFile(path, s); + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + _logger.LogException(ex, "", path); + } + } + private BLiveSiteOptions _siteOptions; + private ICommentOptions _options; + private ILogger _logger; + + public BLiveSiteContext(ICommentOptions options, ILogger logger, IUserStoreManager userStoreManager) + : base(options,userStoreManager, logger) + { + _options = options; + _logger = logger; + } + } +} diff --git a/BLiveSitePlugin/BLiveSiteOptions.cs b/BLiveSitePlugin/BLiveSiteOptions.cs new file mode 100644 index 00000000..ef9275e7 --- /dev/null +++ b/BLiveSitePlugin/BLiveSiteOptions.cs @@ -0,0 +1,100 @@ +using Common; +using System; +using System.ComponentModel; +using System.Linq; +using System.Windows; +using System.Windows.Media; + +namespace BLiveSitePlugin +{ + public interface IBLiveSiteOptions: INotifyPropertyChanged + { + int StampSize { get; } + bool IsPlayStampMusic { get; } + string StampMusicFilePath { get; } + bool IsPlayYellMusic { get; } + string YellMusicFilePath { get; } + bool IsAutoSetNickname { get; } + } + internal class BLiveSiteOptions : DynamicOptionsBase, IBLiveSiteOptions + { + public int StampSize { get => GetValue(); set => SetValue(value); } + public bool IsPlayStampMusic { get => GetValue(); set => SetValue(value); } + public string StampMusicFilePath { get => GetValue(); set => SetValue(value); } + public bool IsPlayYellMusic { get => GetValue(); set => SetValue(value); } + public string YellMusicFilePath { get => GetValue(); set => SetValue(value); } + public bool IsAutoSetNickname { get => GetValue(); set => SetValue(value); } + protected override void Init() + { + Dict.Add(nameof(StampSize), new Item { DefaultValue = 64, Predicate = n => n > 0, Serializer = n => n.ToString(), Deserializer = s => int.Parse(s) }); + Dict.Add(nameof(IsPlayStampMusic), new Item { DefaultValue = false, Predicate = b => true, Serializer = b => b.ToString(), Deserializer = s => bool.Parse(s) }); + Dict.Add(nameof(StampMusicFilePath), new Item { DefaultValue = "", Predicate = s => !string.IsNullOrEmpty(s), Serializer = s => s, Deserializer = s => s }); + Dict.Add(nameof(IsPlayYellMusic), new Item { DefaultValue = false, Predicate = b => true, Serializer = b => b.ToString(), Deserializer = s => bool.Parse(s) }); + Dict.Add(nameof(YellMusicFilePath), new Item { DefaultValue = "", Predicate = s => !string.IsNullOrEmpty(s), Serializer = s => s, Deserializer = s => s }); + Dict.Add(nameof(IsAutoSetNickname), new Item { DefaultValue = false, Predicate = b => true, Serializer = b => b.ToString(), Deserializer = s => bool.Parse(s) }); + } + internal BLiveSiteOptions Clone() + { + return (BLiveSiteOptions)this.MemberwiseClone(); + } + internal void Set(BLiveSiteOptions changedOptions) + { + foreach (var src in changedOptions.Dict) + { + var v = src.Value; + SetValue(v.Value, src.Key); + } + } + #region Converters + private FontFamily FontFamilyFromString(string str) + { + return new FontFamily(str); + } + private string FontFamilyToString(FontFamily family) + { + return family.FamilyNames.Values.First(); + } + private FontStyle FontStyleFromString(string str) + { + return (FontStyle)new FontStyleConverter().ConvertFromString(str); + } + private string FontStyleToString(FontStyle style) + { + return new FontStyleConverter().ConvertToString(style); + } + private FontWeight FontWeightFromString(string str) + { + return (FontWeight)new FontWeightConverter().ConvertFromString(str); + } + private string FontWeightToString(FontWeight weight) + { + return new FontWeightConverter().ConvertToString(weight); + } + private Color ColorFromArgb(string argb) + { + if (argb == null) + throw new ArgumentNullException("argb"); + var pattern = "#(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})"; + var match = System.Text.RegularExpressions.Regex.Match(argb, pattern, System.Text.RegularExpressions.RegexOptions.Compiled); + + if (!match.Success) + { + throw new ArgumentException("形式が不正"); + } + else + { + var a = byte.Parse(match.Groups["a"].Value, System.Globalization.NumberStyles.HexNumber); + var r = byte.Parse(match.Groups["r"].Value, System.Globalization.NumberStyles.HexNumber); + var g = byte.Parse(match.Groups["g"].Value, System.Globalization.NumberStyles.HexNumber); + var b = byte.Parse(match.Groups["b"].Value, System.Globalization.NumberStyles.HexNumber); + return Color.FromArgb(a, r, g, b); + } + } + private string ColorToArgb(Color color) + { + var argb = color.ToString(); + return argb; + } + #endregion + } +} diff --git a/BLiveSitePlugin/BLiveSitePlugin.csproj b/BLiveSitePlugin/BLiveSitePlugin.csproj new file mode 100644 index 00000000..7ae15299 --- /dev/null +++ b/BLiveSitePlugin/BLiveSitePlugin.csproj @@ -0,0 +1,78 @@ + + + true + net462 + 8.0 + Release;Beta;Alpha;Debug + true + + + bin\Release\ + TRACE + 4 + pdbonly + true + prompt + 4 + + + bin\Beta\ + TRACE;BETA + 4 + true + pdbonly + AnyCPU + prompt + + + TRACE;DEBUG;ALPHA + 4 + full + true + false + + + DEBUG;TRACE + 4 + full + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + \ No newline at end of file diff --git a/BLiveSitePlugin/BLiveWebsocket.cs b/BLiveSitePlugin/BLiveWebsocket.cs new file mode 100644 index 00000000..fa4c300d --- /dev/null +++ b/BLiveSitePlugin/BLiveWebsocket.cs @@ -0,0 +1,105 @@ +using System; +using System.Threading.Tasks; +using System.Diagnostics; +using System.Collections.Generic; +using System.Net; +using Common; + +namespace BLiveSitePlugin +{ + class BLiveWebsocket : IBLiveWebsocket + { + private Websocket _websocket; + private readonly ILogger _logger; + //public event EventHandler CommentReceived; + public event EventHandler Received; + + public async Task ReceiveAsync(string liveId, string userAgent, List cookies) + { + var cookieList = new List>(); + + var origin = "https://live.carol-i.com"; + + _websocket = new Websocket(); + _websocket.Received += Websocket_Received; + // liveIdを渡すためのラムダ式でOpenedイベントを設定 + _websocket.Opened += (sender, e) => Websocket_Opened(sender, e, liveId); + + var url = $"wss://live.carol-i.com:6001/socket.io/?EIO=3&transport=websocket"; + await _websocket.ReceiveAsync(url, cookieList, userAgent, origin); + //切断後処理 + _heartbeatTimer.Enabled = false; + + } + + private void Websocket_Opened(object sender, EventArgs e, string liveId) + { + _heartbeatTimer.Enabled = true; + + // WebSocketが開かれた後にチャンネルにサブスクライブする + string subscribeMessage = $"42[\"subscribe\", {{\"channel\": \"message.received.{liveId}\"}}]"; + Task.Run(async () => await SendAsync(subscribeMessage)); + } + + private void Websocket_Received(object sender, string e) + { + Debug.WriteLine(e); + IPacket packet = null; + try + { + packet = Packet.Parse(e); + } + catch (ParseException ex) + { + _logger.LogException(ex); + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + } + if (packet == null) + return; + Received?.Invoke(this, packet); + } + + public async Task SendAsync(IPacket packet) + { + if (packet is PacketPing ping) + { + await SendAsync(ping.Raw); + } + else + { + throw new NotImplementedException(); + } + } + public async Task SendAsync(string s) + { + await _websocket.SendAsync(s); + } + System.Timers.Timer _heartbeatTimer = new System.Timers.Timer(); + public BLiveWebsocket(ILogger logger) + { + _logger = logger; + _heartbeatTimer.Interval = 25 * 1000; + _heartbeatTimer.Elapsed += _heartBeatTimer_Elapsed; + _heartbeatTimer.AutoReset = true; + } + private async void _heartBeatTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { + try + { + await SendAsync(new PacketPing()); + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + } + } + + public void Disconnect() + { + _websocket.Disconnect(); + } + } +} diff --git a/BLiveSitePlugin/CommentPostPanel.xaml b/BLiveSitePlugin/CommentPostPanel.xaml new file mode 100644 index 00000000..5609c5dc --- /dev/null +++ b/BLiveSitePlugin/CommentPostPanel.xaml @@ -0,0 +1,16 @@ + + + +