private async Task ReactionReply(SocketGuildUser user, IReadOnlyCollection <WarnedUser> warnings, Embed embed, int warnCount, Server server, string reason) { Emoji[] emojis = GlobalProperties.EmojisOneThroughNine(); var data = new ReactionCallbackData("", embed, false, false, TimeSpan.FromSeconds(300)); var callbacks = new List <(IEmote, Func <SocketCommandContext, SocketReaction, Task>)>(); for (int j = 0; j < warnCount; j++) { int j1 = j; callbacks.Add((emojis[j], async(c, r) => { var uwArgs = new ModeratorEventArgs(server, Context.Guild, user, (SocketGuildUser)Context.User, reason, null); KaguyaEvents.TriggerUnwarn(uwArgs); await DatabaseQueries.DeleteAsync(warnings.ElementAt(j1)); await c.Channel.SendMessageAsync($"{r.User.Value.Mention} " + $"`Successfully removed warning #{j1 + 1}`"); })); } data.SetCallbacks(callbacks); await InlineReactionReplyAsync(data); }
private async Task MultipleMatchingRolesHandler(SocketGuild guild, string roleName, IReadOnlyCollection <SocketRole> roles) { List <SocketRole> matchingRoles = roles.Where(x => x.Name.ToLower() == roleName.ToLower()).ToList(); int matchCount = matchingRoles.Count; if (matchCount > 9) { matchCount = 9; } var emojis = new Emoji[] { new Emoji("1⃣"), new Emoji("2⃣"), new Emoji("3⃣"), new Emoji("4⃣"), new Emoji("5⃣"), new Emoji("6⃣"), new Emoji("7⃣"), new Emoji("8⃣"), new Emoji("9⃣") }; var embed = new KaguyaEmbedBuilder { Description = $"I found `{matchCount.ToWords()}` roles that match this name. Please " + $"select the role that you want to display.", Fields = new List <EmbedFieldBuilder>() }; var callbacks = new List <(IEmote, Func <SocketCommandContext, SocketReaction, Task>)>(); for (int i = 0; i < matchCount; i++) { int i1 = i; if (i1 == matchCount) { i1 = matchCount - 1; } SocketRole role = matchingRoles.ElementAt(i1); List <GuildPermission> rolePerms = matchingRoles[i].Permissions.ToList(); IEnumerable <SocketGuildUser> usersWithRole = guild.Users.Where(x => x.Roles.Contains(role)); embed.Fields.Add(new EmbedFieldBuilder { Name = $"Role #{i + 1}", Value = $"Exact Name: `{role.Name}`\nNumber of users who have the role: {usersWithRole.Count()}" + $"\nPermissions: `{rolePerms.Count}`\n" + $"Created: `{role.CreatedAt.Humanize()}`\n" + $"Position in role list (higher number = higher position): `{role.Position}`" }); callbacks.Add((emojis[i], async(c, r) => { var pager = new PaginatedMessage { Pages = Pages(guild, role), Color = Color.Blue }; await PagedReplyAsync(pager, new ReactionList { Backward = true, First = true, Forward = true, Jump = true, Last = true, Trash = true }); } )); } var data = new ReactionCallbackData("", embed.Build(), false, false, TimeSpan.FromSeconds(120), c => c.Channel.SendMessageAsync("Role selection has timed out. Please try again.")); data.SetCallbacks(callbacks); await InlineReactionReplyAsync(data); }
public async Task RemoveRole([Remainder] string targetRole) { var roles = new List <SocketRole>(); roles = Context.Guild.Roles.Where(r => r.Name.ToLower() == targetRole.ToLower()).ToList(); if (roles.Count > 1) { var emojis = new Emoji[] { new Emoji("1⃣"), new Emoji("2⃣"), new Emoji("3⃣"), new Emoji("4⃣"), new Emoji("5⃣"), new Emoji("6⃣"), new Emoji("7⃣"), new Emoji("8⃣"), new Emoji("9⃣") }; var embed = new KaguyaEmbedBuilder { Description = $"I found `{roles.Count.ToWords()}` roles that match this name. Please " + $"select the role that you want to delete, or use the ⛔ reaction " + $"to delete all roles with this name.", Fields = new List <EmbedFieldBuilder>() }; var callbacks = new List <(IEmote, Func <SocketCommandContext, SocketReaction, Task>)>(); for (int i = 0; i < roles.Count; i++) { int roleIndex = i + 1; SocketRole role = roles.ElementAt(i); embed.Fields.Add(new EmbedFieldBuilder { Name = $"Role #{roleIndex}", Value = $"Exact Name: `{role.Name}`\n" + $"Number of users who have this role: " + $"`{Context.Guild.Users.Count(x => x.Roles.Contains(role))}`\n" + $"Permissions: `{roles.Count}`\n" + $"Created: `{role.CreatedAt.Humanize()}`\n" + $"Position in role list (higher number = higher position): `{role.Position}`" }); callbacks.Add((emojis[i], async(c, r) => { await role.DeleteAsync(); await ReplyAsync($"{Context.User.Mention} `Successfully deleted Role #{roleIndex}`"); } )); } callbacks.Add((new Emoji("⛔"), async(c, r) => { foreach (SocketRole role in roles) { await role.DeleteAsync(); } await ReplyAsync($"{Context.User.Mention} Successfully deleted `{roles.Count.ToWords()}` roles."); } )); var data = new ReactionCallbackData("", embed.Build(), false, false, TimeSpan.FromSeconds(120)); data.SetCallbacks(callbacks); await InlineReactionReplyAsync(data); } else if (roles.Count == 1) { SocketRole role = roles.First(); var embed = new KaguyaEmbedBuilder { Description = $"{Context.User.Mention} Successfully deleted role `{role.Name}`" }; await role.DeleteAsync(); await ReplyAsync(embed : embed.Build()); } else { var embed = new KaguyaEmbedBuilder { Description = $"I could not find the specified role." }; embed.SetColor(EmbedColor.RED); await ReplyAsync(embed : embed.Build()); } }
/// <summary> /// Searches the specified <see cref="SearchProvider" /> for the provided <see cref="query" />. /// This method also adds the song to the guild's player queue and will even join the user's voice /// channel automatically. /// </summary> /// <param name="context"></param> /// <param name="query">The song to search for, user input.</param> /// <param name="playFirst"></param> /// <param name="provider"></param> /// <returns></returns> public async Task <ReactionCallbackData> SearchAndPlayAsync(ShardedCommandContext context, string query, bool playFirst = false, SearchProvider provider = SearchProvider.YOU_TUBE) { User user = await DatabaseQueries.GetOrCreateUserAsync(context.User.Id); Server server = await DatabaseQueries.GetOrCreateServerAsync(context.Guild.Id); LavaNode node = ConfigProperties.LavaNode; SocketVoiceChannel curVc = (context.User as SocketGuildUser).VoiceChannel; await ConsoleLogger.LogAsync($"Found node and voice channel for guild {context.Guild.Id}.", LogLvl.TRACE); if (curVc == null) { await context.Channel.SendMessageAsync($"{context.User.Mention} You must be in a voice " + "channel to use this command."); await ConsoleLogger.LogAsync("User was not in voice channel, cancelling music search operation.", LogLvl.TRACE); return(null); } SearchResponse result = provider switch { SearchProvider.YOU_TUBE => await node.SearchYouTubeAsync(query), SearchProvider.SOUNDCLOUD => await node.SearchSoundCloudAsync(query), _ => await node.SearchAsync(query) }; if (provider == SearchProvider.TWITCH) { const string PROVIDER_URL = "www.twitch.tv"; string errorString = "Your search returned no results. Ensure you are only " + "typing the name of the streamer who you want to watch or a direct link to their stream.\n\n" + "Note: The streamer must be live for this feature to work."; if (!query.ToLower().Contains(PROVIDER_URL)) { result = await node.SearchAsync($"https://{PROVIDER_URL}/{query}"); if (result.Tracks.Count == 0) { await context.Channel.SendBasicErrorEmbedAsync(errorString); await ConsoleLogger.LogAsync($"No livestream found for search {query} in guild {context.Guild.Id}.", LogLvl.TRACE); return(null); } } else { if ((await node.SearchAsync($"https://{PROVIDER_URL}/{query.Split('\\').Last()}")).Tracks.Count == 0 && (await node.SearchAsync(query)).Tracks.Count == 0) { await context.Channel.SendBasicErrorEmbedAsync(errorString); await ConsoleLogger.LogAsync($"No livestream found for search {query} in guild {context.Guild.Id}.", LogLvl.TRACE); return(null); } } } var tracks = new List <LavaTrack>(); if (user.IsPremium || server.IsPremium) { if (result.Tracks.Any()) { tracks.AddRange(result.Tracks); } } else { // Limit track duration to 10 minutes for non-premium servers/users. if (result.Tracks.Any()) { tracks.AddRange(result.Tracks.Where(x => x.Duration.TotalMinutes < 10).ToList()); } } if (!tracks.Any()) { string suppString = user.IsPremium ? "" : "If you are " + $"not a [Kaguya Premium Subscriber]({ConfigProperties.KAGUYA_STORE_URL}), " + "you are only limited to playing songs less than `10 minutes` in duration."; await context.Channel.SendBasicErrorEmbedAsync($"Your requested search returned no results. {suppString}"); await ConsoleLogger.LogAsync("Search request returned no usable " + $"results in guild {Context.Guild.Id} for query {query}", LogLvl.TRACE); } var fields = new List <EmbedFieldBuilder>(); var callbacks = new List <(IEmote, Func <SocketCommandContext, SocketReaction, Task>)>(); Emoji[] emojiNums = GlobalProperties.EmojisOneThroughNine(); LavaPlayer player = node.HasPlayer(context.Guild) ? node.GetPlayer(context.Guild) : await node.JoinAsync(curVc); await ConsoleLogger.LogAsync($"Player found for guild {context.Guild.Id}. Connected to voice channel.", LogLvl.TRACE); #region If the track is a livestream: if (tracks.Any(x => x.IsStream)) { LavaTrack trackSel = tracks.First(x => x.IsStream); // Gathers the first stream from the collection. string twitchName = (await ConfigProperties.TwitchApi.V5.Users.GetUserByNameAsync(trackSel.Author)).Matches[0].DisplayName; string playString = player.PlayerState == PlayerState.Playing ? $"Queued stream into position {player.Queue.Count}." : $"Now playing `{twitchName}`'s stream."; if (player.PlayerState == PlayerState.Playing) { try { player.Queue.Enqueue(trackSel); await ConsoleLogger.LogAsync($"Enqueued livestream {trackSel.Title} in guild {context.Guild.Id}", LogLvl.TRACE); } catch (Exception e) { await ConsoleLogger.LogAsync("An exception was thrown when trying to enqueue the livestream " + $"{trackSel.Title} in guild {Context.Guild.Id}.\n" + $"Exception Message: {e.Message}\n" + $"Stack Trace: {e.StackTrace}", LogLvl.WARN); } } else { try { await player.PlayAsync(trackSel); await ConsoleLogger.LogAsync($"Playing livestream {trackSel.Title} in guild {context.Guild.Id}", LogLvl.TRACE); } catch (Exception e) { await ConsoleLogger.LogAsync("An exception was thrown when trying to play track " + $"{trackSel.Title} in guild {Context.Guild.Id}.\n" + $"Exception Message: {e.Message}\n" + $"Stack Trace: {e.StackTrace}", LogLvl.WARN); } } var field = new EmbedFieldBuilder { Name = $"`{twitchName}`'s Stream", Value = $"{playString}\n" // We get rid of backticks for formatting. }; var embed = new KaguyaEmbedBuilder { Fields = new List <EmbedFieldBuilder> { field } }; await context.Channel.SendEmbedAsync(embed); return(null); } #endregion #region If we have chosen to only play the default track (via $play). if (playFirst && tracks.Any()) { LavaTrack trackSel = tracks[0]; var field = new EmbedFieldBuilder { Name = "Track #1.", Value = $"Title: `{trackSel.Title.Replace("`", "")}`\n" + // We get rid of backticks for formatting. $"Duration: `{trackSel.Duration.Humanize(minUnit: TimeUnit.Second, maxUnit: TimeUnit.Hour, precision: 3)}`\n" + $"Uploader: `{trackSel.Author}`" }; string playString = player.PlayerState == PlayerState.Playing && !player.Track.IsStream ? $"Queued track #1 into position {player.Queue.Count + 1}." : "Now playing track #1."; if (player.PlayerState == PlayerState.Playing) { if (player.Queue.Count() == 50 && !server.IsPremium) { await ConsoleLogger.LogAsync($"Queue is full in {context.Guild.Id}, sending error.", LogLvl.TRACE); await SendBasicErrorEmbedAsync("Your queue is full! `50 songs` is the maximum " + $"for non [Kaguya Premium]({ConfigProperties.KAGUYA_STORE_URL}) " + "servers."); } else { player.Queue.Enqueue(trackSel); await ConsoleLogger.LogAsync($"Enqueued track {trackSel.Title} in guild {context.Guild.Id}.", LogLvl.TRACE); if (player.Track.IsStream) { await player.SkipAsync(); await ConsoleLogger.LogAsync($"Skipped livestream to play incoming track in guild {context.Guild.Id}.", LogLvl.TRACE); } } } else { try { await player.PlayAsync(trackSel); await ConsoleLogger.LogAsync($"Playing track {trackSel.Title} in guild {context.Guild.Id}", LogLvl.TRACE); } catch (Exception e) { await ConsoleLogger.LogAsync("An exception was thrown when trying to play track " + $"{trackSel.Title} in guild {Context.Guild.Id}.\n" + $"Exception Message: {e.Message}\n" + $"Stack Trace: {e.StackTrace}", LogLvl.WARN); } } if (player.Volume == 0 && player.PlayerState == PlayerState.Playing) { await player.UpdateVolumeAsync(75); // Sets the volume back to default if it is muted. await ConsoleLogger.LogAsync($"Automatically set player volume to 75 in guild {context.Guild.Id}.", LogLvl.TRACE); } var embed = new KaguyaEmbedBuilder { Title = $"Kaguya Music {Centvrio.Emoji.Music.Notes}", Description = playString, ThumbnailUrl = await trackSel.FetchArtworkAsync(), Fields = new List <EmbedFieldBuilder> { field } }; await SendEmbedAsync(embed, context); return(null); } #endregion int h = tracks.Count; for (int i = 0; i < (h < 7 ? h : 7); i++) { int i1 = i; LavaTrack trackSel = tracks[i]; var field = new EmbedFieldBuilder { Name = $"Track {i1 + 1}.", Value = $"Title: `{trackSel.Title.Replace("`", "")}`\n" + // We get rid of backticks for formatting. $"Duration: `{trackSel.Duration.Humanize(minUnit: TimeUnit.Second, maxUnit: TimeUnit.Hour, precision: 3)}`\n" + $"Uploader: `{trackSel.Author}`" }; fields.Add(field); callbacks.Add((emojiNums[i], async(c, r) => { string playString = player.PlayerState == PlayerState.Playing && !player.Track.IsStream ? $"Queued track #{i1 + 1} into position {player.Queue.Count + 1}." : $"Now playing track #{i1 + 1}."; if (player.PlayerState == PlayerState.Playing) { if (player.Queue.Count() == 50 && !server.IsPremium) { await ConsoleLogger.LogAsync($"Queue was full in guild {context.Guild.Id}. Sending error message.", LogLvl.TRACE); await SendBasicErrorEmbedAsync("Your queue is full! `50 songs` is the maximum " + $"for non [Kaguya Premium]({ConfigProperties.KAGUYA_STORE_URL}) " + "servers."); return; } player.Queue.Enqueue(trackSel); await ConsoleLogger.LogAsync($"Enqueued track {trackSel} in guild {context.Guild.Id}", LogLvl.TRACE); if (player.Track.IsStream) { await player.SkipAsync(); await ConsoleLogger.LogAsync("Automatically skipped livestream to play" + $" incoming track in guild {context.Guild.Id}", LogLvl.TRACE); } } else { try { await player.PlayAsync(trackSel); await ConsoleLogger.LogAsync($"Playing track {trackSel.Title} in guild {context.Guild.Id}", LogLvl.TRACE); } catch (Exception e) { await ConsoleLogger.LogAsync("An exception was thrown when trying to play track " + $"{trackSel.Title} in guild {Context.Guild.Id}.\n" + $"Exception Message: {e.Message}\n" + $"Stack Trace: {e.StackTrace}", LogLvl.WARN); } } if (player.Volume == 0 && player.PlayerState == PlayerState.Playing) { await player.UpdateVolumeAsync(75); // Sets the volume back to default if it is muted. await ConsoleLogger.LogAsync($"Automatically set volume to 75 in guild {context.Guild.Id}", LogLvl.TRACE); } var embed = new KaguyaEmbedBuilder { Title = $"Kaguya Music {Centvrio.Emoji.Music.Notes}", Description = playString, ThumbnailUrl = await trackSel.FetchArtworkAsync(), Fields = new List <EmbedFieldBuilder> { field } }; await SendEmbedAsync(embed); } )); } callbacks.Add((GlobalProperties.NoEntryEmoji(), async(c, r) => { await c.Message.DeleteAsync(); await r.Message.Value.DeleteAsync(); })); string s = tracks.Count == 1 ? "" : "s"; var songDisplayEmbed = new KaguyaEmbedBuilder { Title = "Kaguya Music Search Results", Description = $" I found {tracks.Count} track{s} from {provider}, " + $"{(tracks.Count > 5 ? "but here are the top 5" : "here they are")}. " + "Please select a track to play.", Fields = fields }; var data = new ReactionCallbackData("", songDisplayEmbed.Build(), false, false, TimeSpan.FromSeconds(60)); data.SetCallbacks(callbacks); return(data); } }