public TwitchStream(string baseURL, ServiceEnum serviceType, Stream stream, ILiveBotUser user, ILiveBotGame game) : base(baseURL, serviceType) { UserId = user.Id; User = user; Id = stream.Id; Title = stream.Title; StartTime = stream.StartedAt; GameId = game.Id; Game = game; ThumbnailURL = stream.ThumbnailUrl; StreamURL = $"{User.ProfileURL}"; }
/// <summary> /// Generates a Discord Embed for the given <paramref name="stream"/> /// </summary> /// <param name="stream"></param> /// <returns>Discord Embed with Stream Information</returns> public static Embed GetStreamEmbed(ILiveBotStream stream, ILiveBotUser user, ILiveBotGame game) { // Build the Author of the Embed EmbedAuthorBuilder authorBuilder = new EmbedAuthorBuilder(); authorBuilder.WithName(user.DisplayName); authorBuilder.WithIconUrl(user.AvatarURL); authorBuilder.WithUrl(user.ProfileURL); // Build the Footer of the Embed EmbedFooterBuilder footerBuilder = new EmbedFooterBuilder(); footerBuilder.WithText("Stream start time"); // Add Basic information to EmbedBuilder EmbedBuilder builder = new EmbedBuilder(); builder.WithColor(Color.DarkPurple); builder.WithAuthor(authorBuilder); builder.WithFooter(footerBuilder); builder.WithTimestamp(stream.StartTime); builder.WithDescription(stream.Title); builder.WithUrl(stream.StreamURL); builder.WithThumbnailUrl(user.AvatarURL); // Add Status Field //EmbedFieldBuilder statusBuilder = new EmbedFieldBuilder(); //statusBuilder.WithIsInline(false); //statusBuilder.WithName("Status"); //statusBuilder.WithValue(""); //builder.AddField(statusBuilder); // Add Game field EmbedFieldBuilder gameBuilder = new EmbedFieldBuilder(); gameBuilder.WithIsInline(true); gameBuilder.WithName("Game"); gameBuilder.WithValue(game.Name); builder.AddField(gameBuilder); // Add Stream URL field EmbedFieldBuilder streamURLField = new EmbedFieldBuilder(); streamURLField.WithIsInline(true); streamURLField.WithName("Stream"); streamURLField.WithValue(stream.StreamURL); builder.AddField(streamURLField); return(builder.Build()); }
public async Task <RuntimeResult> MonitorStop_RequestURL() { string profileURL = await _RequestStreamUser(); if (profileURL == null) { return(MonitorResult.FromError($"{Context.Message.Author.Mention}, Please provide a valid Stream URL for me to stop monitoring")); } ILiveBotMonitor monitor = _GetServiceMonitor(profileURL); ILiveBotUser user = await monitor.GetUser(profileURL : profileURL); return(await MonitorStop(user)); }
public async Task <RuntimeResult> MonitorStop(ILiveBotUser user) { ILiveBotMonitor monitor = _GetServiceMonitor(user); DiscordGuild discordGuild = new DiscordGuild { DiscordId = Context.Guild.Id, Name = Context.Guild.Name, IconUrl = Context.Guild.IconUrl }; await _work.GuildRepository.AddOrUpdateAsync(discordGuild, g => g.DiscordId == Context.Guild.Id); discordGuild = await _work.GuildRepository.SingleOrDefaultAsync(g => g.DiscordId == Context.Guild.Id); StreamUser streamUser = new StreamUser() { ServiceType = user.ServiceType, SourceID = user.Id, Username = user.Username, DisplayName = user.DisplayName, AvatarURL = user.AvatarURL, ProfileURL = user.ProfileURL }; await _work.UserRepository.AddOrUpdateAsync(streamUser, (i => i.ServiceType == user.ServiceType && i.SourceID == user.Id)); streamUser = await _work.UserRepository.SingleOrDefaultAsync(i => i.ServiceType == user.ServiceType && i.SourceID == user.Id); Expression <Func <StreamSubscription, bool> > streamSubscriptionPredicate = (i => i.User == streamUser && i.DiscordGuild == discordGuild ); IEnumerable <StreamSubscription> streamSubscriptions = await _work.SubscriptionRepository.FindAsync(streamSubscriptionPredicate); try { foreach (StreamSubscription streamSubscription in streamSubscriptions) { await _work.SubscriptionRepository.RemoveAsync(streamSubscription.Id); } await ReplyAsync($"{Context.Message.Author.Mention}, I have removed the Subscription for {user.DisplayName}"); return(MonitorResult.FromSuccess()); } catch (Exception e) { Log.Error($"Error running MonitorStop for {Context.Message.Author.Id} {Context.Message.Author.Username}#{Context.Message.Author.Discriminator} GuildID: {Context.Guild.Id} ChannelID: {Context.Channel.Id}\n{e}"); return(MonitorResult.FromSuccess($"{Context.Message.Author.Mention}, I couldn't remove the Subscription for user {user.DisplayName}. Please try again or contact my owner")); } }
public async Task CheckStream(ILiveBotUser user) { ILiveBotMonitor monitor = _GetServiceMonitor(user); ILiveBotStream stream = await monitor.GetStream(user); if (stream == null) { await ReplyAsync($"Doesn't look like the user {user.DisplayName} is live on {user.ServiceType}"); return; } Embed streamEmbed = NotificationHelpers.GetStreamEmbed(stream: stream, user: stream.User, game: stream.Game); StreamSubscription bogusSubscription = new StreamSubscription() { Message = Defaults.NotificationMessage }; string notificationMessage = NotificationHelpers.GetNotificationMessage(stream, bogusSubscription); await ReplyAsync(message : $"Service: `{stream.ServiceType}`\n{notificationMessage}", embed : streamEmbed); }
public async Task <RuntimeResult> MonitorStart(ILiveBotUser user) { ILiveBotMonitor monitor = _GetServiceMonitor(user); DiscordGuild discordGuild = new DiscordGuild { DiscordId = Context.Guild.Id, Name = Context.Guild.Name, IconUrl = Context.Guild.IconUrl }; await _work.GuildRepository.AddOrUpdateAsync(discordGuild, g => g.DiscordId == Context.Guild.Id); discordGuild = await _work.GuildRepository.SingleOrDefaultAsync(g => g.DiscordId == Context.Guild.Id); // Get Notification Channel DiscordChannel discordChannel = await _RequestNotificationChannel(discordGuild); if (discordChannel == null) { return(MonitorResult.FromError($"{Context.Message.Author.Mention}, Please re-run the command and be sure to mention a channel.")); } // Get Notification Message string notificationMessage = await _RequestNotificationMessage(); if (notificationMessage == null) { return(MonitorResult.FromError($"{Context.Message.Author.Mention}, Please re-run the command and provide a valid message for notifications")); } // Get Notification Role DiscordRole discordRole = await _RequestNotificationRole(discordGuild); // Process their answers StreamUser streamUser = new StreamUser() { ServiceType = user.ServiceType, SourceID = user.Id, Username = user.Username, DisplayName = user.DisplayName, AvatarURL = user.AvatarURL, ProfileURL = user.ProfileURL }; await _work.UserRepository.AddOrUpdateAsync(streamUser, (i => i.ServiceType == user.ServiceType && i.SourceID == user.Id)); streamUser = await _work.UserRepository.SingleOrDefaultAsync(i => i.ServiceType == user.ServiceType && i.SourceID == user.Id); Expression <Func <StreamSubscription, bool> > streamSubscriptionPredicate = (i => i.User == streamUser && i.DiscordGuild == discordGuild ); StreamSubscription newSubscription = new StreamSubscription() { User = streamUser, DiscordGuild = discordGuild, DiscordChannel = discordChannel, DiscordRole = discordRole, Message = notificationMessage }; try { StreamSubscription existingSubscription = await _work.SubscriptionRepository.SingleOrDefaultAsync(streamSubscriptionPredicate); if (existingSubscription != null) { existingSubscription.DiscordGuild = newSubscription.DiscordGuild; existingSubscription.DiscordChannel = newSubscription.DiscordChannel; existingSubscription.DiscordRole = newSubscription.DiscordRole; existingSubscription.Message = newSubscription.Message; await _work.SubscriptionRepository.UpdateAsync(existingSubscription); } else { await _work.SubscriptionRepository.AddOrUpdateAsync(newSubscription, streamSubscriptionPredicate); } StreamSubscription streamSubscription = await _work.SubscriptionRepository.SingleOrDefaultAsync(streamSubscriptionPredicate); monitor.AddChannel(user); string escapedMessage = NotificationHelpers.EscapeSpecialDiscordCharacters(streamSubscription.Message); await ReplyAsync($"{Context.Message.Author.Mention}, I have setup a subscription for {user.DisplayName} on {user.ServiceType} with message {escapedMessage}"); return(MonitorResult.FromSuccess()); } catch (Exception e) { Log.Error($"Error running MonitorStart for {Context.Message.Author.Id} {Context.Message.Author.Username}#{Context.Message.Author.Discriminator} GuildID: {Context.Guild.Id} ChannelID: {Context.Channel.Id}\n{e}"); return(MonitorResult.FromError($"{Context.Message.Author.Mention}, I wasn't able to create a subscription for the user {user.DisplayName} on {user.ServiceType}. Please try again or contact my owner")); } }
public async Task Consume(ConsumeContext <IStreamOnline> context) { ILiveBotStream stream = context.Message.Stream; ILiveBotMonitor monitor = _monitors.Where(i => i.ServiceType == stream.ServiceType).FirstOrDefault(); if (monitor == null) { return; } ILiveBotUser user = stream.User ?? await monitor.GetUserById(stream.UserId); ILiveBotGame game = stream.Game ?? await monitor.GetGame(stream.GameId); Expression <Func <StreamGame, bool> > templateGamePredicate = (i => i.ServiceType == stream.ServiceType && i.SourceId == "0"); var templateGame = await _work.GameRepository.SingleOrDefaultAsync(templateGamePredicate); var streamUser = await _work.UserRepository.SingleOrDefaultAsync(i => i.ServiceType == stream.ServiceType && i.SourceID == user.Id); var streamSubscriptions = await _work.SubscriptionRepository.FindAsync(i => i.User == streamUser); StreamGame streamGame; if (game.Id == "0" || string.IsNullOrEmpty(game.Id)) { if (templateGame == null) { StreamGame newStreamGame = new StreamGame { ServiceType = stream.ServiceType, SourceId = "0", Name = "[Not Set]", ThumbnailURL = "" }; await _work.GameRepository.AddOrUpdateAsync(newStreamGame, templateGamePredicate); templateGame = await _work.GameRepository.SingleOrDefaultAsync(templateGamePredicate); } streamGame = templateGame; } else { StreamGame newStreamGame = new StreamGame { ServiceType = stream.ServiceType, SourceId = game.Id, Name = game.Name, ThumbnailURL = game.ThumbnailURL }; await _work.GameRepository.AddOrUpdateAsync(newStreamGame, i => i.ServiceType == stream.ServiceType && i.SourceId == stream.GameId); streamGame = await _work.GameRepository.SingleOrDefaultAsync(i => i.ServiceType == stream.ServiceType && i.SourceId == stream.GameId); } if (streamSubscriptions.Count() == 0) { return; } foreach (StreamSubscription streamSubscription in streamSubscriptions) { if (streamSubscription.DiscordGuild == null || streamSubscription.DiscordChannel == null) { await _work.SubscriptionRepository.RemoveAsync(streamSubscription.Id); continue; } var discordChannel = streamSubscription.DiscordChannel; var discordRole = streamSubscription.DiscordRole; var discordGuild = streamSubscription.DiscordGuild; var guild = _client.GetGuild(streamSubscription.DiscordGuild.DiscordId); SocketTextChannel channel = (SocketTextChannel)_client.GetChannel(streamSubscription.DiscordChannel.DiscordId);; string notificationMessage = NotificationHelpers.GetNotificationMessage(stream: stream, subscription: streamSubscription, user: user, game: game); Embed embed = NotificationHelpers.GetStreamEmbed(stream: stream, user: user, game: game); StreamNotification newStreamNotification = new StreamNotification { ServiceType = stream.ServiceType, Success = false, Message = notificationMessage, User_SourceID = streamUser.SourceID, User_Username = streamUser.Username, User_DisplayName = streamUser.DisplayName, User_AvatarURL = streamUser.AvatarURL, User_ProfileURL = streamUser.ProfileURL, Stream_SourceID = stream.Id, Stream_Title = stream.Title, Stream_StartTime = stream.StartTime, Stream_ThumbnailURL = stream.ThumbnailURL, Stream_StreamURL = stream.StreamURL, Game_SourceID = streamGame?.SourceId, Game_Name = streamGame?.Name, Game_ThumbnailURL = streamGame?.ThumbnailURL, DiscordGuild_DiscordId = discordGuild.DiscordId, DiscordGuild_Name = discordGuild.Name, DiscordChannel_DiscordId = discordChannel.DiscordId, DiscordChannel_Name = discordChannel.Name, DiscordRole_DiscordId = discordRole == null ? 0 : discordRole.DiscordId, DiscordRole_Name = discordRole?.Name }; Expression <Func <StreamNotification, bool> > notificationPredicate = (i => i.User_SourceID == newStreamNotification.User_SourceID && i.Stream_SourceID == newStreamNotification.Stream_SourceID && i.Stream_StartTime == newStreamNotification.Stream_StartTime && i.DiscordGuild_DiscordId == newStreamNotification.DiscordGuild_DiscordId && i.DiscordChannel_DiscordId == newStreamNotification.DiscordChannel_DiscordId && i.Game_SourceID == newStreamNotification.Game_SourceID ); Expression <Func <StreamNotification, bool> > previousNotificationPredicate = (i => i.User_SourceID == streamUser.SourceID && i.DiscordGuild_DiscordId == discordGuild.DiscordId && i.DiscordChannel_DiscordId == discordChannel.DiscordId ); var previousNotifications = await _work.NotificationRepository.FindAsync(previousNotificationPredicate); previousNotifications = previousNotifications.Where(i => stream.StartTime.Subtract(i.Stream_StartTime).TotalMinutes <= 60 && // If within an hour of their last start time i.Success == true // Only pull Successful notifications ); // If there is already 1 or more notifications that were successful in the past hour // mark this current one as a success if (previousNotifications.Count() > 0) { newStreamNotification.Success = true; } // Do some checks to see if the channel can be found. If not, just mark as a success // and move on int channelCheckCount = 0; while (channel == null && _client.LoginState == LoginState.LoggedIn) { if (channelCheckCount >= 12) // Ends up being 10 seconds { string errorMessage = $"Unable to get a Discord Channel for {streamSubscription.DiscordChannel.DiscordId} after {channelCheckCount} attempts"; Log.Error(errorMessage); newStreamNotification.Success = true; var streamSubscriptionJSON = JsonConvert.SerializeObject(streamSubscription); newStreamNotification.LogMessage = $"{errorMessage}\n{streamSubscriptionJSON}"; break; } channel = (SocketTextChannel)_client.GetChannel(streamSubscription.DiscordChannel.DiscordId); channelCheckCount += 1; await Task.Delay(TimeSpan.FromSeconds(5)); // Delay check for 5 seconds } await _work.NotificationRepository.AddOrUpdateAsync(newStreamNotification, notificationPredicate); StreamNotification streamNotification = await _work.NotificationRepository.SingleOrDefaultAsync(notificationPredicate); // If the current notification was marked as a success, end processing if (streamNotification.Success == true) { continue; } try { var discordMessage = await channel.SendMessageAsync(text : notificationMessage, embed : embed); streamNotification.DiscordMessage_DiscordId = discordMessage.Id; streamNotification.Success = true; await _work.NotificationRepository.UpdateAsync(streamNotification); } catch (Exception e) { if (e is HttpException) { HttpException discordError = (HttpException)e; // You lack permissions to perform that action if (discordError.DiscordCode == 50013 || discordError.DiscordCode == 50001) { // I'm tired of seeing errors for Missing Permissions continue; } } else { Log.Error($"Error sending notification for {streamNotification.Id} {streamNotification.ServiceType} {streamNotification.User_Username} {streamNotification.DiscordGuild_DiscordId} {streamNotification.DiscordChannel_DiscordId} {streamNotification.DiscordRole_DiscordId} {streamNotification.Message}\n{e}"); } } } }
/// <summary> /// Formats a notification string with the necessary parameters /// </summary> /// <param name="stream"></param> /// <param name="message"></param> /// <returns></returns> public static string GetNotificationMessage(ILiveBotStream stream, StreamSubscription subscription, ILiveBotUser user = null, ILiveBotGame game = null) { string RoleMention = ""; if (subscription.DiscordRole != null) { if (subscription.DiscordRole.Name == "@everyone") { RoleMention = "@everyone"; } else { RoleMention = MentionUtils.MentionRole(subscription.DiscordRole.DiscordId); } } var tempUser = user ?? stream.User; var tempGame = game ?? stream.Game; return(subscription.Message .Replace("{Name}", EscapeSpecialDiscordCharacters(tempUser.DisplayName), ignoreCase: true, culture: CultureInfo.CurrentCulture) .Replace("{Username}", EscapeSpecialDiscordCharacters(tempUser.DisplayName), ignoreCase: true, culture: CultureInfo.CurrentCulture) .Replace("{Game}", EscapeSpecialDiscordCharacters(tempGame.Name), ignoreCase: true, culture: CultureInfo.CurrentCulture) .Replace("{Title}", EscapeSpecialDiscordCharacters(stream.Title), ignoreCase: true, culture: CultureInfo.CurrentCulture) .Replace("{URL}", stream.StreamURL ?? "", ignoreCase: true, culture: CultureInfo.CurrentCulture) .Replace("{Role}", RoleMention, ignoreCase: true, culture: CultureInfo.CurrentCulture) .Trim()); }
public async Task Consume(ConsumeContext <IStreamUpdate> context) { ILiveBotStream stream = context.Message.Stream; ILiveBotMonitor monitor = _monitors.Where(i => i.ServiceType == stream.ServiceType).FirstOrDefault(); if (monitor == null) { return; } ILiveBotUser user = stream.User ?? await monitor.GetUserById(stream.UserId); ILiveBotGame game = stream.Game ?? await monitor.GetGame(stream.GameId); Expression <Func <StreamGame, bool> > templateGamePredicate = (i => i.ServiceType == stream.ServiceType && i.SourceId == "0"); var templateGame = await _work.GameRepository.SingleOrDefaultAsync(templateGamePredicate); var streamUser = await _work.UserRepository.SingleOrDefaultAsync(i => i.ServiceType == stream.ServiceType && i.SourceID == user.Id); var streamSubscriptions = await _work.SubscriptionRepository.FindAsync(i => i.User == streamUser); StreamGame streamGame; if (game.Id == "0" || string.IsNullOrEmpty(game.Id)) { if (templateGame == null) { StreamGame newStreamGame = new StreamGame { ServiceType = stream.ServiceType, SourceId = "0", Name = "[Not Set]", ThumbnailURL = "" }; await _work.GameRepository.AddOrUpdateAsync(newStreamGame, templateGamePredicate); templateGame = await _work.GameRepository.SingleOrDefaultAsync(templateGamePredicate); } streamGame = templateGame; } else { StreamGame newStreamGame = new StreamGame { ServiceType = stream.ServiceType, SourceId = game.Id, Name = game.Name, ThumbnailURL = game.ThumbnailURL }; await _work.GameRepository.AddOrUpdateAsync(newStreamGame, i => i.ServiceType == stream.ServiceType && i.SourceId == stream.GameId); streamGame = await _work.GameRepository.SingleOrDefaultAsync(i => i.ServiceType == stream.ServiceType && i.SourceId == stream.GameId); } if (streamSubscriptions.Count() == 0) { return; } List <StreamSubscription> unsentSubscriptions = new List <StreamSubscription>(); foreach (StreamSubscription streamSubscription in streamSubscriptions) { if (streamSubscription.DiscordGuild == null || streamSubscription.DiscordChannel == null) { await _work.SubscriptionRepository.RemoveAsync(streamSubscription.Id); continue; } var discordChannel = streamSubscription.DiscordChannel; var discordRole = streamSubscription.DiscordRole; var discordGuild = streamSubscription.DiscordGuild; Expression <Func <StreamNotification, bool> > previousNotificationPredicate = (i => i.User_SourceID == streamUser.SourceID && i.DiscordGuild_DiscordId == discordGuild.DiscordId && i.DiscordChannel_DiscordId == discordChannel.DiscordId && i.Stream_StartTime == stream.StartTime && i.Stream_SourceID == stream.Id && i.Success == true ); var previousNotifications = await _work.NotificationRepository.FindAsync(previousNotificationPredicate); if (previousNotifications.Count() > 0) { continue; } unsentSubscriptions.Add(streamSubscription); } if (unsentSubscriptions.Count() > 0) { await _bus.Publish <IStreamOnline>(new { Stream = stream }); } }